Rails4: disable edit, delete in dashboard/backend - ruby-on-rails-4

I want to show my work to clients. Clients could access the front-end and back-end/dashboard but I do not want them to change anything as multiple clients may visit the site. Only user with role 'admin' can access the back-end (user_type!='admin'). It works fine but I cannot disable create,edit,update actions at backend. Not sure why ? I want to keep to the RESTful routes.
Application controller
def authorize_to_backend
if (!current_user or current_user.user_type!='admin')
#login_dashboard is defined in router.rb
redirect_to login_dashboard_url, notice: "Please login"
end
end
i.e Brands controller
class BrandsController < ApplicationController
layout :set_layout
before_action :authorize_to_backend, only: [:create,:edit,:update]
def index
...
end
...
..
end

Added another checking in controller and it works :)
before_action :authorize_to_backend,:redirect_user, only: [:create,:update,:destroy]
private
def redirect_user
redirect_to request.referrer
end

Related

Rails Pundit Authorization not performed

I'm getting an "Pundit::PolicyScopingNotPerformedError" when calling a GET request to the show method of this controller. Any help appreciated.
Controller:
class DashboardsController < ApplicationController
before_action :authorize_dashboard_for_customer, only: :show
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :show
expose(:dashboards) {
Customer.find(params[:customer_id]).dashboards
}
expose(:dashboard) {
Dashboard.find(params[:id])
}
expose(:customer) {
Customer.find(params[:customer_id])
}
def index
end
def show
end
private
def authorize_dashboard_for_customer
authorize dashboard, :show?
end
end
Here is the Pundit Policy:
class DashboardPolicy < ApplicationPolicy
def index?
show?
end
def show?
customer = user.try(:customer)
return false if customer.blank?
#record.customers.present? && #record.customers.include?(customer) || user.role == 'admin'
end
end
I've read other posts about this, but still not seeing what I'm doing wrong here. I'm still fuzzy on what resolving a policy scope is doing, but in this case I can see from debug statements that it's hitting the policy, so I'm not sure what the issue is.
In your controller you're checking to make sure the policy scope is called with after_action :verify_policy_scoped, only: :show but you aren't calling anything for the scope in your action.
You can use Scopes to restrict the results based on the logged in users permissions. For instance an admin user on an index screen would likely see all the results, but a non-admin could maybe only see certain records. IMO you shouldn't need scopes on a show so you should be able to remove the verify_policy_scoped.

Ruby on Rails Pundit's current_user is nil in integration test

