on_success callback for Authorization

pull/1636/head
Markus Kahl 10 years ago
parent c430349640
commit 41603b0f43
  1. 20
      app/controllers/account_controller.rb
  2. 21
      app/controllers/concerns/omniauth_login.rb
  3. 69
      lib/open_project/omni_auth/authorization.rb
  4. 16
      spec/controllers/concerns/omniauth_login_spec.rb
  5. 59
      spec/lib/open_project/omni_auth/authorization_spec.rb

@ -299,23 +299,25 @@ class AccountController < ApplicationController
end
# Register a user depending on Setting.self_registration
def register_user_according_to_setting(user, &block)
def register_user_according_to_setting(user, opts = {}, &block)
case Setting.self_registration
when '1'
register_by_email_activation(user, &block)
register_by_email_activation(user, opts, &block)
when '3'
register_automatically(user, &block)
register_automatically(user, opts, &block)
else
register_manually_by_administrator(user, &block)
register_manually_by_administrator(user, opts, &block)
end
end
# Register a user for email activation.
#
# Pass a block for behavior when a user fails to save
def register_by_email_activation(user, &block)
def register_by_email_activation(user, opts = {})
token = Token.new(:user => user, :action => "register")
if user.save and token.save
opts[:on_success].call user if opts[:on_success]
UserMailer.user_signed_up(token).deliver
flash[:notice] = l(:notice_account_register_done)
redirect_to :action => 'login'
@ -327,12 +329,14 @@ class AccountController < ApplicationController
# Automatically register a user
#
# Pass a block for behavior when a user fails to save
def register_automatically(user, &block)
def register_automatically(user, opts = {})
# Automatic activation
user.activate
user.last_login_on = Time.now
if user.save
opts[:on_success].call user if opts[:on_success]
self.logged_user = user
flash[:notice] = l(:notice_account_registered_and_logged_in)
redirect_after_login(user)
@ -344,8 +348,10 @@ class AccountController < ApplicationController
# Manual activation by the administrator
#
# Pass a block for behavior when a user fails to save
def register_manually_by_administrator(user, &block)
def register_manually_by_administrator(user, opts = {})
if user.save
opts[:on_success].call user if opts[:on_success]
# Sends an email to the administrators
admins = User.admin.active
admins.each do |admin|

