casino 1.3.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.gitignore +3 -0
- data/.travis.yml +1 -1
- data/README.md +1 -8
- data/Rakefile +0 -2
- data/app/assets/javascripts/casino/application.js +1 -0
- data/app/assets/javascripts/casino/index.js +0 -2
- data/app/assets/javascripts/casino/sessions.js +32 -0
- data/app/authenticators/casino/static_authenticator.rb +23 -0
- data/app/builders/casino/ticket_validation_response_builder.rb +84 -0
- data/app/controllers/casino/api/v1/tickets_controller.rb +7 -4
- data/app/controllers/casino/application_controller.rb +2 -3
- data/{lib/casino/listener/legacy_validator.rb → app/listeners/casino/legacy_validator_listener.rb} +2 -2
- data/app/listeners/casino/listener.rb +16 -0
- data/{lib/casino/listener/login_credential_acceptor.rb → app/listeners/casino/login_credential_acceptor_listener.rb} +2 -2
- data/{lib/casino/listener/login_credential_requestor.rb → app/listeners/casino/login_credential_requestor_listener.rb} +2 -2
- data/{lib/casino/listener/logout.rb → app/listeners/casino/logout_listener.rb} +2 -2
- data/{lib/casino/listener/other_sessions_destroyer.rb → app/listeners/casino/other_sessions_destroyer_listener.rb} +2 -2
- data/{lib/casino/listener/proxy_ticket_provider.rb → app/listeners/casino/proxy_ticket_provider_listener.rb} +2 -2
- data/{lib/casino/listener/second_factor_authentication_acceptor.rb → app/listeners/casino/second_factor_authentication_acceptor_listener.rb} +2 -2
- data/{lib/casino/listener/session_destroyer.rb → app/listeners/casino/session_destroyer_listener.rb} +2 -2
- data/{lib/casino/listener/session_overview.rb → app/listeners/casino/session_overview_listener.rb} +2 -2
- data/{lib/casino/listener/ticket_validator.rb → app/listeners/casino/ticket_validator_listener.rb} +2 -2
- data/{lib/casino/listener/two_factor_authenticator_activator.rb → app/listeners/casino/two_factor_authenticator_activator_listener.rb} +2 -2
- data/{lib/casino/listener/two_factor_authenticator_destroyer.rb → app/listeners/casino/two_factor_authenticator_destroyer_listener.rb} +2 -2
- data/{lib/casino/listener/two_factor_authenticator_overview.rb → app/listeners/casino/two_factor_authenticator_overview_listener.rb} +2 -2
- data/{lib/casino/listener/two_factor_authenticator_registrator.rb → app/listeners/casino/two_factor_authenticator_registrator_listener.rb} +2 -2
- data/app/models/casino/login_ticket.rb +12 -0
- data/app/models/casino/proxy_granting_ticket.rb +8 -0
- data/app/models/casino/proxy_ticket.rb +25 -0
- data/app/models/casino/service_rule.rb +27 -0
- data/app/models/casino/service_ticket.rb +43 -0
- data/app/models/casino/service_ticket/single_sign_out_notifier.rb +44 -0
- data/app/models/casino/ticket_granting_ticket.rb +57 -0
- data/app/models/casino/two_factor_authenticator.rb +18 -0
- data/app/models/casino/user.rb +12 -0
- data/app/models/casino/validation_result.rb +5 -0
- data/app/processors/casino/api/login_credential_acceptor_processor.rb +46 -0
- data/app/processors/casino/api/logout_processor.rb +17 -0
- data/app/processors/casino/api/service_ticket_provider_processor.rb +69 -0
- data/app/processors/casino/legacy_validator_processor.rb +19 -0
- data/app/processors/casino/login_credential_acceptor_processor.rb +63 -0
- data/app/processors/casino/login_credential_requestor_processor.rb +66 -0
- data/app/processors/casino/logout_processor.rb +23 -0
- data/app/processors/casino/other_sessions_destroyer_processor.rb +26 -0
- data/app/processors/casino/processor.rb +5 -0
- data/app/processors/casino/processor_concern/authentication.rb +87 -0
- data/app/processors/casino/processor_concern/browser.rb +14 -0
- data/app/processors/casino/processor_concern/login_tickets.rb +28 -0
- data/app/processors/casino/processor_concern/proxy_granting_tickets.rb +43 -0
- data/app/processors/casino/processor_concern/proxy_tickets.rb +56 -0
- data/app/processors/casino/processor_concern/service_tickets.rb +50 -0
- data/app/processors/casino/processor_concern/ticket_granting_tickets.rb +65 -0
- data/app/processors/casino/processor_concern/tickets.rb +17 -0
- data/app/processors/casino/processor_concern/two_factor_authenticators.rb +22 -0
- data/app/processors/casino/proxy_ticket_provider_processor.rb +41 -0
- data/app/processors/casino/proxy_ticket_validator_processor.rb +22 -0
- data/app/processors/casino/second_factor_authentication_acceptor_processor.rb +45 -0
- data/app/processors/casino/service_ticket_validator_processor.rb +46 -0
- data/app/processors/casino/session_destroyer_processor.rb +25 -0
- data/app/processors/casino/session_overview_processor.rb +21 -0
- data/app/processors/casino/two_factor_authenticator_activator_processor.rb +41 -0
- data/app/processors/casino/two_factor_authenticator_destroyer_processor.rb +33 -0
- data/app/processors/casino/two_factor_authenticator_overview_processor.rb +20 -0
- data/app/processors/casino/two_factor_authenticator_registrator_processor.rb +24 -0
- data/app/views/casino/application/_footer.html.erb +1 -1
- data/app/views/casino/sessions/new.html.erb +2 -1
- data/app/views/casino/sessions/validate_otp.html.erb +1 -1
- data/app/views/casino/two_factor_authenticators/new.html.erb +2 -2
- data/app/views/layouts/application.html.erb +1 -1
- data/casino.gemspec +9 -4
- data/db/migrate/20130809135400_create_core_schema.rb +117 -0
- data/db/migrate/20130809135401_rename_base_models.rb +101 -0
- data/db/migrate/20131022110146_cleanup_indexes.rb +27 -0
- data/db/migrate/20131022110246_fix_long_index_names.rb +12 -0
- data/db/migrate/20131022110346_change_service_to_text.rb +6 -0
- data/lib/casino.rb +47 -3
- data/lib/casino/authenticator.rb +9 -0
- data/lib/casino/engine.rb +26 -0
- data/lib/casino/inflections.rb +7 -0
- data/lib/casino/tasks.rb +1 -0
- data/lib/casino/tasks/cleanup.rake +59 -0
- data/lib/casino/tasks/service_rule.rake +49 -0
- data/lib/casino/version.rb +1 -1
- data/lib/generators/casino/install/USAGE +13 -0
- data/lib/generators/casino/install/install_generator.rb +47 -0
- data/lib/generators/casino/{templates → install/templates}/README +3 -4
- data/lib/generators/casino/{templates → install/templates}/cas.yml +2 -2
- data/lib/generators/casino/{templates → install/templates}/casino_and_overrides.scss +0 -0
- data/lib/generators/casino/templates/casino_core.rb +1 -1
- data/spec/authenticator/base_spec.rb +13 -0
- data/spec/authenticator/static_spec.rb +42 -0
- data/spec/controllers/api/v1/tickets_controller_spec.rb +15 -15
- data/spec/controllers/listener/legacy_validator_spec.rb +1 -1
- data/spec/controllers/listener/login_credential_acceptor_spec.rb +1 -1
- data/spec/controllers/listener/login_credential_requestor_spec.rb +1 -1
- data/spec/controllers/listener/logout_spec.rb +1 -1
- data/spec/controllers/listener/other_sessions_destroyer_spec.rb +1 -1
- data/spec/controllers/listener/proxy_ticket_provider_spec.rb +1 -1
- data/spec/controllers/listener/second_factor_authentication_acceptor_spec.rb +1 -1
- data/spec/controllers/listener/session_destroyer_spec.rb +1 -1
- data/spec/controllers/listener/session_overview_spec.rb +1 -1
- data/spec/controllers/listener/ticket_validator_spec.rb +1 -1
- data/spec/controllers/listener/two_factor_authenticator_activator_spec.rb +1 -1
- data/spec/controllers/listener/two_factor_authenticator_destroyer_spec.rb +1 -1
- data/spec/controllers/listener/two_factor_authenticator_overview_spec.rb +1 -1
- data/spec/controllers/listener/two_factor_authenticator_registrator_spec.rb +1 -1
- data/spec/controllers/proxy_tickets_controller_spec.rb +4 -4
- data/spec/controllers/service_tickets_controller_spec.rb +4 -4
- data/spec/controllers/sessions_controller_spec.rb +15 -15
- data/spec/controllers/two_factor_authenticators_controller_spec.rb +6 -6
- data/spec/dummy/app/assets/stylesheets/casino_and_overrides.scss +13 -0
- data/spec/dummy/config/cas.yml +11 -11
- data/spec/dummy/config/routes.rb +1 -2
- data/spec/dummy/db/migrate/20130910094259_create_base_models.casino.rb +95 -0
- data/spec/dummy/db/schema.rb +107 -0
- data/spec/model/login_ticket_spec.rb +23 -0
- data/spec/model/proxy_ticket_spec.rb +63 -0
- data/spec/model/service_rule_spec.rb +65 -0
- data/spec/model/service_ticket/single_sign_out_notifier_spec.rb +61 -0
- data/spec/model/service_ticket_spec.rb +124 -0
- data/spec/model/ticket_granting_ticket_spec.rb +204 -0
- data/spec/model/two_factor_authenticator_spec.rb +31 -0
- data/spec/processor/api/login_credential_acceptor_spec.rb +52 -0
- data/spec/processor/api/logout_spec.rb +34 -0
- data/spec/processor/api/service_ticket_provider_spec.rb +61 -0
- data/spec/processor/legacy_validator_spec.rb +78 -0
- data/spec/processor/login_credential_acceptor_spec.rb +164 -0
- data/spec/processor/login_credential_requestor_spec.rb +135 -0
- data/spec/processor/logout_other_sessions_spec.rb +53 -0
- data/spec/processor/logout_spec.rb +72 -0
- data/spec/processor/processor_concern/service_tickets_spec.rb +49 -0
- data/spec/processor/proxy_ticket_provider_spec.rb +66 -0
- data/spec/processor/proxy_ticket_validator_spec.rb +65 -0
- data/spec/processor/second_factor_authenticaton_acceptor_spec.rb +94 -0
- data/spec/processor/session_destroyer_spec.rb +75 -0
- data/spec/processor/session_overview_spec.rb +49 -0
- data/spec/processor/ticket_validator_spec.rb +199 -0
- data/spec/processor/two_factor_authenticator_activator_spec.rb +122 -0
- data/spec/processor/two_factor_authenticator_destroyer_spec.rb +71 -0
- data/spec/processor/two_factor_authenticator_overview_spec.rb +56 -0
- data/spec/processor/two_factor_authenticator_registrator_spec.rb +48 -0
- data/spec/spec_helper.rb +8 -19
- data/spec/support/casino.rb +12 -0
- data/spec/support/factories/login_ticket_factory.rb +16 -0
- data/spec/support/factories/proxy_granting_ticket_factory.rb +16 -0
- data/spec/support/factories/proxy_ticket_factory.rb +17 -0
- data/spec/support/factories/service_rule_factory.rb +16 -0
- data/spec/support/factories/service_ticket_factory.rb +17 -0
- data/spec/support/factories/ticket_granting_ticket_factory.rb +15 -0
- data/spec/support/factories/two_factor_authenticator_factory.rb +16 -0
- data/spec/support/factories/user_factory.rb +11 -0
- data/spec/support/rspec.rb +8 -0
- data/spec/support/sqlite3.rb +4 -0
- metadata +284 -48
- metadata.gz.sig +2 -0
- data/.powrc +0 -4
- data/Gemfile.lock +0 -149
- data/app/assets/javascripts/casino/application.js.coffee +0 -5
- data/app/assets/javascripts/casino/sessions.js.coffee +0 -15
- data/config/initializers/frontend_config.rb +0 -9
- data/config/initializers/inflections.rb +0 -19
- data/config/initializers/yaml.rb +0 -1
- data/lib/casino/listener.rb +0 -31
- data/lib/generators/casino/install_generator.rb +0 -35
- data/spec/dummy/config/initializers/casino_core.rb +0 -1
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
# The ProxyTicketProvider processor should be used to handle GET requests to /proxy
|
4
|
+
class CASino::ProxyTicketProviderProcessor < CASino::Processor
|
5
|
+
include CASino::ProcessorConcern::ProxyGrantingTickets
|
6
|
+
include CASino::ProcessorConcern::ProxyTickets
|
7
|
+
|
8
|
+
# This method will call `#request_succeeded` or `#request_failed`. In both cases, it supplies
|
9
|
+
# a string as argument. The web application should present that string (and nothing else) to the
|
10
|
+
# requestor. The Content-Type should be set to 'text/xml; charset=utf-8'
|
11
|
+
#
|
12
|
+
# @param [Hash] params parameters delivered by the client
|
13
|
+
def process(params = nil)
|
14
|
+
if params[:pgt].nil? || params[:targetService].nil?
|
15
|
+
@listener.request_failed build_xml false, error_code: 'INVALID_REQUEST', error_message: '"pgt" and "targetService" parameters are both required'
|
16
|
+
else
|
17
|
+
proxy_granting_ticket = CASino::ProxyGrantingTicket.where(ticket: params[:pgt]).first
|
18
|
+
if proxy_granting_ticket.nil?
|
19
|
+
@listener.request_failed build_xml false, error_code: 'BAD_PGT', error_message: 'PGT not found'
|
20
|
+
else
|
21
|
+
proxy_ticket = acquire_proxy_ticket(proxy_granting_ticket, params[:targetService])
|
22
|
+
@listener.request_succeeded build_xml true, proxy_ticket: proxy_ticket
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def build_xml(success, options = {})
|
29
|
+
xml = Builder::XmlMarkup.new(indent: 2)
|
30
|
+
xml.cas :serviceResponse, 'xmlns:cas' => 'http://www.yale.edu/tp/cas' do |service_response|
|
31
|
+
if success
|
32
|
+
service_response.cas :proxySuccess do |proxy_success|
|
33
|
+
proxy_success.cas :proxyTicket, options[:proxy_ticket].ticket
|
34
|
+
end
|
35
|
+
else
|
36
|
+
service_response.cas :proxyFailure, options[:error_message], code: options[:error_code]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
xml.target!
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# The ProxyTicketValidator processor should be used to handle GET requests to /proxyValidate
|
2
|
+
class CASino::ProxyTicketValidatorProcessor < CASino::ServiceTicketValidatorProcessor
|
3
|
+
|
4
|
+
# This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies
|
5
|
+
# a string as argument. The web application should present that string (and nothing else) to the
|
6
|
+
# requestor. The Content-Type should be set to 'text/xml; charset=utf-8'
|
7
|
+
#
|
8
|
+
# @param [Hash] params parameters delivered by the client
|
9
|
+
def process(params = nil)
|
10
|
+
params ||= {}
|
11
|
+
if request_valid?(params)
|
12
|
+
ticket = if params[:ticket].start_with?('PT-')
|
13
|
+
CASino::ProxyTicket.where(ticket: params[:ticket]).first
|
14
|
+
elsif params[:ticket].start_with?('ST-')
|
15
|
+
CASino::ServiceTicket.where(ticket: params[:ticket]).first
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
validate_ticket!(ticket, params)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# The SecondFactorAuthenticationAcceptor processor can be used to activate a previously generated ticket-granting ticket with pending two-factor authentication.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side.
|
5
|
+
class CASino::SecondFactorAuthenticationAcceptorProcessor < CASino::Processor
|
6
|
+
include CASino::ProcessorConcern::ServiceTickets
|
7
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
8
|
+
include CASino::ProcessorConcern::TwoFactorAuthenticators
|
9
|
+
|
10
|
+
# The method will call one of the following methods on the listener:
|
11
|
+
# * `#user_not_logged_in`: The user should be redirected to /login.
|
12
|
+
# * `#user_logged_in`: The first argument (String) is the URL (if any), the user should be redirected to.
|
13
|
+
# The second argument (String) is the ticket-granting ticket. It should be stored in a cookie named "tgt".
|
14
|
+
# * `#invalid_one_time_password`: The user should be asked for a new OTP.
|
15
|
+
#
|
16
|
+
# @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :service.
|
17
|
+
# @param [String] user_agent user-agent delivered by the client
|
18
|
+
def process(params = nil, user_agent = nil)
|
19
|
+
cookies ||= {}
|
20
|
+
tgt = find_valid_ticket_granting_ticket(params[:tgt], user_agent, true)
|
21
|
+
if tgt.nil?
|
22
|
+
@listener.user_not_logged_in
|
23
|
+
else
|
24
|
+
validation_result = validate_one_time_password(params[:otp], tgt.user.active_two_factor_authenticator)
|
25
|
+
if validation_result.success?
|
26
|
+
tgt.awaiting_two_factor_authentication = false
|
27
|
+
tgt.save!
|
28
|
+
begin
|
29
|
+
url = unless params[:service].blank?
|
30
|
+
acquire_service_ticket(tgt, params[:service], true).service_with_ticket_url
|
31
|
+
end
|
32
|
+
if tgt.long_term?
|
33
|
+
@listener.user_logged_in(url, tgt.ticket, CASino.config.ticket_granting_ticket[:lifetime_long_term].seconds.from_now)
|
34
|
+
else
|
35
|
+
@listener.user_logged_in(url, tgt.ticket)
|
36
|
+
end
|
37
|
+
rescue ServiceNotAllowedError => e
|
38
|
+
@listener.service_not_allowed(clean_service_url params[:service])
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@listener.invalid_one_time_password
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# The ServiceTicketValidator processor should be used to handle GET requests to /serviceValidate
|
2
|
+
class CASino::ServiceTicketValidatorProcessor < CASino::Processor
|
3
|
+
include CASino::ProcessorConcern::ServiceTickets
|
4
|
+
include CASino::ProcessorConcern::ProxyGrantingTickets
|
5
|
+
|
6
|
+
# This method will call `#validation_succeeded` or `#validation_failed`. In both cases, it supplies
|
7
|
+
# a string as argument. The web application should present that string (and nothing else) to the
|
8
|
+
# requestor. The Content-Type should be set to 'text/xml; charset=utf-8'
|
9
|
+
#
|
10
|
+
# @param [Hash] params parameters delivered by the client
|
11
|
+
def process(params = nil)
|
12
|
+
params ||= {}
|
13
|
+
if request_valid?(params)
|
14
|
+
ticket = CASino::ServiceTicket.where(ticket: params[:ticket]).first
|
15
|
+
validate_ticket!(ticket, params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
def build_service_response(success, options = {})
|
21
|
+
builder = CASino::TicketValidationResponseBuilder.new(success, options)
|
22
|
+
builder.build
|
23
|
+
end
|
24
|
+
|
25
|
+
def request_valid?(params)
|
26
|
+
if params[:ticket].nil? || params[:service].nil?
|
27
|
+
@listener.validation_failed build_service_response(false, error_code: 'INVALID_REQUEST', error_message: '"ticket" and "service" parameters are both required')
|
28
|
+
false
|
29
|
+
else
|
30
|
+
true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate_ticket!(ticket, params)
|
35
|
+
validation_result = validate_ticket_for_service(ticket, params[:service], !!params[:renew])
|
36
|
+
if validation_result.success?
|
37
|
+
options = { ticket: ticket }
|
38
|
+
unless params[:pgtUrl].nil?
|
39
|
+
options[:proxy_granting_ticket] = acquire_proxy_granting_ticket(params[:pgtUrl], ticket)
|
40
|
+
end
|
41
|
+
@listener.validation_succeeded(build_service_response(true, options))
|
42
|
+
else
|
43
|
+
@listener.validation_failed(build_service_response(false, error_code: validation_result.error_code, error_message: validation_result.error_message))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# The SessionDestroyer processor is used to destroy a ticket-granting ticket.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side. It is especially useful in
|
5
|
+
# combination with the {CASino::SessionOverviewProcessor} processor.
|
6
|
+
class CASino::SessionDestroyerProcessor < CASino::Processor
|
7
|
+
|
8
|
+
# This method will call `#ticket_not_found` or `#ticket_deleted` on the listener.
|
9
|
+
# @param [Hash] params parameters supplied by user (ID of ticket-granting ticket to delete should by in params[:id])
|
10
|
+
# @param [Hash] cookies cookies supplied by user
|
11
|
+
# @param [String] user_agent user-agent delivered by the client
|
12
|
+
def process(params = nil, cookies = nil, user_agent = nil)
|
13
|
+
params ||= {}
|
14
|
+
cookies ||= {}
|
15
|
+
ticket = CASino::TicketGrantingTicket.where(id: params[:id]).first
|
16
|
+
owner_ticket = CASino::TicketGrantingTicket.where(ticket: cookies[:tgt]).first
|
17
|
+
if ticket.nil? || !ticket.same_user?(owner_ticket)
|
18
|
+
@listener.ticket_not_found
|
19
|
+
else
|
20
|
+
Rails.logger.info "Destroying ticket-granting ticket '#{ticket.ticket}'"
|
21
|
+
ticket.destroy
|
22
|
+
@listener.ticket_deleted
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# The SessionOverview processor to list all open session for the currently signed in user.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side.
|
5
|
+
class CASino::SessionOverviewProcessor < CASino::Processor
|
6
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
7
|
+
|
8
|
+
# This method will call `#user_not_logged_in` or `#ticket_granting_tickets_found(Enumerable)` on the listener.
|
9
|
+
# @param [Hash] cookies cookies delivered by the client
|
10
|
+
# @param [String] user_agent user-agent delivered by the client
|
11
|
+
def process(cookies = nil, user_agent = nil)
|
12
|
+
cookies ||= {}
|
13
|
+
tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)
|
14
|
+
if tgt.nil?
|
15
|
+
@listener.user_not_logged_in
|
16
|
+
else
|
17
|
+
ticket_granting_tickets = tgt.user.ticket_granting_tickets.where(awaiting_two_factor_authentication: false).order('updated_at DESC')
|
18
|
+
@listener.ticket_granting_tickets_found(ticket_granting_tickets)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# The TwoFactorAuthenticatorActivator processor can be used to activate a previously generated two-factor authenticator.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side.
|
5
|
+
class CASino::TwoFactorAuthenticatorActivatorProcessor < CASino::Processor
|
6
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
7
|
+
include CASino::ProcessorConcern::TwoFactorAuthenticators
|
8
|
+
|
9
|
+
# The method will call one of the following methods on the listener:
|
10
|
+
# * `#user_not_logged_in`: The user is not logged in and should be redirected to /login.
|
11
|
+
# * `#two_factor_authenticator_activated`: The two-factor authenticator was successfully activated.
|
12
|
+
# * `#invalid_two_factor_authenticator`: The two-factor authenticator is not valid.
|
13
|
+
# * `#invalid_one_time_password`: The user should be asked for a new OTP.
|
14
|
+
#
|
15
|
+
# @param [Hash] params parameters supplied by user. The processor will look for keys :otp and :id.
|
16
|
+
# @param [Hash] cookies cookies delivered by the client
|
17
|
+
# @param [String] user_agent user-agent delivered by the client
|
18
|
+
def process(params = nil, cookies = nil, user_agent = nil)
|
19
|
+
cookies ||= {}
|
20
|
+
params ||= {}
|
21
|
+
tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)
|
22
|
+
if tgt.nil?
|
23
|
+
@listener.user_not_logged_in
|
24
|
+
else
|
25
|
+
authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first
|
26
|
+
validation_result = validate_one_time_password(params[:otp], authenticator)
|
27
|
+
if validation_result.success?
|
28
|
+
tgt.user.two_factor_authenticators.where(active: true).delete_all
|
29
|
+
authenticator.active = true
|
30
|
+
authenticator.save!
|
31
|
+
@listener.two_factor_authenticator_activated
|
32
|
+
else
|
33
|
+
if validation_result.error_code == 'INVALID_OTP'
|
34
|
+
@listener.invalid_one_time_password(authenticator)
|
35
|
+
else
|
36
|
+
@listener.invalid_two_factor_authenticator
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# The TwoFactorAuthenticatorDestroyer processor can be used to deactivate a previously activated two-factor authenticator.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side.
|
5
|
+
class CASino::TwoFactorAuthenticatorDestroyerProcessor < CASino::Processor
|
6
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
7
|
+
include CASino::ProcessorConcern::TwoFactorAuthenticators
|
8
|
+
|
9
|
+
# The method will call one of the following methods on the listener:
|
10
|
+
# * `#user_not_logged_in`: The user is not logged in and should be redirected to /login.
|
11
|
+
# * `#two_factor_authenticator_destroyed`: The two-factor authenticator was successfully destroyed.
|
12
|
+
# * `#invalid_two_factor_authenticator`: The two-factor authenticator is not valid.
|
13
|
+
#
|
14
|
+
# @param [Hash] params parameters supplied by user. The processor will look for key :id.
|
15
|
+
# @param [Hash] cookies cookies delivered by the client
|
16
|
+
# @param [String] user_agent user-agent delivered by the client
|
17
|
+
def process(params = nil, cookies = nil, user_agent = nil)
|
18
|
+
cookies ||= {}
|
19
|
+
params ||= {}
|
20
|
+
tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)
|
21
|
+
if tgt.nil?
|
22
|
+
@listener.user_not_logged_in
|
23
|
+
else
|
24
|
+
authenticator = tgt.user.two_factor_authenticators.where(id: params[:id]).first
|
25
|
+
if authenticator
|
26
|
+
authenticator.destroy
|
27
|
+
@listener.two_factor_authenticator_destroyed
|
28
|
+
else
|
29
|
+
@listener.invalid_two_factor_authenticator
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# The TwoFactorAuthenticatorOverview processor lists registered two factor devices for the currently signed in user.
|
2
|
+
#
|
3
|
+
# This feature is not described in the CAS specification so it's completly optional
|
4
|
+
# to implement this on the web application side.
|
5
|
+
class CASino::TwoFactorAuthenticatorOverviewProcessor < CASino::Processor
|
6
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
7
|
+
|
8
|
+
# This method will call `#user_not_logged_in` or `#two_factor_authenticators_found(Enumerable)` on the listener.
|
9
|
+
# @param [Hash] cookies cookies delivered by the client
|
10
|
+
# @param [String] user_agent user-agent delivered by the client
|
11
|
+
def process(cookies = nil, user_agent = nil)
|
12
|
+
cookies ||= {}
|
13
|
+
tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)
|
14
|
+
if tgt.nil?
|
15
|
+
@listener.user_not_logged_in
|
16
|
+
else
|
17
|
+
@listener.two_factor_authenticators_found(tgt.user.two_factor_authenticators.where(active: true))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rotp'
|
2
|
+
|
3
|
+
# The TwoFactorAuthenticatorRegistrator processor can be used as the first step to register a new two-factor authenticator.
|
4
|
+
# It is inactive until activated using TwoFactorAuthenticatorActivator.
|
5
|
+
#
|
6
|
+
# This feature is not described in the CAS specification so it's completly optional
|
7
|
+
# to implement this on the web application side.
|
8
|
+
class CASino::TwoFactorAuthenticatorRegistratorProcessor < CASino::Processor
|
9
|
+
include CASino::ProcessorConcern::TicketGrantingTickets
|
10
|
+
|
11
|
+
# This method will call `#user_not_logged_in` or `#two_factor_authenticator_registered(two_factor_authenticator)` on the listener.
|
12
|
+
# @param [Hash] cookies cookies delivered by the client
|
13
|
+
# @param [String] user_agent user-agent delivered by the client
|
14
|
+
def process(cookies = nil, user_agent = nil)
|
15
|
+
cookies ||= {}
|
16
|
+
tgt = find_valid_ticket_granting_ticket(cookies[:tgt], user_agent)
|
17
|
+
if tgt.nil?
|
18
|
+
@listener.user_not_logged_in
|
19
|
+
else
|
20
|
+
two_factor_authenticator = tgt.user.two_factor_authenticators.create! secret: ROTP::Base32.random_base32
|
21
|
+
@listener.two_factor_authenticator_registered(two_factor_authenticator)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -16,7 +16,7 @@
|
|
16
16
|
<%= hidden_field_tag :lt, @login_ticket.ticket %>
|
17
17
|
<%= hidden_field_tag :service, params[:service] unless params[:service].nil? %>
|
18
18
|
<%= label_tag :username, t('login.label_username') %>
|
19
|
-
<%= text_field_tag :username, params[:username] %>
|
19
|
+
<%= text_field_tag :username, params[:username], autofocus:true %>
|
20
20
|
<%= label_tag :password, t('login.label_password') %>
|
21
21
|
<%= password_field_tag :password %>
|
22
22
|
<%= label_tag :rememberMe do %>
|
@@ -28,3 +28,4 @@
|
|
28
28
|
</div>
|
29
29
|
<%= render 'footer' %>
|
30
30
|
</div>
|
31
|
+
|
@@ -8,7 +8,7 @@
|
|
8
8
|
<%= hidden_field_tag :tgt, @ticket_granting_ticket || params[:tgt] %>
|
9
9
|
<%= hidden_field_tag :service, params[:service] %>
|
10
10
|
<%= label_tag :code, t('validate_otp.code') %>
|
11
|
-
<%= text_field_tag :otp, nil, maxlength: 6 %>
|
11
|
+
<%= text_field_tag :otp, nil, maxlength: 6, autofocus:true %>
|
12
12
|
<%= button_tag t('validate_otp.submit'), :class => 'button' %>
|
13
13
|
<% end %>
|
14
14
|
</div>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<%= t('two_factor_authenticators.instructions') %>
|
12
12
|
</p>
|
13
13
|
<div id="qr-code">
|
14
|
-
<img src="https://chart.googleapis.com/chart?cht=qr&chs=250x250&chl=<%= u "otpauth://totp/#{u
|
14
|
+
<img src="https://chart.googleapis.com/chart?cht=qr&chs=250x250&chl=<%= u "otpauth://totp/#{u CASino.config.frontend[:sso_name] + ': ' + @two_factor_authenticator.user.username}?secret=#{@two_factor_authenticator.secret}" %>" height="250" width="250"><br />
|
15
15
|
</div>
|
16
16
|
<p id="secret">
|
17
17
|
<%= t('two_factor_authenticators.secret') %>: <%= @two_factor_authenticator.secret %>
|
@@ -22,7 +22,7 @@
|
|
22
22
|
<%= form_tag(two_factor_authenticators_path, method: :post, id: 'two_factor_authenticators-form') do %>
|
23
23
|
<%= hidden_field_tag :id, @two_factor_authenticator.id %>
|
24
24
|
<%= label_tag :code, t('two_factor_authenticators.code') %>
|
25
|
-
<%= text_field_tag :otp, nil, maxlength: 6 %>
|
25
|
+
<%= text_field_tag :otp, nil, maxlength: 6, autofocus:true %>
|
26
26
|
<%= link_to t('two_factor_authenticators.cancel'), sessions_path, :class => 'secondary button' %>
|
27
27
|
<%= button_tag t('two_factor_authenticators.submit'), :class => 'button' %>
|
28
28
|
<% end %>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<title><%=
|
4
|
+
<title><%= CASino.config.frontend[:sso_name] %></title>
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
6
6
|
<%= stylesheet_link_tag "application", :media => "all" %>
|
7
7
|
<%= javascript_include_tag "application" %>
|
data/casino.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.homepage = 'http://rbcas.org/'
|
11
11
|
s.license = 'MIT'
|
12
12
|
s.summary = 'A simple CAS server written in Ruby using the Rails framework.'
|
13
|
-
s.description = 'CASino is a simple CAS (Central Authentication Service) server
|
13
|
+
s.description = 'CASino is a simple CAS (Central Authentication Service) server.'
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n")
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -27,11 +27,16 @@ Gem::Specification.new do |s|
|
|
27
27
|
s.add_development_dependency 'rake', '~> 10.0'
|
28
28
|
s.add_development_dependency 'rspec', '~> 2.12'
|
29
29
|
s.add_development_dependency 'rspec-rails', '~> 2.0'
|
30
|
-
s.add_development_dependency 'simplecov', '~> 0.7'
|
31
30
|
s.add_development_dependency 'sqlite3', '~> 1.3'
|
31
|
+
s.add_development_dependency 'factory_girl', '~> 4.1'
|
32
|
+
s.add_development_dependency 'webmock', '~> 1.9'
|
33
|
+
s.add_development_dependency 'coveralls', '~> 0.7'
|
32
34
|
|
33
35
|
s.add_runtime_dependency 'rails', '~> 3.2.9'
|
34
|
-
s.add_runtime_dependency 'jquery-rails', '~> 2.1'
|
35
36
|
s.add_runtime_dependency 'http_accept_language', '~> 2.0.0.pre'
|
36
|
-
s.add_runtime_dependency '
|
37
|
+
s.add_runtime_dependency 'addressable', '~> 2.3'
|
38
|
+
s.add_runtime_dependency 'terminal-table', '~> 1.4'
|
39
|
+
s.add_runtime_dependency 'useragent', '~> 0.4'
|
40
|
+
s.add_runtime_dependency 'faraday', '~> 0.8'
|
41
|
+
s.add_runtime_dependency 'rotp', '~> 1.4'
|
37
42
|
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# In order to support pre-2.0 installations of CASino that included CASinoCore,
|
2
|
+
# we must rebuild the un-namespaced CASinoCore schema so that we can upgrade
|
3
|
+
class CreateCoreSchema < ActiveRecord::Migration
|
4
|
+
CoreTables = %w{login_tickets proxy_granting_tickets proxy_tickets service_rules service_tickets ticket_granting_tickets two_factor_authenticators users}
|
5
|
+
|
6
|
+
def up
|
7
|
+
CoreTables.each do |table_name|
|
8
|
+
if !ActiveRecord::Base.connection.table_exists? table_name
|
9
|
+
send "create_#{table_name}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
# No-op
|
16
|
+
# Handled by 20130809135401_rename_base_models.rb
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_login_tickets
|
20
|
+
create_table :login_tickets do |t|
|
21
|
+
t.string :ticket, :null => false
|
22
|
+
|
23
|
+
t.timestamps
|
24
|
+
end
|
25
|
+
add_index :login_tickets, :ticket, :unique => true
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_proxy_granting_tickets
|
29
|
+
create_table :proxy_granting_tickets do |t|
|
30
|
+
t.string :ticket, :null => false
|
31
|
+
t.string :iou, :null => false
|
32
|
+
t.integer :granter_id, :null => false
|
33
|
+
t.string :pgt_url, :null => false
|
34
|
+
t.string :granter_type, :null => false
|
35
|
+
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
add_index :proxy_granting_tickets, :ticket, :unique => true
|
39
|
+
add_index :proxy_granting_tickets, :iou, :unique => true
|
40
|
+
add_index :proxy_granting_tickets, [:granter_type, :granter_id], :name => "index_proxy_granting_tickets_on_granter", :unique => true
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_proxy_tickets
|
44
|
+
create_table :proxy_tickets do |t|
|
45
|
+
t.string :ticket, :null => false
|
46
|
+
t.string :service, :null => false
|
47
|
+
t.boolean :consumed, :default => false, :null => false
|
48
|
+
t.integer :proxy_granting_ticket_id, :null => false
|
49
|
+
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
add_index :proxy_tickets, :ticket, :unique => true
|
53
|
+
add_index :proxy_tickets, :proxy_granting_ticket_id
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_service_rules
|
57
|
+
create_table :service_rules do |t|
|
58
|
+
t.boolean :enabled, :default => true, :null => false
|
59
|
+
t.integer :order, :default => 10, :null => false
|
60
|
+
t.string :name, :null => false
|
61
|
+
t.string :url, :null => false
|
62
|
+
t.boolean :regex, :default => false, :null => false
|
63
|
+
|
64
|
+
t.timestamps
|
65
|
+
end
|
66
|
+
add_index :service_rules, :url, :unique => true
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_service_tickets
|
70
|
+
create_table :service_tickets do |t|
|
71
|
+
t.string :ticket, :null => false
|
72
|
+
t.string :service, :null => false
|
73
|
+
t.integer :ticket_granting_ticket_id
|
74
|
+
t.boolean :consumed, :default => false, :null => false
|
75
|
+
t.boolean :issued_from_credentials, :default => false, :null => false
|
76
|
+
|
77
|
+
t.timestamps
|
78
|
+
end
|
79
|
+
add_index :service_tickets, :ticket, :unique => true
|
80
|
+
add_index :service_tickets, :ticket_granting_ticket_id
|
81
|
+
end
|
82
|
+
|
83
|
+
def create_ticket_granting_tickets
|
84
|
+
create_table :ticket_granting_tickets do |t|
|
85
|
+
t.string :ticket, :null => false
|
86
|
+
t.string :user_agent
|
87
|
+
t.integer :user_id, :null => false
|
88
|
+
t.boolean :awaiting_two_factor_authentication, :default => false, :null => false
|
89
|
+
t.boolean :long_term, :default => false, :null => false
|
90
|
+
|
91
|
+
t.timestamps
|
92
|
+
end
|
93
|
+
add_index :ticket_granting_tickets, :ticket, :unique => true
|
94
|
+
end
|
95
|
+
|
96
|
+
def create_two_factor_authenticators
|
97
|
+
create_table :two_factor_authenticators do |t|
|
98
|
+
t.integer :user_id, :null => false
|
99
|
+
t.string :secret, :null => false
|
100
|
+
t.boolean :active, :default => false, :null => false
|
101
|
+
|
102
|
+
t.timestamps
|
103
|
+
end
|
104
|
+
add_index :two_factor_authenticators, :user_id
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_users
|
108
|
+
create_table :users do |t|
|
109
|
+
t.string :authenticator, :null => false
|
110
|
+
t.string :username, :null => false
|
111
|
+
t.text :extra_attributes
|
112
|
+
|
113
|
+
t.timestamps
|
114
|
+
end
|
115
|
+
add_index :users, [:authenticator, :username], :unique => true
|
116
|
+
end
|
117
|
+
end
|