I'm trying to implement my first authlogic-based app in ruby on rails. I need to sign user using a custom oauth2 flow, at the end of the process, I want to tell authlogic which user I want him to authenticate. Without providing a login nor password, just directly by passing a user object.
I've found in the README that I can do that using UserSession.create(user, true) but putting this at the end of my OAuth callback step is not persisting my user. There is no error nor exception, everything seems to be fine but UserSession.find returns nil (even in the next line after the UserSession.create). I'm on rails 5.1.6 and authlogic 4.0.1.
User model
class User < ApplicationRecord
acts_as_authentic
end
UserSession model
class UserSession < Authlogic::Session::Base
end
Thanks in advance for any clues about what I did wrong.
This is what I found out, from Authlogic::Session::Base
module Authlogic
module Session # :nodoc:
# This is the most important class in Authlogic. You will inherit this class
# for your own eg. `UserSession`.
#
# Code is organized topically. Each topic is represented by a module. So, to
# learn about password-based authentication, read the `Password` module.
#
# It is common for methods (.initialize and #credentials=, for example) to
# be implemented in multiple mixins. Those methods will call `super`, so the
# order of `include`s here is important.
#
# Also, to fully understand such a method (like #credentials=) you will need
# to mentally combine all of its definitions. This is perhaps the primary
# disadvantage of topical organization using modules.
as I read from their instructions, they do never say that you can authenticate user without email and password, but that you can create a session with the User object
# skip authentication and log the user in directly, the true means "remember me"
UserSession.create(my_user_object, true)
I would read more about the Session.rb
https://github.com/binarylogic/authlogic/blob/master/lib/authlogic/session/session.rb
sorry if I can not give you more help
PS you can also debug this gem by setting a binding.pry in the gem files
Related
In my Rails app, I use devise to manage users with devise's default configuration. I need to md5 the user-provided password before going down into the devise layer. That is, two steps are included:
(1) (2)
password_in_plain --- password_in_md5 --- password_in_bcrypt.
The first one(1) is our concern, the last one(2) is not (devise takes care of it).
I generated two devise controllers: registrations and sessions, and added a before_filter to do the job -- md5 the plain password user provided. The user can be registered with a success, but login always fail. Here is my code:
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :md5_password_params, only: [:create]
protected
def md5_password_params
params[:user][:password] = Digest::MD5.hexdigest(params[:user][:password])
params[:user][:password_confirmation] = Digest::MD5.hexdigest(params[:user][:password_confirmation])
end
end
class Users::SessionsController < Devise::SessionsController
before_filter :md5_password, only: [:create]
protected
def md5_password
params[:user][:password] = Digest::MD5.hexdigest(params[:user][:password])
end
end
What's wrong here? Please help. Thanks in advance.
Background: one of my other server apps (written in c++) will create user records in the same database directly. For safety reason, a md5 password will be sent to this server when users make registrations in their phones.
Instead of doing this on the controller level and mucking about with the params you can create your own encryptor for Devise.
One major reason you would want to do this is that if your user fills in the registration form and submits it with invalid values both the password and confirmation fields will contain a MD5 digest of what the user originally typed. This is because you are mutating the params hash before it is fed to the model.
Submitting the form again means that the password is encrypted twice and no longer matches what the user thinks the original password is. On the plus side its extremely secure since nobody can actually guess their password ;)
Based on the current version of Devise you should be able to do something like the following to ensure the password is encrypted before a digest is created and before trying to compare plaintext with the encrypted password.
Make sure to add the devise-encryptable gem to your Gemfile. Its required for anything else than default encryption.
# config/initializers/md5.rb
require 'digest/md5'
module Devise
module Encryptable
module Encryptors
class BCryptPlusMD5 < Base
def self.digest(klass, password)
super(klass, Digest::MD5.hexdigest(password))
end
def self.compare(klass, hashed_password, password)
super(klass, hashed_password, Digest::MD5.hexdigest(password))
end
end
end
end
end
You would can configure what encryptor to use in config/initializers/devise.rb - however I have not been able to find a good source on how the class lookup works.
https://github.com/plataformatec/devise/blob/master/lib/devise/encryptor.rb
I have a multi-domain Rails 4 app where the request.domain of the http request determines which functionality I expose a given visitor to. A visitor can sign up through Devise. During the user creation a user.domain field will be filled in with the domain he signed up for, and he will then only have access to this particular domain and its functionality.
Question:
Each domain in my app should be served by its own MongoDB database. If user.domain == 'domain1.com' the user object, as well as all objects that belongs_to it, should be stored in the database that serves domain1.com. How do I set this up?
Complication 1:
I do not only interact with my database through Mongoid, but also through mongo Shell methods like db.collection.insert(). I need to have the correct database connection in both cases. Example:
class ApplicationController < ActionController::Base
before_filter :connect_to_db
def connect_to_db
domain = request.domain
# Establish connection
end
end
Complication 2:
A lot of the database interaction happens outside the MVC context. I need to have the correct database connection in e.g. a Sidekiq context. Example:
class MyJob
include Sidekiq::Worker
def perform(domain)
connect_to_db(domain)
User.all.each do |user|
...
end
end
def connect_to_db(domain)
# Establish connection
end
end
Potentially relevant SO answers:
This answer and this answer suggests that you can apply a set_database or store_in session method on a model level, but this would not be sufficient for me, because models are shared between my domains. Various stragegies have also been discussed in this Google group.
I'm in the process of setting up Doorkeeper and OAuth2 for one of my Rails Applications. My goal is to allow api access to a user depending on their access_token, so that only a user can see their 'user_show' json. So far I have my development and production applications set up and authorized on the 'oauth2/applications' route.
My '/config/initializers/doorkeeper.rb'
Doorkeeper.configure do
# Change the ORM that doorkeeper will use.
# Currently supported options are :active_record, :mongoid2, :mongoid3,
# :mongoid4, :mongo_mapper
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
# Put your resource owner authentication logic here.
# Example implementation:
User.find_by_id(session[:current_user_id]) || redirect_to('/')
end
end
and my '/api/v1/user/controller.rb' looks as such:
class Api::V1::UserController < Api::ApiController
include ActionController::MimeResponds
before_action :doorkeeper_authorize!
def index
user = User.find(doorkeeper_token.resource_owner_id)
respond_with User.all
end
def show
user = User.find(doorkeeper_token.resource_owner_id)
respond_with user
end
end
I have tried to gain access to the OAuth Applications table to see what is being created but I cannot access it in the rails console.
Thanks in advance for the insight!
It seems that Doorkeeper doesn't find any token.
Make sure you're sending it, either from url with ?access_token=#{token} or ?bearer_token=#{token}, either giving this token in headers using Bearer Authorization.
You also need to have in mind that a token could be associated only to an app, without a resource owner. So resource_owner_id value could be nil even with a valid token. It depends on what grant flow you're using (client credential flow is not associated with a resource owner). See https://github.com/doorkeeper-gem/doorkeeper/wiki#flows
For the OAuth tables, try with Doorkeeper::AccessToken.all in a rails console.
Hope this helped
I am building a daily deal app on Rails to train myself to Ruby on Rails.
I have installed authentication with devise/cancan/rolify.
I'd like to create in cancan two type of users
users who confirmed
users who did not confirmed yet
How can I achieve that ? how can I access on devise users who have and those who have not confirmed their account(i.e clicked on the activation link sent to them by email).
There is no need to add roles for confirmed and unconfirmed. You can use user.confirmed? in your ability.rb file to control authorization:
# models/ability.rb
if user.confirmed?
can :manage, Model
end
if !user.confirmed?
can :view, Model
end
Note: you can use an if/else construct, but I prefer to keep my rules nicely separated.
In regards to your comments, you're reimplementing what's already been done. With cancan you can use load_and_authorize_resource (see: here).
class ProductsController < ActionController::Base
load_and_authorize_resource
end
That's it. The user will receive an "unauthorized" response if they try to access without the required permissions.
I highly recommend you read through the documentation for rolify and cancan.
My rails app is pretty much a front-end to several web services. I persist my User model, and that's about it. I need to add authorization to my web app, using Devise for authentication. I've noticed CanCan and acl9 seem to work mostly on instances of ActiveRecord models. Would CanCan or acl9 still fit my needs? Any tips on using either of these libraries in my situation?
Should I look for something that works more on actions instead of instances?
Also, these are both Role based systems, and I'm thinking of using a permission based system. Would they still be a good fit?
I can't speak for acl9. However, the cancan wiki does claim that "It is easy to make your own [model] adapter if one is not provided." https://github.com/ryanb/cancan/wiki/Model-Adapter In other words, even though you're not using ActiveRecord, you might still be able to use cancan.
Then again, if you're not planning on having roles, your ability definitions in cancan might be a little redundant looking, eg.:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new # guest user (not logged in)
can :create, Widget if user.has_permission(:create_widgets)
can :delete, Widget if user.has_permission(:delete_widgets)
can :herp, Derp if user.has_permission(:herp_derp)
end
end
It would be great if you could use cancan just for its controller action authorization methods, but I don't know if that's possible. Good luck.
Just to (finally :) answer for acl9.
Acl9 is composed of two very separate pieces, the Access Control Subsystem which is all the authorizing stuff you put in your controller, and the Role Subsystem which is setting/checking/removing roles from an authenticated user.
The only thing that the Access Control Subsystem ever calls is current_user.has_role?( role, obj=nil). So, the Role Subsystem has zero dependency on ActiveRecord, associations, database, etc. There is a helper (acts_as_authorization_subject) which adds an ActiveRecord-dependent has_role? method to a class, but that's entirely optional and you're free to implement your own has_role? method (which can also fallback to calling super to get the acl9 one) and implement your access checks however you please. So, you said that you do persist your user model, but let's say you want a role for your user to be the admin of a school, but that school is a web service call into some remote system.
## in your model
class User < ActiveRecord::Base
def has_role? role, obj=nil
role == :admin &&
obj == school &&
school[:admin] == id # <-- just making up how we know we're the admin of the remote school
end
end
def school
#school ||= School.get_by_user_id(id)
end
end
## in your controller
class SomeController < ApplicationController
before_action :set_school
access_control do
allow :admin, of: :school
end
private
def set_school
#school = School.get_by_id(params[:school_id])
end
end