@ -51,7 +51,10 @@ module Concerns::OmniauthLogin
if user.new_record?
create_user_from_omniauth user, auth_hash
else
user.log_successful_login if user.active?
if user.active?
user.log_successful_login
OpenProject::OmniAuth::Authorization.authorized! user
end
login_user_if_active(user)
end
end
@ -74,8 +77,10 @@ module Concerns::OmniauthLogin
fill_user_fields_from_omniauth user, auth_hash
opts = { on_success: ->(u) { OpenProject::OmniAuth::Authorization.authorized! u } }
# Create on the fly
register_user_according_to_setting(user) do
register_user_according_to_setting(user, opts) do
# Allow registration form to show provider-specific title
@omniauth_strategy = auth_hash[:provider]
@ -91,9 +96,9 @@ module Concerns::OmniauthLogin
auth = session[:auth_source_registration]
return if handle_omniauth_registration_expired(auth)
fill_user_fields_from_omniauth(@user, auth)
@user.update_attributes(permitted_params.user_register_via_omniauth)
register_user_according_to_setting(@user)
fill_user_fields_from_omniauth(user, auth)
user.update_attributes(permitted_params.user_register_via_omniauth)
register_user_according_to_setting(user)
end
def fill_user_fields_from_omniauth(user, auth)
@ -102,12 +107,6 @@ module Concerns::OmniauthLogin
user
end
def user_with_info(user, auth_hash)
user.dup.tap do |u|
u.assign_attributes omniauth_hash_to_user_attributes(auth_hash)
end
end
def omniauth_hash_to_user_attributes(auth)
info = auth[:info]
{

@ -20,6 +20,16 @@ module OpenProject
rejection || Approval.new
end
##
# Signals that the given user has been successfully authorized.
#
# Note: Only call if you know what you are doing.
def self.authorized!(user)
authorized_callbacks.each do |callback|
callback.authorized user
end
end
##
# Adds a callback to be executed before a user is logged in.
# The given callback may reject the user to prevent authorization by
@ -40,12 +50,12 @@ module OpenProject
if opts[:provider]
authorize_user_for_provider opts[:provider], &block
else
add_authorize_user_callback BlockCallback.new(&block)
add_authorize_user_callback AuthorizationBlockCallback.new(&block)
end
end
def self.authorize_user_for_provider(provider, &block)
callback = BlockCallback.new do |dec, auth_hash|
callback = AuthorizationBlockCallback.new do |dec, auth_hash|
if auth_hash.provider.to_sym == provider.to_sym
block.call dec, auth_hash
else
@ -56,6 +66,19 @@ module OpenProject
add_authorize_user_callback callback
end
##
# Registers a callback on the event of a successful user authorization.
#
# @yield [user] Callback called with the successfully authorized user.
# @yieldparam user [User] User who has been authorized.
def self.on_success(&block)
add_authorized_callback AuthorizedBlockCallback.new(&block)
end
##
# Registers a new callback to decide whether or not a user is to be authorized.
#
# @param [AuthorizationCallback] Callback to be called upon user authorization.
def self.add_authorize_user_callback(callback)
callbacks << callback
end
@ -64,9 +87,21 @@ module OpenProject
@callbacks ||= []
end
##
# Registers a new callback to successful user authorization.
#
# @param [AuthorizedCallback] Callback to be called upon successful authorization.
def self.add_authorized_callback(callback)
authorized_callbacks << callback
end
def self.authorized_callbacks
@authorized_callbacks ||= []
end
##
# Performs user authorization.
class Callback
class AuthorizationCallback
##
# Given an OmniAuth auth hash this decides if a user is authorized or not.
#
@ -81,7 +116,7 @@ module OpenProject
##
# A callback triggering a given block.
class BlockCallback < Callback
class AuthorizationBlockCallback < AuthorizationCallback
attr_reader :block
def initialize(&block)
@ -96,6 +131,32 @@ module OpenProject
end
end
##
# A callback for reacting to a user being authorized.
class AuthorizedCallback
##
# Is called after a user has been authorized successfully.
#
# @param [User] User who has been authorized.
def authorized(user)
fail "subclass responsibility: authorized(#{user})"
end
end
##
# A authorized callback triggering a given block.
class AuthorizedBlockCallback < AuthorizedCallback
attr_reader :block
def initialize(&block)
@block = block
end
def authorized(user)
block.call user
end
end
##
# Abstract base class for an authorization decision.
# Any subclass must either override #approve? or #reject?

@ -258,6 +258,8 @@ describe AccountController do
end
it 'works' do
expect(OpenProject::OmniAuth::Authorization).to receive(:authorized!).with(user)
post :omniauth_login
expect(response).to redirect_to my_page_path
@ -269,6 +271,8 @@ describe AccountController do
end
it 'is rejected against google' do
expect(OpenProject::OmniAuth::Authorization).not_to receive(:authorized!).with(user)
post :omniauth_login
expect(response).to redirect_to signin_path
@ -276,6 +280,8 @@ describe AccountController do
end
it 'is rejected against any other provider too' do
expect(OpenProject::OmniAuth::Authorization).not_to receive(:authorized!).with(user)
omniauth_hash.provider = 'any other'
post :omniauth_login
@ -292,6 +298,8 @@ describe AccountController do
end
it 'is rejected against google' do
expect(OpenProject::OmniAuth::Authorization).not_to receive(:authorized!).with(user)
post :omniauth_login
expect(response).to redirect_to signin_path
@ -299,6 +307,12 @@ describe AccountController do
end
it 'is approved against any other provider' do
expect(OpenProject::OmniAuth::Authorization).to receive(:authorized!) do |u|
new_user = User.find_by_identity_url 'some other:123545'
expect(u).to eq new_user
end
omniauth_hash.provider = 'some other'
post :omniauth_login
@ -312,6 +326,8 @@ describe AccountController do
# ... and to confirm that, here's what happens when the authorization fails
it 'is rejected against any other provider with the wrong email' do
expect(OpenProject::OmniAuth::Authorization).not_to receive(:authorized!).with(user)
omniauth_hash.provider = 'yet another'
config.global_email = 'yarrrr@joro.es'

@ -0,0 +1,59 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2014 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
describe OpenProject::OmniAuth::Authorization do
describe '.authorized!' do
let(:user) { FactoryGirl.create :user, mail: 'foo@bar.de' }
let(:state) { Struct.new(:number, :user_email).new 0, nil }
before do
OpenProject::OmniAuth::Authorization.authorized_callbacks.clear
OpenProject::OmniAuth::Authorization.on_success do |_|
state.number = 42
end
OpenProject::OmniAuth::Authorization.on_success do |user|
state.user_email = user.mail
end
end
after do
OpenProject::OmniAuth::Authorization.authorized_callbacks.clear
end
it 'triggers every callback setting number to 42 and user_email to foo@bar.de' do
OpenProject::OmniAuth::Authorization.authorized! user
expect(state.number).to eq 42
expect(state.user_email).to eq 'foo@bar.de'
end
end
end
Loading…
Cancel
Save