devise_wind 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ class SessionsController < Devise::SessionsController
2
+ protect_from_forgery
3
+ def new
4
+ create
5
+ redirect_to root_path
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,3 @@
1
+ class Role < ActiveRecord::Base
2
+ has_and_belongs_to_many :users
3
+ end
@@ -0,0 +1,5 @@
1
+ class User
2
+ devise :wind_authenticatable
3
+
4
+ has_and_belongs_to_many :roles
5
+ end
@@ -0,0 +1 @@
1
+ We shouldn't ever be here.
@@ -0,0 +1,211 @@
1
+ # Use this hook to configure devise mailer, warden hooks and so forth.
2
+ # Many of these configuration options can be set straight in your model.
3
+ Devise.setup do |config|
4
+ # ==> Mailer Configuration
5
+ # Configure the e-mail address which will be shown in Devise::Mailer,
6
+ # note that it will be overwritten if you use your own mailer class with default "from" parameter.
7
+ config.mailer_sender = "please-change-me-at-config-initializers-devise@example.com"
8
+
9
+ # Configure the class responsible to send e-mails.
10
+ # config.mailer = "Devise::Mailer"
11
+
12
+ # ==> ORM configuration
13
+ # Load and configure the ORM. Supports :active_record (default) and
14
+ # :mongoid (bson_ext recommended) by default. Other ORMs may be
15
+ # available as additional gems.
16
+ require 'devise/orm/active_record'
17
+
18
+ # ==> Configuration for any authentication mechanism
19
+ # Configure which keys are used when authenticating a user. The default is
20
+ # just :email. You can configure it to use [:username, :subdomain], so for
21
+ # authenticating a user, both parameters are required. Remember that those
22
+ # parameters are used only when authenticating and not when retrieving from
23
+ # session. If you need permissions, you should implement that in a before filter.
24
+ # You can also supply a hash where the value is a boolean determining whether
25
+ # or not authentication should be aborted when the value is not present.
26
+ # config.authentication_keys = [ :email ]
27
+ config.authentication_keys = [ :login ]
28
+
29
+ # Configure parameters from the request object used for authentication. Each entry
30
+ # given should be a request method and it will automatically be passed to the
31
+ # find_for_authentication method and considered in your model lookup. For instance,
32
+ # if you set :request_keys to [:subdomain], :subdomain will be used on authentication.
33
+ # The same considerations mentioned for authentication_keys also apply to request_keys.
34
+ # config.request_keys = []
35
+
36
+ # Configure which authentication keys should be case-insensitive.
37
+ # These keys will be downcased upon creating or modifying a user and when used
38
+ # to authenticate or find a user. Default is :email.
39
+ config.case_insensitive_keys = [ :email ]
40
+
41
+ # Configure which authentication keys should have whitespace stripped.
42
+ # These keys will have whitespace before and after removed upon creating or
43
+ # modifying a user and when used to authenticate or find a user. Default is :email.
44
+ config.strip_whitespace_keys = [ :email ]
45
+
46
+ # Tell if authentication through request.params is enabled. True by default.
47
+ # config.params_authenticatable = true
48
+
49
+ # Tell if authentication through HTTP Basic Auth is enabled. False by default.
50
+ # config.http_authenticatable = false
51
+
52
+ # If http headers should be returned for AJAX requests. True by default.
53
+ # config.http_authenticatable_on_xhr = true
54
+
55
+ # The realm used in Http Basic Authentication. "Application" by default.
56
+ # config.http_authentication_realm = "Application"
57
+
58
+ # It will change confirmation, password recovery and other workflows
59
+ # to behave the same regardless if the e-mail provided was right or wrong.
60
+ # Does not affect registerable.
61
+ # config.paranoid = true
62
+
63
+ # ==> Configuration for :database_authenticatable
64
+ # For bcrypt, this is the cost for hashing the password and defaults to 10. If
65
+ # using other encryptors, it sets how many times you want the password re-encrypted.
66
+ #
67
+ # Limiting the stretches to just one in testing will increase the performance of
68
+ # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use
69
+ # a value less than 10 in other environments.
70
+ config.stretches = 20
71
+
72
+ # Setup a pepper to generate the encrypted password.
73
+ # config.pepper = "82a9c0e7569255f2c12a5ea52cbc6f9e6e8ab72c94583d60622f7bd49f9f509dfc2c9902654d67a4d1bdb3d604680576c26e1de6b283f6bbd33cedab0b722222"
74
+
75
+ # ==> Configuration for :confirmable
76
+ # A period that the user is allowed to access the website even without
77
+ # confirming his account. For instance, if set to 2.days, the user will be
78
+ # able to access the website for two days without confirming his account,
79
+ # access will be blocked just in the third day. Default is 0.days, meaning
80
+ # the user cannot access the website without confirming his account.
81
+ # config.confirm_within = 2.days
82
+
83
+ # Defines which key will be used when confirming an account
84
+ # config.confirmation_keys = [ :email ]
85
+
86
+ # ==> Configuration for :rememberable
87
+ # The time the user will be remembered without asking for credentials again.
88
+ # config.remember_for = 2.weeks
89
+
90
+ # If true, a valid remember token can be re-used between multiple browsers.
91
+ # config.remember_across_browsers = true
92
+
93
+ # If true, extends the user's remember period when remembered via cookie.
94
+ # config.extend_remember_period = false
95
+
96
+ # If true, uses the password salt as remember token. This should be turned
97
+ # to false if you are not using database authenticatable.
98
+ config.use_salt_as_remember_token = true
99
+
100
+ # Options to be passed to the created cookie. For instance, you can set
101
+ # :secure => true in order to force SSL only cookies.
102
+ # config.cookie_options = {}
103
+
104
+ # ==> Configuration for :validatable
105
+ # Range for password length. Default is 6..128.
106
+ # config.password_length = 6..128
107
+
108
+ # Email regex used to validate email formats. It simply asserts that
109
+ # an one (and only one) @ exists in the given string. This is mainly
110
+ # to give user feedback and not to assert the e-mail validity.
111
+ # config.email_regexp = /\A[^@]+@[^@]+\z/
112
+
113
+ # ==> Configuration for :timeoutable
114
+ # The time you want to timeout the user session without activity. After this
115
+ # time the user will be asked for credentials again. Default is 30 minutes.
116
+ # config.timeout_in = 30.minutes
117
+
118
+ # ==> Configuration for :lockable
119
+ # Defines which strategy will be used to lock an account.
120
+ # :failed_attempts = Locks an account after a number of failed attempts to sign in.
121
+ # :none = No lock strategy. You should handle locking by yourself.
122
+ # config.lock_strategy = :failed_attempts
123
+
124
+ # Defines which key will be used when locking and unlocking an account
125
+ # config.unlock_keys = [ :email ]
126
+
127
+ # Defines which strategy will be used to unlock an account.
128
+ # :email = Sends an unlock link to the user email
129
+ # :time = Re-enables login after a certain amount of time (see :unlock_in below)
130
+ # :both = Enables both strategies
131
+ # :none = No unlock strategy. You should handle unlocking by yourself.
132
+ # config.unlock_strategy = :both
133
+
134
+ # Number of authentication tries before locking an account if lock_strategy
135
+ # is failed attempts.
136
+ # config.maximum_attempts = 20
137
+
138
+ # Time interval to unlock the account if :time is enabled as unlock_strategy.
139
+ # config.unlock_in = 1.hour
140
+
141
+ # ==> Configuration for :recoverable
142
+ #
143
+ # Defines which key will be used when recovering the password for an account
144
+ # config.reset_password_keys = [ :email ]
145
+
146
+ # Time interval you can reset your password with a reset password key.
147
+ # Don't put a too small interval or your users won't have the time to
148
+ # change their passwords.
149
+ config.reset_password_within = 2.hours
150
+
151
+ # ==> Configuration for :encryptable
152
+ # Allow you to use another encryption algorithm besides bcrypt (default). You can use
153
+ # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1,
154
+ # :authlogic_sha512 (then you should set stretches above to 20 for default behavior)
155
+ # and :restful_authentication_sha1 (then you should set stretches to 10, and copy
156
+ # REST_AUTH_SITE_KEY to pepper)
157
+ # config.encryptor = :sha512
158
+ config.encryptor = :authlogic_sha512
159
+
160
+ # ==> Configuration for :token_authenticatable
161
+ # Defines name of the authentication token params key
162
+ # config.token_authentication_key = :auth_token
163
+
164
+ # If true, authentication through token does not store user in session and needs
165
+ # to be supplied on each request. Useful if you are using the token as API token.
166
+ # config.stateless_token = false
167
+
168
+ # ==> Scopes configuration
169
+ # Turn scoped views on. Before rendering "sessions/new", it will first check for
170
+ # "users/sessions/new". It's turned off by default because it's slower if you
171
+ # are using only default views.
172
+ # config.scoped_views = false
173
+
174
+ # Configure the default scope given to Warden. By default it's the first
175
+ # devise role declared in your routes (usually :user).
176
+ # config.default_scope = :user
177
+
178
+ # Configure sign_out behavior.
179
+ # Sign_out action can be scoped (i.e. /users/sign_out affects only :user scope).
180
+ # The default is true, which means any logout action will sign out all active scopes.
181
+ # config.sign_out_all_scopes = true
182
+
183
+ # ==> Navigation configuration
184
+ # Lists the formats that should be treated as navigational. Formats like
185
+ # :html, should redirect to the sign in page when the user does not have
186
+ # access, but formats like :xml or :json, should return 401.
187
+ #
188
+ # If you have any extra navigational formats, like :iphone or :mobile, you
189
+ # should add them to the navigational formats lists.
190
+ #
191
+ # The :"*/*" and "*/*" formats below is required to match Internet
192
+ # Explorer requests.
193
+ # config.navigational_formats = [:"*/*", "*/*", :html]
194
+
195
+ # The default HTTP method used to sign out a resource. Default is :delete.
196
+ config.sign_out_via = :get
197
+
198
+ # ==> OmniAuth
199
+ # Add a new OmniAuth provider. Check the wiki for more information on setting
200
+ # up on your models and hooks.
201
+ # config.omniauth :github, 'APP_ID', 'APP_SECRET', :scope => 'user,public_repo'
202
+
203
+ # ==> Warden configuration
204
+ # If you want to use other strategies, that are not supported by Devise, or
205
+ # change the failure app, you can configure them inside the config.warden block.
206
+ #
207
+ # config.warden do |manager|
208
+ # manager.intercept_401 = false
209
+ # manager.default_strategies(:scope => :user).unshift :some_external_strategy
210
+ # end
211
+ end
@@ -0,0 +1,58 @@
1
+ # Additional translations at https://github.com/plataformatec/devise/wiki/I18n
2
+
3
+ en:
4
+ errors:
5
+ messages:
6
+ expired: "has expired, please request a new one"
7
+ not_found: "not found"
8
+ already_confirmed: "was already confirmed, please try signing in"
9
+ not_locked: "was not locked"
10
+ not_saved:
11
+ one: "1 error prohibited this %{resource} from being saved:"
12
+ other: "%{count} errors prohibited this %{resource} from being saved:"
13
+
14
+ devise:
15
+ failure:
16
+ already_authenticated: 'You are already signed in.'
17
+ unauthenticated: 'You need to sign in or sign up before continuing.'
18
+ unconfirmed: 'You have to confirm your account before continuing.'
19
+ locked: 'Your account is locked.'
20
+ invalid: 'Invalid email or password.'
21
+ invalid_token: 'Invalid authentication token.'
22
+ timeout: 'Your session expired, please sign in again to continue.'
23
+ inactive: 'Your account was not activated yet.'
24
+ sessions:
25
+ signed_in: 'Signed in successfully.'
26
+ signed_out: 'Signed out successfully.'
27
+ passwords:
28
+ send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
29
+ updated: 'Your password was changed successfully. You are now signed in.'
30
+ updated_not_active: 'Your password was changed successfully.'
31
+ send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
32
+ confirmations:
33
+ send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
34
+ send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
35
+ confirmed: 'Your account was successfully confirmed. You are now signed in.'
36
+ registrations:
37
+ signed_up: 'Welcome! You have signed up successfully.'
38
+ inactive_signed_up: 'You have signed up successfully. However, we could not sign you in because your account is %{reason}.'
39
+ updated: 'You updated your account successfully.'
40
+ destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
41
+ reasons:
42
+ inactive: 'inactive'
43
+ unconfirmed: 'unconfirmed'
44
+ locked: 'locked'
45
+ unlocks:
46
+ send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
47
+ unlocked: 'Your account was successfully unlocked. You are now signed in.'
48
+ send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
49
+ omniauth_callbacks:
50
+ success: 'Successfully authorized from %{kind} account.'
51
+ failure: 'Could not authorize you from %{kind} because "%{reason}".'
52
+ mailer:
53
+ confirmation_instructions:
54
+ subject: 'Confirmation instructions'
55
+ reset_password_instructions:
56
+ subject: 'Reset password instructions'
57
+ unlock_instructions:
58
+ subject: 'Unlock Instructions'
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ # devise_for :users, :controllers => {:sessions => 'sessions'}
3
+ end
@@ -0,0 +1,19 @@
1
+ require 'devise'
2
+
3
+ require 'devise_wind/engine'
4
+ require 'devise_wind/mixins/urls'
5
+ require 'devise_wind/model'
6
+ require 'devise_wind/schema'
7
+ require 'devise_wind/strategy'
8
+ require 'devise_wind/version'
9
+ module DeviseWind
10
+ def self.add_routes(router, options={})
11
+ router.devise_for :users, :controllers => {:sessions => 'sessions'}
12
+ end
13
+ end
14
+
15
+ Devise.add_module :wind_authenticatable,
16
+ :strategy => true,
17
+ :model => 'devise_wind/model',
18
+ :controller => :sessions,
19
+ :route => :session
@@ -0,0 +1,40 @@
1
+ module DeviseWind::Controllers::Sessions
2
+
3
+ def authenticating_with_wind?
4
+ # Controller isn't available in all contexts (e.g. irb)
5
+ return false unless session_class.controller
6
+
7
+ # Initial request when user presses one of the button helpers
8
+ (session_class.controller.params && !session_class.controller.params[:login_with_wind].blank?) ||
9
+ # When the oauth provider responds and we made the initial request
10
+ (defined?(wind_response) && wind_response && session_class.controller.session && session_class.controller.session[:wind_request_class] == self.class.name)
11
+ end
12
+
13
+ def validate_password_with_wind?
14
+ !using_wind? && require_password?
15
+ end
16
+
17
+ def using_wind?
18
+ !wind_login.blank?
19
+ end
20
+
21
+ def generate_verified_login
22
+ validate_path = "/validate?ticketid=#{wind_controller.params['ticketid']}"
23
+ wind_validate = Net::HTTP.new("wind.columbia.edu",443)
24
+ wind_validate.use_ssl = true
25
+ wind_validate.start
26
+ wind_resp = wind_validate.get(validate_path)
27
+ wind_validate.finish
28
+ #puts wind_resp.body
29
+ authdoc = Nokogiri::XML(wind_resp.body)
30
+ ns = {'wind'=>'http://www.columbia.edu/acis/rad/authmethods/wind'}
31
+ _user = authdoc.xpath('//wind:authenticationSuccess/wind:user', ns)
32
+ wind_data = nil
33
+ if _user.length > 0
34
+ wind_data = {}
35
+ wind_data[:uni] = _user[0].content
36
+ wind_data[:affils] = authdoc.xpath('//wind:authenticationSuccess/wind:affiliations/wind:affil',ns).collect {|x| x.content}
37
+ end
38
+ wind_data
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module DeviseWind::Controllers::Users
2
+ before_filter :verify_user, :only => :show # can't show without a logged in user
3
+
4
+ def show
5
+ end
6
+
7
+ def new
8
+ @user ||= User.new(params[:user])
9
+ end
10
+
11
+ def create
12
+ @user ||= User.new(params[:user])
13
+ if @user.save
14
+ flash[:notice] = "Welcome #{@user.login}"
15
+ redirect_to user_path(@user.id)
16
+ else
17
+ render :action => "new"
18
+ end
19
+ end
20
+
21
+ protected
22
+ def verify_user
23
+ flash[:notice] = "Please log in to view your profile." and raise Blacklight::Exceptions::AccessDenied unless current_user
24
+ end
25
+
26
+ end
@@ -0,0 +1,4 @@
1
+ module DeviseWind
2
+ class Engine < Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,40 @@
1
+ module Warden::Mixins
2
+ module Urls
3
+ def sanitize_query_string
4
+ query_hash = env["rack.request.query_hash"]
5
+ query_hash.delete("_method")
6
+ query_hash.delete_if do |key, value|
7
+ key =~ /^openid\./
8
+ end
9
+
10
+ env["QUERY_STRING"] = env["rack.request.query_string"] =
11
+ Rack::Utils.build_query(env["rack.request.query_hash"])
12
+
13
+ qs = env["QUERY_STRING"]
14
+ request_uri = (env["PATH_INFO"] || "").dup
15
+ request_uri << "?" + qs unless qs == ""
16
+ env["REQUEST_URI"] = request_uri
17
+ end
18
+
19
+ def realm_url
20
+ url = request.scheme + "://"
21
+ url << request.host
22
+
23
+ scheme, port = request.scheme, request.port
24
+ if scheme == "https" && port != 443 ||
25
+ scheme == "http" && port != 80
26
+ url << ":#{port}"
27
+ end
28
+
29
+ url
30
+ end
31
+
32
+ def request_url
33
+ url = realm_url
34
+ url << request.script_name
35
+ url << request.path_info
36
+ url << "?#{request.query_string}" if request.query_string.to_s.length > 0
37
+ url
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,92 @@
1
+ module Devise
2
+ module Models
3
+ module WindAuthenticatable
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ @wind_config = {}
8
+ end
9
+
10
+ module InstanceMethods
11
+ def affiliations=(affils)
12
+ # do nothing with affiliations by default, let implementers override this
13
+ end
14
+
15
+ def wind_login
16
+ self.send self.class.wind_login_field
17
+ end
18
+
19
+ end # InstanceMethods
20
+
21
+ module ClassMethods
22
+ # The name of the wind login field in the database.
23
+ #
24
+ # * <tt>Default:</tt> :wind_login, :login, or :username, if they exist
25
+ # * <tt>Accepts:</tt> Symbol
26
+ def wind_login_field(value = nil)
27
+ configure_property(:wind_login_field, value, first_column_to_exist(nil, :wind_login, :login, :username))
28
+ end
29
+ alias_method :wind_login_field=, :wind_login_field
30
+
31
+ def wind_service(value=nil)
32
+ configure_property(:wind_service, value)
33
+ end
34
+ alias_method :wind_service=, :wind_login_field
35
+
36
+ def wind_host(value=nil)
37
+ configure_property(:wind_host, value)
38
+ end
39
+ alias_method :wind_host=, :wind_host
40
+ # Whether or not to validate the wind_login field. If set to false ALL wind validation will need to be
41
+ # handled by you.
42
+ #
43
+ # * <tt>Default:</tt> true
44
+ # * <tt>Accepts:</tt> Boolean
45
+ def validate_wind_login(value = true)
46
+ self.validates wind_login_field, :presence => value
47
+ end
48
+ alias_method :validate_wind_login=, :validate_wind_login
49
+
50
+ def find_by_wind_login_field(login)
51
+ # we should create a user here if login was valid but record is missing?
52
+ self.send( ("find_by_" + wind_login_field.to_s).to_sym, login )
53
+ end
54
+
55
+ def find_or_create_by_wind_login_field(login)
56
+ # we should create a user here if login was valid but record is missing
57
+ mname = ("find_or_create_by_" + wind_login_field.to_s)
58
+ logger.debug "#{self.name}.#{mname}(#{login})"
59
+ self.send mname.to_sym, login
60
+ end
61
+
62
+ private
63
+ def configure_property(prop, value=nil, default=nil)
64
+ value ||= default
65
+ if value
66
+ @wind_config[prop] = value
67
+ else
68
+ @wind_config[prop]
69
+ end
70
+ end
71
+ # shamelessly riped from authlogic
72
+ def db_setup?
73
+ begin
74
+ column_names
75
+ true
76
+ rescue Exception
77
+ false
78
+ end
79
+ end
80
+
81
+ def first_column_to_exist(*columns_to_check)
82
+ if db_setup?
83
+ columns_to_check.each { |column_name| return column_name.to_sym if column_names.include?(column_name.to_s) }
84
+ end
85
+ columns_to_check.first && columns_to_check.first.to_sym
86
+ end
87
+
88
+ end # ClassMethods
89
+
90
+ end # WindAuthenticatable
91
+ end # Models
92
+ end # Devise
@@ -0,0 +1,5 @@
1
+ Devise::Schema.class_eval do
2
+ def wind_authenticatable
3
+ # do some stuff
4
+ end
5
+ end
@@ -0,0 +1,108 @@
1
+ class Devise::Strategies::WindAuthenticatable < Devise::Strategies::Authenticatable
2
+ include Warden::Mixins::Urls
3
+ # :stopdoc:
4
+
5
+ HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS)
6
+
7
+ RESPONSE = "rack.wind.response"
8
+ AUTHENTICATE_HEADER = "WWW-Authenticate"
9
+ AUTHENTICATE_REGEXP = /^Wind/
10
+
11
+ URL_FIELD_SELECTOR = lambda { |field| field.to_s =~ %r{^https?://} }
12
+
13
+ # :startdoc:
14
+
15
+ # Helper method for building the "WWW-Authenticate" header value.
16
+ #
17
+ # Rack::Wind.build_header(:server => "http://josh.openid.com/")
18
+ # #=> Wind server="https://wind.columbia.edu/"
19
+ def self.build_header(params = {})
20
+ 'Wind ' + params.map { |key, value|
21
+ if value.is_a?(Array)
22
+ "#{key}=\"#{value.join(',')}\""
23
+ else
24
+ "#{key}=\"#{value}\""
25
+ end
26
+ }.join(', ')
27
+ end
28
+
29
+ # Helper method for parsing "WWW-Authenticate" header values into
30
+ # a hash.
31
+ #
32
+ # Rack::Wind.parse_header("Wind identifier='http://josh.openid.com/'")
33
+ # #=> {:identifier => "http://josh.openid.com/"}
34
+ def self.parse_header(str)
35
+ params = {}
36
+ if str =~ AUTHENTICATE_REGEXP
37
+ str = str.gsub(/#{AUTHENTICATE_REGEXP}\s+/, '')
38
+ str.split(', ').each { |pair|
39
+ key, *value = pair.split('=')
40
+ value = value.join('=')
41
+ value.gsub!(/^\"/, '').gsub!(/\"$/, "")
42
+ value = value.split(',')
43
+ params[key] = value.length > 1 ? value : value.first
44
+ }
45
+ end
46
+ params
47
+ end
48
+
49
+ # valid? indicates the applicability of this strategy to the authn request
50
+ def valid?
51
+ valid_mapping? # apply to any request for a wind user
52
+ end
53
+
54
+ def valid_mapping?
55
+ mapping.to.respond_to?(:find_by_wind_login_field)
56
+ end
57
+
58
+ def wind_response?
59
+ not wind_response.nil?
60
+ end
61
+
62
+ def wind_response
63
+ params['ticketid']
64
+ end
65
+
66
+ def authenticate!
67
+ logger.debug("Authenticating with WIND for mapping #{mapping.to}")
68
+
69
+ if wind_response
70
+ handle_response!
71
+ else # redirect to WIND login with a 30x status
72
+ redirect! wind_redirect_url
73
+ end
74
+ end
75
+
76
+ def wind_redirect_url
77
+ "https://#{mapping.to.wind_host}/login?destination=#{CGI.escapeHTML(request_url)}&service=#{CGI.escapeHTML(mapping.to.wind_service)}"
78
+ end
79
+
80
+ def handle_response!
81
+ ticket_id = params['ticketid']
82
+ validate_path = "/validate?ticketid=#{ticket_id}"
83
+ wind_validate = Net::HTTP.new("wind.columbia.edu",443)
84
+ wind_validate.use_ssl = true
85
+ wind_validate.start
86
+ wind_resp = wind_validate.get(validate_path)
87
+ wind_validate.finish
88
+ #puts wind_resp.body
89
+ authdoc = Nokogiri::XML(wind_resp.body)
90
+ ns = {'wind'=>'http://www.columbia.edu/acis/rad/authmethods/wind'}
91
+ _user = authdoc.xpath('//wind:authenticationSuccess/wind:user', ns)
92
+ wind_data = nil
93
+ if _user.length > 0
94
+ wind_data = {}
95
+ wind_data[:uni] = _user[0].content
96
+ wind_data[:affils] = authdoc.xpath('//wind:authenticationSuccess/wind:affiliations/wind:affil',ns).collect {|x| x.content}
97
+ logger.debug wind_data.inspect
98
+ _resource = mapping.to.find_or_create_by_wind_login_field(wind_data[:uni])
99
+ _resource.affiliations= wind_data[:affils]
100
+ _resource.save!
101
+ success! _resource
102
+ #else
103
+ # fail!
104
+ end
105
+ end
106
+ end
107
+
108
+ Warden::Strategies.add :wind_authenticatable, Devise::Strategies::WindAuthenticatable
@@ -0,0 +1,3 @@
1
+ module DeviseWind
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,23 @@
1
+ module DeviseWind
2
+ module Generators
3
+ class InstallGenerator < ::Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ desc "add the migrations"
7
+
8
+ def self.next_migration_number(path)
9
+ unless @prev_migration_nr
10
+ @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
11
+ else
12
+ @prev_migration_nr += 1
13
+ end
14
+ @prev_migration_nr.to_s
15
+ end
16
+
17
+ def copy_migrations
18
+ migration_template "add_roles.rb", "db/migrate/add_roles.rb"
19
+ migration_template "add_users_roles.rb", "db/migrate/add_users_roles.rb"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ class AddRoles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :roles do |t|
4
+ t.string :role_sym, :null => false
5
+ t.timestamps
6
+ end
7
+
8
+ add_index :roles, :role_sym, :unique => true
9
+ end
10
+
11
+ def self.down
12
+ drop_table :roles
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ class AddUsersRoles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :roles_users, :id => false do |t|
4
+ t.column :user_id, :integer, :null => false
5
+ t.column :role_id, :integer, :null => false
6
+ end
7
+ add_index :roles_users, :user_id, :unique => false
8
+ add_index :roles_users, :role_id, :unique => false
9
+ end
10
+
11
+ def self.down
12
+ drop_table :roles_users
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,264 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: devise_wind
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Benjamin Armintor, James Stuart
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-08 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rails
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 19
29
+ segments:
30
+ - 3
31
+ - 0
32
+ - 10
33
+ version: 3.0.10
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: devise
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 5
45
+ segments:
46
+ - 1
47
+ - 5
48
+ - 3
49
+ version: 1.5.3
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: yard
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ type: :development
65
+ version_requirements: *id003
66
+ - !ruby/object:Gem::Dependency
67
+ name: ruby-debug
68
+ prerelease: false
69
+ requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ hash: 3
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ type: :development
79
+ version_requirements: *id004
80
+ - !ruby/object:Gem::Dependency
81
+ name: ruby-debug-base
82
+ prerelease: false
83
+ requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ type: :development
93
+ version_requirements: *id005
94
+ - !ruby/object:Gem::Dependency
95
+ name: rspec-rails
96
+ prerelease: false
97
+ requirement: &id006 !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 19
103
+ segments:
104
+ - 2
105
+ - 7
106
+ - 0
107
+ version: 2.7.0
108
+ type: :development
109
+ version_requirements: *id006
110
+ - !ruby/object:Gem::Dependency
111
+ name: mocha
112
+ prerelease: false
113
+ requirement: &id007 !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ type: :development
123
+ version_requirements: *id007
124
+ - !ruby/object:Gem::Dependency
125
+ name: cucumber
126
+ prerelease: false
127
+ requirement: &id008 !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ hash: 53
133
+ segments:
134
+ - 0
135
+ - 8
136
+ - 5
137
+ version: 0.8.5
138
+ type: :development
139
+ version_requirements: *id008
140
+ - !ruby/object:Gem::Dependency
141
+ name: cucumber-rails
142
+ prerelease: false
143
+ requirement: &id009 !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ hash: 23
149
+ segments:
150
+ - 1
151
+ - 0
152
+ - 0
153
+ version: 1.0.0
154
+ type: :development
155
+ version_requirements: *id009
156
+ - !ruby/object:Gem::Dependency
157
+ name: gherkin
158
+ prerelease: false
159
+ requirement: &id010 !ruby/object:Gem::Requirement
160
+ none: false
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ hash: 3
165
+ segments:
166
+ - 0
167
+ version: "0"
168
+ type: :development
169
+ version_requirements: *id010
170
+ - !ruby/object:Gem::Dependency
171
+ name: factory_girl
172
+ prerelease: false
173
+ requirement: &id011 !ruby/object:Gem::Requirement
174
+ none: false
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ hash: 3
179
+ segments:
180
+ - 0
181
+ version: "0"
182
+ type: :development
183
+ version_requirements: *id011
184
+ - !ruby/object:Gem::Dependency
185
+ name: rake
186
+ prerelease: false
187
+ requirement: &id012 !ruby/object:Gem::Requirement
188
+ none: false
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ hash: 3
193
+ segments:
194
+ - 0
195
+ version: "0"
196
+ type: :development
197
+ version_requirements: *id012
198
+ description: some stuff
199
+ email:
200
+ - armintor@gmail.com
201
+ executables: []
202
+
203
+ extensions: []
204
+
205
+ extra_rdoc_files: []
206
+
207
+ files:
208
+ - lib/devise_wind/controllers/sessions.rb
209
+ - lib/devise_wind/controllers/users.rb
210
+ - lib/devise_wind/engine.rb
211
+ - lib/devise_wind/mixins/urls.rb
212
+ - lib/devise_wind/model.rb
213
+ - lib/devise_wind/schema.rb
214
+ - lib/devise_wind/strategy.rb
215
+ - lib/devise_wind/version.rb
216
+ - lib/devise_wind.rb
217
+ - lib/generators/devise_wind/install/install_generator.rb
218
+ - lib/generators/devise_wind/install/templates/add_roles.rb
219
+ - lib/generators/devise_wind/install/templates/add_users_roles.rb
220
+ - app/controllers/sessions_controller.rb
221
+ - app/helpers/application_helper.rb
222
+ - app/models/role.rb
223
+ - app/models/user.rb
224
+ - app/views/sessions/new.html.erb
225
+ - config/initializers/devise.rb
226
+ - config/locales/devise.en.yml
227
+ - config/locales/en.yml
228
+ - config/routes.rb
229
+ homepage: http://github.com/cul/devise_wind
230
+ licenses: []
231
+
232
+ post_install_message:
233
+ rdoc_options: []
234
+
235
+ require_paths:
236
+ - lib
237
+ required_ruby_version: !ruby/object:Gem::Requirement
238
+ none: false
239
+ requirements:
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ hash: 3
243
+ segments:
244
+ - 0
245
+ version: "0"
246
+ required_rubygems_version: !ruby/object:Gem::Requirement
247
+ none: false
248
+ requirements:
249
+ - - ">="
250
+ - !ruby/object:Gem::Version
251
+ hash: 3
252
+ segments:
253
+ - 0
254
+ version: "0"
255
+ requirements: []
256
+
257
+ rubyforge_project:
258
+ rubygems_version: 1.8.15
259
+ signing_key:
260
+ specification_version: 3
261
+ summary: Devise/WIND Rails Engine (requires Rails3)
262
+ test_files: []
263
+
264
+ has_rdoc: