How do I make it so the user can only delete/edit what he/she posted? and not all posts? My current songs_controller only has authorization which allows users to edit, destroy, update once they're signed in. The problem is, all users can edit all posts. That said, how can I allow just the user to edit his/her own posts? and not have access to that functionality with others posts?
songs_controller.rb
class SongsController < ApplicationController
before_action :set_song, only: [:show, :edit, :update, :destroy]
before_filter :authorize, only: [:create ,:edit, :update, :destroy]
# GET /Songs
# GET /Songs.json
def index
#songs = Song.all
end
# GET /Songs/1
# GET /Songs/1.json
def show
end
# GET /Songs/new
def new
#song = Song.new
end
# GET /Songs/1/edit
def edit
end
# POST /Songs
# POST /Songs.json
def create
#song = Song.new(song_params)
respond_to do |format|
if #song.save
format.html { redirect_to #song, notice: 'Song was successfully created.' }
format.json { render action: 'show', status: :created, location: #song }
else
format.html { render action: 'new' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /Songs/1
# PATCH/PUT /Songs/1.json
def update
respond_to do |format|
if #song.update(Song_params)
format.html { redirect_to #song, notice: 'Song was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #song.errors, status: :unprocessable_entity }
end
end
end
# Song /Songs/1
# Song /Songs/1.json
def destroy
#song.destroy
respond_to do |format|
format.html { redirect_to songs_url }
format.json { head :no_content }
end
end
private
def set_song
#song = Song.find(params[:id])
end
def song_params
params.require(:song).permit(:title, :artist, :bio, :track)
end
end
You most likely have some sort of User model that users are able to authenticate to. Try adding a has_many :songs association on your User model. Add a foreign key called user_id on the Song model along with a belongs_to :user. Migrate. Pull the user's id from the current_user helper and do the following:
#user = User.find(current_user.id)
#songs = #user.songs #will give you only the songs the user added
Here is a good guide to reference:
http://guides.rubyonrails.org/association_basics.html
if you only want the user to see the posts they have made then what jbearden suggests would work well although it doesn't prevent someone from accessing the delete or update etc from the address line manually which is bad.
if you want the user to see all songs but only have the option to delete etc on their own songs then you probably want to have the view only show the edit and delete links for songs owned by the user (which would use jbearden's idea of makign an association for the songs to users) - that helps with the UI but still doesn't solve your authentication problem.
the authentication can be handled by using the cancan gem (see railscasts on this - Ryan is the author of the gem). cancan takes some getting used to for configuring it but works quite well for controlling whether a given user can view, edit, delete etc objects (like your songs).
good luck!
Related
I would like users to be able to create/update my "Person" resource, including overwriting each other. Currently I'm able to capture the user who created the initial "Person" but i can't figure out how to capture and display the user that updated the resource.
For example if user 1 creates an item, then user 2 updates this item, I would like to display that this item was most recently edited by user 2.
Here's my controller, any help would be much appreciated thanks!
class PeopleController < ApplicationController
before_action :set_person, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /people
# GET /people.json
def index
#people = Person.all
end
# GET /people/1
# GET /people/1.json
def show
end
# GET /people/new
def new
#person = current_user.person.build
end
# GET /people/1/edit
def edit
end
# POST /people
# POST /people.json
def create
#person = current_user.person.build(person_params)
respond_to do |format|
if #person.save
format.html { redirect_to #person, notice: 'Person was successfully created.' }
format.json { render action: 'show', status: :created, location: #person }
else
format.html { render action: 'new' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /people/1
# PATCH/PUT /people/1.json
def update
respond_to do |format|
if #person.update(person_params)
format.html { redirect_to #person, notice: 'Person was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #person.errors, status: :unprocessable_entity }
end
end
end
# DELETE /people/1
# DELETE /people/1.json
def destroy
#person.destroy
respond_to do |format|
format.html { redirect_to people_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_person
#person = Person.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def person_params
params.require(:person).permit(:name, :twitter, :facebook, :instagram, :vine)
end
end
Simple way for doing it is to maintain the a column called updated_by and store the current user when its updated as #Andrey mentioned in previous comment.
But if your looking for a more extensive for tracking you can use auditable gem
You can check this out :
https://github.com/harley/auditable
Create updated_by column in posts table, and each time user updates the post, update the column updated_by by the value of current_user.
So I had my app set up with ids like so:
resources :studios do
resources :bookings
end
This gave me the route to the index (which later I'm going to use json for to get calendars for each studio.
studio_bookings GET /studios/:studio_id/bookings(.:format) bookings#index
This is good, but I wanted to get rid of the ID and use a permalink instead, just for a friendlier URL.
Change to:
namespace :studio, :path =>'/:permalink' do
resources :bookings
end
Now I'm getting
studio_bookings GET /:permalink/bookings(.:format) studio/bookings#index
Great! this is how I want my url to look, however, now the :id isn't anywhere in the route so... I get
Couldn't find Booking without an ID
It isn't even being passed. Is there a way to pass the :id in with the url without it being actually USED in the url? Otherwise, do I change the primary key from :id to :permalink in order to fix this?
I tried changing my controller from
#studio = Studio.find(params[:id])
to
#studio = Studio.find(params[:permalink])
but that gives me
Couldn't find Booking with 'id'=40frost
Which tells me what I'm doing isn't really meant to be done? It's trying to put the permalink as the id, so even though I'm telling rails to look for the permalink, it's still seemingly looking it up as an ID.
Hopefully my problem is clear: essentially - how can I pass the id so it knows which studio without displaying it in the URL. If there's some controller magic I can do instead that would be convenient.
Here's my controller for good measure
class Studio::BookingsController < ApplicationController
before_action :set_booking, only: [:show, :edit, :update, :destroy]
# GET /bookings
# GET /bookings.json
def index
#studio = Studio.find(params[:permalink])
#bookings = Booking.where("studio_id => '#studio.id'")
end
# GET /bookings/1
# GET /bookings/1.json
def show
end
# GET /bookings/new
def new
#booking = Booking.new
end
# GET /bookings/1/edit
def edit
end
# POST /bookings
# POST /bookings.json
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to #booking, notice: 'Booking was successfully created.' }
format.json { render action: 'show', status: :created, location: #booking }
else
format.html { render action: 'new' }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /bookings/1
# PATCH/PUT /bookings/1.json
def update
respond_to do |format|
if #booking.update(booking_params)
format.html { redirect_to #booking, notice: 'Booking was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
# DELETE /bookings/1
# DELETE /bookings/1.json
def destroy
#booking.destroy
respond_to do |format|
format.html { redirect_to bookings_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_booking
#booking = Booking.find(params[:permalink])
end
# Never trust parameters from the scary internet, only allow the white list through.
def booking_params
params.require(:booking).permit(:start_time, :end_time, :studio_id, :engineer_id, :title, :allDay)
end
end
You could just do
self.primary_key = 'permalink'
in your Studio model, or you could do
def index
#studio = Studio.find_by permalink: params[:permalink]
#bookings = Booking.where(studio_id: #studio.id)
end
depends if you just want to locally change the behavior or adress the Studio model by permalink always.
Hope that helps!
Can I disable the "Edit" and "Destory" in the Rails ?for example, if I want to disable the "Edit" for everyone,what show I do in the test_controller.rb ? or anything else?
I am new to Rails, thanks in advance!
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update,:destroy ]
# GET /books
# GET /books.json
def index
#books = Book.all
end
# GET /books/1
# GET /books/1.json
def show
end
# GET /books/new
def new
#book = Book.new
end
# GET /books/1/edit
def edit
end
# POST /books
# POST /books.json
def create
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render :show, status: :created, location: #book }
else
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /books/1
# PATCH/PUT /books/1.json
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { render :show, status: :ok, location: #book }
else
format.html { render :edit }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# DELETE /books/1
# DELETE /books/1.json
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
#book = Book.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:name, :author, :price)
end
end
`Rails.application.routes.draw do
resources :books
root :to => "home#index"
get 'home/index'
end`
You can restrict the restful routes to make edit and destroy actions inaccessible.
In your routes.rb,
resources :books, except: [:edit, :destroy]
See: http://guides.rubyonrails.org/routing.html#restricting-the-routes-created
EDIT
If you want to keep to the RESTful routes (so that you don't have to modify code in your views), you can use before_action in controller to redirect users.
before_action :redirect_user, only: [:edit,:destroy]
def redirect_user
redirect_to root_path
end
This approach is generally used when you want to restrict access to certain actions based on some condition.
For example, if you want only admins to edit and remove books, you can have condition inside redirect_user that checks if current user is admin or not and redirects non-admin users.
You should look in to the cancancan gem.
https://github.com/CanCanCommunity/cancancan
It's an authorization library for Ruby on Rails which restricts what resources a given user is allowed to access. So you can create an admin class, and only allow administrators to edit and destroy. Its pretty simple to use and works well with devise.
Okay guys, I am fairly new to rails. I have successfully created a rails app that stores login information for you. I used devise for the user management and installed cancan but no idea how to use it.
Anyways,
Right now, not matter if you are logged in or not, the site shows you all the "post" or "entrees" that have been entered by any user. I need a way to restrict this to only show post that were made by the user that is currently logged in.
I have found through research that I need do something here:
class FtpLoginsController < ApplicationController
before_action :set_ftp_login, only: [:show, :edit, :update, :destroy]
# GET /ftp_logins
# GET /ftp_logins.json
def index
#ftp_logins = FtpLogin.all
end
# GET /ftp_logins/1
# GET /ftp_logins/1.json
def show
end
# GET /ftp_logins/new
def new
#ftp_login = FtpLogin.new
end
# GET /ftp_logins/1/edit
def edit
end
# POST /ftp_logins
# POST /ftp_logins.json
def create
#ftp_login = FtpLogin.new(ftp_login_params)
respond_to do |format|
if #ftp_login.save
format.html { redirect_to #ftp_login, notice: 'Ftp login was successfully created.' }
format.json { render action: 'show', status: :created, location: #ftp_login }
else
format.html { render action: 'new' }
format.json { render json: #ftp_login.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /ftp_logins/1
# PATCH/PUT /ftp_logins/1.json
def update
respond_to do |format|
if #ftp_login.update(ftp_login_params)
format.html { redirect_to #ftp_login, notice: 'Ftp login was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #ftp_login.errors, status: :unprocessable_entity }
end
end
end
# DELETE /ftp_logins/1
# DELETE /ftp_logins/1.json
def destroy
#ftp_login.destroy
respond_to do |format|
format.html { redirect_to ftp_logins_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_ftp_login
#ftp_login = FtpLogin.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def ftp_login_params
params.require(:ftp_login).permit(:client_name, :website_name, :ftp_login, :ftp_password, :notes)
end
end
If someone could please send me in the right direction here that would be fantastic!
Thanks in advance.
for that you first have to make ensure that user is logged in before it goes to your action . so you need a before filter for that . authenticate_user! is a method given by devise . so if a user is not logged in he will redirect to the sign in page automatically
before_filter :authenticate_user!, only: [:posts, :entries]
for collecting the posts of a specific user
#posts = current_user.posts
or if it is coming for show a particular post you can do
#post = current_user.posts.where(id: params[:id])
You can use before_filter :authenticate_user!, only: [:posts, :entries] to restrict only the logged in user to view these actions.
To restrict users to view only posts created by them, you can create your own filter like
def check_user
redircet_to :back, notice: "Restricted area!" if current_user.posts.include?(#post)
end
I'm pretty new to rails and I'm just now developing my first rails app, so this might be a dumb question to some. I would like to let the current_user see only their own orders if they are not an admin. I was able to set only admins can see all orders, but I'm having a hard time enabling current user to see, list and delete only their own orders. My app has a :orders model that belongs_to :users and a :users model with has_many :orders.
This is how my orders_controller.rb look like:
class OrdersController < ApplicationController
before_action :set_order, only: [:show, :edit, :update, :destroy]
# GET /orders
# GET /orders.json
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#orders = Order.all
end
# GET /orders/1
# GET /orders/1.json
def show
end
# GET /orders/new
def new
#order = Order.new
end
# GET /orders/1/edit
def edit
end
# POST /orders
# POST /orders.json
def create
#order = Order.new(order_params)
respond_to do |format|
if #order.save
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render action: 'show', status: :created, location: #order }
else
format.html { render action: 'new' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /orders/1
# PATCH/PUT /orders/1.json
def update
respond_to do |format|
if #order.update(order_params)
format.html { redirect_to #order, notice: 'Order was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
# DELETE /orders/1
# DELETE /orders/1.json
def destroy
#order.destroy
respond_to do |format|
format.html { redirect_to orders_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_order
#order = Order.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def order_params
params.require(:order).permit(:user_id, :drop_address)
end
end
My question is how do I allow only the current user to list and see all orders made by them only?
Thanks
There is gem named cancan for you.
Please read wiki page.
Need more help? let me know :)
define ability
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
if user.admin?
can :manage, :all
else
can :read, Order, :user_id => user.id
end
end
end
from controller query by accessible_by
#orders = Order.accessible_by(current_ability)
you have to do it on two levels. In index you have to fetch orders for the current users so users can only see his orders. the second level is you make sure that the user may enter an order url that doesnt belong to him, so check for that in the other actions(edit,update,delete,show).
Or you can use declarative authorization gem. it is very helpful https://github.com/stffn/declarative_authorization
-hint: for naming conventions change belongs_to :users in order model to belongs_to :user (belongs_to is always singular)
This is how your controller should look like
#this is the filter called before each method to make sure the user is authorized to access the order
before_filter :authorize_user, :only => [:edit,:update,:delete,:show]
def index
authorize! :index, #user, :message => 'Not authorized as an administrator.'
#here fetch the orders of the current user only
#orders = current_user.orders
end
#and then goes all your methods here as normal
private
def authorize_user
unless current_user.eql?(#order.user)
flash[:notice]="You are not authorized to access this order"
redirect_to orders_path
end
end