Redmine: check if the user has a specific privilege - redmine

Trying to write a plug-in for which I need to check if the user has a particular permission .
For example
I am thinking of something like this
if (user.current has "view_private_notes" )
do something
end

Check User model. There is near exact you want:
# Return true if the user is allowed to do the specified action on a specific context
# Action can be:
# * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
# * a permission Symbol (eg. :edit_project)
# Context can be:
# * a project : returns true if user is allowed to do the specified action on this project
# * an array of projects : returns true if user is allowed on every project
# * nil with options[:global] set : check if user has at least one role allowed for this action,
# or falls back to Non Member / Anonymous permissions depending if the user is logged
def allowed_to?(action, context, options={}, &block)
You can extend existing models by your plugin and add methods you like combining existing:
//init.rb
ActionDispatch::Callbacks.to_prepare do
unless User.included_modules.include?(MyPlugin::UserPatch)
User.send(:include, MyPlugin::UserPatch)
end
end
//user_patch.rb Something like this:
def self.included(base)
base.class_eval do
unloadable
# need to pass context to be able trigger allowed_to method in old way.
has_right_view_project(project,context)
self.allowed_to?({:controller => context[:controller], :action => context[:action]}, project, :global => true)
end
end
Actually it's easy to use existing methods.

if User.current.allowed_to?(:view_private_notes, #project)
puts "i know what you did!"
end

Related

Redirecting a request in a routing constraint

I have Sidekiq mounted in my routes file to the /sidekiq endpoint.
I use a constraints option to have it call an external class for validation as a way of preventing non-privelaged users from accessing that endpoint.
# config/routes.rb
mount Sidekiq::Web => "/sidekiq", constraints: Sidekiq::AdminConstraint.new
# lib/sidekiq/admin_constraint.rb
module Sidekiq
class AdminConstraint
def matches?(request)
return false unless request.session[:user_id]
user = User.find_by_id(request.session[:user_id])
user && Ability.new(user).can?(:manage, :sidekiq)
end
end
end
This setup works great. However, it only lets me return true / false on whether the request should go through or not. It does not let me -
Set a flash message (e.g. "You are not permitted to access that page") and
Redirect to some arbitrary page
In that sense, I'm looking for it to behave more like a controller's before_filter.
Is there a way I can modify the request object that's passed in to implement that redirect?
Thanks!
I don't have idea directly set the flash messages, But we can use in different way.
Use the following solution
In your routes.rb, add the following line in the end of the file
match "*path", :to => "application#error_404"
This basically means, any path that is not defined in your route will end up going to error_404 in application_controller. Its very important to put this at the end of your file
And in your ApplicationController, add
def error_404
redirect_to root_path
end
Thanks

Cancancan check submitted parameter

How do I get cancancan to check an parameter to see if the user can update?
Controller gets:
Parameters: {"offer"=>{"revoked"=>"1", "user_id"=>"14"}, "id"=>"53"}
ability.rb:
can :update, Controller, :user_id => user.id
cannot :update, Controller, { :revoked => nil }
controller code:
#offer.update(params)
This is giving me a Cancancan error saying that the user is not authorized. I think I need to specify that :revoked is inside the offer hash, but I can't figure out the correct code for that.
How to change what the Ability class can access:
https://github.com/ryanb/cancan/wiki/Accessing-Request-Data
Or how to pass params into the Ability class more specifically for your purposes:
https://stackoverflow.com/a/9472881/4880924
# CanCan - pass params in to Ability
# https://github.com/ryanb/cancan/issues/133
def current_ability
#current_ability ||= Ability.new(current_user, params)
end
Then it's a matter of simply accessing the relevant part of the params and checking whether it passes.

param is missing or the value is empty: holiday

Hi guys new to ruby on rails, having this problem when i try to create new holiday records for a particular profile . it says error:
param is missing or the value is empty: holiday.
# Never trust parameters from the scary internet, only allow the white list through.
def holiday_params
params.require(:holiday).permit(:details, :Profile_id)
end
end
profile params:
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
#profile = Profile.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:firstname, :lastname, :work_email, :phone, :employment_type, :employment_start_date, :linkedin_profile, :nationality, :date_of_birth, :notes)
end
end
Ensure that params[:holiday] exists for whatever path you are calling for the Holiday controller.
See: param is missing or the value is empty: user rails 4

Rolify and Rails 4 role management

I am trying to implement a role based access system in my rails 4 app, and I want the end user (super_admin) to have the ability to edit role assignments via the UI.
I have achieved some success but can't help feeling that there has to be a better way (Since I'm new to rails). Here is my code:
users_roles_controller.rb
# GET /user_roles/new/:id
def new
#roles = Role.all
end
# POST /user_roles/new/:id
def create
populated = params[:roles][:name].reject(&:empty?)
populated.each do |key|
#user.add_role Role.find(key).name
end
redirect_to users_path
end
And in my Form (HAML and simple_form):
= simple_form_for :roles, :url => create_user_roles_path(#user.id), :method => "post" do |f|
= f.input :name, :collection => #roles, as: :check_boxes
= f.button :submit
I'm struggling with the following:
How do I validate form entries, since there is no model?
Should I be using strong parameters and if so how do I implement on a form without a model
How do I create something similar, but with Current roles already checked? (True role management)
UPDATE
I have looked at using the reform Gem as suggested by the comments. This seems like a great solution. I am however having issues with the implementation on this case.
Let me map it out:
I have 3 tables in the database:
users
users_roles (Mapping Table with 2 Attributes : user_id & role_id {Join Table -> HABTM})
roles
I want to construct a form with all the values in the Roles model as checkboxes.The checkboxes should dictate what values are fed into the users_roles table (With relation to a specific user). What I want reform to do is validate the input of this form. This form will always display all of the values in Roles, but some/all of the boxes might be unchecked.
I have created a form folder in my app and started with the following code:
class UserRoleForm < Reform::Form
property :user__id, on: :user
property :role_id, on: :role
validates :user__id, presence: true
validates :role__id, presence: true
end
Am I going in the right direction?
Thanks for the help.
You need two things to build your form: a user's roles and the possible roles.
If I recall correctly, rolify gives your model associations ad should just let you do something like some_user.roles to return all the roles applied to some_user.
To get possible roles, try Role.all.
To combine both, try
user_roles = some_user.roles.pluck(:name) # suppose this returns ["admin"]
Role.pluck(:name).map { |r| [r, user_roles.include?(r)] }
You now have an array like this that you can use to build your form checkboxes with an each loop.
[["customer", false], ["admin", true], ["editor", false]]
Define your reform object's sync or save method to handle what to do with the submitted input, however you are handling it. You can (SHOULD) make a custom validation to verify if the submitted roles are valid roles.

Generating a unique URL with tokens in Rails 4 for an external form response

I have a 'Feedback' model whereby a user should be able to request feedback on his/her job performance. I have written basic actions for creating a new feedback request, and the mailer for sending the request to the provider (person who will respond with feedback).
I would like advice from the community on implementing the following:
Once a new feedback request is created, the email that is sent should contain a link to a form where the provider can input his feedback on the users performance.
The feedback provider should not be required to log-in or sign-up in any way (i.e. completely external to the application).
Once submitted, feedback from the provider should be captured in the
system.
Now, I have the following ideas to implement it, but am not sure if this is the best way to proceed:
Generate a unique token upon the creation of a new feedback request. Something like this: Best way to create unique token in Rails?.
The token should then be entered into 'feedbacks' table.
Mailer should then generate variable (e.g. #url) which generates link to another controller (let's say 'external_feedback' and action which does not require log-in (e.g. no before_filter :authenticate_user! from Devise).
That URL should contain a parameter with the token for the specific feedback request.
The action should be to update the 'feedback' request and a form generated with simple_form.
The whole thing is similar to responding to a questionnaire or survey (like Survey Monkey).
After some research I believe the Friendly ID gem may be useful here. I was also reading Section 8 of http://guides.rubyonrails.org/form_helpers.html and perhaps I need to implement an authenticity_token in the formal sense. What I am really looking for is:
Is the above approach the generally correct way to go about doing this?
If so, any specifics on how you would implement it (with or without Friendly ID)?
Do you know of any gems that exist for generating such URLs/tokens?
Thank you in advance. I am now including the current state of model and controller details:
feedback.rb
# == Schema Information
#
# Table name: feedbacks
#
# id :integer not null, primary key
# user_id :integer
# p_first_name :string(255)
# p_last_name :string(255)
# p_email :string(255)
# goal_id :integer
# u_comment :text
# p_comment :text
# created_at :datetime
# updated_at :datetime
#
class Feedback < ActiveRecord::Base
belongs_to :user
belongs_to :goal
has_many :feedback_attributes
validates_presence_of :p_first_name, :p_last_name, :p_email, :goal_id
end
And this is my mailer:
class FeedbackMailer < ActionMailer::Base
def feedback_request(user, feedback)
#user = user
#feedback = feedback
#url = 'http://thisistheexampleurlforfeedback'
mail(to: #feedback.p_email, subject: "#{#user.first_name} #{#user.last_name} has requested your feedback", from: #user.email)
end
end
Add a token field to the feedback model with an index and add a callback to populate it on create e.g.
feedback.rb
before_create :add_token
private
def add_token
begin
self.token = SecureRandom.hex[0,10].upcase
end while self.class.exists?(token: token)
end
now add a new route for the providers feedback
resources :feedbacks do
get 'provider'
put 'provider_update' # you might not need this one, if you are happy to use update
end
In your controller make sure they don't get rejected by devise
before_filter :authenticate_user!, except: [:provider, :provider_update]
...
def provider
#feedback = Feedback.find_by token: params[:token]
end
then in the app/views/feedback/provider.html.haml you can use url in simple_form to send it to the correct update location and only provide the input that they should see.
f.inputs :p_comment
Now update your mailer.
#url = provider_feedback_url(#feedback, token: #feedback.token)
You could do something similar to this using friendly id but you would still need to create some sort of unique slug and then use Feedback.friendly.find instead. I think you would want to combine it with a token to ensure it's still the provider giving the feedback - so the only benefit would really be hiding the true id/count. I think you should update p_* fields to provider_* so that the next dev knows what's in it - it's not the 90s!