From a4093ed7dcc504e667675166c5eb019d2482b3b8 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Fri, 25 Oct 2013 00:34:31 -0400 Subject: [PATCH 1/2] Removes overriding the visibility of ActionController::Base#cookies Rails declares ActionController::Base#cookies to be a private method. CASino should respect this and not change it to be public just so that listeners can interact with them. This adds a convenience method to CASino::Listener that exposes an API into ActionController::Base#cookies by calling #send in order to access the private method. --- app/controllers/casino/application_controller.rb | 4 ---- app/listeners/casino/listener.rb | 4 ++++ app/listeners/casino/login_credential_acceptor_listener.rb | 2 +- app/listeners/casino/login_credential_requestor_listener.rb | 2 +- app/listeners/casino/logout_listener.rb | 2 +- .../casino/second_factor_authentication_acceptor_listener.rb | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/casino/application_controller.rb b/app/controllers/casino/application_controller.rb index 8a207864..c4b55fd2 100644 --- a/app/controllers/casino/application_controller.rb +++ b/app/controllers/casino/application_controller.rb @@ -7,10 +7,6 @@ class CASino::ApplicationController < ::ApplicationController layout 'application' before_filter :set_locale - def cookies - super - end - protected def processor(processor_name, listener_name = nil) listener_name ||= processor_name diff --git a/app/listeners/casino/listener.rb b/app/listeners/casino/listener.rb index a05b7d1a..9ce30d22 100644 --- a/app/listeners/casino/listener.rb +++ b/app/listeners/casino/listener.rb @@ -12,5 +12,9 @@ def initialize(controller) def assign(name, value) @controller.instance_variable_set("@#{name}", value) end + + def cookies + @controller.send(:cookies) + end end end diff --git a/app/listeners/casino/login_credential_acceptor_listener.rb b/app/listeners/casino/login_credential_acceptor_listener.rb index c5819a46..e1c84908 100644 --- a/app/listeners/casino/login_credential_acceptor_listener.rb +++ b/app/listeners/casino/login_credential_acceptor_listener.rb @@ -2,7 +2,7 @@ class CASino::LoginCredentialAcceptorListener < CASino::Listener def user_logged_in(url, ticket_granting_ticket, cookie_expiry_time = nil) - @controller.cookies[:tgt] = { value: ticket_granting_ticket, expires: cookie_expiry_time } + cookies[:tgt] = { value: ticket_granting_ticket, expires: cookie_expiry_time } if url.nil? @controller.redirect_to sessions_path, status: :see_other else diff --git a/app/listeners/casino/login_credential_requestor_listener.rb b/app/listeners/casino/login_credential_requestor_listener.rb index 019e27c0..c723cd7c 100644 --- a/app/listeners/casino/login_credential_requestor_listener.rb +++ b/app/listeners/casino/login_credential_requestor_listener.rb @@ -3,7 +3,7 @@ class CASino::LoginCredentialRequestorListener < CASino::Listener def user_not_logged_in(login_ticket) assign(:login_ticket, login_ticket) - @controller.cookies.delete :tgt + cookies.delete :tgt end def service_not_allowed(service) diff --git a/app/listeners/casino/logout_listener.rb b/app/listeners/casino/logout_listener.rb index bc66fa90..a52ed51e 100644 --- a/app/listeners/casino/logout_listener.rb +++ b/app/listeners/casino/logout_listener.rb @@ -7,6 +7,6 @@ def user_logged_out(url, redirect_immediately = false) else assign(:url, url) end - @controller.cookies.delete :tgt + cookies.delete :tgt end end diff --git a/app/listeners/casino/second_factor_authentication_acceptor_listener.rb b/app/listeners/casino/second_factor_authentication_acceptor_listener.rb index 0248c604..81fa958f 100644 --- a/app/listeners/casino/second_factor_authentication_acceptor_listener.rb +++ b/app/listeners/casino/second_factor_authentication_acceptor_listener.rb @@ -7,7 +7,7 @@ def user_not_logged_in end def user_logged_in(url, ticket_granting_ticket, cookie_expiry_time = nil) - @controller.cookies[:tgt] = { value: ticket_granting_ticket, expires: cookie_expiry_time } + cookies[:tgt] = { value: ticket_granting_ticket, expires: cookie_expiry_time } if url.nil? @controller.redirect_to sessions_path, status: :see_other else From 4519cc44afe526c25727383b5a35ba64531dc845 Mon Sep 17 00:00:00 2001 From: Derek Lindahl Date: Thu, 24 Oct 2013 23:21:45 -0400 Subject: [PATCH 2/2] Adds CurrentUser processor This extracts the "find user by TGT" logic that exists in all of the processors and puts it in a CurrentUser processor. This processor is automatically run in a :before_filter for most controller actions. If a user is found, then the User record is assigned to the @current_user instance variable. From there it can be passed into the other processors as well as be used in the various CASino views. An additional benefit of this feature is that the host application can also use this value in its own views: ```ruby class WidgetsController < ApplicationController include CASino::CurrentUserHelper def index # ... end end
<% if user_signed_in? %> <%= debug current_user %> <% end %>
``` * Adds CurrentUser processor to encapsulate all user look-up based on TGT values * Provides #current_user and #user_signed_in? controller helper methods * Refactored some more complicated internal logic out into private methods for a handful of processors --- .../casino/application_controller.rb | 8 +- app/controllers/casino/sessions_controller.rb | 17 +-- .../two_factor_authenticators_controller.rb | 6 +- app/helpers/application_helper.rb | 2 - app/helpers/casino/application_helper.rb | 9 ++ app/helpers/casino/current_user_helper.rb | 23 ++++ app/helpers/casino/processor_helper.rb | 8 ++ app/listeners/casino/current_user_listener.rb | 13 +++ app/listeners/casino/listener.rb | 7 +- app/listeners/casino/logout_listener.rb | 1 + app/models/casino/ticket_granting_ticket.rb | 7 ++ app/models/casino/user.rb | 19 ++++ .../casino/current_user_processor.rb | 52 +++++++++ .../login_credential_requestor_processor.rb | 17 ++- app/processors/casino/logout_processor.rb | 10 +- .../other_sessions_destroyer_processor.rb | 16 ++- .../casino/processor_concern/current_user.rb | 18 +++ .../ticket_granting_tickets.rb | 12 +- ...actor_authentication_acceptor_processor.rb | 57 ++++++---- .../casino/session_destroyer_processor.rb | 15 +-- .../casino/session_overview_processor.rb | 23 ++-- ...actor_authenticator_activator_processor.rb | 31 ++++-- ...actor_authenticator_destroyer_processor.rb | 24 ++-- ...factor_authenticator_overview_processor.rb | 22 ++-- ...tor_authenticator_registrator_processor.rb | 23 ++-- app/views/casino/sessions/index.html.erb | 2 +- spec/controllers/sessions_controller_spec.rb | 17 ++- ...o_factor_authenticators_controller_spec.rb | 4 +- spec/model/user_spec.rb | 82 ++++++++++++++ spec/processor/current_user_spec.rb | 104 ++++++++++++++++++ .../login_credential_requestor_spec.rb | 57 +++------- spec/processor/logout_other_sessions_spec.rb | 11 +- spec/processor/logout_spec.rb | 31 ++---- .../processor/other_session_destroyer_spec.rb | 54 +++++++++ ...cond_factor_authenticaton_acceptor_spec.rb | 33 +++--- spec/processor/session_destroyer_spec.rb | 29 ++--- spec/processor/session_overview_spec.rb | 25 ++--- ...two_factor_authenticator_activator_spec.rb | 43 ++++---- ...two_factor_authenticator_destroyer_spec.rb | 30 ++--- .../two_factor_authenticator_overview_spec.rb | 29 ++--- ...o_factor_authenticator_registrator_spec.rb | 24 ++-- .../ticket_granting_ticket_factory.rb | 4 + 42 files changed, 695 insertions(+), 324 deletions(-) delete mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/casino/application_helper.rb create mode 100644 app/helpers/casino/current_user_helper.rb create mode 100644 app/helpers/casino/processor_helper.rb create mode 100644 app/listeners/casino/current_user_listener.rb create mode 100644 app/processors/casino/current_user_processor.rb create mode 100644 app/processors/casino/processor_concern/current_user.rb create mode 100644 spec/model/user_spec.rb create mode 100644 spec/processor/current_user_spec.rb create mode 100644 spec/processor/other_session_destroyer_spec.rb diff --git a/app/controllers/casino/application_controller.rb b/app/controllers/casino/application_controller.rb index c4b55fd2..de94b0f7 100644 --- a/app/controllers/casino/application_controller.rb +++ b/app/controllers/casino/application_controller.rb @@ -2,18 +2,12 @@ require 'http_accept_language' class CASino::ApplicationController < ::ApplicationController - include ApplicationHelper + include CASino::ApplicationHelper layout 'application' before_filter :set_locale protected - def processor(processor_name, listener_name = nil) - listener_name ||= processor_name - listener = CASino.const_get(:"#{listener_name}Listener").new(self) - @processor = CASino.const_get(:"#{processor_name}Processor").new(listener) - end - def set_locale I18n.locale = extract_locale_from_accept_language_header || I18n.default_locale end diff --git a/app/controllers/casino/sessions_controller.rb b/app/controllers/casino/sessions_controller.rb index b5d774e0..a673dff7 100644 --- a/app/controllers/casino/sessions_controller.rb +++ b/app/controllers/casino/sessions_controller.rb @@ -1,13 +1,15 @@ class CASino::SessionsController < CASino::ApplicationController include CASino::SessionsHelper + skip_before_filter :set_current_user, only:[:create, :validate_otp] + def index - processor(:TwoFactorAuthenticatorOverview).process(cookies, request.user_agent) - processor(:SessionOverview).process(cookies, request.user_agent) + processor(:TwoFactorAuthenticatorOverview).process(current_user, request.user_agent) + processor(:SessionOverview).process(current_user, request.user_agent) end def new - processor(:LoginCredentialRequestor).process(params, cookies, request.user_agent) + processor(:LoginCredentialRequestor).process(params, current_user, request.user_agent) end def create @@ -15,18 +17,19 @@ def create end def destroy - processor(:SessionDestroyer).process(params, cookies, request.user_agent) + processor(:SessionDestroyer).process(params, current_user, request.user_agent) end def destroy_others - processor(:OtherSessionsDestroyer).process(params, cookies, request.user_agent) + processor(:OtherSessionsDestroyer).process(params, current_user, request.user_agent) end def logout - processor(:Logout).process(params, cookies, request.user_agent) + processor(:Logout).process(params, current_user, request.user_agent) end def validate_otp - processor(:SecondFactorAuthenticationAcceptor).process(params, request.user_agent) + processor(:CurrentUser).process(params, request.user_agent, ignore_two_factor:true) + processor(:SecondFactorAuthenticationAcceptor).process(params, current_user, request.user_agent) end end diff --git a/app/controllers/casino/two_factor_authenticators_controller.rb b/app/controllers/casino/two_factor_authenticators_controller.rb index 8d5271ff..97dbdc47 100644 --- a/app/controllers/casino/two_factor_authenticators_controller.rb +++ b/app/controllers/casino/two_factor_authenticators_controller.rb @@ -2,14 +2,14 @@ class CASino::TwoFactorAuthenticatorsController < CASino::ApplicationController include CASino::SessionsHelper def new - processor(:TwoFactorAuthenticatorRegistrator).process(cookies, request.user_agent) + processor(:TwoFactorAuthenticatorRegistrator).process(current_user, request.user_agent) end def create - processor(:TwoFactorAuthenticatorActivator).process(params, cookies, request.user_agent) + processor(:TwoFactorAuthenticatorActivator).process(params, current_user, request.user_agent) end def destroy - processor(:TwoFactorAuthenticatorDestroyer).process(params, cookies, request.user_agent) + processor(:TwoFactorAuthenticatorDestroyer).process(params, current_user, request.user_agent) end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb deleted file mode 100644 index de6be794..00000000 --- a/app/helpers/application_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module ApplicationHelper -end diff --git a/app/helpers/casino/application_helper.rb b/app/helpers/casino/application_helper.rb new file mode 100644 index 00000000..fd86ee7a --- /dev/null +++ b/app/helpers/casino/application_helper.rb @@ -0,0 +1,9 @@ +module CASino + module ApplicationHelper + extend ActiveSupport::Concern + + included do + include CASino::CurrentUserHelper + end + end +end diff --git a/app/helpers/casino/current_user_helper.rb b/app/helpers/casino/current_user_helper.rb new file mode 100644 index 00000000..d542f2c1 --- /dev/null +++ b/app/helpers/casino/current_user_helper.rb @@ -0,0 +1,23 @@ +module CASino::CurrentUserHelper + include CASino::ProcessorHelper + + def self.included(base) + return unless base.ancestors.include? ActionController::Base + + base.helper_method :current_user, :user_signed_in? + base.before_filter :set_current_user + end + + def set_current_user + params[:tgt] ||= cookies[:tgt] + processor(:CurrentUser).process(params, request.user_agent) + end + + def current_user + @current_user + end + + def user_signed_in? + !!current_user + end +end diff --git a/app/helpers/casino/processor_helper.rb b/app/helpers/casino/processor_helper.rb new file mode 100644 index 00000000..27696a3a --- /dev/null +++ b/app/helpers/casino/processor_helper.rb @@ -0,0 +1,8 @@ +module CASino::ProcessorHelper + protected + def processor(processor_name, listener_name = nil) + listener_name ||= processor_name + listener = CASino.const_get(:"#{listener_name}Listener").new(self) + @processor = CASino.const_get(:"#{processor_name}Processor").new(listener) + end +end \ No newline at end of file diff --git a/app/listeners/casino/current_user_listener.rb b/app/listeners/casino/current_user_listener.rb new file mode 100644 index 00000000..fd14fef0 --- /dev/null +++ b/app/listeners/casino/current_user_listener.rb @@ -0,0 +1,13 @@ +class CASino::CurrentUserListener < CASino::Listener + def user_not_logged_in + # NO-OP + end + + def user_not_logged_in! + @controller.redirect_to login_path + end + + def current_user_found(user) + assign(:current_user, user) + end +end diff --git a/app/listeners/casino/listener.rb b/app/listeners/casino/listener.rb index 9ce30d22..6af03269 100644 --- a/app/listeners/casino/listener.rb +++ b/app/listeners/casino/listener.rb @@ -1,6 +1,5 @@ module CASino class Listener - # include helpers to have the route path methods (like sessions_path) include CASino::Engine.routes.url_helpers @@ -8,11 +7,15 @@ def initialize(controller) @controller = controller end - protected def assign(name, value) @controller.instance_variable_set("@#{name}", value) end + def assigned(name) + @controller.instance_variable_get("@#{name}") + end + + protected def cookies @controller.send(:cookies) end diff --git a/app/listeners/casino/logout_listener.rb b/app/listeners/casino/logout_listener.rb index a52ed51e..7fe78828 100644 --- a/app/listeners/casino/logout_listener.rb +++ b/app/listeners/casino/logout_listener.rb @@ -2,6 +2,7 @@ class CASino::LogoutListener < CASino::Listener def user_logged_out(url, redirect_immediately = false) + assign(:current_user, nil) if redirect_immediately @controller.redirect_to url, status: :see_other else diff --git a/app/models/casino/ticket_granting_ticket.rb b/app/models/casino/ticket_granting_ticket.rb index 10c9ddaf..7f607718 100644 --- a/app/models/casino/ticket_granting_ticket.rb +++ b/app/models/casino/ticket_granting_ticket.rb @@ -7,6 +7,13 @@ class CASino::TicketGrantingTicket < ActiveRecord::Base belongs_to :user has_many :service_tickets, dependent: :destroy + # Backport Rails 4's ActiveRecord::NullRelation + # From: http://stackoverflow.com/questions/4877931/how-to-return-an-empty-activerecord-relation + # See also: http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-none + if Rails.version.to_i < 4 + scope :none, where('1=0') + end + def self.cleanup(user = nil) if user.nil? base = self diff --git a/app/models/casino/user.rb b/app/models/casino/user.rb index 6c1a3aa4..1352afc5 100644 --- a/app/models/casino/user.rb +++ b/app/models/casino/user.rb @@ -9,4 +9,23 @@ class CASino::User < ActiveRecord::Base def active_two_factor_authenticator self.two_factor_authenticators.where(active: true).first end + + def ticket(params = {}) + query = if params + params = params.delete_if{ |k,v| v.nil? } + ticket_granting_tickets.where(params) + else + ticket_granting_tickets + end + + query.first + end + + def other_tickets(user_agent = nil) + if primary = ticket(user_agent:user_agent) + ticket_granting_tickets.where('id != ?', primary.id) + else + ticket_granting_tickets.none + end + end end diff --git a/app/processors/casino/current_user_processor.rb b/app/processors/casino/current_user_processor.rb new file mode 100644 index 00000000..7170462b --- /dev/null +++ b/app/processors/casino/current_user_processor.rb @@ -0,0 +1,52 @@ +class CASino::CurrentUserProcessor < CASino::Processor + include CASino::ProcessorConcern::LoginTickets + include CASino::ProcessorConcern::TicketGrantingTickets + + # This method will call `#user_not_logged_in` or `#current_user_found(User)` on the listener. + # @param [Hash] params A Hash delivered by the client used to located the User's Ticket Granting Ticket + # @param [String] user_agent user-agent delivered by the client + def process(params = nil, user_agent = nil, options = {}) + options ||= {} + if user = handle_process(params, user_agent, options) + @listener.current_user_found(user) + else + @listener.user_not_logged_in + end + end + + # This method will call `#user_not_logged_in!` or `#current_user_found(User)` on the listener. + # @param [Hash] params A Hash delivered by the client used to located the User's Ticket Granting Ticket + # @param [String] user_agent user-agent delivered by the client + def process!(params = nil, user_agent = nil, options = {}) + options ||= {} + if handle_process(params, user_agent, options) + @listener.current_user_found + else + @listener.user_not_logged_in! + end + end + + private + def handle_process(params, user_agent, options) + return current_user if user_signed_in? + + @params, @user_agent = (params || {}), user_agent + + ticket_granting_ticket(options).try(:user) + end + + def current_user + @listener.assigned :current_user + end + + def user_signed_in? + current_user + end + + def ticket_granting_ticket(options) + @ticket_granting_ticket ||= begin + find_valid_ticket_granting_ticket(@params[:tgt], @user_agent, options[:ignore_two_factor]) + end + end + +end \ No newline at end of file diff --git a/app/processors/casino/login_credential_requestor_processor.rb b/app/processors/casino/login_credential_requestor_processor.rb index fcc86c16..bba5ff42 100644 --- a/app/processors/casino/login_credential_requestor_processor.rb +++ b/app/processors/casino/login_credential_requestor_processor.rb @@ -3,7 +3,7 @@ class CASino::LoginCredentialRequestorProcessor < CASino::Processor include CASino::ProcessorConcern::Browser include CASino::ProcessorConcern::LoginTickets include CASino::ProcessorConcern::ServiceTickets - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # Use this method to process the request. # @@ -13,12 +13,13 @@ class CASino::LoginCredentialRequestorProcessor < CASino::Processor # * `#service_not_allowed`: The user tried to access a service that this CAS server is not allowed to serve. # # @param [Hash] params parameters supplied by user - # @param [Hash] cookies cookies supplied by user + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user = nil, user_agent = nil) @params = params || {} - @cookies = cookies || {} - @user_agent = user_agent || {} + @user = user || current_user + @user_agent = user_agent + if check_service_allowed handle_allowed_service end @@ -26,7 +27,7 @@ def process(params = nil, cookies = nil, user_agent = nil) private def handle_allowed_service - if !@params[:renew] && (@ticket_granting_ticket = find_valid_ticket_granting_ticket(@cookies[:tgt], @user_agent)) + if !@params[:renew] && ticket_granting_ticket handle_logged_in else handle_not_logged_in @@ -63,4 +64,8 @@ def check_service_allowed def gateway_request? @params[:gateway] == 'true' && @params[:service] end + + def ticket_granting_ticket + @ticket_granting_ticket ||= @user.ticket(user_agent:@user_agent) + end end diff --git a/app/processors/casino/logout_processor.rb b/app/processors/casino/logout_processor.rb index 6ac8a523..a8320cac 100644 --- a/app/processors/casino/logout_processor.rb +++ b/app/processors/casino/logout_processor.rb @@ -1,6 +1,6 @@ # The Logout processor should be used to process GET requests to /logout. class CASino::LogoutProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # This method will call `#user_logged_out` and may supply an URL that should be presented to the user. # As per specification, the URL specified by "url" SHOULD be on the logout page with descriptive text. @@ -8,12 +8,12 @@ class CASino::LogoutProcessor < CASino::Processor # Please click here to access http://www.go-back.edu/." # # @param [Hash] params parameters supplied by user - # @param [Hash] cookies cookies supplied by user + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user = nil, user_agent = nil) params ||= {} - cookies ||= {} - remove_ticket_granting_ticket(cookies[:tgt], user_agent) + user ||= current_user + user.ticket(user_agent:user_agent).try(:destroy) if params[:service] && CASino::ServiceRule.allowed?(params[:service]) @listener.user_logged_out(params[:service], true) else diff --git a/app/processors/casino/other_sessions_destroyer_processor.rb b/app/processors/casino/other_sessions_destroyer_processor.rb index 808b234e..f6dcab08 100644 --- a/app/processors/casino/other_sessions_destroyer_processor.rb +++ b/app/processors/casino/other_sessions_destroyer_processor.rb @@ -5,22 +5,20 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::OtherSessionsDestroyerProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # This method will call `#other_sessions_destroyed` and may supply an URL that should be presented to the user. # The user should be redirected to this URL immediately. # # @param [Hash] params parameters supplied by user - # @param [Hash] cookies cookies supplied by user + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user = nil, user_agent = nil) params ||= {} - cookies ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - unless tgt.nil? - other_ticket_granting_tickets = tgt.user.ticket_granting_tickets.where('id != ?', tgt.id) - other_ticket_granting_tickets.destroy_all - end + user ||= current_user + + user.other_tickets(user_agent).destroy_all + @listener.other_sessions_destroyed(params[:service]) end end diff --git a/app/processors/casino/processor_concern/current_user.rb b/app/processors/casino/processor_concern/current_user.rb new file mode 100644 index 00000000..0cd7daa7 --- /dev/null +++ b/app/processors/casino/processor_concern/current_user.rb @@ -0,0 +1,18 @@ +module CASino + module ProcessorConcern + module CurrentUser + def user_signed_in? + !!assigned_user + end + + def current_user + assigned_user || CASino::User.new + end + + private + def assigned_user + @listener.assigned :current_user + end + end + end +end diff --git a/app/processors/casino/processor_concern/ticket_granting_tickets.rb b/app/processors/casino/processor_concern/ticket_granting_tickets.rb index 45ffbd07..c1ef8ac5 100644 --- a/app/processors/casino/processor_concern/ticket_granting_tickets.rb +++ b/app/processors/casino/processor_concern/ticket_granting_tickets.rb @@ -3,8 +3,7 @@ module CASino module ProcessorConcern module TicketGrantingTickets - - include CASino::ProcessorConcern::Browser + include Browser def find_valid_ticket_granting_ticket(tgt, user_agent, ignore_two_factor = false) ticket_granting_ticket = CASino::TicketGrantingTicket.where(ticket: tgt).first @@ -50,10 +49,13 @@ def load_or_initialize_user(authenticator, username, extra_attributes) end def remove_ticket_granting_ticket(ticket_granting_ticket, user_agent = nil) - tgt = find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) - unless tgt.nil? - tgt.destroy + tgt = if ticket_granting_ticket.is_a? CASino::TicketGrantingTicket + ticket_granting_ticket + else + find_valid_ticket_granting_ticket(ticket_granting_ticket, user_agent) end + + tgt.destroy if tgt end def cleanup_expired_ticket_granting_tickets(user) diff --git a/app/processors/casino/second_factor_authentication_acceptor_processor.rb b/app/processors/casino/second_factor_authentication_acceptor_processor.rb index 9f0856be..102d674e 100644 --- a/app/processors/casino/second_factor_authentication_acceptor_processor.rb +++ b/app/processors/casino/second_factor_authentication_acceptor_processor.rb @@ -4,7 +4,7 @@ # to implement this on the web application side. class CASino::SecondFactorAuthenticationAcceptorProcessor < CASino::Processor include CASino::ProcessorConcern::ServiceTickets - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser include CASino::ProcessorConcern::TwoFactorAuthenticators # The method will call one of the following methods on the listener: @@ -14,32 +14,49 @@ class CASino::SecondFactorAuthenticationAcceptorProcessor < CASino::Processor # * `#invalid_one_time_password`: The user should be asked for a new OTP. # # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :service. + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, user_agent = nil) - cookies ||= {} - tgt = find_valid_ticket_granting_ticket(params[:tgt], user_agent, true) - if tgt.nil? - @listener.user_not_logged_in - else - validation_result = validate_one_time_password(params[:otp], tgt.user.active_two_factor_authenticator) + def process(params = nil, user = nil, user_agent = nil) + @user = user || current_user + @params = params || {} + + if tgt = @user.ticket({ user_agent:user_agent, ticket:@params[:tgt] }) if validation_result.success? - tgt.awaiting_two_factor_authentication = false - tgt.save! + tgt.update_attributes!(awaiting_two_factor_authentication:false) begin - url = unless params[:service].blank? - acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url - end - if tgt.long_term? - @listener.user_logged_in(url, tgt.ticket, CASino.config.ticket_granting_ticket[:lifetime_long_term].seconds.from_now) - else - @listener.user_logged_in(url, tgt.ticket) - end - rescue ServiceNotAllowedError => e - @listener.service_not_allowed(clean_service_url params[:service]) + handle_logged_in_user(tgt) + rescue ServiceNotAllowedError + @listener.service_not_allowed(clean_service_url @params[:service]) end else @listener.invalid_one_time_password end + else + @listener.user_not_logged_in end end + + private + def validation_result + @validation_result ||= begin + validate_one_time_password(@params[:otp], @user.active_two_factor_authenticator) + end + end + + def service_url(tgt) + return if @params[:service].blank? + + acquire_service_ticket(tgt, @params[:service], true).service_with_ticket_url + end + + def expiry_time(tgt) + return unless tgt.long_term? + + CASino.config.ticket_granting_ticket[:lifetime_long_term].seconds.from_now + end + + def handle_logged_in_user(tgt) + @listener.user_logged_in(service_url(tgt), tgt.ticket, expiry_time(tgt)) + end + end diff --git a/app/processors/casino/session_destroyer_processor.rb b/app/processors/casino/session_destroyer_processor.rb index 82ffe750..833398fd 100644 --- a/app/processors/casino/session_destroyer_processor.rb +++ b/app/processors/casino/session_destroyer_processor.rb @@ -4,22 +4,23 @@ # to implement this on the web application side. It is especially useful in # combination with the {CASino::SessionOverviewProcessor} processor. class CASino::SessionDestroyerProcessor < CASino::Processor + include CASino::ProcessorConcern::CurrentUser # This method will call `#ticket_not_found` or `#ticket_deleted` on the listener. # @param [Hash] params parameters supplied by user (ID of ticket-granting ticket to delete should by in params[:id]) - # @param [Hash] cookies cookies supplied by user + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) + def process(params = nil, user = nil, user_agent = nil) params ||= {} - cookies ||= {} + user ||= current_user ticket = CASino::TicketGrantingTicket.where(id: params[:id]).first - owner_ticket = CASino::TicketGrantingTicket.where(ticket: cookies[:tgt]).first - if ticket.nil? || !ticket.same_user?(owner_ticket) - @listener.ticket_not_found - else + owner_ticket = user.ticket(user_agent:user_agent) + if ticket && ticket.same_user?(owner_ticket) Rails.logger.info "Destroying ticket-granting ticket '#{ticket.ticket}'" ticket.destroy @listener.ticket_deleted + else + @listener.ticket_not_found end end end diff --git a/app/processors/casino/session_overview_processor.rb b/app/processors/casino/session_overview_processor.rb index b464c4ff..def41333 100644 --- a/app/processors/casino/session_overview_processor.rb +++ b/app/processors/casino/session_overview_processor.rb @@ -3,19 +3,24 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::SessionOverviewProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener. - # @param [Hash] cookies cookies delivered by the client + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(cookies = nil, user_agent = nil) - cookies ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - if tgt.nil? - @listener.user_not_logged_in - else - ticket_granting_tickets = tgt.user.ticket_granting_tickets.where(awaiting_two_factor_authentication: false).order('updated_at DESC') + def process(user = nil, user_agent = nil) + @user ||= current_user + + if @user.ticket(user_agent:user_agent) @listener.ticket_granting_tickets_found(ticket_granting_tickets) + else + @listener.user_not_logged_in end end + + private + def ticket_granting_tickets + @user.ticket_granting_tickets.where(awaiting_two_factor_authentication: false).order('updated_at DESC') + end + end diff --git a/app/processors/casino/two_factor_authenticator_activator_processor.rb b/app/processors/casino/two_factor_authenticator_activator_processor.rb index 8f38596b..6bd34d62 100644 --- a/app/processors/casino/two_factor_authenticator_activator_processor.rb +++ b/app/processors/casino/two_factor_authenticator_activator_processor.rb @@ -3,7 +3,7 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::TwoFactorAuthenticatorActivatorProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser include CASino::ProcessorConcern::TwoFactorAuthenticators # The method will call one of the following methods on the listener: @@ -13,19 +13,15 @@ class CASino::TwoFactorAuthenticatorActivatorProcessor < CASino::Processor # * `#invalid_one_time_password`: The user should be asked for a new OTP. # # @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :id. - # @param [Hash] cookies cookies delivered by the client + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) - cookies ||= {} - params ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - if tgt.nil? - @listener.user_not_logged_in - else - authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first - validation_result = validate_one_time_password(params[:otp], authenticator) + def process(params = nil, user = nil, user_agent = nil) + @user = user || current_user + @params = params || {} + + if tgt = @user.ticket(user_agent:user_agent) if validation_result.success? - tgt.user.two_factor_authenticators.where(active: true).delete_all + @user.two_factor_authenticators.where(active: true).delete_all authenticator.active = true authenticator.save! @listener.two_factor_authenticator_activated @@ -36,6 +32,17 @@ def process(params = nil, cookies = nil, user_agent = nil) @listener.invalid_two_factor_authenticator end end + else + @listener.user_not_logged_in end end + + private + def authenticator + @authenticator ||= @user.two_factor_authenticators.where(id: @params[:id]).first + end + + def validation_result + @validation_result ||= validate_one_time_password(@params[:otp], authenticator) + end end diff --git a/app/processors/casino/two_factor_authenticator_destroyer_processor.rb b/app/processors/casino/two_factor_authenticator_destroyer_processor.rb index 16396517..21367153 100644 --- a/app/processors/casino/two_factor_authenticator_destroyer_processor.rb +++ b/app/processors/casino/two_factor_authenticator_destroyer_processor.rb @@ -3,7 +3,7 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::TwoFactorAuthenticatorDestroyerProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser include CASino::ProcessorConcern::TwoFactorAuthenticators # The method will call one of the following methods on the listener: @@ -12,22 +12,26 @@ class CASino::TwoFactorAuthenticatorDestroyerProcessor < CASino::Processor # * `#invalid_two_factor_authenticator`: The two-factor authenticator is not valid. # # @param [Hash] params parameters supplied by user. The processor will look for key :id. - # @param [Hash] cookies cookies delivered by the client + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(params = nil, cookies = nil, user_agent = nil) - cookies ||= {} - params ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - if tgt.nil? - @listener.user_not_logged_in - else - authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first + def process(params = nil, user = nil, user_agent = nil) + @params = params || {} + @user = user || current_user + + if @user.ticket(user_agent:user_agent) if authenticator authenticator.destroy @listener.two_factor_authenticator_destroyed else @listener.invalid_two_factor_authenticator end + else + @listener.user_not_logged_in end end + + private + def authenticator + authenticator ||= @user.two_factor_authenticators.where(id: @params[:id]).first + end end diff --git a/app/processors/casino/two_factor_authenticator_overview_processor.rb b/app/processors/casino/two_factor_authenticator_overview_processor.rb index 42ffd6d8..81b1dead 100644 --- a/app/processors/casino/two_factor_authenticator_overview_processor.rb +++ b/app/processors/casino/two_factor_authenticator_overview_processor.rb @@ -3,18 +3,24 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::TwoFactorAuthenticatorOverviewProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # This method will call `#user_not_logged_in` or `#two_factor_authenticators_found(Enumerable)` on the listener. - # @param [Hash] cookies cookies delivered by the client + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(cookies = nil, user_agent = nil) - cookies ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - if tgt.nil? - @listener.user_not_logged_in + def process(user = nil, user_agent = nil) + @user = user || current_user + + if @user.ticket(user_agent:user_agent) + @listener.two_factor_authenticators_found(authenticators) else - @listener.two_factor_authenticators_found(tgt.user.two_factor_authenticators.where(active: true)) + @listener.user_not_logged_in end end + + private + def authenticators + @user.two_factor_authenticators.where(active: true) + end + end diff --git a/app/processors/casino/two_factor_authenticator_registrator_processor.rb b/app/processors/casino/two_factor_authenticator_registrator_processor.rb index b50e0a54..e3312f00 100644 --- a/app/processors/casino/two_factor_authenticator_registrator_processor.rb +++ b/app/processors/casino/two_factor_authenticator_registrator_processor.rb @@ -6,19 +6,24 @@ # This feature is not described in the CAS specification so it's completly optional # to implement this on the web application side. class CASino::TwoFactorAuthenticatorRegistratorProcessor < CASino::Processor - include CASino::ProcessorConcern::TicketGrantingTickets + include CASino::ProcessorConcern::CurrentUser # This method will call `#user_not_logged_in` or `#two_factor_authenticator_registered(two_factor_authenticator)` on the listener. - # @param [Hash] cookies cookies delivered by the client + # @param [Object] user A previously initializer User instance # @param [String] user_agent user-agent delivered by the client - def process(cookies = nil, user_agent = nil) - cookies ||= {} - tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent) - if tgt.nil? - @listener.user_not_logged_in - else - two_factor_authenticator = tgt.user.two_factor_authenticators.create! secret: ROTP::Base32.random_base32 + def process(user = nil, user_agent = nil) + @user = user || current_user + + if @user.ticket(user_agent:user_agent) @listener.two_factor_authenticator_registered(two_factor_authenticator) + else + @listener.user_not_logged_in end end + + private + def two_factor_authenticator + @two_factor_authenticator ||= @user.two_factor_authenticators.create! secret: ROTP::Base32.random_base32 + end + end diff --git a/app/views/casino/sessions/index.html.erb b/app/views/casino/sessions/index.html.erb index 036dadf0..adaf25c3 100644 --- a/app/views/casino/sessions/index.html.erb +++ b/app/views/casino/sessions/index.html.erb @@ -4,7 +4,7 @@

