rack-casual 0.0.1

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Gudleik Rasch
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,121 @@
1
+ Rack::Casual
2
+ ============
3
+
4
+ A very simple Rack authentication plugin using CAS or a token.
5
+ It kicks in whenever a 401 response is returned from the server.
6
+
7
+ The plugin has only been tested using ActiveRecord and Rails 3.
8
+
9
+ Installation
10
+ ============
11
+
12
+ Add this to your Gemfile:
13
+
14
+ gem 'rack-casual'
15
+
16
+ Run bundle install, and add a configuration file:
17
+
18
+ $ rails generate rack_casual
19
+
20
+ This creates a config/initializers/rack-casual.rb file.
21
+ Make sure base_url points to your CAS server.
22
+ If your user model is called something other than "User", you can change this here.
23
+
24
+ Next you must configure your application to use the plugin.
25
+ For Rails3, you can add this to your config/application.rb
26
+ config.middleware.use "Rack::Casual::Authentication"
27
+
28
+ Finally, to authenticate your users, add a before_filter to your controller:
29
+
30
+ class ApplicationController < ActionController::Base
31
+ before_filter :authenticate!
32
+ end
33
+
34
+
35
+ Usage
36
+ =====
37
+
38
+ Rack::Casual adds some helper methods to ActionController::Base
39
+
40
+ * logged_in?
41
+ Returns true if session contains user-id
42
+
43
+ * current_user
44
+ Returns the currently logged in user.
45
+
46
+ * authenticate!
47
+ This is the method you want to use in a before_filter
48
+
49
+
50
+ Authentication token
51
+ ====================
52
+
53
+ CAS is nice and all that, but it's not so nice for webservices.
54
+ Therefore Rack::Casual can authenticate requests using a token.
55
+ Make sure your User model has a auth_token attribute. You can call it whatever you want, but it defaults to auth_token.
56
+
57
+ From your client you can now authenticate using this token:
58
+
59
+ http://your-app.com/my-protected-webservice?auth_token=secret
60
+
61
+ If there are no users with that token, the client just receives the 401 error.
62
+ It does not fallback to CAS or create a user automatically (doh).
63
+
64
+
65
+ Finding users
66
+ =============
67
+
68
+ If you want to control how Rack::Casual finds the user, you can set a scope to be used.
69
+ # config/initializers/rack-casual.rb:
70
+ config.authentication_scope = :active
71
+
72
+ # app/models/user.rb
73
+ class User < ActiveRecord::Base
74
+ def self.active
75
+ where(:active => true)
76
+ end
77
+ end
78
+
79
+ Then Rack::Casual will only search among users where active is true.
80
+ A side effect of this is that Rack::Casual will try to create a user that already exists.
81
+ However, this should not be a problem as long as your User model validates the uniqueness of the username.
82
+
83
+ The default scope to use is
84
+
85
+ Extra attributes
86
+ ================
87
+
88
+ When creating users automatically, Rack::Casual can also add extra attributes if your CAS server provides this.
89
+ For this to work your User model must have a cas_extra_attributes= instance method.
90
+ Here's an example:
91
+
92
+ class User < ActiveRecord::Base
93
+ def cas_extra_attributes=(extra_attributes)
94
+ extra_attributes.each do |name, value|
95
+ case name.to_sym
96
+ when :name then self.name = value
97
+ when :email then self.email = value
98
+ when :phone then self.phone = value
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+
105
+ Tracking
106
+ ========
107
+
108
+ If you have enabled tracking, Rack::Casual can update the logged in user with information about last login time and IP.
109
+ These variables will be updated if they are present in your User model:
110
+ * last_login_at (datetime)
111
+ * last_login_ip (string)
112
+ * login_count (integer)
113
+
114
+ TODO
115
+ ====
116
+
117
+ 1. Testing. How embarrasing. A gem without tests is like a forrest without trees.
118
+ 2. Replace ruby-cas with something "lighter", like casual, but casual doesn't seem to support extra attributes...
119
+ Note to self: http://rubycas-client.rubyforge.org/classes/CASClient/ValidationResponse.src/M000044.html
120
+
121
+ Copyright (c) 2010 Gudleik Rasch <gudleik@gmail.com>, released under the MIT license
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Installs an initializer in config/initializers/ for Rack::Casual
3
+
4
+ Example:
5
+ rails generate rack_casual
6
+
7
+ This will create:
8
+ config/initializers/rack-casual.rb
@@ -0,0 +1,8 @@
1
+ class RackCasualGenerator < Rails::Generators::Base
2
+ source_root File.expand_path('../templates', __FILE__)
3
+
4
+ def copy_initializer
5
+ copy_file "initializer.rb", "config/initializers/rack-casual.rb"
6
+ end
7
+
8
+ end
@@ -0,0 +1,58 @@
1
+ # Configuration for Rack::Casual
2
+ # Commented values are default.
3
+
4
+ Rack::Casual.setup do |config|
5
+
6
+ # Base URL to your CAS server -- required
7
+ config.base_url = 'http://localhost:8088'
8
+
9
+ # If you want users to authenticate using an authentication token,
10
+ # set the auth_token_key to the name of the attribute in your user model.
11
+ # Users can now authenticate using http://your-app.com/?auth_token=a-very-secret-key
12
+ # Setting this value to nil disables token authentication.
13
+ # config.auth_token_key = 'auth_token'
14
+
15
+ # Name of the session key used to store the user-id
16
+ # Default is "user", so you get session[:user]
17
+ # config.session_key_user = "user"
18
+
19
+ # Rack::Casual can create the user automatically on successful login
20
+ # Set this to false to disable this feature.
21
+ # If you want to include extra attributes provided by your CAS server,
22
+ # you must add a cas_extra_attributes=(attributes) method in your User model.
23
+ # See the README for an example.
24
+ # config.create_user = true
25
+
26
+ # If you have enabled create_user, here you can specify the name of your
27
+ # user class. Defaults to 'User'.
28
+ # config.user_class = "User"
29
+
30
+ # This is the username attribute used by your User model.
31
+ # config.username = "username"
32
+
33
+ # Finding the user
34
+ # You can set a custom scope that Rack::Casual should use when finding the user.
35
+ # If you have a active scope, you can set this to :active.
36
+ # config.authentication_scope = nil
37
+
38
+ # Tracking
39
+ # If you have last_login_at and/or last_login_ip attributes on your User model,
40
+ # Rack::Casual can update these when user logs in.
41
+ # config.enable_tracking = true
42
+
43
+ # Name of the ticket parameter used by CAS.
44
+ # config.ticket_param = 'ticket'
45
+
46
+ # URL to the service validation on your CAS server.
47
+ # nil = use defaults
48
+ # config.validate_url = nil
49
+
50
+ # CAS login url.
51
+ # nil = use defaults
52
+ # config.login_url = nil
53
+
54
+ # CAS logout url.
55
+ # nil = use defaults
56
+ # config.logout_url = nil
57
+
58
+ end
@@ -0,0 +1,79 @@
1
+ module Rack
2
+
3
+ module Casual
4
+
5
+ class Authentication
6
+
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ @request = Rack::Request.new(env)
13
+ @response = @app.call(env)
14
+ @env = env
15
+
16
+ process_request_from_cas
17
+ handle_401
18
+ end
19
+
20
+ private
21
+
22
+ def process_request_from_cas
23
+ if ticket = read_ticket
24
+ if user = UserFactory.authenticate_with_cas_ticket(ticket, @request)
25
+ # TODO: remove the params['ticket'] so the app doesn't see this...
26
+ @request.session[Rack::Casual.session_key_user] = user.id
27
+ else
28
+ # [ 403, { "Content-Type" => "text/plain" }, "Sorry, I was unable to authenticate you" ]
29
+ end
30
+ end
31
+ end
32
+
33
+ def handle_401
34
+ return @response unless @response[0] == 401
35
+
36
+ if Rack::Casual.auth_token_key && @request.params[Rack::Casual.auth_token_key]
37
+ authenticate_with_token
38
+ else
39
+ redirect_to_cas
40
+ end
41
+ end
42
+
43
+ def authenticate_with_token
44
+ user = UserFactory.authenticate_with_token(@request)
45
+ @request.session[Rack::Casual.session_key_user] = user.id if user
46
+ @app.call(@env)
47
+ end
48
+
49
+ def redirect_to_cas
50
+ url = Rack::Casual.cas_client.add_service_to_login_url(service_url)
51
+ [ 302,
52
+ {
53
+ "Location" => url,
54
+ "Content-Type" => "text/plain"
55
+ },
56
+ "Redirecting to CAS for authentication"
57
+ ]
58
+ end
59
+
60
+ def service_url
61
+ @request.url.sub(/[\?&]#{Rack::Casual.ticket_param}=[^\?&]+/, '')
62
+ end
63
+
64
+ # Read ticket from params and create a CASClient ticket
65
+ def read_ticket
66
+ ticket = @request.params[Rack::Casual.ticket_param]
67
+ return nil unless ticket
68
+
69
+ if ticket =~ /^PT-/
70
+ ::CASClient::ProxyTicket.new(ticket, service_url, @request.params[:renew])
71
+ else
72
+ ::CASClient::ServiceTicket.new(ticket, service_url, @request.params[:renew])
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,22 @@
1
+ module Rack
2
+
3
+ module Casual
4
+
5
+ module Controller
6
+
7
+ def authenticate!
8
+ authenticate_or_request_with_http_token unless logged_in?
9
+ end
10
+
11
+ def logged_in?
12
+ !session[::Rack::Casual.session_key_user].nil?
13
+ end
14
+
15
+ def current_user
16
+ @current_user ||= ::Rack::Casual::UserFactory.authentication_scope.find(session[:user])
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+ module Rack
3
+ module Casual
4
+
5
+ class UserFactory
6
+
7
+ def self.authenticate_with_cas_ticket(ticket, request)
8
+ user = nil
9
+ Rack::Casual.cas_client.validate_service_ticket(ticket) unless ticket.has_been_validated?
10
+
11
+ if ticket.is_valid?
12
+
13
+ # find user
14
+ user = find(ticket.response.user)
15
+
16
+ if user.nil? && Rack::Casual.create_user
17
+ user = make(ticket.response.user)
18
+ end
19
+
20
+ return nil unless user
21
+
22
+ if user.respond_to?(:cas_extra_attributes=)
23
+ user.cas_extra_attributes = ticket.response.extra_attributes
24
+ end
25
+
26
+ update_tracking(user, request)
27
+ end
28
+
29
+ user
30
+ end
31
+
32
+ protected
33
+
34
+ def self.authenticate_with_token(request)
35
+ user = authentication_scope.where(Rack::Casual.auth_token_key => request.params[Rack::Casual.auth_token_key]).first
36
+ update_tracking(user, request) if user
37
+ user
38
+ end
39
+
40
+ # Update tracking info (last logged in at / ip) if tracking_enabled is set.
41
+ # Saves the user regardless of whether tracking was updated or not.
42
+ def self.update_tracking(user, request)
43
+ if Rack::Casual.tracking_enabled
44
+ user.last_login_at = Time.now if user.respond_to?(:last_login_at)
45
+ user.last_login_ip = request.ip if user.respond_to?(:last_login_ip)
46
+ user.login_count += 1 if user.respond_to?(:login_count)
47
+ end
48
+ user.save
49
+ end
50
+
51
+ # Find user by username
52
+ def self.find(username)
53
+ authentication_scope.where(Rack::Casual.username => username).first
54
+ end
55
+
56
+ # Initializes a new user
57
+ def self.make(username)
58
+ resource.new(Rack::Casual.username => username)
59
+ end
60
+
61
+ # Returns the user class
62
+ def self.resource
63
+ Rack::Casual.user_class.constantize
64
+ end
65
+
66
+ # Returns the scope used to find users
67
+ def self.authentication_scope
68
+ if Rack::Casual.authentication_scope
69
+ puts "Authentication scope is kinda broken and should be avoided"
70
+ resource.send(Rack::Casual.authentication_scope)
71
+ else
72
+ resource.scoped
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,92 @@
1
+ # RackCasual
2
+ # encoding: utf-8
3
+
4
+ module Rack
5
+
6
+ module Casual
7
+
8
+ autoload :Authentication, 'rack/casual/authentication'
9
+ autoload :UserFactory, 'rack/casual/user_factory'
10
+ autoload :Controller, 'rack/casual/controller'
11
+
12
+ # Base URI to your CAS server
13
+ mattr_accessor :base_url
14
+ @@base_url = 'http://192.168.0.15:8088'
15
+
16
+ # URI to CAS login
17
+ # Default is base_url/login
18
+ mattr_accessor :login_url
19
+ @@login_url = nil
20
+
21
+ # URI to CAS logout
22
+ # Default is base_url/logout
23
+ mattr_accessor :logout_url
24
+ @@logout_url = nil
25
+
26
+ # URI to service validation
27
+ # Default is base_url/serviceValidate
28
+ mattr_accessor :validate_url
29
+ @@validate_url = nil
30
+
31
+ # Name of authentication token to use in params
32
+ # Set to nil to disable token authentication
33
+ mattr_accessor :auth_token_key
34
+ @@auth_token_key = "auth_token"
35
+
36
+ # Name of the ticket parameter used by CAS
37
+ mattr_accessor :ticket_param
38
+ @@ticket_param = "ticket"
39
+
40
+ # Name of key to store user id
41
+ # Default is 'user' => session[:user]
42
+ mattr_accessor :session_key_user
43
+ @@session_key_user = "user"
44
+
45
+ # Use this scope when finding users
46
+ mattr_accessor :authentication_scope
47
+ @@authentication_scope = nil
48
+
49
+ # Set to true to auto-create users
50
+ mattr_accessor :create_user
51
+ @@create_user = true
52
+
53
+ # Name of the User class
54
+ mattr_accessor :user_class
55
+ @@user_class = "User"
56
+
57
+ # Username attribute on user
58
+ mattr_accessor :username
59
+ @@username = "username"
60
+
61
+ # Update user with last_login_at and last_login_ip info
62
+ mattr_accessor :tracking_enabled
63
+ @@tracking_enabled = true
64
+
65
+ # Default way to setup Devise. Run rails generate devise_install to create
66
+ # a fresh initializer with all configuration values.
67
+ def self.setup
68
+ yield self
69
+ end
70
+
71
+ def self.cas_client
72
+ @@cas_client ||= ::CASClient::Client.new(
73
+ :cas_base_url => @@base_url,
74
+ :login_url => @@login_url,
75
+ :logout_url => @@logout_url,
76
+ :validate_url => @@validate_url
77
+ )
78
+ end
79
+
80
+ end
81
+
82
+ end
83
+
84
+ if defined?(ActionController::Base)
85
+ class ActionController::Base
86
+ include ::Rack::Casual::Controller
87
+
88
+ # before_filter :authenticate!
89
+
90
+ helper_method :logged_in?, :current_user
91
+ end
92
+ end
@@ -0,0 +1,2 @@
1
+ require 'rubycas-client'
2
+ require 'rack/casual'
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rack-casual
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Gudleik Rasch
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-02 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rubycas-client
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 2
31
+ - 1
32
+ version: 2.2.1
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: activerecord
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ segments:
44
+ - 3
45
+ - 0
46
+ version: "3.0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: Rack module for authentication using CAS and/or tokens
50
+ email:
51
+ - gudleik@gmail.com
52
+ executables: []
53
+
54
+ extensions: []
55
+
56
+ extra_rdoc_files: []
57
+
58
+ files:
59
+ - lib/generators/rack_casual_generator.rb
60
+ - lib/generators/templates/initializer.rb
61
+ - lib/generators/USAGE
62
+ - lib/rack/casual/authentication.rb
63
+ - lib/rack/casual/controller.rb
64
+ - lib/rack/casual/user_factory.rb
65
+ - lib/rack/casual.rb
66
+ - lib/rack-casual.rb
67
+ - LICENSE
68
+ - README.md
69
+ has_rdoc: true
70
+ homepage: http://github.com/gudleik/rack-casual
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options: []
75
+
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 1
93
+ - 3
94
+ - 7
95
+ version: 1.3.7
96
+ requirements: []
97
+
98
+ rubyforge_project:
99
+ rubygems_version: 1.3.7
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: CAS and token authentication using Rack
103
+ test_files: []
104
+