How to structure authenticated routes when using Devise? - ruby-on-rails

In my question How to have root view when user is not logged in rails? max answered that we can use authenticated to make routes available only when someone is authenticated. I am having a probem that how can I structure this:
Rails.application.routes.draw do
devise_for :users
authenticated :user do
# when authenticated allow all action on student
resources :subjects do
resources :students
end
end
# when not only allow read on student
resources :subjects do
resources :students, only: [:get]
end
root "home#index"
end
The problem is I don't want to allow any unauthenticated action on :subjects how to stop that?

If you want to limit access to subjects you should do it on the controller layer - not in the routes. Using before_action :authenticate_user! will give a 401 Unauthorized response and redirect to the sign in.
class ApplicationController
# secure by default
before_action :authenticate_user!, unless: :devise_controller?
end
class SubjectsController < ApplicationController
# whitelist actions that should not require authentication
skip_before_action :authenticate_user!, only: [:show, :index]
# ...
end
Rails.application.routes.draw do
devise_for :users
resources :subjects do
resources :students
end
root "home#index"
end
Using the authenticated and unauthenticated route helpers are useful when you want the have different responses for the same route for authenticated and unauthenticated users but is not how you should structure your application.
If you simply use authenticated in your routes unauthenticated users will get a 404 Not Found response instead of being prompted to sign in. Which is not helpful.
Also resources :students, only: [:get] does not generate any routes at all. The onlyoption is for limiting the actions (show, index, edit, update ...) not the HTTP method. Use rake routes to see the routes in your app.

Here is the simple way to structure authenticated and unauthenticated routes.
In app/controllers/application_controller.rb, add
"before_action :authenticate_user!".
My app/controllers/application_controller.rb file:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :authenticate_user!
end
My config/routes.rb:
Rails.application.routes.draw do
devise_for :users
root "home#index"
devise_for :users, controllers: {
:sessions => "users/sessions",
:registrations => "users/registrations" }
authenticated :user do
resources :students
end
unauthenticated :user do
#Some route
end
end

Related

Subdomains in routes.rb

as I am trying to create web with subodmain for every user i coded as below:
In below controller i am checking if subdomain from sign_in devise page belons to user in User.rb table. If not i am logging out.
class ApplicationController < ActionController::Base
before_action :authenticate_user!
before_action :check_domain
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:slug])
devise_parameter_sanitizer.permit(:account_update, keys: [:slug])
end
def check_domain
unless current_user.nil?
#domain = User.find_by(email: current_user.email)
if #domain.slug != request.subdomain
sign_out_and_redirect(current_user)
flash.alert = "User not found."
end
end
end
end
At this moment I have routes.rb as below:
Rails.application.routes.draw do
resources :devise
resources :places
resources :people_items
resources :people, only: [:edit, :update]
resources :gift_items
resources :gifts, only: [:edit, :update]
resources :program_items
resources :story_items
devise_for :users
root to: 'pages#index'
get '/home', to: 'output#home'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
resources :pages do
member do
delete :delete_file
end
end
resources :stories, only: [:edit, :update]
resources :programs, only: [:edit, :update]
resources :galleries, only: [:edit, :update] do
member do
delete :delete_image
end
end
get 'informations/information'
resources :aboutus, only: [:edit, :update]
end
I modified one more settings in config/environments/development.rb
config.action_dispatch.tld_length = 1
Everything is working fine.
In tutorials i found out that for working subdomains properly I should modify routes.rb as below. But what is the purpose of modification file below? Do I really need to touch routes.rb? That's my question.
constraints subdomain: /.*/ do
resources :pages
end
Do I really need to modify this? Or what is purpose of this modification? Is that really necessary?
Thank you so much for your advices!
Constraints in routes.rb allow you to restrict when routes should or shouldn't be matched, returning a 404 in the event that they do not.
You can do this on a per-route basis for params like so:
get 'products/:category', to: "products#index", category: /(fruit|electronics|medicine)/, as: :products
This would allow requests to http://yourdomain.com/products/fruit but not to http://yourdomain.com/products/vehicles for example. Accessing any forbidden domains would provide you with a 404 response.
You can provide generic constraints to a bunch of different routes by enclosing them in a constraints block, like so:
constraints ->(req) { Site.exists?(subdomain: req.subdomain) } do
resources :articles, path: "blog", only: [:index, :show]
...
end
So here, we're checking that a Site record exists with the subdomain provided. If not, we return a 404.

