authlogic_cloudfuji 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +2 -0
  5. data/app/controllers/authlogic/cas/cas_authentication_controller.rb +7 -0
  6. data/app/controllers/authlogic/cas/cas_client_controller.rb +7 -0
  7. data/authlogic_bushido.gemspec +22 -0
  8. data/config/routes.rb +3 -0
  9. data/lib/authlogic_bushido.rb +1 -0
  10. data/lib/authlogic_cas.rb +104 -0
  11. data/lib/authlogic_cas/controller_actions/service.rb +72 -0
  12. data/lib/authlogic_cas/controller_actions/session.rb +32 -0
  13. data/lib/authlogic_cas/engine.rb +6 -0
  14. data/lib/authlogic_cas/rails_routes.rb +16 -0
  15. data/lib/authlogic_cas/single_sign_out/cache.rb +38 -0
  16. data/spec/authlogic_cas_spec.rb +150 -0
  17. data/spec/controllers/service_controller_spec.rb +51 -0
  18. data/spec/controllers/session_controller_spec.rb +31 -0
  19. data/spec/scenario/.gitignore +15 -0
  20. data/spec/scenario/Gemfile +5 -0
  21. data/spec/scenario/Rakefile +7 -0
  22. data/spec/scenario/app/assets/images/rails.png +0 -0
  23. data/spec/scenario/app/assets/javascripts/application.js +9 -0
  24. data/spec/scenario/app/assets/javascripts/main_controller.js.coffee +3 -0
  25. data/spec/scenario/app/assets/javascripts/user_sessions.js.coffee +3 -0
  26. data/spec/scenario/app/assets/javascripts/users.js.coffee +3 -0
  27. data/spec/scenario/app/assets/stylesheets/application.css +7 -0
  28. data/spec/scenario/app/assets/stylesheets/main_controller.css.scss +3 -0
  29. data/spec/scenario/app/assets/stylesheets/scaffolds.css.scss +56 -0
  30. data/spec/scenario/app/assets/stylesheets/user_sessions.css.scss +3 -0
  31. data/spec/scenario/app/assets/stylesheets/users.css.scss +3 -0
  32. data/spec/scenario/app/controllers/application_controller.rb +17 -0
  33. data/spec/scenario/app/controllers/main_controller.rb +8 -0
  34. data/spec/scenario/app/controllers/user_sessions_controller.rb +50 -0
  35. data/spec/scenario/app/controllers/users_controller.rb +86 -0
  36. data/spec/scenario/app/helpers/application_helper.rb +2 -0
  37. data/spec/scenario/app/helpers/main_controller_helper.rb +2 -0
  38. data/spec/scenario/app/helpers/user_sessions_helper.rb +2 -0
  39. data/spec/scenario/app/helpers/users_helper.rb +2 -0
  40. data/spec/scenario/app/mailers/.gitkeep +0 -0
  41. data/spec/scenario/app/models/.gitkeep +0 -0
  42. data/spec/scenario/app/models/user.rb +3 -0
  43. data/spec/scenario/app/models/user_session.rb +2 -0
  44. data/spec/scenario/app/views/layouts/application.html.erb +25 -0
  45. data/spec/scenario/app/views/main/another_cool_page.html.erb +3 -0
  46. data/spec/scenario/app/views/main/index.html.erb +2 -0
  47. data/spec/scenario/app/views/user_sessions/_form.html.erb +25 -0
  48. data/spec/scenario/app/views/user_sessions/edit.html.erb +3 -0
  49. data/spec/scenario/app/views/user_sessions/index.html.erb +25 -0
  50. data/spec/scenario/app/views/user_sessions/new.html.erb +5 -0
  51. data/spec/scenario/app/views/user_sessions/show.html.erb +15 -0
  52. data/spec/scenario/app/views/users/_form.html.erb +34 -0
  53. data/spec/scenario/app/views/users/edit.html.erb +6 -0
  54. data/spec/scenario/app/views/users/index.html.erb +27 -0
  55. data/spec/scenario/app/views/users/new.html.erb +5 -0
  56. data/spec/scenario/app/views/users/show.html.erb +20 -0
  57. data/spec/scenario/config.ru +4 -0
  58. data/spec/scenario/config/application.rb +54 -0
  59. data/spec/scenario/config/boot.rb +6 -0
  60. data/spec/scenario/config/environment.rb +17 -0
  61. data/spec/scenario/config/environments/development.rb +30 -0
  62. data/spec/scenario/config/environments/production.rb +60 -0
  63. data/spec/scenario/config/environments/test.rb +39 -0
  64. data/spec/scenario/config/initializers/authlogic_cas.rb +5 -0
  65. data/spec/scenario/config/initializers/backtrace_silencers.rb +7 -0
  66. data/spec/scenario/config/initializers/inflections.rb +10 -0
  67. data/spec/scenario/config/initializers/mime_types.rb +5 -0
  68. data/spec/scenario/config/initializers/secret_token.rb +7 -0
  69. data/spec/scenario/config/initializers/session_store.rb +8 -0
  70. data/spec/scenario/config/initializers/wrap_parameters.rb +14 -0
  71. data/spec/scenario/config/locales/en.yml +5 -0
  72. data/spec/scenario/config/routes.rb +16 -0
  73. data/spec/scenario/db/migrate/20120223141435_create_users.rb +17 -0
  74. data/spec/scenario/db/migrate/20120226154646_add_sessions_table.rb +16 -0
  75. data/spec/scenario/db/schema.rb +40 -0
  76. data/spec/scenario/db/seeds.rb +7 -0
  77. data/spec/scenario/script/rails +6 -0
  78. data/spec/single_sign_out/cache_spec.rb +47 -0
  79. data/spec/spec_helper.rb +22 -0
  80. metadata +256 -0
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in authlogic_cloudfuji.gemspec
4
+ gemspec
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ module Authlogic
2
+ module Cas
3
+ class CasAuthenticationController < ::ApplicationController
4
+ include ::Authlogic::Cas::ControllerActions::Session
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Authlogic
2
+ module Cas
3
+ class CasClientController < ::ApplicationController
4
+ include ::Authlogic::Cas::ControllerActions::Service
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Akash Manohar J"]
5
+ gem.email = ["akash@akash.im"]
6
+ gem.description = %q{Cloudfuji support for Authlogic}
7
+ gem.summary = %q{Cloudfuji support for Authlogic}
8
+ gem.homepage = "http://github.com/Cloudfuji/authlogic_cloudfuji"
9
+
10
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ gem.name = "authlogic_cloudfuji"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = "0.9.3"
16
+
17
+ gem.add_development_dependency 'rspec-rails'
18
+ gem.add_development_dependency 'sqlite3'
19
+ gem.add_development_dependency 'rails', '>= 3.2.1'
20
+ gem.add_dependency 'rubycas-client', '2.2.1'
21
+ gem.add_dependency 'authlogic'
22
+ end
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ authlogic_cas_routes
3
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__)) + '/authlogic_cas'
@@ -0,0 +1,104 @@
1
+ require 'rails'
2
+ require 'rubycas-client'
3
+ require 'authlogic/random'
4
+ require 'authlogic_cas/engine'
5
+ require 'authlogic_cas/rails_routes'
6
+ require 'authlogic_cas/single_sign_out/cache'
7
+ require 'authlogic_cas/controller_actions/service'
8
+ require 'authlogic_cas/controller_actions/session'
9
+
10
+
11
+ module Authlogic
12
+ module Cas
13
+
14
+ @@cas_base_url = "https://cloudfuji.com/cas"
15
+
16
+ # The login URL of the CAS server. If undefined, will default based on cas_base_url.
17
+ @@cas_login_url = nil
18
+
19
+ # The login URL of the CAS server. If undefined, will default based on cas_base_url.
20
+ @@cas_logout_url = nil
21
+
22
+ # The login URL of the CAS server. If undefined, will default based on cas_base_url.
23
+ @@cas_validate_url = nil
24
+
25
+ # Should devise_cas_authenticatable enable single-sign-out? Requires use of a supported
26
+ # session_store. Currently supports active_record or redis.
27
+ # False by default.
28
+ @@cas_enable_single_sign_out = true
29
+
30
+ # Should devise_cas_authenticatable attempt to create new user records for
31
+ # unknown usernames? True by default.
32
+ @@cas_create_user = true
33
+
34
+ # The model attribute used for query conditions. Should be the same as
35
+ # the rubycas-server username_column. :username by default
36
+ @@cas_username_column = :ido_id
37
+
38
+ # Name of the parameter passed in the logout query
39
+ @@cas_destination_logout_param_name = nil
40
+
41
+ mattr_accessor(
42
+ :cas_base_url,
43
+ :authentication_model,
44
+ :actor_model,
45
+ :cas_login_url,
46
+ :cas_logout_url,
47
+ :cas_validate_url,
48
+ :cas_create_user,
49
+ :cas_destination_logout_param_name,
50
+ :cas_username_column,
51
+ :cas_enable_single_sign_out)
52
+
53
+
54
+ class << self
55
+
56
+ def cas_client
57
+ @@cas_client ||= ::CASClient::Client.new(
58
+ :cas_destination_logout_param_name => @@cas_destination_logout_param_name,
59
+ :cas_base_url => @@cas_base_url,
60
+ :login_url => @@cas_login_url,
61
+ :logout_url => @@cas_logout_url,
62
+ :validate_url => @@cas_validate_url,
63
+ :enable_single_sign_out => @@cas_enable_single_sign_out
64
+ )
65
+ end
66
+
67
+
68
+ def setup_authentication
69
+ define_authentication_method_for Authlogic::Cas.actor_model
70
+ end
71
+
72
+
73
+ def define_authentication_method_for(model)
74
+ model.instance_eval do
75
+ define_singleton_method :authenticate_with_cas_ticket do |ticket|
76
+ ::Authlogic::Cas.cas_client.validate_service_ticket(ticket) unless ticket.has_been_validated?
77
+ return nil if not ticket.is_valid?
78
+
79
+ conditions = {::Authlogic::Cas.cas_username_column => ticket.respond_to?(:user) ? ticket.user : ticket.response.user}
80
+ resource = find(:first, :conditions => conditions)
81
+
82
+ resource = new(conditions.merge({:persistence_token => ::Authlogic::Random.hex_token})) if (resource.nil? and ::Authlogic::Cas.cas_create_user?)
83
+
84
+ return nil if not resource
85
+
86
+ if resource.respond_to? :cloudfuji_extra_attributes
87
+ extra_attributes = ticket.respond_to?(:extra_attributes) ? ticket.extra_attributes : ticket.response.extra_attributes
88
+ resource.cloudfuji_extra_attributes(extra_attributes)
89
+ end
90
+
91
+ resource.save
92
+ resource
93
+ end
94
+
95
+ end
96
+ end
97
+
98
+ def cas_create_user?
99
+ cas_create_user
100
+ end
101
+
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,72 @@
1
+ module Authlogic
2
+ module Cas
3
+ module ControllerActions
4
+ module Service
5
+ def service
6
+ cas_scope = ::Authlogic::Cas.actor_model
7
+ ticket = ticket_from params
8
+ auth_result = cas_scope.authenticate_with_cas_ticket(ticket)
9
+
10
+ (redirect_to(root_path, :notice => "Could not authenticate user") && return) if not auth_result
11
+
12
+ if ::Authlogic::Cas.cas_enable_single_sign_out
13
+ unique_cas_id = ticket.respond_to?(:user) ? ticket.user : ticket.response.user
14
+ ::Authlogic::Cas::SingleSignOut::Cache.store_unique_cas_id_for_service_ticket(ticket.ticket, unique_cas_id)
15
+ end
16
+
17
+ if Authlogic::Cas.authentication_model.create(auth_result)
18
+ redirect_to(root_path)
19
+ else
20
+ redirect_to(root_path, :notice => "Could not login. Try again please.")
21
+ end
22
+ end
23
+
24
+ def single_signout
25
+ if ::Authlogic::Cas.cas_enable_single_sign_out
26
+ service_ticket = read_service_ticket_name
27
+
28
+ if service_ticket
29
+ logger.info "Intercepted single-sign-out request for CAS session #{service_ticket}."
30
+ unique_cas_id = ::Authlogic::Cas::SingleSignOut::Cache.find_unique_cas_id_by_service_ticket(service_ticket)
31
+ update_persistence_token_for(unique_cas_id)
32
+ end
33
+ else
34
+ logger.warn "Ignoring CAS single-sign-out request as feature is not currently enabled."
35
+ end
36
+
37
+ render :nothing => true
38
+ end
39
+
40
+ protected
41
+
42
+ def update_persistence_token_for(unique_cas_id)
43
+ user = User.send("find_by_#{::Authlogic::Cas.cas_username_column.to_s}", unique_cas_id)
44
+ user.update_attribute(:persistence_token, ::Authlogic::Random.hex_token) if user
45
+ end
46
+
47
+ def ticket_from(controller_params)
48
+ ticket_name = controller_params[:ticket]
49
+ return nil unless ticket_name
50
+
51
+ if ticket_name =~ /^PT-/
52
+ ::CASClient::ProxyTicket.new(ticket_name, cas_service_url, controller_params[:renew])
53
+ else
54
+ ::CASClient::ServiceTicket.new(ticket_name, cas_service_url, controller_params[:renew])
55
+ end
56
+ end
57
+
58
+ def read_service_ticket_name
59
+ if request.headers['CONTENT_TYPE'] =~ %r{^multipart/}
60
+ false
61
+ elsif request.post? && params['logoutRequest'] =~
62
+ %r{^<samlp:LogoutRequest.*?<samlp:SessionIndex>(.*)</samlp:SessionIndex>}m
63
+ $~[1]
64
+ else
65
+ false
66
+ end
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,32 @@
1
+ module Authlogic
2
+ module Cas
3
+ module ControllerActions
4
+ module Session
5
+
6
+ def new_cas_session
7
+ redirect_to(cas_login_url) unless returning_from_cas?
8
+ end
9
+
10
+ def destroy_cas_session
11
+ @user_session = ::Authlogic::Cas.authentication_model.find
12
+ @user_session.destroy if @user_session
13
+ redirect_to ::Authlogic::Cas.cas_client.logout_url
14
+ end
15
+
16
+ protected
17
+
18
+ def returning_from_cas?
19
+ params[:ticket] || request.referer =~ /^#{::Authlogic::Cas.cas_client.cas_base_url}/
20
+ end
21
+
22
+
23
+ def cas_login_url
24
+ login_url_from_cas_client = ::Authlogic::Cas.cas_client.add_service_to_login_url(cas_service_url)
25
+ redirect_url = ""# "&redirect=#{cas_return_to_url}"
26
+ return "#{login_url_from_cas_client}#{redirect_url}"
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,6 @@
1
+ module Authlogic
2
+ module Cas
3
+ class Engine < Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ module ActionDispatch::Routing
2
+ class RouteSet #:nodoc:
3
+ Mapper.class_eval do
4
+ def authlogic_cas_routes
5
+ Rails.application.routes.draw do
6
+ scope :module => :authlogic do
7
+ scope :module => :cas do
8
+ match "cas_client/service" => "cas_client#service", :via => :get, :as => "cas_service"
9
+ match "cas_client/service" => "cas_client#single_signout", :via => :post, :as => "cas_single_signout"
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ module Authlogic
2
+ module Cas
3
+ module SingleSignOut
4
+ class Cache
5
+
6
+ class << self
7
+
8
+ def logger
9
+ @logger ||= Rails.logger
10
+ end
11
+
12
+ def delete_service_ticket(service_ticket_name)
13
+ logger.info("Deleting index #{service_ticket_name}")
14
+ Rails.cache.delete(cache_key(service_ticket_name))
15
+ end
16
+
17
+ def find_unique_cas_id_by_service_ticket(service_ticket_name)
18
+ unique_cas_id = Rails.cache.read(cache_key(service_ticket_name))
19
+ logger.debug("Found session id #{unique_cas_id.inspect} for index #{service_ticket_name.inspect}")
20
+ unique_cas_id
21
+ end
22
+
23
+ def store_unique_cas_id_for_service_ticket(service_ticket_name, unique_cas_id)
24
+ Rails.cache.write(cache_key(service_ticket_name), unique_cas_id)
25
+ end
26
+
27
+ protected
28
+
29
+ def cache_key(service_ticket_name)
30
+ "authlogic_cas:#{service_ticket_name}"
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Authlogic::Cas" do
4
+
5
+ subject { ::Authlogic::Cas }
6
+
7
+ before :all do
8
+ ActorExample = Class.new
9
+ subject.actor_model = ActorExample
10
+ end
11
+
12
+ it "should have methods to set actor_model and the authentication_model" do
13
+ subject.respond_to?(:actor_model).should be_true
14
+ subject.respond_to?(:actor_model=).should be_true
15
+ subject.respond_to?(:authentication_model).should be_true
16
+ subject.respond_to?(:authentication_model=).should be_true
17
+ end
18
+
19
+ describe "defaults" do
20
+ it "should have the Cloudfuji CAS server as the default" do
21
+ subject.cas_base_url.should == "https://cloudfuji.com/cas"
22
+ end
23
+
24
+ it "should have cas_create_user set to true" do
25
+ subject.cas_create_user.should be_true
26
+ end
27
+
28
+ it "should have single signout enabled" do
29
+ subject.cas_enable_single_sign_out.should be_true
30
+ end
31
+
32
+ it "should have cas_username_column set to ido_id" do
33
+ subject.cas_username_column.should == :ido_id
34
+ end
35
+ end
36
+
37
+
38
+ describe "cas_client" do
39
+ it "should return an instance of CASClient" do
40
+ subject.cas_client.should be_kind_of(::CASClient::Client)
41
+ end
42
+ end
43
+
44
+
45
+ describe "setup_authentication" do
46
+ it "should define authentication method for the actor model" do
47
+ subject.should_receive(:define_authentication_method_for).with(subject.actor_model)
48
+ subject.setup_authentication
49
+ end
50
+ end
51
+
52
+
53
+ describe "define_authentication_method_for" do
54
+ it "should define the authentication_with_cas_ticket method on the specified model" do
55
+ ActorExample.respond_to?(:authenticate_with_cas_ticket).should be_false
56
+
57
+ subject.define_authentication_method_for(ActorExample)
58
+ ActorExample.respond_to?(:authenticate_with_cas_ticket).should be_true
59
+ end
60
+ end
61
+
62
+ describe "authenticate_with_cas_ticket" do
63
+ before :all do
64
+ subject.define_authentication_method_for(ActorExample)
65
+ end
66
+
67
+ before :each do
68
+ @example_ticket = double ::CASClient::ServiceTicket
69
+ end
70
+
71
+ it "should check if a ticket has been validated" do
72
+ @example_ticket.stub!(:has_been_validated?).and_return(true)
73
+ @example_ticket.stub!(:is_valid?).and_return(false)
74
+
75
+ ActorExample.authenticate_with_cas_ticket(@example_ticket)
76
+ end
77
+
78
+ it "should validate ticket if it has not been validated" do
79
+ @example_ticket.stub!(:has_been_validated?).and_return(false)
80
+ @example_ticket.stub!(:is_valid?).and_return(false)
81
+
82
+ subject.cas_client.should_receive(:validate_service_ticket).with(@example_ticket)
83
+ ActorExample.authenticate_with_cas_ticket(@example_ticket)
84
+ end
85
+
86
+ it "should return nil if the ticket is not valid" do
87
+ @example_ticket.stub!(:has_been_validated?).and_return(true)
88
+ @example_ticket.stub!(:is_valid?).and_return(false)
89
+
90
+ ActorExample.authenticate_with_cas_ticket(@example_ticket).should be_nil
91
+ end
92
+
93
+ it "should try to find the user and create a session for the user" do
94
+ @example_user = ActorExample.new
95
+ @example_user.stub!(:save)
96
+
97
+ @example_ticket.stub!(:has_been_validated?).and_return(true)
98
+ @example_ticket.stub!(:is_valid?).and_return(true)
99
+ @example_ticket.stub!(:user).and_return("example_unique_user_id")
100
+
101
+
102
+ ActorExample.stub!(:find).and_return(@example_user)
103
+
104
+ ActorExample.authenticate_with_cas_ticket(@example_ticket).should == @example_user
105
+ end
106
+
107
+ it "should create a new user incase the user with the unique CAS ID isn't found" do
108
+ @example_user = ActorExample.new
109
+ @example_user.stub!(:save)
110
+
111
+ @example_ticket.stub!(:has_been_validated?).and_return(true)
112
+ @example_ticket.stub!(:is_valid?).and_return(true)
113
+ @example_ticket.stub!(:user).and_return("example_unique_user_id")
114
+
115
+ ActorExample.should_receive(:find).and_return(nil)
116
+ ActorExample.should_receive(:new).and_return(@example_user)
117
+
118
+ ActorExample.authenticate_with_cas_ticket(@example_ticket).should == @example_user
119
+ end
120
+
121
+ it "should call the cloudfuji_extra_attributes method on the actor model if defined" do
122
+ @example_user = ActorExample.new
123
+ @example_user.stub!(:save)
124
+ @example_user.stub!(:cloudfuji_extra_attributes)
125
+
126
+ @example_ticket.stub!(:has_been_validated?).and_return(true)
127
+ @example_ticket.stub!(:is_valid?).and_return(true)
128
+ @example_ticket.stub!(:user).and_return("example_unique_user_id")
129
+ @example_ticket.stub!(:extra_attributes).and_return({})
130
+ ActorExample.stub!(:find).and_return(@example_user)
131
+
132
+ @example_user.should_receive(:cloudfuji_extra_attributes)
133
+
134
+ ActorExample.authenticate_with_cas_ticket(@example_ticket).should == @example_user
135
+ end
136
+ end
137
+
138
+ describe "cas_create_user?" do
139
+ it "should return true if @@cas_create_user is true" do
140
+ subject.cas_create_user = true
141
+ subject.cas_create_user?.should be_true
142
+ end
143
+
144
+ it "should return true if @@cas_create_user is false" do
145
+ subject.cas_create_user = false
146
+ subject.cas_create_user?.should be_false
147
+ end
148
+ end
149
+
150
+ end