devise_bushido_authenticatable 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -20
- data/Gemfile.lock +151 -74
- data/README.md +4 -2
- data/Rakefile +1 -46
- data/app/controllers/devise/cas_sessions_controller.rb +59 -10
- data/app/views/devise/cas_sessions/unregistered.html.erb +150 -2
- data/app/views/devise/cas_sessions/unregistered.html.erb.old +2 -0
- data/devise_bushido_authenticatable.gemspec +28 -95
- data/lib/devise_bushido_authenticatable/version.rb +3 -0
- data/lib/devise_cas_authenticatable.rb +45 -3
- data/lib/devise_cas_authenticatable/missing_session_helpers.rb +9 -0
- data/lib/devise_cas_authenticatable/model.rb +5 -6
- data/lib/devise_cas_authenticatable/routes.rb +5 -3
- data/lib/devise_cas_authenticatable/single_sign_out.rb +22 -0
- data/lib/devise_cas_authenticatable/single_sign_out/session_store/active_record.rb +12 -0
- data/lib/devise_cas_authenticatable/single_sign_out/session_store/redis.rb +27 -0
- data/lib/devise_cas_authenticatable/single_sign_out/strategies.rb +58 -0
- data/lib/devise_cas_authenticatable/single_sign_out/strategies/base.rb +11 -0
- data/lib/devise_cas_authenticatable/single_sign_out/strategies/rails_cache.rb +31 -0
- data/lib/devise_cas_authenticatable/strategy.rb +15 -11
- data/spec/devise_cas_authenticatable/model_spec.rb +39 -0
- data/spec/routes_spec.rb +25 -7
- data/spec/scenario/app/models/user.rb +2 -2
- data/spec/scenario/config/environments/development.rb +0 -1
- data/spec/scenario/config/initializers/castronaut.rb +1 -0
- data/spec/scenario/config/initializers/session_store.rb +2 -2
- data/spec/scenario/db/migrate/20100401102949_create_tables.rb +3 -2
- data/spec/scenario/db/migrate/20111002012903_add_sessions_table.rb +16 -0
- data/spec/scenario/db/schema.rb +25 -0
- data/spec/spec_helper.rb +3 -2
- data/spec/strategy_spec.rb +20 -29
- metadata +118 -27
- data/VERSION +0 -1
- data/devise_cas_authenticatable.gemspec +0 -118
@@ -0,0 +1,12 @@
|
|
1
|
+
ActiveRecord::SessionStore.class_eval do
|
2
|
+
|
3
|
+
include DeviseCasAuthenticatable::SingleSignOut::SetSession
|
4
|
+
alias_method_chain :set_session, :storage
|
5
|
+
|
6
|
+
#def destroy_session(env, session_id, options)
|
7
|
+
# if session = Session::find_by_session_id(sid)
|
8
|
+
# session.destroy
|
9
|
+
# end
|
10
|
+
#end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "action_controller/session/redis_session_store"
|
2
|
+
|
3
|
+
module DeviseCasAuthenticatable
|
4
|
+
module SingleSignOut
|
5
|
+
module RedisSessionStore
|
6
|
+
|
7
|
+
include DeviseCasAuthenticatable::SingleSignOut::SetSession
|
8
|
+
|
9
|
+
def destroy_session(sid)
|
10
|
+
@pool.del(sid)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
if ::Redis::Store.rails3?
|
18
|
+
ActionDispatch::Session::RedisSessionStore.class_eval do
|
19
|
+
include DeviseCasAuthenticatable::SingleSignOut::RedisSessionStore
|
20
|
+
alias_method_chain :set_session, :storage
|
21
|
+
end
|
22
|
+
else
|
23
|
+
ActionController::Session::RedisSessionStore.class_eval do
|
24
|
+
include DeviseCasAuthenticatable::SingleSignOut::RedisSessionStore
|
25
|
+
alias_method_chain :set_session, :storage
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module DeviseCasAuthenticatable
|
2
|
+
module SingleSignOut
|
3
|
+
module Strategies
|
4
|
+
class << self
|
5
|
+
|
6
|
+
# Add a strategy and store it in a hash.
|
7
|
+
def add(label, strategy, &block)
|
8
|
+
strategy ||= Class.new(DeviseCasAuthenticatable::SingleSignOut::Strategies::Base)
|
9
|
+
strategy.class_eval(&block) if block_given?
|
10
|
+
|
11
|
+
check_method(label, strategy, :store_session_id_for_index)
|
12
|
+
check_method(label, strategy, :find_session_id_by_index)
|
13
|
+
check_method(label, strategy, :delete_session_index)
|
14
|
+
|
15
|
+
unless strategy.ancestors.include?(DeviseCasAuthenticatable::SingleSignOut::Strategies::Base)
|
16
|
+
raise "#{label.inspect} is not a #{base}"
|
17
|
+
end
|
18
|
+
|
19
|
+
_strategies[label] = strategy.new()
|
20
|
+
end
|
21
|
+
|
22
|
+
# Update a previously given strategy.
|
23
|
+
def update(label, &block)
|
24
|
+
strategy = _strategies[label]
|
25
|
+
raise "Unknown strategy #{label.inspect}" unless strategy
|
26
|
+
add(label, strategy, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Provides access to strategies by label
|
30
|
+
def [](label)
|
31
|
+
_strategies[label]
|
32
|
+
end
|
33
|
+
|
34
|
+
def current_strategy
|
35
|
+
self[::Devise.cas_single_sign_out_mapping_strategy]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Clears all declared.
|
39
|
+
def clear!
|
40
|
+
_strategies.clear
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def _strategies
|
46
|
+
@strategies ||= {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_method(label, strategy, method)
|
50
|
+
unless strategy.method_defined?(method)
|
51
|
+
raise NoMethodError, "#{method.to_s} is not declared in the #{label.inspect} strategy"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module DeviseCasAuthenticatable
|
2
|
+
module SingleSignOut
|
3
|
+
module Strategies
|
4
|
+
class RailsCache < Base
|
5
|
+
def store_session_id_for_index(session_index, session_id)
|
6
|
+
logger.debug("Storing #{session_id} for index #{session_index}")
|
7
|
+
Rails.cache.write(cache_key(session_index), session_id)
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_session_id_by_index(session_index)
|
11
|
+
sid = Rails.cache.read(cache_key(session_index))
|
12
|
+
logger.debug("Found session id #{sid.inspect} for index #{session_index.inspect}")
|
13
|
+
sid
|
14
|
+
end
|
15
|
+
|
16
|
+
def delete_session_index(session_index)
|
17
|
+
logger.info("Deleting index #{session_index}")
|
18
|
+
Rails.cache.delete(cache_key(session_index))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def cache_key(session_index)
|
24
|
+
"devise_cas_authenticatable:#{session_index}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
::DeviseCasAuthenticatable::SingleSignOut::Strategies.add( :rails_cache, DeviseCasAuthenticatable::SingleSignOut::Strategies::RailsCache )
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'devise/strategies/base'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
2
4
|
|
3
5
|
module Devise
|
4
6
|
module Strategies
|
@@ -15,18 +17,20 @@ module Devise
|
|
15
17
|
# or attempt to redirect to the CAS server's login URL.
|
16
18
|
def authenticate!
|
17
19
|
ticket = read_ticket(params)
|
18
|
-
if ticket
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
redirect!(::Devise.cas_unregistered_url(request.url, mapping), :ido_id => ticket.response.user)
|
26
|
-
#fail!("The user #{ticket.response.user} is not registered with this site. Please use a different account.")
|
27
|
-
else
|
28
|
-
fail!(:invalid)
|
20
|
+
fail!(:invalid) if not ticket
|
21
|
+
|
22
|
+
if resource = mapping.to.authenticate_with_cas_ticket(ticket)
|
23
|
+
# Store the ticket in the session for later usage
|
24
|
+
if ::Devise.cas_enable_single_sign_out
|
25
|
+
session['cas_last_valid_ticket'] = ticket.ticket
|
26
|
+
session['cas_last_valid_ticket_store'] = true
|
29
27
|
end
|
28
|
+
|
29
|
+
success!(resource)
|
30
|
+
elsif ticket.is_valid?
|
31
|
+
ido_id = ticket.respond_to?(:user) ? ticket.user : ticket.response.user
|
32
|
+
redirect!(::Devise.cas_unregistered_url(request.url, mapping), :ido_id => ido_id)
|
33
|
+
#fail!("The user #{ticket.response.user} is not registered with this site. Please use a different account.")
|
30
34
|
else
|
31
35
|
fail!(:invalid)
|
32
36
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Devise::Models::BushidoAuthenticatable do
|
4
|
+
|
5
|
+
class ExampleAuth
|
6
|
+
include Devise::Models::BushidoAuthenticatable
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "authenticate_with_cas_ticket" do
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
@ticket = Object.new
|
13
|
+
@user = Object.new
|
14
|
+
|
15
|
+
@ticket.should_receive(:user).and_return(@user)
|
16
|
+
@ticket.should_receive(:has_been_validated?).and_return(true)
|
17
|
+
@ticket.should_receive(:is_valid?).and_return(true)
|
18
|
+
::Devise.cas_create_user = true
|
19
|
+
|
20
|
+
ExampleAuth.should_receive(:find_for_authentication).and_return(@user)
|
21
|
+
@user.should_receive(:save)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should call the bushido_extra_attributes method if it's defined on the devise resource" do
|
25
|
+
@ticket.should_receive(:extra_attributes)
|
26
|
+
@user.should_receive(:bushido_extra_attributes)
|
27
|
+
ExampleAuth.authenticate_with_cas_ticket(@ticket)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should *not* call the bushido_extra_attributes method if it's *not* defined on the devise resource" do
|
31
|
+
|
32
|
+
@user.should_receive(:respond_to?).and_return(false)
|
33
|
+
@user.should_not_receive(:bushido_extra_attributes)
|
34
|
+
|
35
|
+
ExampleAuth.authenticate_with_cas_ticket(@ticket)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/spec/routes_spec.rb
CHANGED
@@ -1,13 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
4
|
-
include RSpec::Rails::
|
3
|
+
describe "routing" do
|
4
|
+
include RSpec::Rails::RoutingExampleGroup
|
5
|
+
|
6
|
+
it "routes to #service" do
|
7
|
+
get("/users/service").should route_to("devise/cas_sessions#service")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "routes to #new" do
|
11
|
+
get("/users/sign_in").should route_to("devise/cas_sessions#new")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "routes to #create" do
|
15
|
+
post("/users/sign_in").should route_to("devise/cas_sessions#create")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "routes to #destroy" do
|
19
|
+
get("/users/sign_out").should route_to("devise/cas_sessions#destroy")
|
20
|
+
end
|
5
21
|
|
6
|
-
it
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
22
|
+
it "routes to #unregistered" do
|
23
|
+
get("/users/unregistered").should route_to("devise/cas_sessions#unregistered")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe Devise::CasSessionsController do
|
28
|
+
include RSpec::Rails::ControllerExampleGroup
|
11
29
|
|
12
30
|
it "should have the right route names" do
|
13
31
|
controller.should respond_to("user_service_path", "new_user_session_path", "user_session_path", "destroy_user_session_path")
|
@@ -1,3 +1,3 @@
|
|
1
1
|
class User < ActiveRecord::Base
|
2
|
-
devise :
|
3
|
-
end
|
2
|
+
devise :bushido_authenticatable, :rememberable
|
3
|
+
end
|
@@ -11,7 +11,6 @@ Scenario::Application.configure do
|
|
11
11
|
|
12
12
|
# Show full error reports and disable caching
|
13
13
|
config.consider_all_requests_local = true
|
14
|
-
config.action_view.debug_rjs = true
|
15
14
|
config.action_controller.perform_caching = false
|
16
15
|
|
17
16
|
# Don't care if the mailer can't send
|
@@ -0,0 +1 @@
|
|
1
|
+
Castronaut.config.connect_activerecord
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# Be sure to restart your server when you modify this file.
|
2
2
|
|
3
|
-
Scenario::Application.config.session_store :cookie_store, :key => '_scenario_session'
|
3
|
+
# Scenario::Application.config.session_store :cookie_store, :key => '_scenario_session'
|
4
4
|
|
5
5
|
# Use the database for sessions instead of the cookie-based default,
|
6
6
|
# which shouldn't be used to store highly confidential information
|
7
7
|
# (create the session table with "rake db:sessions:create")
|
8
|
-
|
8
|
+
Scenario::Application.config.session_store :active_record_store
|
@@ -1,7 +1,8 @@
|
|
1
1
|
class CreateTables < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
3
|
create_table :users do |t|
|
4
|
-
t.
|
4
|
+
t.bushido_authenticatable
|
5
|
+
t.string :username
|
5
6
|
t.rememberable
|
6
7
|
t.string :email
|
7
8
|
t.timestamps
|
@@ -11,4 +12,4 @@ class CreateTables < ActiveRecord::Migration
|
|
11
12
|
def self.down
|
12
13
|
drop_table :users
|
13
14
|
end
|
14
|
-
end
|
15
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class AddSessionsTable < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :sessions do |t|
|
4
|
+
t.string :session_id, :null => false
|
5
|
+
t.text :data
|
6
|
+
t.timestamps
|
7
|
+
end
|
8
|
+
|
9
|
+
add_index :sessions, :session_id
|
10
|
+
add_index :sessions, :updated_at
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :sessions
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# Note that this schema.rb definition is the authoritative source for your
|
6
|
+
# database schema. If you need to create the application database on another
|
7
|
+
# system, you should be using db:schema:load, not running all the migrations
|
8
|
+
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
9
|
+
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
10
|
+
#
|
11
|
+
# It's strongly recommended to check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(:version => 20111002012903) do
|
14
|
+
|
15
|
+
create_table "sessions", :force => true do |t|
|
16
|
+
t.string "session_id", :null => false
|
17
|
+
t.text "data"
|
18
|
+
t.datetime "created_at"
|
19
|
+
t.datetime "updated_at"
|
20
|
+
end
|
21
|
+
|
22
|
+
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
|
23
|
+
add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
|
24
|
+
|
25
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,6 +2,7 @@ ENV["RAILS_ENV"] = "test"
|
|
2
2
|
$:.unshift File.dirname(__FILE__)
|
3
3
|
$:.unshift File.expand_path('../../lib', __FILE__)
|
4
4
|
|
5
|
+
require "devise_bushido_authenticatable"
|
5
6
|
require "scenario/config/environment"
|
6
7
|
require "rails/test_help"
|
7
8
|
require 'rspec/rails'
|
@@ -9,7 +10,7 @@ require 'sham_rack'
|
|
9
10
|
require 'capybara/rspec'
|
10
11
|
|
11
12
|
RSpec.configure do |config|
|
12
|
-
config.mock_with :
|
13
|
+
config.mock_with :rspec
|
13
14
|
end
|
14
15
|
|
15
16
|
ShamRack.at('www.example.com') do |env|
|
@@ -19,4 +20,4 @@ ShamRack.at('www.example.com') do |env|
|
|
19
20
|
Castronaut::Application.call(request.env)
|
20
21
|
end
|
21
22
|
|
22
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
23
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
data/spec/strategy_spec.rb
CHANGED
@@ -17,12 +17,14 @@ describe Devise::Strategies::CasAuthenticatable, :type => "acceptance" do
|
|
17
17
|
visit destroy_user_session_url
|
18
18
|
end
|
19
19
|
|
20
|
-
def cas_login_url
|
20
|
+
def cas_login_url(redirect_path=nil)
|
21
21
|
@cas_login_url ||= begin
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
redirect_path = "/" if not redirect_path
|
23
|
+
uri = URI.parse(Devise.cas_base_url + "/login")
|
24
|
+
uri.query = "service=#{CGI.escape(user_service_url)}&redirect=#{redirect_path}"
|
25
|
+
puts "URI CHECK #{uri.to_s}"
|
26
|
+
uri.to_s
|
27
|
+
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def cas_logout_url
|
@@ -31,7 +33,6 @@ describe Devise::Strategies::CasAuthenticatable, :type => "acceptance" do
|
|
31
33
|
|
32
34
|
def sign_into_cas(username, password)
|
33
35
|
visit root_url
|
34
|
-
current_url.should == cas_login_url
|
35
36
|
fill_in "Username", :with => username
|
36
37
|
fill_in "Password", :with => password
|
37
38
|
click_on "Login"
|
@@ -51,7 +52,7 @@ describe Devise::Strategies::CasAuthenticatable, :type => "acceptance" do
|
|
51
52
|
|
52
53
|
it 'should redirect to CAS server' do
|
53
54
|
response.should be_redirect
|
54
|
-
response.should redirect_to(cas_login_url)
|
55
|
+
response.should redirect_to(cas_login_url("/"))
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
@@ -66,31 +67,21 @@ describe Devise::Strategies::CasAuthenticatable, :type => "acceptance" do
|
|
66
67
|
end
|
67
68
|
|
68
69
|
it "should register new CAS users if set up to do so" do
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
70
|
+
expect {
|
71
|
+
TestAdapter.register_valid_user("newuser", "newpassword")
|
72
|
+
Devise.cas_create_user = true
|
73
|
+
sign_into_cas "newuser", "newpassword"
|
73
74
|
|
74
|
-
|
75
|
-
User.count.should == 2
|
76
|
-
User.find_by_username("newuser").should_not be_nil
|
75
|
+
}.to change(User, :count).by(1)
|
77
76
|
end
|
78
77
|
|
79
78
|
it "should fail CAS login if user is unregistered and cas_create_user is false" do
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
current_url.should_not == root_url
|
86
|
-
User.count.should == 1
|
87
|
-
User.find_by_username("newuser").should be_nil
|
79
|
+
expect {
|
80
|
+
TestAdapter.register_valid_user("newuser", "newpassword")
|
81
|
+
Devise.cas_create_user = false
|
82
|
+
sign_into_cas "newuser", "newpassword"
|
88
83
|
|
89
|
-
|
90
|
-
|
91
|
-
fill_in "Username", :with => "joeuser"
|
92
|
-
fill_in "Password", :with => "joepassword"
|
93
|
-
click_on "Login"
|
94
|
-
current_url.should == root_url
|
84
|
+
current_url.should_not == root_url
|
85
|
+
}.to change(User, :count).by(0)
|
95
86
|
end
|
96
|
-
end
|
87
|
+
end
|