I'm having a hard time understanding how CanCan works. I have the following model
class Ability
include CanCan::Ability
def initialize(user)
if user && user.email == "jason#gmail.com"
can :access, :rails_admin # only allow admin users to access Rails Admin
can :dashboard # allow access to dashboard
end
end
end
When it comes to my rails_admin file in the initializers folder
RailsAdmin.config do |config|
config.authorize_with :cancan
config.main_app_name = ['Pr', 'Admin']
config.current_user_method { } # auto-generated
end
I want to have one user to access the admins dashboard with the email "jason#gmail.com", but how does CanCan know who is currently signed in at the time? Does it rely on a helper method I'm missing?
CanCan uses a current_ability method to supply the ability, and in that it uses current_user. I know at least Devise has this method, other auth frameworks must commonly supply it too, not sure.
Related
Rails version is 6.0.0.3 and Ruby version is ruby 2.6.6-p146
I need to send the incoming request to the application controller to 2 different concerns based on the request format.
I have an application controller
class ApplicationController < ActionController::Base
include Authentication
include Authorization
before_action :authenticate_or_authorize
private
def authenticate_or_authorize
request.format.json? ? :authorize_request : :authenticate_request
end
end
I have 2 concerns namely authentication and authorization. if the incoming request is of type JSON, I need to call the authorization concern.
If it is a non json request i need to call authenticate_request.
Here are my concerns.
module Authentication
extend ActiveSupport::Concern
included do
before_action :authenticate_request, :return_url
end
private
def authenticate_request
// do something to authenticate
end
end
Here is the second concern.
module Authorization
extend ActiveSupport::Concern
include ::JwtVerifiable
included do
before_action :authorize_request
end
private
def authorize_request
// do something to authorize.
end
end
The current behavior is request always goes to the Authentication concern. Then afterward comes to the authenticate_or_authorize defined in the ApplicationController.
Can anyone help to correct the issue?
Thanks in advance.
The Concerns are already providing their own before_action calls when they are included (that's what included do (block) end is doing). This means that :authenticate_request, :return_url and :authorize_request are going to be run on every request. If you can't remove those from the concerns themselves, you skip them with:
skip_before_action :authenticate_request, :authorize_request
after including the concerns in ApplicationController. The before_action you've defined in ApplicationController will still run.
I'm using Grape to build an API.
I created an ActiveSupport::Concern let's say with the name Authentication and I applied some before filter so my concern looks like:
module Authentication
extend ActiveSupport::Concern
included do
before do
error!('401 Unauthorized', 401) unless authenticated?
end
....
end
end
Now let's say in my UserController I want to apply this concern only for a specific action. How can I do that?
class SocialMessagesController < Grape::API
include Authentication
get '/action_one' do
end
get '/action_two' do
end
end
Any easy way to specify the concern for a specific method just like before_filter in rails with only option?
Separate your actions into different classes and then mount them within a wrapper class:
class SocialMessagesOne < Grape::API
include Authentication
get '/action_one' do
# Subject to the Authentication concern
end
end
class SocialMessagesTwo < Grape::API
get '/action_two' do
# Not subject to the Authentication concern
end
end
class SocialMessagesController < Grape::API
mount SocialMessagesOne
mount SocialMessagesTwo
end
More information on mounting is available in the Grape README.
I'm looking for recommendations on how to implement multi-tenancy with couchrest model in a rails app. For my multi-tenant app, I'm thinking of two options:
{ edit - removed my ugly options because they'll only confuse future readers }
I would like this to work well with 10K users.
SOLUTION:
Based on Sam's advice, here's what I did and it's working well -
In my case, I needed to override the proxy_database method because the standard naming for proxy databases didn't match my naming.
created the master
class Site < CouchRest::Model::Base
property :name
property :slug
proxy_for :users
proxy_for ...(all the other multi-tenant models)
# Databases are on same server in this example
def proxy_database
#db ||= self.server.database!(slug)
end
end
Then in each multi-tenant model
class User < CouchRest::Model::Base
...
proxied_by :site
In ApplicationHelper create a 'site' method that you can reuse in all your controllers.
module ApplicationHelper
def site
db_name = current_user.db_name
#site ||= Site.create(slug: "#{db_name}_#{Rails.env}" )
end
Then controller might do something like:
def show
user = site.users.find(params[:id])
render :json => user
end
You might want to checkout the Proxying feature of CouchRest Model for this. More details can be found here:
http://www.couchrest.info/model/proxying.html
Although I have no personal experience, I understand that CouchDB handles >10k databases. Here is a good thread of ways of scaling the number of users:
http://comments.gmane.org/gmane.comp.db.couchdb.user/13862
A few considerations to take into account when dealing with lots of databases:
File system sub-directory count, not a problem with Ext4.
Namespace databases to split between sub-directories and/or servers.
System open file limit. Usually around 10k. Probably fine if not all databases are accessed at the same time.
Hope that helps.
I'm attempting to create a restful, json api in ruby - so I'm using grape (https://github.com/intridea/grape) inside of Rack.
I'm not using Rails for this project, so cancan, sorcery, etc... don't seem to be the best options. Plus, I'd hate to mix in a bunch of imperative logic into grape's declarative DSL.
While grape has built in authentication support, I do not see anything about authorization. It seems like this would be a common enough use case that this road would have been traveled before, but after some pretty thorough digging in google and the grape codebase itself I have turned up nothing.
Has anyone implemented something like this for their project in grape? What did you use?
This may be a little too late, but anyway. I'd recommend you use Pundit for authorization, it's deadly simple. To use it in your Grape API endpoints, you would need to include Pundit helpers:
class API < Grape::API
format :json
helpers Pundit
helpers do
def current_user
resource_owner
end
end
mount FoosAPI
end
Now in you API endpoints, you should be able to use authorize foo, action? as you would always do in Rails controllers:
class FoosAPI < Grape::API
get ':id' do
foo = Foo.find(params[:id])
authorize foo, :show?
present foo, with: FooEntity
end
end
Hope it helps!
I thought I can give short comment on this, but the field is to short, sorry if it will not be right answer but:
You mentioned sorcery - I think it is authentication system and got nothing to do with an authorization. (I do not know sorcery gem implementation - just repeating statement from documentation and assuming that description enumerates such systems it replaces and it is valid definition). I guess it is just mistake.
Fundamental question you should ask yourself is...
How much role-based system do you develop? I think if this is only matter of public/private/admin roles probably you should consider just moving it to different APIs.
That can be cumbersome in some circumstances but worth a try for not complicated none additive roles. Simple mount in grape will solve the problem OOTB.
Real problem is if you think about some expandable/dynamic role system or you want to be just DRY. That can be painful ;-). I think Rayan's Bytes cancan gem implementation should help you understand how such problem can be solved on higher abstract level. For particular (without higher abstraction - such as dynamic roles) implementation it should be fine to just use currently given helpers from grape and delegate their responsibilities to model (basic usage).
helpers do
def current_user
#current_user ||= User.authorize!(env)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
so all the story is about how to implement User.authorize!(env) and I believe that should be done in your model and depends strictly on your needs.
I don't know whether my answer is in time for you. I recently have the same problem with the Grape and authorization in a Rails4 project. And after trying, I found out a way for this.
In my project, I'm using the pundit for authorization, it asks me to create a policy folder, and create the authorize rules for each Model, each rule is a Ruby class, something like this(from pundit Github page)
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
then in the Grape API, I just use this class for the authorization, code like this:
desc "hide a post"
post :hide do
authenticate!
error!( "user unauthorized for this" ) unless PostPolicy.new(current_user, #post).hide_post?
#post.update hidden: true
{ hidden: #post.hidden }
end
the authenticate! and current_user helpers are customized helpers. By this way, I can reuse the authorize rules created when developing website parts.
This works for me. Hope the Pundit way can solve your problems for Grape authorization
i have a question about devise system.
my last ruby project was done with nifty:authnentication. With nifty, i can manage session for current_user and other information from '"controller_authentication.rb"'. But now, i want to add a new current_* information.
I want to know where i can find current_user method? where is it defined?
with nifty i used something like
#current_company ||= Company.find(session[:company_id]) if session[:company_id]
thanks.
current_user is defined as a helper in devise source code. When you install devise on your project, it gets activated too.
current_user is defined dynamically in Devise. Since your user model could actually be a different model, the method uses your model name for 'mapping' when it defines the current_whatever helper method:
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(:scope => :#{mapping})
end
https://github.com/plataformatec/devise/blob/master/lib/devise/controllers/helpers.rb#L123