diff --git a/lib/devise_crowd/config.rb b/lib/devise_crowd/config.rb index 2d64881..794a39a 100644 --- a/lib/devise_crowd/config.rb +++ b/lib/devise_crowd/config.rb @@ -7,6 +7,9 @@ module Devise mattr_accessor :crowd_service_url @@crowd_service_url = "http://localhost:8095/crowd" + mattr_accessor :crowd_noop + @@crowd_noop = false + mattr_accessor :crowd_app_name @@crowd_app_name = "crowd" @@ -22,7 +25,7 @@ module Devise # The name of the crowd username parameter/field. If nil (default), the # first authentication_keys key will be used (e.g. email). mattr_accessor :crowd_username_key - @@crowd_username_key = nil + @@crowd_username_key = "crowd_username" mattr_accessor :crowd_logger @@crowd_logger = true @@ -33,6 +36,12 @@ module Devise mattr_accessor :crowd_auto_register @@crowd_auto_register = true + mattr_accessor :cookie_domain + @@cookie_domain = nil + + mattr_accessor :cookie_secure + @@cookie_secure = nil + mattr_accessor :add_crowd_records @@add_crowd_records = false diff --git a/lib/devise_crowd/cookie.rb b/lib/devise_crowd/cookie.rb index 897012a..48def38 100644 --- a/lib/devise_crowd/cookie.rb +++ b/lib/devise_crowd/cookie.rb @@ -1,8 +1,17 @@ module DeviseCrowd + def self.get_cookie_info(record_class, crowd_client) + if record_class.cookie_domain + {domain: record_class.cookie_domain, secure: record_class.cookie_secure} + else + Rails.logger.debug "DEVISE CROWD : set_cookie : get_cookie_info" + DeviseCrowd.crowd_fetch { crowd_client.get_cookie_info } + end + end + def self.set_cookie(token, warden, record_class, crowd_client=nil) crowd_client ||= record_class.crowd_client - cookie_info = DeviseCrowd.crowd_fetch { crowd_client.get_cookie_info } + cookie_info = self.get_cookie_info(record_class, crowd_client) if cookie_info warden.cookies[record_class.crowd_token_key] = { :domain => cookie_info[:domain], @@ -14,7 +23,7 @@ def self.set_cookie(token, warden, record_class, crowd_client=nil) def self.destroy_cookie(warden, record_class, crowd_client=nil) crowd_client ||= record_class.crowd_client - cookie_info = DeviseCrowd.crowd_fetch { crowd_client.get_cookie_info } + cookie_info = self.get_cookie_info(record_class, crowd_client) if cookie_info warden.cookies.delete(record_class.crowd_token_key, :domain => cookie_info[:domain]) end diff --git a/lib/devise_crowd/models/common.rb b/lib/devise_crowd/models/common.rb index e27b172..c503d0e 100644 --- a/lib/devise_crowd/models/common.rb +++ b/lib/devise_crowd/models/common.rb @@ -42,7 +42,7 @@ def has_crowd_record? end def needs_crowd_auth?(last_auth) - last_auth && last_auth <= self.class.crowd_auth_every.ago + last_auth && last_auth <= self.class.crowd_auth_every.seconds.ago end def next_crowd_auth(last_auth) @@ -55,6 +55,7 @@ def crowd_record if @crowd_record.nil? @crowd_record = false username = self.send(:"#{self.class.crowd_username_key}") + Rails.logger.debug "DEVISE CROWD model : crowd_record : find_user_by_name #{username}" record = DeviseCrowd.crowd_fetch {crowd_client.find_user_by_name(username)} if username @crowd_record = record if record end @@ -149,9 +150,20 @@ def do_sync_to_crowd private :do_sync_to_crowd + module CrowdUsernameKeyWithDefault + def default + key = super + unless key + key = (authentication_keys.is_a?(Hash) ? authentication_keys.keys : authentication_keys).first + end + key + end + end module ClassMethods - Devise::Models.config(self, :crowd_enabled, :crowd_service_url, :crowd_app_name, :crowd_app_password, :crowd_token_key, :crowd_username_key, :crowd_auth_every, :crowd_allow_forgery_protection, :crowd_auto_register, :add_crowd_records, :update_crowd_records) + prepend CrowdUsernameKeyWithDefault + + Devise::Models.config(self, :crowd_enabled, :crowd_noop, :crowd_service_url, :crowd_app_name, :crowd_app_password, :crowd_token_key, :crowd_username_key, :crowd_auth_every, :crowd_allow_forgery_protection, :crowd_auto_register, :add_crowd_records, :update_crowd_records, :cookie_domain, :cookie_secure) def crowd_client SimpleCrowd::Client.new({ @@ -159,6 +171,7 @@ def crowd_client :app_name => self.crowd_app_name, :app_password => self.crowd_app_password, :cache_store => Rails.cache, + :noop => self.crowd_noop }) end @@ -170,15 +183,6 @@ def crowd_enabled?(strategy=nil) end end - def crowd_username_key_with_default - key = crowd_username_key_without_default - unless key - key = (authentication_keys.is_a?(Hash) ? authentication_keys.keys : authentication_keys).first - end - key - end - alias_method_chain :crowd_username_key, :default - def find_for_crowd_username(username) find_for_authentication({self.crowd_username_key => username}) end diff --git a/lib/devise_crowd/strategies/common.rb b/lib/devise_crowd/strategies/common.rb index 541b739..479e6a5 100644 --- a/lib/devise_crowd/strategies/common.rb +++ b/lib/devise_crowd/strategies/common.rb @@ -13,7 +13,12 @@ def crowd_username=(username) end def crowd_record - @crowd_record ||= @crowd_username && DeviseCrowd.crowd_fetch { crowd_client.find_user_by_name(@crowd_username) } + if !@crowd_record && @crowd_username + Rails.logger.debug "DEVISE CROWD : crowd_record : find_user_by_name #{@crowd_username}" + @crowd_record = DeviseCrowd.crowd_fetch { crowd_client.find_user_by_name(@crowd_username) } + end + + @crowd_record end def crowd_record=(record) @@ -24,7 +29,13 @@ def crowd_record=(record) private def validate_crowd_username! + # lookup resource on DB first if crowd_username + resource = resource_class.find_by_username_or_email(crowd_username) + end + + # if resource not found lookup on CROWD + if crowd_username && !resource resource = resource_class.find_for_crowd_username(crowd_username) resource = create_from_crowd if !resource && crowd_auto_register? end @@ -36,7 +47,7 @@ def validate_crowd_username! fail(:crowd_unverified_request) else DeviseCrowd::Logger.send("authenticated via #{authenticatable_name}!") - sync_from_crowd(resource) if sync_from_crowd? + sync_from_crowd(resource) if sync_from_crowd? and update_crowd_records? cache_authentication if store? yield(resource) if block_given? success!(resource) @@ -68,6 +79,10 @@ def crowd_auto_register? !!resource_class.crowd_auto_register end + def update_crowd_records? + !!resource_class.update_crowd_records + end + def crowd_client @crowd_client ||= resource_class.crowd_client end diff --git a/lib/devise_crowd/strategies/credentials_authenticatable.rb b/lib/devise_crowd/strategies/credentials_authenticatable.rb index 78ac1af..b2d82bc 100644 --- a/lib/devise_crowd/strategies/credentials_authenticatable.rb +++ b/lib/devise_crowd/strategies/credentials_authenticatable.rb @@ -48,12 +48,51 @@ def valid_params? end def authenticate_crowd_credentials - username = authentication_hash[resource_class.crowd_username_key] - token = DeviseCrowd.crowd_fetch { crowd_client.authenticate_user(username, password) } if username + crowd_username = authentication_hash.with_indifferent_access[resource_class.crowd_username_key] + email = params[@scope][:username] || params[@scope][:email] || crowd_username + + # authentication against crowd + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{crowd_username} : in CROWD ..." + token = DeviseCrowd.crowd_fetch { crowd_client.authenticate_user(crowd_username, password) } + + if token + resource = resource_class.find_by_username_or_email(email) + # if user does not exist create and update from crowd user + unless resource + resource = resource_class.new + crowd_user = DeviseCrowd.crowd_fetch { crowd_client.find_user_by_name(crowd_username) } + resource.update_from_crowd_user crowd_user + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{resource.email} : created user from CROWD #{crowd_user.inspect}" + end + + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{resource.email} : upsert user token in DB : #{token}" + resource.upsert_user_token token + + # if successful update password hash in DB via devise + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{resource.email} : update password in DB" + resource.password = password + resource.save! + end + + if email && !token + # authenticate against DB password + resource = resource || resource_class.find_by_username_or_email(email) + unless resource + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{email} : no User found in DB" + else + if resource.valid_password?(password) + token = resource.user_token && resource.user_token.token + token = resource.upsert_user_token(token).token + crowd_username = email + else + DeviseCrowd::Logger.send "DEVISE CREDS AUTH : #{email} : password in DB does not match" + end + end + end if token self.crowd_token = token - self.crowd_username = username + self.crowd_username = crowd_username else self.crowd_token = self.crowd_username = nil end diff --git a/lib/devise_crowd/strategies/token_authenticatable.rb b/lib/devise_crowd/strategies/token_authenticatable.rb index 05703ec..07e153a 100644 --- a/lib/devise_crowd/strategies/token_authenticatable.rb +++ b/lib/devise_crowd/strategies/token_authenticatable.rb @@ -71,14 +71,43 @@ def crowd_token_param def authenticate_crowd_token self.crowd_record = nil if has_crowd_token? - if DeviseCrowd.crowd_fetch { crowd_client.is_valid_user_token?(crowd_token) } - crowd_session = DeviseCrowd.session(warden, scope) - if crowd_session['crowd.last_token'] == crowd_token && crowd_session['crowd.last_username'] - self.crowd_username = crowd_session['crowd.last_username'] + # try to first authenticate against DB token if exists + resource = resource_class.find_by_token(crowd_token) + if resource + resource.upsert_user_token(crowd_token) + self.crowd_username = resource.send(resource_class.crowd_username_key) + end + + unless self.crowd_username + if DeviseCrowd.crowd_fetch { crowd_client.is_valid_user_token?(crowd_token) } + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{crowd_token} : is valid in CROWD" + crowd_session = DeviseCrowd.session(warden, scope) + if crowd_session['crowd.last_token'] == crowd_token && crowd_session['crowd.last_username'] + self.crowd_username = crowd_session['crowd.last_username'] + else + self.crowd_record = DeviseCrowd.crowd_fetch { crowd_client.find_user_by_token(crowd_token) } + if self.crowd_record + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{crowd_token} : found user by token in CROWD : #{self.crowd_username}" + resource = resource_class.find_by_username(self.crowd_username) + # if user does not exist create and update from crowd user + unless resource + resource = resource_class.new + resource.update_from_crowd_user self.crowd_record + resource.save! + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{self.crowd_username} : created user #{resource.id} from CROWD" + end + + # if successful update user token in DB + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{self.crowd_username} : update user token in DB" + resource.upsert_user_token(crowd_token) + else + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{crowd_token} : no user found by token in CROWD" + end + end + DeviseCrowd::Logger.send("cannot find user for token key") unless self.crowd_username else - self.crowd_record = DeviseCrowd.crowd_fetch { crowd_client.find_user_by_token(crowd_token) } + DeviseCrowd::Logger.send "DEVISE TOKEN AUTH : #{crowd_token} : NOT valid in CROWD" end - DeviseCrowd::Logger.send("cannot find user for token key") unless self.crowd_username end end end diff --git a/spec/rails_app/config/boot.rb b/spec/rails_app/config/boot.rb index 4489e58..f2830ae 100644 --- a/spec/rails_app/config/boot.rb +++ b/spec/rails_app/config/boot.rb @@ -3,4 +3,4 @@ # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])