Ckeditor in Rails, can't set authentication session-driven - ruby-on-rails

I have a Ckeditor textarea in Rails 4, using gem "ckeditor". All works fine, and it's placed in an administration interface. So, when I click 'browse server' to upload the assets, it links to the url:
http://localhost:3000/ckeditor/pictures?CKEditor=skill_description&CKEditorFuncNum=1&langCode=es
The problem is that I don't want anybody to be able to access this page, only the administrator. So I use the gem cancan (supported by the ckeditor gem) to do it.
class Ability
include CanCan::Ability
def initialize(user)
can :access, :ckeditor # needed to access Ckeditor filebrowser
can [:access, :read, :create, :destroy], Ckeditor::Picture
can [:access, :read, :create, :destroy], Ckeditor::AttachmentFile
end
end
The problem is that the logic for cancan goes in a Model, so I can't get sessions there. If a make a before_filter in ApplicationController, somehow the app doesn't pass through it when going to the previous url. I think that with cancan the best way is to create a User model and create a is_admin field, but that's no what I want at this moment. Any ideas for how to handle this?

I found a solution for this issue.
Create a controller under controller with name ckeditor/pictures_controller.rb.
class Ckeditor::PicturesController < Ckeditor::ApplicationController
before_action: something
def index
#pictures = Ckeditor.picture_adapter.find_all(ckeditor_pictures_scope)
#pictures = Ckeditor::Paginatable.new(#pictures).page(params[:page])
respond_to do |format|
format.html { render :layout => #pictures.first_page? }
end
end
def create
#picture = Ckeditor.picture_model.new
respond_with_asset(#picture)
end
def destroy
#picture.destroy
respond_to do |format|
format.html { redirect_to pictures_path }
format.json { render :nothing => true, :status => 204 }
end
end
protected
def find_asset
#picture = Ckeditor.picture_adapter.get!(params[:id])
end
def authorize_resource
model = (#picture || Ckeditor.picture_model)
#authorization_adapter.try(:authorize, params[:action], model)
end
end
Or you can find the ckeditor controller in Ruby2.1.0\lib\ruby\gems\x.x.x\gems\ckeditor-x.x.x\app\controllers\ckeditor. In there, you can custom it.

Related

Rails 4 with Pundit

I am trying to make an app in Rails 4.
I want to use Pundit for authorisations. I also use Devise for authentication and Rolify for role management.
I have a user model and am making my first policy, following along with this tutorial:
https://www.youtube.com/watch?v=qruGD_8ry7k
I have a users controller with:
class UsersController < ApplicationController
before_action :set_user, only: [:index, :show, :edit, :update, :destroy]
def index
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
# GET /users/:id.:format
def show
# authorize! :read, #user
end
# GET /users/:id/edit
def edit
# authorize! :update, #user
end
# PATCH/PUT /users/:id.:format
def update
# authorize! :update, #user
respond_to do |format|
if #user.update(user_params)
sign_in(#user == current_user ? #user : current_user, :bypass => true)
format.html { redirect_to #user, notice: 'Your profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# GET/PATCH /users/:id/finish_signup
def finish_signup
# authorize! :update, #user
if request.patch? && params[:user] #&& params[:user][:email]
if #user.update(user_params)
#user.skip_reconfirmation!
sign_in(#user, :bypass => true)
redirect_to #user, notice: 'Your profile was successfully updated.'
else
#show_errors = true
end
end
end
# DELETE /users/:id.:format
def destroy
# authorize! :delete, #user
#user.destroy
respond_to do |format|
format.html { redirect_to root_url }
format.json { head :no_content }
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(policy(#user).permitted_attributes)
# accessible = [ :first_name, :last_name, :email ] # extend with your own params
# accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
# accessible << [:approved] if user.admin
# params.require(:user).permit(accessible)
end
end
And this is my first go at the User policy.
class UserPolicy < ApplicationPolicy
def initialize(current_user, user)
#current_user = current_user
#user = user
end
def index?
#current_user.admin?
end
def show?
#current_user.admin?
end
def edit?
#current_user.admin?
end
def update?
#current_user.admin?
end
def finish_signup?
#current_user = #user
end
def destroy?
return false if #current_user == #user
#current_user.admin?
end
private
def permitted_attributes
accessible = [ :first_name, :last_name, :email ] # extend with your own params
accessible << [ :password, :password_confirmation ] unless params[:user][:password].blank?
accessible << [:approved] if user.admin
params.require(:user).permit(accessible)
end
end
My questions are:
The tutorial shows something called attr_reader. I have started learning rails from rails 4 so I don't know what these words mean. I think it has something to do with the old way of whitelisting user params in the controller, so I think I don't need to include this in my user policy. Is that correct?
is it right that i have to initialise the user model the way I have above (or is that only the case in models other than user, since I'm initialising current_user, it might already get the user initialised?
is it necessary to move the strong params to the policy, or will this work if I leave them in the controller?
The tutorial shows something called attr_reader. I have started learning rails from rails 4 so I don't know what these words mean. I think it has something to do with the old way of whitelisting user params in the controller, so I think I don't need to include this in my user policy. Is that correct?
No, it is very important.
attr_reader creates instance variables and corresponding methods that return the value of each instance variable. - From Ruby Official Documentation
Basically if you do
class A
attr_reader :b
end
a = A.new
you can do a.b to access b instance variable. It is important because in every policies you might allow read access of instance variables. #current_user and #user is instance variable.
is it right that i have to initialise the user model the way I have above (or is that only the case in models other than user, since I'm initialising current_user, it might already get the user initialised?
You have to initialise it manually. Currently, the way you did it is correctly. Good.
is it necessary to move the strong params to the policy, or will this work if I leave them in the controller?
It is the matter of choice. It will work even if you kept it into controller. Move to policy only if you want to whitelist attributes in quite complex way.
Note: device , pundit and rolify gem works good but there share some of the same functionality so be careful and consistence what to do with what.
For example, You can use devise_for :users , :students , :teachers which will give 3 different links to login the respective resources. You can do lot of things with it. You can further authenticate the urls as per the resources with authenticate method. Check https://github.com/plataformatec/devise/wiki/How-To:-Define-resource-actions-that-require-authentication-using-routes.rb This sort of thing can also be done with pundit with policies and rolify with roles.

Check if current_user is the owner of a resource and allow edit/delete actions

Example:
User A (id=10) has created a photo resource
photo: (id: 1 user_id = 10, url: "http://...")
Now, if User B (id=20) go to this url: /photos/1/edit it can edit photo of user A!!!
Rails+Devise provides something for this by default? It seems it's a very common issue
I just need to allow that any user can edit/delete ONLY resource it has created (where current_user == resource.user)
Using: Rails 4, Devise
Update:
I think CanCan it's something too advanced. I don't need roles or restrict some actions to certain users
In your PhotosController:
before_filter :require_permission, only: :edit
def require_permission
if current_user != Photo.find(params[:id]).user
redirect_to root_path
#Or do something else here
end
end
You can make use of Rails' associations and write it like this:
def edit
#photo = current_user.photos.find(params[:id])
# ... do everything else
end
This will only find a record when the photo with the supplied ID belongs to the current user. If it doesn't, Rails will raise a ActiveRecord::RecordNotFound exception.
Of course, I'm assuming the current_user method is available and your User model contains the statement has_many :photos.
Check this railscasts,
http://railscasts.com/episodes/192-authorization-with-cancan
Complications you will run into,
When you want cancan authorization on User Model that Devise gem is using for authentication
When you want to store your Roles in the Database
When you want to assign Permissions to the Roles as an Admin from the webUI
and more ..
Please comment if you want any of those features, I will be happy to help, because I recently did them with great help from others and its always amazing to pass it on.
A sample Ability for your resources can be like as follows,
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest users
send(user.role.name)
if user.role.blank?
can :read, User #for guest without roles
end
end
def man
can :manage, Photo
end
def boy
can :read, Photo
end
def kid
can :read, Article
end
end
I captured the exception from within a before_filter action:
before_action :set_photo, only: [:edit, :update, :destroy]
def set_photo
#photo = current_user.photos.find(params[:id])
rescue ActiveRecord::RecordNotFound
redirect_to(root_url, :notice => 'Record not found')
end
Hope this helps someone. I'm using Rails 4 and Ruby 2.
So you are using gem devise.
This gem provides the current_user for the currently logged in user.
In your PhotosController#edit method. I'd do something like below.
def edit
#photo = Photo.find(params[:id])
redirect_to root_path, notice: 'Thou Shalt Nought duuu dat :(' unless current_user.id == #photo.user_id
...
end
This method is cheaper because you already have 2 objects to compare instead of running a query in the comparison.
The simplest would be to to modify routes.rb.
Assign photos to live in the current_user path.
For example,
devise_for :users
resources 'users' do
resources 'photos'
end
cancan is difficult and complicate
i have coding is_onwer method
it's very simple, easy
https://gist.github.com/x1wins/0d3f0058270cef37b2d3f25a56a3745d
application controller
def is_owner user_id
unless user_id == current_user.id
render json: nil, status: :forbidden
return
end
end
def is_owner_object data
if data.nil? or data.user_id.nil?
return render status: :not_found
else
is_owner data.user_id
end
end
your controller
before_action only: [:edit, :update, :destroy] do
is_owner_object #article ##your object
end
If CanCan is too advanced, you should loon into checking the id of the accessor in the controller using...
if #user.id == #photo.user_id
# edit photo details
else
redirect_to root_path, notice: "You! Shall! Not! Edit!"
...or something like that
Write another before_filter in application_controller:
before_filter :has_permission?
has_permission?
controllers=["articles", "photos", "..."]
actions=["edit", "destroy", "..."]
id = params[:id] if (controllers.include?(params[:controller] && actions.include?(params[:action]) end
if id && (current_user.id==(params[:controller][0...1].capitalize!+params[:controller].singularize[1...-1] + ".find(#{id}).user_id").send)
return true
else
redirect_to root_url, :notice=>"no permission for this action"
end
helper_method :has_permission?
And you can use it in views, not to show users link they can't follow.
Some kind of this, of course you need to modify it to suit your needs.

return redirect_to in private controller method

Preface: I'm using devise for authentication.
I'm trying to catch unauthorized users from being able to see, edit, or update another user's information. My biggest concern is a user modifying the form in the DOM to another user's ID, filling out the form, and clicking update. I've read specifically on SO that something like below should work, but it doesn't. A post on SO recommended moving the validate_current_user method into the public realm, but that didn't work either.
Is there something obvious I'm doing wrong? Or is there a better approach to what I'm trying to do, either using devise or something else?
My UsersController looks like this:
class UsersController < ApplicationController
before_filter :authenticate_admin!, :only => [:new, :create, :destroy]
before_filter :redirect_guests
def index
redirect_to current_user unless current_user.try(:admin?)
if params[:approved] == "false"
#users = User.find_all_by_approved(false)
else
#users = User.all
end
end
def show
#user = User.find(params[:id])
validate_current_user
#user
end
def new
#user = User.new
end
def edit
#user = User.find(params[:id])
validate_current_user
#user
end
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to #user, :notice => 'User was successfully created.' }
else
format.html { render :action => "new" }
end
end
end
def update
#user = User.find(params[:id])
validate_current_user
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, :notice => 'User was successfully updated.' }
else
format.html { render :action => "edit" }
end
end
end
private
def redirect_guests
redirect_to new_user_session_path if current_user.nil?
end
def validate_current_user
if current_user && current_user != #user && !current_user.try(:admin?)
return redirect_to(current_user)
end
end
end
The authenticate_admin! method looks like this:
def authenticate_admin!
return redirect_to new_user_session_path if current_user.nil?
unless current_user.try(:admin?)
flash[:error] = "Unauthorized access!"
redirect_to root_path
end
end
EDIT -- What do you mean "it doesn't work?"
To help clarify, I get this error when I try to "hack" another user's account:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
If I put the method code inline in the individual controller actions, they do work. But, I don't want to do that because it isn't DRY.
I should also specify I've tried:
def validate_current_user
if current_user && current_user != #user && !current_user.try(:admin?)
redirect_to(current_user) and return
end
end
If you think about it, return in the private method just exits the method and passes control back to the controller - it doesn't quit the action. If you want to quit the action you have to return again
For example, you could have something like this:
class PostsController < ApplicationController
def show
return if redirect_guest_posts(params[:guest], params[:id])
...
end
private
def redirect_guest_post(author_is_guest, post_id)
redirect_to special_guest_post_path(post_id) if author_is_guest
end
end
If params[:guest] is present and not false, the private method returns something truthy and the #show action quits. If the condition fails then it returns nil, and the action continues.
You are trying and you want to authorize users before every action. I would suggest you to use standard gems like CanCan or declarative_authorization.
Going ahead with this approach you might end up reinventing the wheel.
In case you decide on using cancan, all you have to do is add permissions in the ability.rb file(generated by rails cancan:install)
can [:read,:write,:destroy], :role => "admin"
And in the controller just add load_and_authorize_resource (cancan filter). It will check if the user has permissions for the current action. If the user doesnt have persmissions, then it will throw a 403 forbidden expection, which can be caught in the ApplicationController and handled appropriately.
Try,
before_filter :redirect_guests, :except => [:new, :create, :destroy]
should work.
This is because you are using redirect twice, in authenticate_admin! and redirect_guests for new, create and destroy actions.
"Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action."
That's the reason of the error. In show method, if you are neither the owner of this account nor the admin, you are facing two actions: redirect_to and render
My suggestion is to put all of the redirect logic into before_filter

Recommendations for constructing RESTful resources for avatar selection scenario in rails

We have a requirement where a user needs to select their avatar for their profile. On the edit profile page, the user clicks on a Change Picture link which takes them to another page and gives them with two links to get their photo from facebook or gravatar. There is also a preview of the image shown on this page, as well as a save button. The controller for this page is AvatarsController. I have edit and update actions, as well as custom GET actions for facebook and gravatar, so that the route looks like avatar/facebook, and avatar/gravatar. These actions simply query the respective services and create a new avatar model containing the url for the photo. When the user clicks save, the update action is called and the avatar model is saved with the profile. The page is delivered by the edit template, as by default, when a user is created, an empty avatar is also created.
The Profile model (using mongoid) essentially looks like:
def Profile
embeds_one :avatar
end
and the avatar model looks like:
def Avatar
embedded_in :profile
end
The route looks like:
resource :avatar, only: [:edit, :update] do
member do
get 'facebook'
get 'gravatar'
end
end
The controller looks like:
class AvatarsController < ApplicationController
def facebook
url = AvatarServices.facebook(current_user, params[:code])
respond_to do |format|
unless url
format.json { head :no_content }
else
#avatar = Avatar.new({:url => url, :source => "Facebook"})
#avatar.member_profile = current_user.member_profile
format.html { render :edit }
format.json { render json: #avatar }
end
end
end
def gravatar
respond_to do |format|
url = AvatarServices.gravatar(current_user)
unless url
format.json { head :no_content }
else
#avatar = Avatar.new({:url => url, :source => "Gravatar"})
#avatar.member_profile = current_user.member_profile
format.html { render :edit }
format.json { render json: #avatar }
end
end
end
def edit
#avatar = current_user.member_profile.avatar
end
def update
#avatar = current_user.member_profile.avatar
respond_to do |format|
if #avatar.update_attributes(params[:avatar])
format.html { redirect_to edit_member_profile_path }
format.json { head :no_content }
else
format.html
format.json { render json: #avatar.errors }
end
end
end
end
This works, but being fairly new to rails, I'm wondering if rails experts would have set up the 'facebook' and 'gravatar' resources differently, perhaps in a more RESTful manner?
Well, the subfolder is putting the facebook and gravatar controllers into a common namespace. You could use nested routes,
resource :avatar, only: [:edit, :update] do
resource :facebook
resource :gravatar
end
This will route to a FacebooksController and a GravatarsController.
This is kind of what you were thinking anyway, and you won't need a record id for a facebook or gravatar record.
Could you add your controller code? I'm interested to see how you have your actions setup.
If you want to keep things restful, it might just be a matter of creating a controller subfolder for avatars, and created subsequent controllers for gravatar & facebook. You can do this just using a generator
rails g controller avatars/facebook
rails g controller avatars/gravatar

Devise/Cancan Signout User On Controller Action

I have implemented Devise for Authentication and Authorization in ROR application everything seems fine but getting one issue.
I have two modals "Account" and "Transactiona" , and so two controllers respectively.
My Transaction Index view call one of Account Controller method like this
$.post("accounts/our_miles_balance/?account_number="+$("#account_number").val(),function(data)
{
$("#our_miles_balance").val(data);
});
When this ajax post run it gives following error and sign out admin user
You need to sign in or sign up before continuing
Here is my Ability Class
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user
if user.role == 1 #admin
can :manage, :all
can :read, :all
elsif user.role == 2 #Vendor
can :manage, VendorTransaction
can :index, Account
end
end
end
What i m doing wrong her, please help....
Edit
Ok Here is my Transaction controller
require 'csv'
class TransactionsController < ApplicationController
load_and_authorize_resource
helper_method :sort_column, :sort_direction
respond_to :html, :js
def index
per_page = 40
#transactions = Transaction.search(params[:id]).order(sort_column + " " + sort_direction)
respond_to do |format|
format.html # index.html.erb
format.csv { render :csv => #transactions}
end
AND Account Controller
class AccountsController < ApplicationController
load_and_authorize_resource
helper_method :sort_column, :sort_direction
def index
#accounts = Account.search(params[:program_id]
respond_to do |format|
format.html # index.html.erb
format.json { render :json => #accounts}
end
def our_miles_balance
a = Account.find_by_account_number(params[:account_number])
#miles = Account.our_miles_balance(a.id) if ?a!=nil
respond_to do |format|
format.json { render json: #miles}
end
end
end
When using load_and_authorize_resource, it's calling authorize!(:our_miles_balance, #account) before the our_miles_balance controller action. Documentation.
Option 1
Add
can :our_miles_balance, Account
to your ability class.
Option 2
In the controller do
load_and_authorize_resource :except => :our_miles_balance
and in the our_miles_balance action do
authorize! :read, #account

Resources