Override default current user to Guardian current user with Canary - authorization

I am trying to implement Canary into my application, and I have come across a problem. The docs (https://github.com/cpjk/canary#overriding-the-default-user) say that I need to have an Ecto record for the current user in conn.assigns.current_user. Since I am using Guardian, my current user is stored in Guardian.Plug.current_resource(conn). What is the best way to let canary know that this is where I store my current user?
I can't use the config :canary, current_user: :my_user_key because it isn't even in conn.assigns.
Help is appreciated!

Following this article, you should create a Plug for that:
defmodule MyApp.Plug.CurrentUser do
def init(opts), do: opts
def call(conn, _opts) do
current_user = Guardian.Plug.current_resource(conn)
Plug.Conn.assign(conn, :current_user, current_user)
end
end
and put it in a router pipeline:
pipeline :require_login do
plug Guardian.Plug.EnsureAuthenticated, handler: MyApp.GuardianErrorHandler
plug MyApp.Plug.CurrentUser
end

Related

Custom strategy for warden not getting called

I am trying to use a different warden strategy to authenticate my action cable end points.
But the strategy is not getting called. I tried to place warden.authenticate!(:action_cable_auth) in a controller to test but none of the debug statements are getting printed on console.
Below are the relevant part of the code.
config/initializers/warden.rb
Warden::Strategies.add(:action_cable_auth) do
def valid?
#check if its a websocket request & for action cable?
#Rails.logger.error request.inspect
p 'checking if strategy is valid?'
true
end
def authenticate!
p 'unauthenticate the user'
fail!('user not active')
end
end
in my controller
warden.authenticate!(:action_cable_auth)
Assuming that you are setting your initializer in the proper place, please recall that if your session is already instantiated somewhere else (for example if you authenticate the user at the point your action is being called, then your strategy will never be called.
This is basically how warden works: if some valid? strategy returns a success! then no other will be called as soon as any authenticate! method in the list of strategies is successful.
Please also be sure that if you want your strategy up the list of strategies to check you may need to also shift it up on the list, such as:
manager.default_strategies(scope: :user).unshift(:action_cable_auth)
Where the manager is your Warden::Manager instance. The scope may also be optional (this is an example where the user scope is used alongside Devise), but you may check your instance .default_strategies to figure out where it is and where you want it.

Phoenix Elixir: mock internal functions

I'm currently testing controller that uses the function create_zone that depends on a function that retrieves a user to associates said user to a zone and then creates a participant entry that is only an association table of both entries.
def create_zone(attrs \\ %{}, user_id) do
user = Accounts.get_user!(user_id)
with{:ok, %Zone{} = zone} <- %Zone{}
|> Zone.changeset(attrs,user)
|> Repo.insert()
do
create_participant(zone,user)
end
end
And I would like to test it using ExUnit but the problem is that the testing framework tries to search a non existent record in the database.
** (Ecto.NoResultsError) expected at least one result but got none in query:
from u in Module.Accounts.User,
where: u.id == ^1
How could I mock or create it just for testing purposes?
Don't mock it, create it with ex_machina: https://github.com/thoughtbot/ex_machina
Mocking is discouraged in Elixir: http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/ (you don't really need to read it now, but in case you are want to mock some external resource, read it).
You can write a simple factory module that uses Ecto to insert into the database. The test will be wrapped in a database transaction and rolled back automatically thanks to the Ecto.Sandbox.
defmodule Factory do
def create(User) do
%User{
name: "A. User",
email: "user_#{:rand.uniform(10000)}#mail.com"
}
end
def create(Zone) do
%Zone{
# ... random / default zone attributes here...
}
end
def create(schema, attrs) do
schema
|> create()
|> struct(attributes)
end
def insert(schema, attrs \\ []) do
Repo.insert!(create(schema, attrs))
end
end
Then in your test custom attributes are merged with the factory defaults, including associations.
test "A test" do
user = Factory.insert(User, name: "User A")
zone = Zones.create_zone(user.id)
assert zone
end
See chapter 7 of what's new in ecto 2.1 for a more detailed explanation.

Active Admin calling original and overriden create action

I am seeing some odd behaviour when trying to override the create action on an Active Admin resource. My reason for overriding the action is that I want to alter the request params slightly first.
ActiveAdmin.register User do
controller do
def create
format_params
create!
end
def update
format_params
update!
end
def format_params
params[:user] = ...
end
end
end
What I am seeing is that a create action is being invoked twice - but only on the second time is it my overridden version. By that point, the new record is already created, albeit incorrectly, and the second invoking (which is the overridden version) looks like a duplicate resource.
If I empty the custom #create action and leave only a logging statement, I can see that a record is still being created prior to it being invoked, almost as if there is some sort of before_ callback, although I can't find any such thing in the code.
Even more odd - everything works fine on the overridden #update method - the params are altered and the original #update! method delegated to correctly.
I am using Active Admin 0.5.1 on Rails 3.2.13
Cheers

Resque and Resque_mailer with Devise

I am implementing background email processing with Resque using the resque_mailer gem (https://github.com/zapnap/resque_mailer). I was able to get it to work for all my emails except the ones sent by Devise.
I went through a bunch of SO questions, and blog posts (for instance http://teeparham.posterous.com/send-devise-emails-with-resque) but could not find a way to get it to work.
What are the precise steps to follow to get resque_mailer to work with Devise?
I went through tee's answer and several resources online, but couldn't find a working solution.
After a few days of reading through resque-mailer and devise code, a solution that worked for me. Thanks to tee for gist which put me in right direction.
Assuming your app/mailers/application_mailer.rb looks similar to
class ApplicationMailer < ActionMailer::Base
include Resque::Mailer # This will add a `self.perform` class method, which we will overwrite in DeviseResqueMailer
end
In config/initializers/devise.rb
Devise.parent_mailer = "ApplicationMailer"
Devise.setup do |config|
config.mailer = 'DeviseResqueMailer'
end
In the resource class which uses devise, overwrite the send_devise_notification method to send resource class and id instead of object to prevent marshalling
# app/models/user.rb
protected
def send_devise_notification(notification, *args)
# Based on https://github.com/zapnap/resque_mailer/blob/64d2be9687e320de4295c1bd1b645f42bd547743/lib/resque_mailer.rb#L81
# Mailer may completely skip Resque::Mailer in certain cases - and will fail as we write custom handle in DeviseResqueMailer assuming mails are handled via resque
# So in those cases, don't retain original devise_mailer so things work properly
if ActionMailer::Base.perform_deliveries && Resque::Mailer.excluded_environments.exclude?(Rails.env.to_sym)
# Originally devise_mailer.send(notification, self, *args).deliver
# Modified to ensure devise mails are safely sent via resque
resource_id, resource_class = self.id, self.class.name
devise_mailer.send(notification, {resource_id: resource_id, resource_class: resource_class}, *args).deliver
else
super
end
end
Finally, in app/mailers/devise_resque_mailer.rb, fetch the record again from the database and continue
class DeviseResqueMailer < Devise::Mailer
def self.perform(action, *args)
# Hack to prevent RuntimeError - Could not find a valid mapping for admin.attributes
record_hash = args.shift
record = record_hash["resource_class"].constantize.find(record_hash["resource_id"])
args.unshift(record)
super # From resque-mailer
end
end
I feel this approach is a better than using devise-async as all the mails go through same code path. Its easier to control and overwrite if needed.
I'd take a look at devise-async. Looks like it fits your use case. Devise Async

Deny entire application with Rack?

I have a client who wishes to lock their entire application under certain conditions. This seems like a good job for Rack Middleware, so I wrote the following:
class Duress
def initialize(app)
#app = app
end
def call(env)
if env['UNDER_DURESS'] == true
# Return HTTP 503 - Service Unavailable
[503, {"Content-Type" => "text/html"},["<h1>Service Unavailable.</h1><p>Please try again later.</p>"]]
else
#app.call(env)
end
end
end
Firstly, I am not sure if an environment variable is necessary or even best practice, so I am open to ideas here. I am not even sure the above code will work.
Secondly, I am having problems hooking into the Devise login process to set this environment variable. I have tried over riding the after_sign_in_path_for method in application_controller, but it does not seem to to triggered. Any tips here?