moved strategies; refactoring

pull/2978/head
Markus Kahl 10 years ago
parent 75ec64d94c
commit 7e40c11ca4
  1. 18
      config/initializers/warden.rb
  2. 16
      lib/open_project/authentication/strategies/warden/basic_auth_failure.rb
  3. 78
      lib/open_project/authentication/strategies/warden/global_basic_auth.rb
  4. 30
      lib/open_project/authentication/strategies/warden/session.rb
  5. 34
      lib/open_project/authentication/strategies/warden/user_basic_auth.rb
  6. 12
      lib/warden/strategies/basic_auth_failure.rb
  7. 62
      lib/warden/strategies/global_basic_auth.rb
  8. 26
      lib/warden/strategies/session.rb
  9. 30
      lib/warden/strategies/user_basic_auth.rb
  10. 21
      spec/lib/api/v3/authentication_spec.rb
  11. 41
      spec/lib/open_project/authentication/strategies/warden/global_basic_auth_spec.rb

@ -1,14 +1,16 @@
require 'open_project/authentication'
# Strategies provided by OpenProject:
require 'warden/strategies/basic_auth_failure'
require 'warden/strategies/global_basic_auth'
require 'warden/strategies/user_basic_auth'
require 'warden/strategies/session'
require 'open_project/authentication/strategies/warden/basic_auth_failure'
require 'open_project/authentication/strategies/warden/global_basic_auth'
require 'open_project/authentication/strategies/warden/user_basic_auth'
require 'open_project/authentication/strategies/warden/session'
strategies = [
[:basic_auth_failure, Warden::Strategies::BasicAuthFailure],
[:global_basic_auth, Warden::Strategies::GlobalBasicAuth],
[:user_basic_auth, Warden::Strategies::UserBasicAuth],
[:session, Warden::Strategies::Session]
[:basic_auth_failure, OpenProject::Authentication::Strategies::Warden::BasicAuthFailure],
[:global_basic_auth, OpenProject::Authentication::Strategies::Warden::GlobalBasicAuth],
[:user_basic_auth, OpenProject::Authentication::Strategies::Warden::UserBasicAuth],
[:session, OpenProject::Authentication::Strategies::Warden::Session]
]
strategies.each do |name, clazz|

@ -0,0 +1,16 @@
module OpenProject
module Authentication
module Strategies
module Warden
##
# This strategy is inserted after optional basic auth strategies to
# indicate that invalid basic auth credentials were provided.
class BasicAuthFailure < ::Warden::Strategies::BasicAuth
def authenticate_user(_username, _password)
nil # always fails
end
end
end
end
end
end

@ -0,0 +1,78 @@
require 'warden/basic_auth'
module OpenProject
module Authentication
module Strategies
module Warden
##
# Allows authentication via a singular set of basic auth credentials for admin access.
#
# The credentials must be configured in `config/configuration.yml` like this:
#
# production:
# authentication:
# global_basic_auth:
# user: admin
# password: 123456
#
# The strategy will only be triggered when the configured user name is sent.
# Meaning that this strategy is skipped if a basic auth attempt involving any
# other user name is made.
class GlobalBasicAuth < ::Warden::Strategies::BasicAuth
def self.configuration
@configuration ||= configure!
end
##
# Updates the configuration for this strategy. It's usually called only once, at startup.
#
# @param [Hash] config The configuration to be used. Must contain :user and :password.
# @raise [ArgumentError] Raises an error if the configured user name collides with the
# user name used for UserBasicAuth (apikey).
def self.configure!(config = openproject_config)
if config[:user] == UserBasicAuth.user
raise ArgumentError, "global user must not be '#{UserBasicAuth.user}'"
end
@configuration = config
end
##
# Reads the configuration for this strategy from OpenProject's `configuration.yml`.
def self.openproject_config
config = OpenProject::Configuration
%w(authentication global_basic_auth).inject(config) do |acc, key|
HashWithIndifferentAccess.new acc[key]
end
end
def self.configuration?
user && password
end
def self.user
configuration[:user]
end
def self.password
configuration[:password]
end
##
# Only valid if global basic auth is configured and tried.
def valid?
self.class.configuration? && super && username == self.class.user
end
def authenticate_user(username, password)
if username == self.class.user && password == self.class.password
User.system.tap do |user|
user.admin = true
end
end
end
end
end
end
end
end

@ -0,0 +1,30 @@
module OpenProject
module Authentication
module Strategies
module Warden
##
# Temporary strategy necessary as long as the OpenProject authentication has not been unified
# in terms of Warden strategies and is only locally applied to the API v3.
class Session < ::Warden::Strategies::Base
def valid?
session
end
def authenticate!
user = user_id ? User.find(user_id) : User.anonymous
success! user
end
def user_id
Hash(session)['user_id']
end
def session
env['rack.session']
end
end
end
end
end
end