<%= t('sessions.title') %>

- <%= raw t('sessions.currently_logged_in_as', :username => @ticket_granting_tickets[0].user.username) %> + <%= raw t('sessions.currently_logged_in_as', :username => current_user.username) %>

<%= link_to t('sessions.label_logout_button'), logout_path, :class => 'button' %> diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb index 7f70953c..8e18ed48 100644 --- a/spec/controllers/sessions_controller_spec.rb +++ b/spec/controllers/sessions_controller_spec.rb @@ -28,9 +28,10 @@ describe 'GET "logout"' do it 'calls the process method of the Logout processor' do - CASino::LogoutProcessor.any_instance.should_receive(:process) do |params, cookies, user_agent| + CASino::CurrentUserProcessor.any_instance.should_receive(:process) + CASino::LogoutProcessor.any_instance.should_receive(:process) do |params, user, user_agent| params.should == controller.params - cookies.should == controller.cookies + user.should == controller.current_user user_agent.should == request.user_agent end get :logout, use_route: :casino @@ -39,6 +40,7 @@ describe 'GET "index"' do it 'calls the process method of the SessionOverview processor' do + CASino::CurrentUserProcessor.any_instance.should_receive(:process) CASino::TwoFactorAuthenticatorOverviewProcessor.any_instance.should_receive(:process) CASino::SessionOverviewProcessor.any_instance.should_receive(:process) get :index, use_route: :casino @@ -47,12 +49,14 @@ describe 'DELETE "destroy"' do let(:id) { '123' } - let(:tgt) { 'TGT-foobar' } + let(:user) { double('user') } it 'calls the process method of the SessionOverview processor' do - request.cookies[:tgt] = tgt - CASino::SessionDestroyerProcessor.any_instance.should_receive(:process) do |params, cookies, user_agent| + controller.stub(:current_user).and_return user + + CASino::CurrentUserProcessor.any_instance.should_receive(:process) + CASino::SessionDestroyerProcessor.any_instance.should_receive(:process) do |params, user, user_agent| params[:id].should == id - cookies[:tgt].should == tgt + user == controller.current_user user_agent.should == request.user_agent @controller.render nothing: true end @@ -62,6 +66,7 @@ describe 'GET "destroy_others"' do it 'calls the process method of the OtherSessionsDestroyer' do + CASino::CurrentUserProcessor.any_instance.should_receive(:process) CASino::OtherSessionsDestroyerProcessor.any_instance.should_receive(:process) do @controller.render nothing: true end diff --git a/spec/controllers/two_factor_authenticators_controller_spec.rb b/spec/controllers/two_factor_authenticators_controller_spec.rb index a6a49047..672d7e53 100644 --- a/spec/controllers/two_factor_authenticators_controller_spec.rb +++ b/spec/controllers/two_factor_authenticators_controller_spec.rb @@ -22,9 +22,9 @@ let(:tgt) { 'TGT-foobar' } it 'calls the process method of the TwoFactorAuthenticatorDestroyer processor' do request.cookies[:tgt] = tgt - CASino::TwoFactorAuthenticatorDestroyerProcessor.any_instance.should_receive(:process) do |params, cookies, user_agent| + CASino::TwoFactorAuthenticatorDestroyerProcessor.any_instance.should_receive(:process) do |params, user, user_agent| params[:id].should == id - cookies[:tgt].should == tgt + user.should == @controller.current_user user_agent.should == request.user_agent @controller.render nothing: true end diff --git a/spec/model/user_spec.rb b/spec/model/user_spec.rb new file mode 100644 index 00000000..10ba696c --- /dev/null +++ b/spec/model/user_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe CASino::User do + describe '#active_two_factor_authenticator' do + let(:user) { FactoryGirl.create :user } + + let!(:active_auth) { FactoryGirl.create :two_factor_authenticator, user:user } + let!(:inactive_auth) { FactoryGirl.create :two_factor_authenticator, :inactive, user:user } + + subject { user.active_two_factor_authenticator } + + it 'returns the active authenticator' do + expect(subject).to eq active_auth + end + end + + describe '#ticket' do + let(:user) { FactoryGirl.create :user } + + let!(:primary_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:'Chrome' } + let!(:other_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:'Firefox' } + let!(:another_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:'Firefox' } + let!(:no_ua_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:nil } + + let(:params) { nil } + + subject { user.ticket params } + + it 'returns the first ticket found' do + expect(subject).to eq primary_ticket + end + + context 'with search parameters' do + let(:params) { { user_agent:other_ticket.user_agent } } + + it 'returns the first matching ticket' do + expect(subject).to eq other_ticket + end + + context 'that contain nil values' do + let(:params) { { user_agent:nil } } + + it 'does not include those parameters in the search' do + expect(subject).to eq primary_ticket + end + end + end + end + + describe '#other_tickets' do + let(:user) { FactoryGirl.create :user } + + let!(:primary_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:'Chrome' } + let!(:other_ticket) { FactoryGirl.create :ticket_granting_ticket, user:user, user_agent:'Firefox' } + + let(:user_agent) { nil } + + subject { user.other_tickets user_agent } + + it 'returns an empty query' do + expect(subject).to be_an ActiveRecord::Relation + end + + context 'with a :user_agent specified' do + context 'that has no matching Ticket Granting Ticket' do + let(:user_agent) { 'Safari' } + + it 'returns an empty query' do + expect(subject).to be_an ActiveRecord::Relation + end + end + + context 'that matches a Ticket Granting Ticket' do + let(:user_agent) { primary_ticket.user_agent } + + it 'returns all other non-matching Ticket Granting Tickets' do + expect(subject).to eq [other_ticket] + end + end + end + end +end diff --git a/spec/processor/current_user_spec.rb b/spec/processor/current_user_spec.rb new file mode 100644 index 00000000..e4684b02 --- /dev/null +++ b/spec/processor/current_user_spec.rb @@ -0,0 +1,104 @@ +require 'spec_helper' + +describe CASino::CurrentUserProcessor do + let(:user) { FactoryGirl.create :user } + let(:current_user) { nil } + let(:cookies) { nil } + let(:user_agent) { 'TestBrowser 1.0' } + let(:listener) do + double('listener').tap do |l| + l.stub('assigned').with(:current_user).and_return(current_user) + end + end + let(:processor) { described_class.new(listener) } + + describe '#process' do + subject { processor.process(cookies, user_agent) } + + context 'with a previously set :current_user value' do + let(:current_user) { user } + + it 'calls the :current_user_found callback' do + listener.should_receive(:current_user_found) + subject + end + end + + context 'with an existing Ticket Granting Ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } + let(:cookies) { { tgt:ticket_granting_ticket.ticket } } + + it 'calls the :current_user_found callback' do + listener.should_receive(:current_user_found) + subject + end + + context 'that has expired' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :expired, user:user } + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in) + subject + end + end + + context 'with a different browser' do + let(:user_agent) { 'FooBar 1.0' } + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in) + subject + end + end + end + + context 'when two-factor authentication is pending' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication, user: user } + let(:cookies) { { tgt:ticket_granting_ticket.ticket } } + + it 'calls the #user_not_logged_in method on the listener' do + listener.should_receive(:user_not_logged_in) + processor.process(cookies, user_agent) + end + + context 'and we opt ignore that' do + it 'calls the #current_user_found method on the listener' do + listener.should_receive(:current_user_found) + processor.process(cookies, user_agent, ignore_two_factor:true) + end + end + end + + context 'with an invalid Ticket Granting Ticket' do + let(:cookies) { { tgt:'TGT-INVALID' } } + + it 'calls the :user_not_logged_in callback' do + listener.should_receive(:user_not_logged_in) + subject + end + end + end + + describe '#process!' do + subject { processor.process!(cookies, user_agent) } + + context 'with an existing Ticket Granting Ticket' do + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } + let(:cookies) { { tgt:ticket_granting_ticket.ticket } } + + it 'calls the :current_user_found callback' do + listener.should_receive(:current_user_found) + subject + end + end + + context 'with an invalid Ticket Granting Ticket' do + let(:cookies) { { tgt:'TGT-INVALID' } } + + it 'calls the :user_not_logged_in! callback' do + listener.should_receive(:user_not_logged_in!) + subject + end + end + end +end diff --git a/spec/processor/login_credential_requestor_spec.rb b/spec/processor/login_credential_requestor_spec.rb index b5654767..b8363564 100644 --- a/spec/processor/login_credential_requestor_spec.rb +++ b/spec/processor/login_credential_requestor_spec.rb @@ -2,7 +2,8 @@ describe CASino::LoginCredentialRequestorProcessor do describe '#process' do - let(:listener) { Object.new } + let(:user) { nil } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } context 'with a not allowed service' do @@ -47,33 +48,12 @@ end context 'when logged in' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { FactoryGirl.create :user } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } let(:user_agent) { ticket_granting_ticket.user_agent } - let(:cookies) { { tgt: ticket_granting_ticket.ticket } } - before(:each) do - listener.stub(:user_logged_in) - end - - context 'when two-factor authentication is pending' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication } - - it 'calls the #user_not_logged_in method on the listener' do - listener.should_receive(:user_not_logged_in).with(kind_of(CASino::LoginTicket)) - processor.process(nil, cookies, user_agent) - end - end - - context 'when ticket-granting ticket expired' do - before(:each) do - ticket_granting_ticket.created_at = 25.hours.ago - ticket_granting_ticket.save! - end - - it 'calls the #user_not_logged_in method on the listener' do - listener.should_receive(:user_not_logged_in).with(kind_of(CASino::LoginTicket)) - processor.process(nil, cookies, user_agent) - end + before do + FactoryGirl.create :ticket_granting_ticket, user: user end context 'with a service' do @@ -82,19 +62,22 @@ it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end it 'generates a service ticket' do + listener.stub(:user_logged_in) lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should change(CASino::ServiceTicket, :count).by(1) end context 'with renew parameter' do + let(:params) { super().merge renew:'true' } + it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(kind_of(CASino::LoginTicket)) - processor.process(params.merge({ renew: 'true' }), cookies) + processor.process(params, user, user_agent) end end end @@ -105,30 +88,22 @@ it 'does not remove the attributes' do listener.should_receive(:user_logged_in).with(/\?a%5B%5D=test&a%5B%5D=example&ticket=ST\-[^&]+$/) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end context 'without a service' do it 'calls the #user_logged_in method on the listener' do listener.should_receive(:user_logged_in).with(nil) - processor.process(nil, cookies, user_agent) + processor.process end it 'does not generate a service ticket' do + listener.stub(:user_logged_in) lambda do - processor.process(nil, cookies, user_agent) + processor.process end.should change(CASino::ServiceTicket, :count).by(0) end - - context 'with a changed browser' do - let(:user_agent) { 'FooBar 1.0' } - - it 'calls the #user_not_logged_in method on the listener' do - listener.should_receive(:user_not_logged_in).with(kind_of(CASino::LoginTicket)) - processor.process(nil, cookies, user_agent) - end - end end end end diff --git a/spec/processor/logout_other_sessions_spec.rb b/spec/processor/logout_other_sessions_spec.rb index 4bc80a81..15407a5f 100644 --- a/spec/processor/logout_other_sessions_spec.rb +++ b/spec/processor/logout_other_sessions_spec.rb @@ -4,7 +4,7 @@ describe '#process' do let(:listener) { Object.new } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } + let(:user) { FactoryGirl.create :user } let(:url) { nil } let(:params) { { :service => url } unless url.nil? } @@ -13,7 +13,6 @@ end context 'with an existing ticket-granting ticket' do - let(:user) { FactoryGirl.create :user } let!(:other_users_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3 } let!(:other_ticket_granting_tickets) { FactoryGirl.create_list :ticket_granting_ticket, 3, user: user } let!(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } @@ -22,13 +21,13 @@ it 'deletes all other ticket-granting tickets' do lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should change(CASino::TicketGrantingTicket, :count).by(-3) end it 'calls the #user_logged_out method on the listener' do listener.should_receive(:other_sessions_destroyed).with(nil) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end context 'with an URL' do @@ -36,7 +35,7 @@ it 'calls the #user_logged_out method on the listener and passes the URL' do listener.should_receive(:other_sessions_destroyed).with(url) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end end @@ -46,7 +45,7 @@ it 'calls the #other_sessions_destroyed method on the listener' do listener.should_receive(:other_sessions_destroyed).with(nil) - processor.process(params, cookies) + processor.process(params, user) end end end diff --git a/spec/processor/logout_spec.rb b/spec/processor/logout_spec.rb index 5ff186d1..27b165fe 100644 --- a/spec/processor/logout_spec.rb +++ b/spec/processor/logout_spec.rb @@ -2,29 +2,25 @@ describe CASino::LogoutProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener') } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } + let(:user) { FactoryGirl.create :user } let(:url) { nil } let(:params) { { :url => url } unless url.nil? } - before(:each) do - listener.stub(:user_logged_out) - end - context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } - let(:tgt) { ticket_granting_ticket.ticket } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } let(:user_agent) { ticket_granting_ticket.user_agent } it 'deletes the ticket-granting ticket' do - processor.process(params, cookies, user_agent) + listener.stub(:user_logged_out) + processor.process(params, user, user_agent) CASino::TicketGrantingTicket.where(id: ticket_granting_ticket.id).first.should == nil end it 'calls the #user_logged_out method on the listener' do listener.should_receive(:user_logged_out).with(nil) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end context 'with an URL' do @@ -32,7 +28,7 @@ it 'calls the #user_logged_out method on the listener and passes the URL' do listener.should_receive(:user_logged_out).with(url) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end @@ -43,7 +39,7 @@ context '(whitelisted)' do it 'calls the #user_logged_out method on the listener and passes the URL and the redirect_immediate flag' do listener.should_receive(:user_logged_out).with(url, true) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end @@ -54,19 +50,10 @@ it 'calls the #user_logged_out method on the listener and passes no URL' do listener.should_receive(:user_logged_out).with(nil) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end end end - - context 'with an invlaid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } - - it 'calls the #user_logged_out method on the listener' do - listener.should_receive(:user_logged_out).with(nil) - processor.process(params, cookies) - end - end end end diff --git a/spec/processor/other_session_destroyer_spec.rb b/spec/processor/other_session_destroyer_spec.rb new file mode 100644 index 00000000..4dce0841 --- /dev/null +++ b/spec/processor/other_session_destroyer_spec.rb @@ -0,0 +1,54 @@ +require 'spec_helper' + +describe CASino::OtherSessionsDestroyerProcessor do + describe '#process' do + let(:listener) { double('listener', assigned:nil) } + let(:processor) { described_class.new(listener) } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } + let(:user) { ticket_granting_ticket.user } + let(:user_agent) { ticket_granting_ticket.user_agent } + let(:params) { { service:'SERVICE' } } + + context 'with an existing ticket-granting ticket' do + let(:params) { super().merge(id: ticket_granting_ticket.id) } + + it 'deletes all other ticket-granting tickets' do + FactoryGirl.create_list :ticket_granting_ticket, 2, user:user + listener.stub(:other_sessions_destroyed) + lambda do + processor.process(params, user, user_agent) + end.should change(CASino::TicketGrantingTicket, :count).by(-2) + end + + it 'does not delete the ticket-granting ticket' do + listener.stub(:other_sessions_destroyed) + processor.process(params, user, user_agent) + CASino::TicketGrantingTicket.find(params[:id]).should_not be_nil + end + + it 'calls the #other_sessions_destroyed method on the listener' do + listener.should_receive(:other_sessions_destroyed).with('SERVICE') + processor.process(params, user, user_agent) + end + end + + context 'with an invalid ticket-granting ticket' do + let(:user) { nil } + it 'does not delete any ticket-granting ticket' do + FactoryGirl.create_list :ticket_granting_ticket, 2 + listener.stub(:other_sessions_destroyed) + + user_agent + + lambda do + processor.process(params, user, user_agent) + end.should_not change(CASino::TicketGrantingTicket, :count) + end + + it 'calls the #ticket_not_found method on the listener' do + listener.should_receive(:other_sessions_destroyed).with('SERVICE') + processor.process(params, user, user_agent) + end + end + end +end \ No newline at end of file diff --git a/spec/processor/second_factor_authenticaton_acceptor_spec.rb b/spec/processor/second_factor_authenticaton_acceptor_spec.rb index f0acd207..b52cf78d 100644 --- a/spec/processor/second_factor_authenticaton_acceptor_spec.rb +++ b/spec/processor/second_factor_authenticaton_acceptor_spec.rb @@ -2,15 +2,9 @@ describe CASino::SecondFactorAuthenticationAcceptorProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:invalid_one_time_password) - listener.stub(:user_logged_in) - end - context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, :awaiting_two_factor_authentication } let(:user) { ticket_granting_ticket.user } @@ -29,12 +23,13 @@ end it 'calls the `#user_logged_in` method an the listener' do - listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/) - processor.process(params, user_agent) + listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/, nil) + processor.process(params, user, user_agent) end it 'does activate the ticket-granting ticket' do - processor.process(params, user_agent) + listener.stub(:user_logged_in) + processor.process(params, user, user_agent) ticket_granting_ticket.reload ticket_granting_ticket.should_not be_awaiting_two_factor_authentication end @@ -46,7 +41,7 @@ it 'calls the #user_logged_in method on the listener with an expiration date set' do listener.should_receive(:user_logged_in).with(/^#{service}\?ticket=ST\-/, /^TGC\-/, kind_of(Time)) - processor.process(params, user_agent) + processor.process(params, user, user_agent) end end @@ -55,10 +50,11 @@ FactoryGirl.create :service_rule, :regex, url: '^https://.*' end let(:service) { 'http://www.example.org/' } + let(:params) { super().merge(service:service) } it 'calls the #service_not_allowed method on the listener' do listener.should_receive(:service_not_allowed).with(service) - processor.process(params.merge(service: service), user_agent) + processor.process(params, user, user_agent) end end end @@ -70,11 +66,12 @@ it 'calls the `#invalid_one_time_password` method an the listener' do listener.should_receive(:invalid_one_time_password).with(no_args) - processor.process(params, user_agent) + processor.process(params, user, user_agent) end it 'does not activate the ticket-granting ticket' do - processor.process(params, user_agent) + listener.stub(:invalid_one_time_password) + processor.process(params, user, user_agent) ticket_granting_ticket.reload ticket_granting_ticket.should be_awaiting_two_factor_authentication end @@ -82,12 +79,12 @@ end end - context 'with an invalid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } - let(:user_agent) { 'TestBrowser 1.0' } + context 'with no logged in user' do + let(:user) { nil } + it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process({tgt: tgt}, user_agent) + processor.process end end end diff --git a/spec/processor/session_destroyer_spec.rb b/spec/processor/session_destroyer_spec.rb index 1a08cbc1..db2f2563 100644 --- a/spec/processor/session_destroyer_spec.rb +++ b/spec/processor/session_destroyer_spec.rb @@ -2,17 +2,11 @@ describe CASino::SessionDestroyerProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } let(:owner_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:user) { owner_ticket_granting_ticket.user } let(:user_agent) { owner_ticket_granting_ticket.user_agent } - let(:cookies) { { tgt: owner_ticket_granting_ticket.ticket } } - - before(:each) do - listener.stub(:ticket_deleted) - listener.stub(:ticket_not_found) - end context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } @@ -21,36 +15,37 @@ let(:params) { { id: ticket_granting_ticket.id } } it 'deletes exactly one ticket-granting ticket' do + listener.stub(:ticket_deleted) ticket_granting_ticket - owner_ticket_granting_ticket lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should change(CASino::TicketGrantingTicket, :count).by(-1) end it 'deletes the ticket-granting ticket' do - processor.process(params, cookies, user_agent) + listener.stub(:ticket_deleted) + processor.process(params, user, user_agent) CASino::TicketGrantingTicket.where(id: params[:id]).length.should == 0 end it 'calls the #ticket_deleted method on the listener' do listener.should_receive(:ticket_deleted).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end context 'with an invalid ticket-granting ticket' do let(:params) { { id: 99999 } } it 'does not delete a ticket-granting ticket' do - owner_ticket_granting_ticket + listener.stub(:ticket_not_found) lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should_not change(CASino::TicketGrantingTicket, :count) end it 'calls the #ticket_not_found method on the listener' do listener.should_receive(:ticket_not_found).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end @@ -59,16 +54,16 @@ let(:params) { { id: ticket_granting_ticket.id } } it 'does not delete a ticket-granting ticket' do - owner_ticket_granting_ticket + listener.stub(:ticket_not_found) ticket_granting_ticket lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should change(CASino::TicketGrantingTicket, :count).by(0) end it 'calls the #ticket_not_found method on the listener' do listener.should_receive(:ticket_not_found).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end end diff --git a/spec/processor/session_overview_spec.rb b/spec/processor/session_overview_spec.rb index 1e48c53c..b49c916a 100644 --- a/spec/processor/session_overview_spec.rb +++ b/spec/processor/session_overview_spec.rb @@ -2,47 +2,40 @@ describe CASino::SessionOverviewProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } let(:other_ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:user) { other_ticket_granting_ticket.user } let(:user_agent) { other_ticket_granting_ticket.user_agent } - let(:cookies) { { tgt: tgt } } - - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:ticket_granting_tickets_found) - other_ticket_granting_ticket - end context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } - let(:tgt) { ticket_granting_ticket.ticket } + before do + FactoryGirl.create :ticket_granting_ticket, user: user + end it 'calls the #ticket_granting_tickets_found method on the listener' do listener.should_receive(:ticket_granting_tickets_found) do |tickets| tickets.length.should == 2 end - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end context 'with a ticket-granting ticket with same username but different authenticator' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } - let(:tgt) { ticket_granting_ticket.ticket } it 'calls the #ticket_granting_tickets_found method on the listener' do listener.should_receive(:ticket_granting_tickets_found) do |tickets| tickets.length.should == 1 end - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end - context 'with an invalid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } + context 'without a logged in user' do + let(:user) { nil } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end end diff --git a/spec/processor/two_factor_authenticator_activator_spec.rb b/spec/processor/two_factor_authenticator_activator_spec.rb index bf106b39..ee2af124 100644 --- a/spec/processor/two_factor_authenticator_activator_spec.rb +++ b/spec/processor/two_factor_authenticator_activator_spec.rb @@ -2,22 +2,12 @@ describe CASino::TwoFactorAuthenticatorActivatorProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double(:listener, assigned:user) } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } - - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:two_factor_authenticator_activated) - listener.stub(:invalid_two_factor_authenticator) - listener.stub(:invalid_one_time_password) - listener.stub(:two_factor_authenticator_activated) - end context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:user) { ticket_granting_ticket.user } - let(:tgt) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } let(:id) { two_factor_authenticator.id } let(:otp) { '123456' } @@ -34,7 +24,7 @@ it 'calls the `#invalid_two_factor_authenticator` method an the listener' do listener.should_receive(:invalid_two_factor_authenticator).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end @@ -48,13 +38,15 @@ it 'calls the `#invalid_two_factor_authenticator` method an the listener' do listener.should_receive(:invalid_two_factor_authenticator).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end end end context 'with a valid authenticator' do - let(:two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, :inactive, user: user } + let(:two_factor_authenticator) do + FactoryGirl.create :two_factor_authenticator, :inactive, user: user + end context 'with a valid OTP' do before(:each) do @@ -63,11 +55,12 @@ it 'calls the `#two_factor_authenticator_activated` method an the listener' do listener.should_receive(:two_factor_authenticator_activated).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end it 'does activate the authenticator' do - processor.process(params, cookies, user_agent) + listener.stub(:two_factor_authenticator_activated) + processor.process(params, user, user_agent) two_factor_authenticator.reload two_factor_authenticator.should be_active end @@ -76,13 +69,15 @@ let!(:other_two_factor_authenticator) { FactoryGirl.create :two_factor_authenticator, user: user } it 'does activate the authenticator' do - processor.process(params, cookies, user_agent) + listener.stub(:two_factor_authenticator_activated) + processor.process(params, user, user_agent) two_factor_authenticator.reload two_factor_authenticator.should be_active end it 'does delete the other authenticator' do - processor.process(params, cookies, user_agent) + listener.stub(:two_factor_authenticator_activated) + processor.process(params, user, user_agent) lambda do other_two_factor_authenticator.reload end.should raise_error(ActiveRecord::RecordNotFound) @@ -98,11 +93,12 @@ it 'calls the `#invalid_one_time_password` method an the listener' do listener.should_receive(:invalid_one_time_password).with(two_factor_authenticator) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end it 'does not activate the authenticator' do - processor.process(params, cookies, user_agent) + listener.stub(:invalid_one_time_password) + processor.process(params, user, user_agent) two_factor_authenticator.reload two_factor_authenticator.should_not be_active end @@ -110,12 +106,11 @@ end end - context 'with an invalid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } - let(:user_agent) { 'TestBrowser 1.0' } + context 'without a logged in user' do + let(:user) { nil } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(nil, cookies, user_agent) + processor.process(nil, user) end end end diff --git a/spec/processor/two_factor_authenticator_destroyer_spec.rb b/spec/processor/two_factor_authenticator_destroyer_spec.rb index 2c1762a3..2061d906 100644 --- a/spec/processor/two_factor_authenticator_destroyer_spec.rb +++ b/spec/processor/two_factor_authenticator_destroyer_spec.rb @@ -2,15 +2,8 @@ describe CASino::TwoFactorAuthenticatorDestroyerProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } - - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:two_factor_authenticator_destroyed) - listener.stub(:invalid_two_factor_authenticator) - end context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } @@ -24,20 +17,22 @@ it 'calls the #two_factor_authenticator_destroyed method on the listener' do listener.should_receive(:two_factor_authenticator_destroyed).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end it 'deletes the two-factor authenticator' do - processor.process(params, cookies, user_agent) + listener.stub(:two_factor_authenticator_destroyed) + processor.process(params, user, user_agent) lambda do two_factor_authenticator.reload end.should raise_error(ActiveRecord::RecordNotFound) end it 'does not delete other two-factor authenticators' do + listener.stub(:two_factor_authenticator_destroyed) other = FactoryGirl.create :two_factor_authenticator lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should change(CASino::TwoFactorAuthenticator, :count).by(-1) end end @@ -47,24 +42,23 @@ it 'calls the #invalid_two_factor_authenticator method on the listener' do listener.should_receive(:invalid_two_factor_authenticator).with(no_args) - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end it 'does not delete two-factor authenticators' do + listener.stub(:invalid_two_factor_authenticator) lambda do - processor.process(params, cookies, user_agent) + processor.process(params, user, user_agent) end.should_not change(CASino::TwoFactorAuthenticator, :count) end end end - context 'with an invalid ticket-granting ticket' do - let(:params) { {} } - let(:tgt) { 'TGT-lalala' } - let(:user_agent) { 'TestBrowser 1.0' } + context 'without a logged in user' do + let(:user) { nil } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(params, cookies, user_agent) + processor.process end end end diff --git a/spec/processor/two_factor_authenticator_overview_spec.rb b/spec/processor/two_factor_authenticator_overview_spec.rb index 8657f0d4..caa5487f 100644 --- a/spec/processor/two_factor_authenticator_overview_spec.rb +++ b/spec/processor/two_factor_authenticator_overview_spec.rb @@ -2,25 +2,18 @@ describe CASino::TwoFactorAuthenticatorOverviewProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:two_factor_authenticators_found) - end - - context 'with an existing ticket-granting ticket' do - let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } - let(:user) { ticket_granting_ticket.user } - let(:tgt) { ticket_granting_ticket.ticket } + context 'with a signed in User' do + let(:user) { FactoryGirl.create :user } + let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket, user: user } let(:user_agent) { ticket_granting_ticket.user_agent } context 'without a two-factor authenticator registered' do it 'calls the #two_factor_authenticators_found method on the listener' do listener.should_receive(:two_factor_authenticators_found).with([]) - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end @@ -29,7 +22,7 @@ it 'does not include the inactive authenticator' do listener.should_receive(:two_factor_authenticators_found).with([]) - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end @@ -39,17 +32,19 @@ it 'calls the #two_factor_authenticators_found method on the listener' do listener.should_receive(:two_factor_authenticators_found).with([two_factor_authenticator]) - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end end - context 'with an invalid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } + context 'without a logged in user' do + let(:user) { nil } + let(:user_agent) { 'TestBrowser 1.0' } + it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(cookies, user_agent) + processor.process(user, user_agent) end end end diff --git a/spec/processor/two_factor_authenticator_registrator_spec.rb b/spec/processor/two_factor_authenticator_registrator_spec.rb index 416ca4dd..cf5e7e64 100644 --- a/spec/processor/two_factor_authenticator_registrator_spec.rb +++ b/spec/processor/two_factor_authenticator_registrator_spec.rb @@ -2,24 +2,18 @@ describe CASino::TwoFactorAuthenticatorRegistratorProcessor do describe '#process' do - let(:listener) { Object.new } + let(:listener) { double('listener', assigned:user) } let(:processor) { described_class.new(listener) } - let(:cookies) { { tgt: tgt } } - - before(:each) do - listener.stub(:user_not_logged_in) - listener.stub(:two_factor_authenticator_registered) - end context 'with an existing ticket-granting ticket' do let(:ticket_granting_ticket) { FactoryGirl.create :ticket_granting_ticket } let(:user) { ticket_granting_ticket.user } - let(:tgt) { ticket_granting_ticket.ticket } let(:user_agent) { ticket_granting_ticket.user_agent } it 'creates exactly one authenticator' do + listener.stub(:two_factor_authenticator_registered) lambda do - processor.process(cookies, user_agent) + processor.process(user, user_agent) end.should change(CASino::TwoFactorAuthenticator, :count).by(1) end @@ -27,21 +21,21 @@ listener.should_receive(:two_factor_authenticator_registered) do |authenticator| authenticator.should == CASino::TwoFactorAuthenticator.last end - processor.process(cookies, user_agent) + processor.process(user, user_agent) end it 'creates an inactive two-factor authenticator' do - processor.process(cookies, user_agent) + listener.stub(:two_factor_authenticator_registered) + processor.process(user, user_agent) CASino::TwoFactorAuthenticator.last.should_not be_active end end - context 'with an invalid ticket-granting ticket' do - let(:tgt) { 'TGT-lalala' } - let(:user_agent) { 'TestBrowser 1.0' } + context 'without a logged in user' do + let(:user) { nil } it 'calls the #user_not_logged_in method on the listener' do listener.should_receive(:user_not_logged_in).with(no_args) - processor.process(cookies, user_agent) + processor.process end end end diff --git a/spec/support/factories/ticket_granting_ticket_factory.rb b/spec/support/factories/ticket_granting_ticket_factory.rb index 91f68db2..180062fb 100644 --- a/spec/support/factories/ticket_granting_ticket_factory.rb +++ b/spec/support/factories/ticket_granting_ticket_factory.rb @@ -11,5 +11,9 @@ trait :awaiting_two_factor_authentication do awaiting_two_factor_authentication true end + + trait :expired do + created_at { 25.hours.ago } + end end end