Understanding CanCan initialize method - ruby

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

Routing the requests to rails concerns based on the incoming request type

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.

How to apply ActiveSupport::Concern to specific actions in my controller?

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.

Recommended use of couchrest model in a multi-tenant app

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.

{grape} authorization

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

devise and current_user

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

Resources