rails - how to target nested controller for nested resource route

In my routes.rb I have
namespace :admin do
resources :clients do, only: [:index] do
resources :products, only: [:index, :new, :create]
end
resources :products, only: [:index]
end
Notice that I have two lines for resources :products. One is nested within resources :clients and the other as a top-level resource on :admin; each of these two has a different purpose in the application.
rake routes gives me:
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/products#index
POST /admin/clients/:client_id/products(.:format) admin/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/products#new
admin_products GET /admin/products(.:format) admin/products#index
I have a admin_client_products for the nested product#index resource. I also have admin_products for the top-level product#index resource. However, they point to the same controller action: admin/product#index.
Question: At this point, I need rails to deduce that these are two different actions. Using rails conventions, is there a way to tell rails that these two resources should have different controller actions i.e. one that should hit admin/products#index and the other should hit admin/clients/products#index?
The nested route should hit this:
class Admin::Clients::ProductsController < Admin::BaseController
def index; end
end
The top-level route should hit this:
class Admin::ProductsController < Admin::BaseController
def index; end
end
Definitely you can!
Here you need to customize your resourceful route by explicitly specifying a controller to use for the resource. The :controller option will let you do that.
So, in your case, specifying the controller as clients/products for the admin_clients_products resource would work in your desired way.
namespace :admin do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end # ------------------------------
resources :products, only: [:index]
end
rails routes will now give you what you want:
admin_client_products GET /admin/clients/:client_id/products(.:format) admin/clients/products#index
POST /admin/clients/:client_id/products(.:format) admin/clients/products#create
new_admin_client_product GET /admin/clients/:client_id/products/new(.:format) admin/clients/products#new
admin_clients GET /admin/clients(.:format) admin/clients#index
admin_products GET /admin/products(.:format) admin/products#index
=========================
Extra bits:
If you want to omit the /admin portion from the url (I mean, if your application's routing design permits to), then you could use: scope module: 'admin' do...end like the following:
scope module: 'admin' do
resources :clients, only: [:index] do
resources :products, only: [:index, :new, :create], controller: 'clients/products'
end
resources :products, only: [:index]
end
and suddenly your routes will start looking awesome :)
client_products GET /clients/:client_id/products(.:format) admin/clients/products#index
POST /clients/:client_id/products(.:format) admin/clients/products#create
new_client_product GET /clients/:client_id/products/new(.:format) admin/clients/products#new
clients GET /clients(.:format) admin/clients#index
products GET /products(.:format) admin/products#index

Routing for sub ressources in rails

So this is in routes file :
resources :users do
resources :lamps
end
I want to be able to display a user's "lamps" with something like :
http://localhost:3000/users/2/lamps
and show all the existing lamps whichever user owns it using :
http://localhost:3000/lamps
both are very different as the first one is more of a management view and the later is more what a user browsing would see.
The thing is they both go to the index action of the lamp_controller
How can I manage this in a clean way without having to use if statements in my action/view?
You can use the module option to route the nested routes to a namespaced controller:
Rails.application.routes.draw do
resources :users do
resources :lamps, only: [:index], module: 'users'
end
resources :lamps, only: [:index]
end
Another way to do this for a group of resources is:
Rails.application.routes.draw do
resources :users do
scope module: 'users' do
resources :lamps, only: [:index]
resources :chairs
resources :rugs
end
end
resources :lamps, only: [:index]
end
This would let you handle the different contexts in separate controllers:
# app/controllers/lamps_controller.rb
class LampsController < ApplicationController
# GET /lamps
def index
#lamps = Lamp.all
end
end
# app/controllers/users/lamps_controller.rb
class Users::LampsController < ApplicationController
before_action :set_user
# GET /users/:user_id/lamps
def index
#lamps = #user.lamps
# renders /views/users/lamps/index.html.erb
end
private
def set_user
#user = User.find(params[:user_id])
end
end

