I have a super simple question. I have a page that lists all the products in my app. I just want to make that page view-able by admin only. But products/new I want everyone to be able to see clearly.
schema.rb
create_table "users", :force => true do |t|
t.string "email"
t.string "password_hash"
t.string "password_salt"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "name"
t.boolean "admin", :default => false
end
products controller
class ProductsController < ApplicationController
before_filter :require_login
before_filter :current_user, only: [:create, :destory]
before_filter :correct_user, only: :destory
def index
#products = Product.all
end
def new
#product = Product.new
end
def create
#product = current_user.products.new(params[:product])
if #product.valid?
#product.save
render "show", :notice => "Sale created!"
else
render "new", :notice => "Somehting went wrong!"
end
end
Put in your controller
before_filter :authorize_admin, only: :index
and in application_controller.rb
def authorize_admin
redirect_to :back, status: 401 unless current_user.admin
#redirects to previous page
end
In your controller write
before_filter :admin_user
and create a def like this
private
def admin_user
redirect_to(root_path) unless current_user && current_user.admin?
end
What have you tried? It's not exactly a thought-provoking question, you have an boolean for admin, and you want to restrict an action to admin only, so just check current_user.admin.
before_filter :require_admin, only: :index
private
def require_admin
if !current_user.admin
if request.xhr?
head :unauthorized # for asynchronous/api requests, if you want.
else
render 'access/no_access' and return # or whatever.
end
end
end
in your ProductsController you can add a function that verify if the user is an admin or not and use filter for the view you want to protect like this :
class ProductsController < ApplicationController
before_filter :admin_user, only: :index # here you specify the action (for views) to protect
.
.
.
private
.
.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
i hope that help you
add correct_user method and admin_user method under private on your controller or create another method with following defination and add :only => :index on before_filter for admin.
before_filter :require_login
before_filter :correct_user
before_filter :admin_user, :only => :index
private
def correct_user
redirect_to(root_path) if current_user.nil? && !current_user.admin?
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
Check Railscasts Episode Super Simple Authentication.
Actually, Ryan Bates is a big fan of making authentication from scratch, he made many episode on this topics. Have a look on them and you will surely get some good ideas.
Related
While using Cancan, I'm not able to edit or delete Comments - Comments are related to Jobs.
Cancan is working fine for Jobs but for Comments the edit and delete are not shown. Is this because the Comments are shown in Jobs?
class Comment < ActiveRecord::Base
belongs_to :job
belongs_to :user
end
class Job < ActiveRecord::Base
belongs_to :jobcategory
has_many :comments, dependent: :destroy
end
ActiveRecord::Schema.define(version: 20150522132410) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "comments", force: true do |t|
t.text "content"
t.integer "job_id"
t.integer "user_id"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "comments", ["job_id"], name: "index_comments_on_job_id", using: :btree
add_index "comments", ["user_id"], name: "index_comments_on_user_id", using: :btree
user ||= User.new # guest user (not logged in)
if user.admin?
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard
can :manage, :all
else
can :read, :all
can [ :edit, :update, :destroy ], Comment do |comment|
comment.try(:user_id) == user.id
end
can [ :edit, :update, :destroy ], Job do |job|
job.user_id == user.id
end
can :create , Comment
can :create , Job
end
- if can? :update, #comment
= link_to "Edit", edit_job_comment_path(comment.job, comment)
- if can? :destroy, #comment
= link_to "Delete", [comment.job, comment], method: :delete, data: { confirm: "Are you sure?" }
class JobsController < ApplicationController
before_action :find_job, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!,except:[:index]
def show
#comments =Comment.where(job_id: #job)
end
UPDATED:
class CommentsController < ApplicationController
before_action :authenticate_user!
def show
end
def create
#job = Job.find(params[:job_id])
#comment = #job.comments.create(params[:comment].permit(:content))
#comment.user_id = current_user.id if current_user
#comment.save
if #comment.save
redirect_to job_path(#job)
else
render 'new'
end
end
def edit
#job = Job.find(params[:job_id])
#comment = Comment.find(params[:id])
authorize! :update, #comment
end
def update
#job = Job.find(params[:job_id])
#comment = #job.comments.find(params[:id])
if #comment.update(params[:comment].permit(:comment))
redirect_to job_path(#job)
else
render 'edit'
end
authorize! :update, #comment
end
def destroy
#job = Job.find(params[:job_id])
#comment = #job.comments.find(params[:id])
#comment.destroy
redirect_to job_path(#job)
authorize! :destroy, #comment
end
end
It seems to be your object is comment but you used #comment
I have an index page that hides/shows different Authors by using a boolean. The problem that I am having is that signed in users can still access hidden authors and their books through the URL.
How can I prevent current users from navigating to hidden Authors and their corresponding Books through the URL? Is there a way to redirect them back to Authors Page if author is hidden?
Currently, I have used the Controllers & a boolean value to help hide/show Authors or Books from signed in users. Can someone please point me in the right direction. Here is my Code.
MODELS
class Author < ActiveRecord::Base
attr_accessible :name, :photo
has_many :books
end
class Book < ActiveRecord::Base
attr_accessible :author_id, :title, :photo
belongs_to :author
end
CONTROLLERS
class AuthorsController < ApplicationController
before_filter :signed_in_user, only: [:index]
before_filter :admin_user, only: [:edit, :update, :destroy, :new, :show]
respond_to :html, :js
###Only displays unhidden authors to non admin users.
def index
if current_user.admin?
#authors = Author.all(:order => "created_at")
else
#authors = Author.where(:display => true).all(:order => "created_at")
end
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to (root_path), notice: "Please sign in."
end
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
class BooksController < ApplicationController
before_filter :signed_in_user, only: [:index]
before_filter :admin_user, only: [:edit, :update, :destroy, :new, :show]
before_filter :get_author
respond_to :html, :js
def get_author
#author = Author.find(params[:author_id])
end
def index
#books = #author.books
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to (root_path), notice: "Please sign in."
end
end
def admin_user
redirect_to(root_path) unless current_user.admin?
end
end
VIEWS
Authors index.html.erb
<% #authors.each do |author| %>
<%= link_to (image_tag author.photo(:medium)),
url_for(author_books_path(author)),
class: "img-rounded" %>
<% end %>
### How Can I prevent Users from accessing Hidden Author's Books (Index Page)
Books index.html.erb
<% #books.each do |book| %>
<%= image_tag book.photo(:medium) %>
<%= book.name %>
<% end %>
ROUTES
resources :authors do
resources :books
end
SCHEMA
create_table "authors", :force => true do |t|
t.string "name"
t.boolean "display", :default => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
end
create_table "books", :force => true do |t|
t.integer "author_id"
t.string "title"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
end
Use a proper authorization model, such as CanCan.
The key part (for CanCan) would be in authorizing based on user roles:
if user.role == admin
can :manage, :all
else
can :read, Author, display: true
end
There is a helpful RailsCast to step you through using CanCan for authorization.
Other options exist, such as Declarative Authorization, or you can roll your own.
Use a proper authorization model, such as CanCan.
Personally, I'm not sure I agree with this, at least given the scope of the issue you seem to be trying to solve. However, your comments in /app/views/books/index.html.erb seem to indicate you wanted to place some logic in your view file. Do NOT do this. Following proper MVC architecture, what you're attempting to do falls under the category of business logic. As such, the code that controls this should be in your controllers.
In your /app/controllers/book_controller.rb file, change the action for an Author's books page to redirect back to the author depending on the author's attributes. Something like this: (not sure what the exact path would be):
def index
# Redirect if author is set to hidden
if !#author.display
redirect_to author_path
else
#books = #author.books
end
end
in the Authors#show Controller, you can write for example --
Authors#Show
def show
#author = Author.find(params[:id])
redirect_to root_url unless #author.display
end
In this case, when a user visits any author's url, it will check to see if that author's display attribute is true or not, and redirect accordingly.
I am really hoping to get this resolved tonight so any help would be great.
I added this class method into my post.rb model
def self.without_review
where(review: false)
end
What I am trying to do is ONLY show all posts on the site where review=false. If review=true, I want to manually approve them before they're displayed. Right now, all posts are getting displayed whether the review is true or false.
Here's my post controller
class PostsController < ApplicationController
before_filter :signed_in_user
before_filter :load_post, only: :destroy
def create
#post = current_user.posts.build(params[:post])
if #post.save
flash[:success] = "Shared!"
redirect_to root_path
else
#feed_items = []
render 'static_pages/home'
end
end
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
def load_post
#post = current_user.admin? ? Post.find(params[:id]) : current_user.posts.find(params[:id])
end
end
and here's the full post.rb model
class Post < ActiveRecord::Base
attr_accessible :content, :review
belongs_to :user
validates :user_id, presence: true
validates :content, presence: true
default_scope order: 'posts.created_at DESC'
def self.without_review
where(review: false)
end
end
The schema of the posts table to show how "review" is set up (last row)
create_table "posts", :force => true do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.boolean "review", :default => false
The Static Pages Controller
class StaticPagesController < ApplicationController
def home
if signed_in?
#post = current_user.posts.build
#feed_items = current_user.feed.paginate(page: params[:page])
end
end
def post1
if signed_in?
#post = current_user.posts.build
end
end
end
UsersController (def show)
def show
#user = User.find(params[:id])
#posts = #user.posts.paginate page: params[:page], :per_page => 15
end
You didn't show the controller/action where the actual listing of posts is generated, but I guess you have to replace Post.all with Post.without_review there.
I am trying to get admins to be able to delete postings by users.
This is the code I'm using inside post.html.erb
<% if current_user.admin? %>
<%= link_to "delete", post, method: :delete %>
<% end %>
This is what I have inside the controller
class PostsController < ApplicationController
before_filter :signed_in_user
before_filter :admin_user, only: :destroy
before_filter :correct_user, only: :destroy
def destroy
#post.destroy
redirect_to root_path
end
private
def correct_user
#post = current_user.posts.find_by_id(params[:id])
redirect_to root_path if #post.nil?
end
I'm able to get it working for correct_user, but not for admin. I get this error message
undefined local variable or method `admin_user' for #<PostsController:0x5fda230>
I tried to def admin_user as user.id ==1
private
def admin_user?
current_user && current_user.id == 1
end
But I'm experiencing the same error message.
before_filter :admin_user, only: :destroy
class ApplicationController
def admin_user?
if current_user && current_user.id == 1
return true
else
redirect_to root_url, :error => "Admin only"
return false
end
end
end
Since admin_user? is called from any controller so put it in application controller
class ApplicationController < ActionController::Base
def admin_user?
unless current_user && current_user.admin?
redirect_to root_url
return false
end
end
end
class PostsController < ApplicationController
before_filter :signed_in_user
# before_filter :admin_user?, only: :destroy
before_filter :load_post, :only: :destroy
def destroy
#post.destroy
redirect_to root_url
end
private
def load_post
#post = current_user.admin? ? Post.find(params[:id]) : current_user.posts.find(params[:id])
end
end
UPD:
As I understood from comments you want users to be able to destroy their own posts and admin to be able to destroy any posts. In this case you don't have to check is user admin in before_filter, e.g. remove this line:
before_filter :admin_user?, only: :destroy
I want to change the
def url_after_create
'/'
end
of UsersController in Clearance. If I do this:
class UsersController < Clearance::UsersController
protected
def url_after_create
'/dashboard'
end
end
When I'm trying to sign up a new user, which works perfectly when not overriding, I get the following: The action 'index' could not be found for UsersController -- The action (post) is to '/users' and it seems that since the index action is not defined it fails. What should I do?
EDIT: Added code of Clearance::UsersController
class Clearance::UsersController < ApplicationController
unloadable
skip_before_filter :authorize, :only => [:new, :create]
before_filter :redirect_to_root, :only => [:new, :create], :if => :signed_in?
def new
#user = ::User.new(params[:user])
render :template => 'users/new'
end
def create
#user = ::User.new(params[:user])
if #user.save
sign_in(#user)
redirect_back_or(url_after_create)
else
flash_failure_after_create
render :template => 'users/new'
end
end
private
def flash_failure_after_create
flash.now[:notice] = translate(:bad_email_or_password,
:scope => [:clearance, :controllers, :passwords],
:default => "Must be a valid email address. Password can't be blank.")
end
def url_after_create
'/'
end
end
This line match ':controller(/:action(/:id(.:format)))' located in routes was causing the trouble. Commenting that solved it.