I'm using the gems pundit and devise. I have a delete link that only shows up if you are an admin. I have an integration test that I would like to verify that the delete link only shows up for admins.
test 'comment delete link shows when it should' do
log_in_as #admin
get movie_path(#movie)
assert_select 'a[href=?]', movie_comment_path(comments(:one), #movie.id)
end
My test_helper.rb looks like this:
...
class ActiveSupport::TestCase
...
def log_in_as(user, options = {})
password = options[:password] || 'password'
if integration_test?
post user_session_path, 'user[email]' => user.email, 'user[password]' => user.password
else
Devise::TestHelpers.sign_in user
end
end
private
# Returns true inside an integration test.
def integration_test?
defined?(post_via_redirect)
end
end
The response.body looks all right, but indeed there is no delete link. There is one when I run the development server and visit the page myself. I've narrowed this down to the current_user that pundit uses in the policies is being passed in with a value of nil. This is my comment_policy.rb:
class CommentPolicy
attr_reader :current_user, :comment
def initialize(current_user, model)
#current_user = current_user
#comment = model
end
def create?
if #current_user
#current_user.member? or #current_user.content_creator? or #current_user.moderator? or #current_user.admin?
end
end
def destroy?
if #current_user
#current_user == #comment.user or #current_user.moderator? or #current_user.admin?
end
end
end
As a closing remark, I've heard that Rails 5 has opted for integration tests instead of controller tests as we know them from Rails 4 for the default type of tests to be generated for our controllers. If this is the case, devise would be a heck of a lot more useful out of the box when using Rails 5 if the sign_in/sign_out helpers that work in controller tests were made to work in integration tests as well. But would I still have this issue of pundit not knowing what current_user is? I'm assuming this all works fine in controller tests because the current_user is scoped to controllers? Any and all light shed on this topic is much appreciated, but I would really like to figure out how to get integration tests to work with this setup because I have about a billion I want to write right now.
Not that it totally matters, but does it need to be using current_user in the policy or can it just use user in the policy. By this I mean according to the elabs/pundit README on Github I would just use #user and user everywhere instead of current_user. Read the README if I confused you.
Additionally the nil for current_user typically occurs when you don't have a valid CSRF token for your request. When you do this on the website manually by going to localhost:3000 or w/e you are first performing a get on the login path before doing the post on the login path with your credentials. In your integration test I don't seem to see where you are performing that get in order to get the CSRF for your session.
Hope this helps!!!

undefined method `model_name' for #<User:XXXXXXXX>

I have a dashboard containing 2 partials. One partial works fine and does what its suppose to (bill). The other partial is setup similar to bill but wont work(request). When I look at my log it shows that the tenant(user) is being queried, also, the 1st partial is queried but the 2nd partial doesn't query. when I went to my dashboard controller and changed the instance of the partial to (Request.new) it works but I can't seem to get it to work right thru the controller. I don't want to have the method in the model. I am using mongoid.
SO here is my render in the dashboard...
<%= render partial: "request", locals: {request: #request} %>
In the partial I have...
<%= form_for [:tenants, request] do |f| %>
And on the dashboard controller I have...
def show
#dashboard = current_tenant
#bill = current_tenant.bill || current_tenant.build_bill
#request = current_tenant.request || current_tenant.build_request
end
(if I change #request = Request.new it works fine but I know that's not right)
The bill partial works and the dashboard finds the tenant but I keep getting
"undefined method `request' for #
Any idea of what I am missing? I compared the bill controller to the request controller and I cant find any differences. When I put the Model.new into the dashboard controller it works but I know this isn't right, its as if the app wont recognize the Request controller...
The error is saying it doesn't recognize "request" method.
Also here is my controller for request...
class Tenants::RequestsController < ApplicationController
before_filter :authenticate_tenant!
def index
#requests = Request.all
end
def show
#request = Request.find(params[:id])
end
def create
if #request = current_tenant.create_request(authorization_params)
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} has been Authorized!"
else
redirect_to tenants_dashboard_path, :error => "#{request.manager_name} has NOT been Authorized, please try again."
end
end
def edit
end
def update
if current_tenant.request.update_attributes(authorization_params)
redirect_to tenants_dashboard_path, :notice => "You have approved #{request.manager_name} to review your report"
else
redirect_to tenants_dashboard_path, :notice => "#{request.manager_name} is NOT allowed to review your report"
end
end
def destroy
#request = Request.find(params[:request_id])
name = #request.name
if #request.destroy
flash[:notice] = "\"#{name}\" was successfully removed from your profile."
redirect_to #dashboard
else
flash[:error] = "There was an error deleting this managers access."
render :show
end
end
Well it looks like
current_tenant.request has an issue. That means that the method is not available. Assuming you're not trying to access the http request , then you have an issue with the request method.
So your issue is with how you defined the request method (maybe in your model). e.g. is it a class method or a instance method etc.
Without knowing your goal, that's the general answer I can give you. Creating a Request.new could be right depending on your goal, but if your goal is to call the request method, you must make it available to current_tenant
One controller shouldn't be calling your other controller as you have suggested...

rails_admin authentication with user's boolean admin

I am currently using rails 4.2.0beta4 with devise. In my schema, my user had boolean :admin default:false. After installing rails_admin, for the time being, every user is allowed to go to my /admin. But how do I make it so that only the users with boolean is_admin? true people log in?
Should I just do rails generate devise admin? Would that be quicker choice than having to configure again?
Thanks in advance.
I'd create an admin namespace...
1 - In routes.rb
namespace :admin do
resources :your_routes
end
2 - Make sure the following directories exist; app/views/admin, app/controllers/admin, and put any controllers and view templates that you want to restrict to admins in those folders.
3 - Add this to application_controller.rb
def user_is_admin?
signed_in? && current_user.is_admin?
end
4 - Then in any controller inside your app/controllers/admin add a before_filter to check
some_controller.rb
class Admin::YourController < ApplicationController
before_filter :user_is_admin?
end
For what it's worth, you don't have to namespace if you don't want to. You could technically just add the before_filter from steps 3 and 4 to whatever action you want to protect, but I think the namespace is a better idea in the long run.

devise_inviteable - issue with GET users/invitation/accept?invitation_token=xxxx re-directed to sign_up

ruby '2.1.2'
rails (4.1.4)
devise (3.2.4)
devise_invitable (1.3.6)
routes.rb
devise_for :users, :controllers => { invitations: 'users/invitations' }
users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
prepend_before_filter :require_no_authentication, :only => [:edit, :update, :destroy]
# i dont need to override anything
end
rake routes
accept_user_invitation GET /users/invitation/accept(.:format) users/invitations#edit
remove_user_invitation GET /users/invitation/remove(.:format) users/invitations#destroy
user_invitation POST /users/invitation(.:format) users/invitations#create
new_user_invitation GET /users/invitation/new(.:format) users/invitations#new
PATCH /users/invitation(.:format) users/invitations#update
PUT /users/invitation(.:format) users/invitations#update
every part of the application needs to be authenticated - except the accept_user_invitation path
my application controller does have:
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
Not sure why this is not working - anyone care to help me understand what I am doing incorrectly ? Every time I go to the URL I am redirected to signup
Thanks in advance. I am almost ready to roll my own !
I figured out why devise_invitable was re-directing and that was because the invitation_token was incorrect.
When overriding the default behaviour to send your own email
user = User.invite!(params) do |u|
u.skip_invitation = true
u.invitation_sent_at = Time.now
u.invited_by_id = 1
u.invited_by_type = 'User'
end
Looking at the code:
https://github.com/scambra/devise_invitable/blob/master/lib/devise_invitable/model.rb#L200
There is a temporary raw_invitation_token which is the actual token needed for your url and invitation_token which is encrypted. I have to admit there was some confusion here!
In your url generation the token you must use is raw_invitation_token as devise_invitable will decrypt this token: https://github.com/scambra/devise_invitable/blob/master/lib/devise_invitable/model.rb#L277
When I send the email or generate the link this works:
# use the raw_invitation_token rather than invition_token
# when generating your links for the mailer
token = user.raw_invitation_token
puts "#{accept_user_invitation_url(::ActionMailer::Base.default_url_options.merge({:invitation_token => token})) }"
Use the user.raw_invitation_token for your urls and the process works as intended
I have requested that the documents be improved and added a pull request - hope this helps someone