Devise root - disable must sign-in

Whenever I access my home page or root, I'm redirected to Devise's /users/sign_in/. I've played with the routes.rb file a fair amount and I'm unable to figure out why Devise kicks in for the pages controller. The Pages controller is effectively empty.
routes.rb
Rails.application.routes.draw do
resources :pages
root 'pages#index'
resources :events do
member do
get :create_attendee
delete :destroy_attendee
end
end
devise_for :users
devise_scope :user do
authenticated :user do
root 'events#index', as: :authenticated_root
end
unauthenticated do
root 'pages#index', as: :unauthenticated_root
end
end
pages_controller.rb
class PagesController < ApplicationController
def index
end
end
Add before_filter :authenticate_user!, except: [:index] to your pages_controller.rb and it'll skip the auth.
The authenticate_user! action is triggered for all controller method calls except index, i.e, for create, update... That way, you don't to go through log in page.

RubyOnRails Authorization : I have changed my authorization method and after making some changes to routes.rb I am now getting NO ROUTE errors

IN the first few lines is where i made the changes. Is there a way I can post the project so i can give the most information possible?
also i am not using the users controller/file at all. Does authorization require me to use the USer scaffold, because i am using the Newuser scaffold.
Cms::Application.routes.draw do
root :to => "home#merchant"
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
resources :newusers
resources :users
resources :maintenances
resources :leases
resources :sales
resources :saleterminals
resources :salesreps
resources :terminaltypes
resources :processings
resources :manufacturers
resources :promotions
get "community/index"
resources :currents
resources :merchants
get "home/index"
get "home/about"
get "home/contact"
get "home/processing101"
get "home/terminaloptions"
get "home/weekend"
get "home/conditionsofuse"
get "home/privacypolicy"
get "home/announcements"
get "home/support"
get "home/sitemap"
get "home/search"
post "home/search"
end
Routes
A piece of advice - you can DRY up your routes by declaring multiple resources sequentially like so:
#config/routes.rb
Cms::Application.routes.draw do
root to: "home#show", id: "merchant"
controller :sessions do
get 'login' => :new
post 'login' => :create
delete 'logout' => :destroy
end
resources :newusers, :users, :maintenances, :leases, :sales, :saleterminals, :salesreps, :terminaltypes, :processings, :manufacturers, :promotions, :currents, :merchants
resources :community, only: :index
resources :home, only: [:index, :show] do
collection do
post :search
end
end
end
Something to note is I moved your entire home actions to send to the show action. Reason being if you want to show individual pages, you'll just need a show action like so:
#app/controllers/home_controller.rb
Class HomeController < ActiveRecord::Base
def show
render "home##{params[:id]}"
end
end
Authentication vs Authorization
Authentication is whether someone is a user or not
Authorization is whether that user has permission to do
certain things or not
If you're looking to authenticate the user, you may be better suited to using a gem called Devise, which handles the entire authentication process, from signup to sessions. There is a Railscast about this here
--
Fix
In reference to your issue, if you want to show projects information, why don't you just have a projects controller with the only actions as index and show:
#config/routes.rb
resources :projects, only: [:index, :show]
If you want to then allow users to upload or change projects, you can use an "admin" area of sorts, like this:
#config/routes.rb
namespace :admin do
resources :projects, except: :index #-> /admin/projects/new
end

Resources