Skip to content

Commit d297422

Browse files
author
Robert Mitwicki
committed
Merge pull request #219 from davexunit/fix-sql-authenticator
Allow multiple SQL authenticators to be used without conflict.
2 parents c111a18 + a4fa03b commit d297422

2 files changed

Lines changed: 139 additions & 15 deletions

File tree

lib/casserver/authenticators/sql.rb

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,36 @@
5454
class CASServer::Authenticators::SQL < CASServer::Authenticators::Base
5555
def self.setup(options)
5656
raise CASServer::AuthenticatorError, "Invalid authenticator configuration!" unless options[:database]
57-
58-
user_model_name = "CASUser_#{options[:auth_index]}"
57+
auth_index = options[:auth_index]
58+
user_table = options[:user_table] || 'users'
59+
user_model_name = "CASUser_#{auth_index}"
5960
$LOG.debug "CREATING USER MODEL #{user_model_name}"
6061

6162
class_eval %{
6263
class #{user_model_name} < ActiveRecord::Base
6364
end
6465
}
6566

66-
@user_model = const_get(user_model_name)
67-
@user_model.establish_connection(options[:database])
67+
user_model = const_get(user_model_name)
68+
# Register new user module, identified by auth_index.
69+
user_models[auth_index] = user_model
70+
user_model.establish_connection(options[:database])
6871
if ActiveRecord::VERSION::STRING >= '3.2'
69-
@user_model.table_name = (options[:user_table] || 'users')
72+
user_model.table_name = user_table
7073
else
71-
@user_model.set_table_name(options[:user_table] || 'users')
74+
user_model.set_table_name(user_table)
7275
end
73-
@user_model.inheritance_column = 'no_inheritance_column' if options[:ignore_type_column]
76+
user_model.inheritance_column = 'no_inheritance_column' if options[:ignore_type_column]
7477
begin
75-
@user_model.connection
78+
user_model.connection
7679
rescue => e
7780
$LOG.debug e
7881
raise "SQL Authenticator can not connect to database"
7982
end
8083
end
8184

82-
def self.user_model
83-
@user_model
85+
def self.user_models
86+
@user_models ||= {}
8487
end
8588

8689
def validate(credentials)
@@ -89,15 +92,16 @@ def validate(credentials)
8992

9093
log_connection_pool_size
9194
user_model.connection_pool.checkin(user_model.connection)
95+
users = matching_users
9296

93-
if matching_users.size > 0
94-
$LOG.warn("#{self.class}: Multiple matches found for user #{@username.inspect}") if matching_users.size > 1
97+
if users.size > 0
98+
$LOG.warn("#{self.class}: Multiple matches found for user #{@username.inspect}") if users.size > 1
9599

96100
unless @options[:extra_attributes].blank?
97-
if matching_users.size > 1
101+
if users.size > 1
98102
$LOG.warn("#{self.class}: Unable to extract extra_attributes because multiple matches were found for #{@username.inspect}")
99103
else
100-
user = matching_users.first
104+
user = users.first
101105

102106
extract_extra(user)
103107
log_extra
@@ -113,7 +117,11 @@ def validate(credentials)
113117
protected
114118

115119
def user_model
116-
self.class.user_model
120+
self.class.user_models[auth_index]
121+
end
122+
123+
def auth_index
124+
@options[:auth_index]
117125
end
118126

119127
def username_column
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
require 'spec_helper'
2+
3+
describe CASServer::Authenticators::SQL do
4+
let(:options) do
5+
{
6+
auth_index: 0,
7+
user_table: 'users',
8+
username_column: 'username',
9+
password_column: 'password',
10+
database: {
11+
adapter: 'mysql2',
12+
database: 'casserver',
13+
username: 'root',
14+
password: 'password',
15+
host: 'localhost'
16+
}
17+
}
18+
end
19+
let(:connection) { double('Connection', run_callbacks: nil) }
20+
let(:connection_pool) { double('ConnectionPool',
21+
connections: [connection],
22+
checkin: nil) }
23+
24+
before do
25+
load_server('default_config') if $LOG.nil? # ensure logger is present
26+
ActiveRecord::Base.stub(:establish_connection)
27+
ActiveRecord::Base.stub(:connection).and_return(connection)
28+
ActiveRecord::Base.stub(:connection_pool).and_return(connection_pool)
29+
CASServer::Authenticators::SQL.setup(options)
30+
end
31+
32+
describe '#validate' do
33+
let(:auth) { CASServer::Authenticators::SQL.new }
34+
let(:username) { 'dave' }
35+
let(:password) { 'secret' }
36+
let(:user_model) { CASServer::Authenticators::SQL.user_models[0] }
37+
38+
before do
39+
auth.configure(HashWithIndifferentAccess.new(options))
40+
end
41+
42+
context 'when credentials match a user in the database' do
43+
it 'returns true' do
44+
conditions = ['username = ? AND password = ?', username, password]
45+
user_model.should_receive(:find).with(:all, conditions: conditions)
46+
.and_return([:user])
47+
credentials = {
48+
username: username,
49+
password: password
50+
}
51+
expect(auth.validate(credentials)).to be true
52+
end
53+
end
54+
55+
context 'when credentials do not match a user in the database' do
56+
it 'returns false' do
57+
conditions = ['username = ? AND password = ?', username, password]
58+
user_model.should_receive(:find).with(:all, conditions: conditions)
59+
.and_return([])
60+
credentials = {
61+
username: username,
62+
password: password
63+
}
64+
expect(auth.validate(credentials)).to be false
65+
end
66+
end
67+
68+
context 'when many SQL authenticators have been setup' do
69+
let(:alt_options) do
70+
{
71+
auth_index: 1,
72+
user_table: 'users',
73+
username_column: 'username',
74+
password_column: 'password',
75+
database: {
76+
adapter: 'mysql2',
77+
database: 'casserver',
78+
username: 'root',
79+
password: 'password',
80+
host: 'localhost'
81+
}
82+
}
83+
end
84+
85+
before do
86+
CASServer::Authenticators::SQL.setup(alt_options)
87+
end
88+
89+
it 'chooses the correct user model based upon auth_index' do
90+
# Original authenticator
91+
conditions = ['username = ? AND password = ?', username, password]
92+
user_model.should_receive(:find).with(:all, conditions: conditions)
93+
.and_return([:user])
94+
credentials = {
95+
username: username,
96+
password: password
97+
}
98+
expect(auth.validate(credentials)).to be true
99+
100+
# Alternate authenticator, different credentials, different user model
101+
alt_auth = CASServer::Authenticators::SQL.new
102+
alt_user_model = CASServer::Authenticators::SQL.user_models[1]
103+
alt_username = 'dan'
104+
conditions = ['username = ? AND password = ?', alt_username, password]
105+
alt_user_model.should_receive(:find).with(:all, conditions: conditions)
106+
.and_return([:user])
107+
alt_credentials = {
108+
username: alt_username,
109+
password: password
110+
}
111+
alt_auth.configure(HashWithIndifferentAccess.new(alt_options))
112+
expect(alt_auth.validate(alt_credentials)).to be true
113+
end
114+
end
115+
end
116+
end

0 commit comments

Comments
 (0)