@ -0,0 +1,34 @@
require 'warden/basic_auth'
module OpenProject
module Authentication
module Strategies
module Warden
##
# Allows users to authenticate using their API key via basic auth.
# Note that in order for a user to be able to generate one
# `Setting.rest_api_enabled` has to be `1`.
#
# The basic auth credentials are expected to contain the literal 'apikey'
# as the user name and the API key as the password.
class UserBasicAuth < ::Warden::Strategies::BasicAuth
def self.user
'apikey'
end
def valid?
super && username == self.class.user
end
def authenticate_user(_, api_key)
token(api_key).try(:user)
end
def token(value)
Token.where(action: 'api', value: value).first
end
end
end
end
end
end

@ -1,12 +0,0 @@
module Warden
module Strategies
##
# This strategy is inserted after optional basic auth strategies to
# indicate that invalid basic auth credentials were provided.
class BasicAuthFailure < BasicAuth
def authenticate_user(_username, _password)
nil # always fails
end
end
end
end

@ -1,62 +0,0 @@
require 'warden/basic_auth'
module Warden
module Strategies
##
# Allows authentication via a singular set of basic auth credentials for admin access.
#
# The credentials must be configured in `config/configuration.yml` like this:
#
# production:
# authentication:
# global_basic_auth:
# user: admin
# password: 123456
#
# The strategy will only be triggered when the configured user name is sent.
# Meaning that this strategy is skipped if a basic auth attempt involving any
# other user name is made.
class GlobalBasicAuth < BasicAuth
def self.configuration
@configuration ||= configuration!
end
def self.configuration!
path = %w(authentication global_basic_auth)
@configuration = path.inject(OpenProject::Configuration) { |acc, key| Hash(acc[key]) }
if user == UserBasicAuth.user
raise ArgumentError, "global user must not be '#{UserBasicAuth.user}'"
end
@configuration
end
def self.configuration?
user && password
end
def self.user
configuration['user']
end
def self.password
configuration['password']
end
##
# Only valid if global basic auth is configured and tried.
def valid?
self.class.configuration && super && username == self.class.user
end
def authenticate_user(username, password)
if username == self.class.user && password == self.class.password
User.system.tap do |user|
user.admin = true
end
end
end
end
end
end

@ -1,26 +0,0 @@
module Warden
module Strategies
##
# Temporary strategy necessary as long as the OpenProject authentication has not been unified
# in terms of Warden strategies and is only locally applied to the API v3.
class Session < Base
def valid?
session
end
def authenticate!
user = user_id ? User.find(user_id) : User.anonymous
success! user
end
def user_id
Hash(session)['user_id']
end
def session
env['rack.session']
end
end
end
end

@ -1,30 +0,0 @@
require 'warden/basic_auth'
module Warden
module Strategies
##
# Allows users to authenticate using their API key via basic auth.
# Note that in order for a user to be able to generate one
# `Setting.rest_api_enabled` has to be `1`.
#
# The basic auth credentials are expected to contain the literal 'apikey'
# as the user name and the API key as the password.
class UserBasicAuth < BasicAuth
def self.user
'apikey'
end
def valid?
super && username == self.class.user
end
def authenticate_user(_, api_key)
token(api_key).try(:user)
end
def token(value)
Token.where(action: 'api', value: value).first
end
end
end
end

@ -33,9 +33,11 @@ describe API::V3, type: :request do
let(:user) { FactoryGirl.create :user }
let(:resource) { "/api/v3/users/#{user.id}"}
Strategies = OpenProject::Authentication::Strategies::Warden
def basic_auth(user, password)
credentials = ActionController::HttpAuthentication::Basic.encode_credentials user, password
{'HTTP_AUTHORIZATION' => credentials}
{ 'HTTP_AUTHORIZATION' => credentials }
end
shared_examples 'it is basic auth protected' do
@ -80,13 +82,7 @@ describe API::V3, type: :request do
let(:password) { 'toor' }
before do
authentication = {
'global_basic_auth' => {
'user' => 'root',
'password' => 'toor'
}
}
OpenProject::Configuration['authentication'] = authentication
Strategies::GlobalBasicAuth.configure! user: 'root', password: 'toor'
end
it_behaves_like 'it is basic auth protected'
@ -116,13 +112,8 @@ describe API::V3, type: :request do
let(:api_key) { FactoryGirl.create :api_key, user: api_user }
before do
authentication = {
'global_basic_auth' => {
'user' => 'global_account',
'password' => 'global_password'
}
}
OpenProject::Configuration['authentication'] = authentication
config = { user: 'global_account', password: 'global_password' }
Strategies::GlobalBasicAuth.configure! config
end
context 'without credentials' do

@ -0,0 +1,41 @@
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 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::Authentication::Strategies::Warden::GlobalBasicAuth do
it 'does not allow the UserBasicAuth user name' do
configuration = lambda do
user = OpenProject::Authentication::Strategies::Warden::UserBasicAuth.user
OpenProject::Authentication::Strategies::Warden::GlobalBasicAuth.configure!(
user: user, password: 'foo')
end
expect(configuration).to raise_error(ArgumentError)
end
end
Loading…
Cancel
Save