Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: fa3a399932
Fetching contributors…

Cannot retrieve contributors at this time

239 lines (217 sloc) 9.958 kb
module Masq
class ServerController < BaseController
# CSRF-protection must be skipped, because incoming
# OpenID requests lack an authenticity token
skip_before_filter :verify_authenticity_token
# Error handling
rescue_from OpenID::Server::ProtocolError, :with => :render_openid_error
# Actions other than index require a logged in user
before_filter :login_required, :except => [:index, :cancel, :seatbelt_config, :seatbelt_login_state]
before_filter :ensure_valid_checkid_request, :except => [:index, :cancel, :seatbelt_config, :seatbelt_login_state]
after_filter :clear_checkid_request, :only => [:cancel, :complete]
# These methods are used to display information about the request to the user
helper_method :sreg_request, :ax_fetch_request, :ax_store_request
# This is the server endpoint which handles all incoming OpenID requests.
# Associate and CheckAuth requests are answered directly - functionality
# therefor is provided by the ruby-openid gem. Handling of CheckId requests
# dependents on the users login state (see handle_checkid_request).
# Yadis requests return information about this endpoint.
def index
clear_checkid_request
respond_to do |format|
format.html do
if openid_request.is_a?(OpenID::Server::CheckIDRequest)
handle_checkid_request
elsif openid_request
handle_non_checkid_request
else
render :text => t(:this_is_openid_not_a_human_ressource)
end
end
format.xrds
end
end
# This action decides how to process the current request and serves as
# dispatcher and re-entry in case the request could not be processed
# directly (for instance if the user had to log in first).
# When the user has already trusted the relying party, the request will
# be answered based on the users release policy. If the request is immediate
# (relying party wants no user interaction, used e.g. for ajax requests)
# the request can only be answered if no further information (like simple
# registration data) is requested. Otherwise the user will be redirected
# to the decision page.
def proceed
identity = identifier(current_account)
if @site = current_account.sites.find_by_url(checkid_request.trust_root)
resp = checkid_request.answer(true, nil, identity)
resp = add_sreg(resp, @site.sreg_properties) if sreg_request
resp = add_ax(resp, @site.ax_properties) if ax_fetch_request
resp = add_pape(resp, auth_policies, auth_level, auth_time)
render_response(resp)
elsif checkid_request.immediate && (sreg_request || ax_store_request || ax_fetch_request)
render_response(checkid_request.answer(false))
elsif checkid_request.immediate
render_response(checkid_request.answer(true, nil, identity))
else
redirect_to decide_path
end
end
# Displays the decision page on that the user can confirm the request and
# choose which data should be transfered to the relying party.
def decide
@site = current_account.sites.find_or_initialize_by(url: checkid_request.trust_root)
@site.persona = current_account.personas.where(params[:persona_id] ).first || current_account.personas.first if sreg_request || ax_store_request || ax_fetch_request
end
# This action is called by submitting the decision form, the information entered by
# the user is used to answer the request. If the user decides to always trust the
# relying party, a new site according to the release policies the will be created.
def complete
if params[:cancel]
cancel
else
resp = checkid_request.answer(true, nil, identifier(current_account))
if params[:always]
@site = current_account.sites.where(persona_id: params[:site][:persona_id], url: params[:site][:url]).first_or_create
@site.update_attributes(site_params)
elsif sreg_request || ax_fetch_request
@site = current_account.sites.where(persona_id: params[:site][:persona_id], url: params[:site][:url]).first_or_create
@site.attributes = site_params
elsif ax_store_request
@site = current_account.sites.where(persona_id: params[:site][:persona_id], url: params[:site][:url]).first_or_create
not_supported, not_accepted, accepted = [], [], []
ax_store_request.data.each do |type_uri, values|
if property = Persona.attribute_name_for_type_uri(type_uri)
store_attribute = params[:site][:ax_store][property.to_sym]
if store_attribute && !store_attribute[:value].blank?
@site.persona.update_attribute(property, values.first)
accepted << type_uri
else
not_accepted << type_uri
end
else
not_supported << type_uri
end
end
ax_store_response = (accepted.count > 0) ? OpenID::AX::StoreResponse.new : OpenID::AX::StoreResponse.new(false, "None of the attributes were accepted.")
resp.add_extension(ax_store_response)
end
resp = add_pape(resp, auth_policies, auth_level, auth_time)
resp = add_sreg(resp, @site.sreg_properties) if sreg_request && @site.sreg_properties
resp = add_ax(resp, @site.ax_properties) if ax_fetch_request && @site.ax_properties
render_response(resp)
end
end
# Cancels the current OpenID request
def cancel
redirect_to checkid_request.cancel_url
end
protected
# Decides how to process an incoming checkid request. If the user is
# already logged in he will be forwarded to the proceed action. If
# the user is not logged in and the request is immediate, the request
# cannot be answered successfully. In case the user is not logged in,
# the request will be stored and the user is asked to log in.
def handle_checkid_request
if allow_verification?
save_checkid_request
redirect_to proceed_path
elsif openid_request.immediate
render_response(openid_request.answer(false))
else
reset_session
request = save_checkid_request
session[:return_to] = proceed_path
redirect_to( request.from_trusted_domain? ? login_path : safe_login_path )
end
end
# Stores the current OpenID request.
# Returns the OpenIdRequest
def save_checkid_request
clear_checkid_request
request = OpenIdRequest.create!(:parameters => openid_params)
session[:request_token] = request.token
request
end
# Deletes the old request when a new one comes in.
def clear_checkid_request
unless session[:request_token].blank?
OpenIdRequest.destroy_all :token => session[:request_token]
session[:request_token] = nil
end
end
# Use this as before_filter for every CheckID request based action.
# Loads the current openid request and cancels if none can be found.
# The user has to log in, if he has not verified his ownership of
# the identifier, yet.
def ensure_valid_checkid_request
self.openid_request = checkid_request
if !openid_request.is_a?(OpenID::Server::CheckIDRequest)
redirect_to root_path, :alert => t(:identity_verification_request_invalid)
elsif !allow_verification?
flash[:notice] = logged_in? && !pape_requirements_met?(auth_time) ?
t(:service_provider_requires_reauthentication_last_login_too_long_ago) :
t(:login_to_verify_identity)
session[:return_to] = proceed_path
redirect_to login_path
end
end
# The user must be logged in, he must be the owner of the claimed identifier
# and the PAPE requirements must be met if applicable.
def allow_verification?
logged_in? && correct_identifier? && pape_requirements_met?(auth_time)
end
# Is the user allowed to verify the claimed identifier? The user
# must be logged in, so that we know his identifier or the identifier
# has to be selected by the server (id_select).
def correct_identifier?
(openid_request.identity == identifier(current_account) || openid_request.id_select)
end
# Clears the stored request and answers
def render_response(resp)
clear_checkid_request
render_openid_response(resp)
end
# Transforms the parameters from the form to valid AX response values
def transform_ax_data(parameters)
data = {}
parameters.each_pair do |key, details|
if details['value']
data["type.#{key}"] = details['type']
data["value.#{key}"] = details['value']
end
end
data
end
# Renders the exception message as text output
def render_openid_error(exception)
error = case exception
when OpenID::Server::MalformedTrustRoot then "Malformed trust root '#{exception.to_s}'"
else exception.to_s
end
render :text => "Invalid OpenID request: #{error}", :status => 500
end
private
# The NIST Assurance Level, see:
# http://openid.net/specs/openid-provider-authentication-policy-extension-1_0-01.html#anchor12
def auth_level
if Masq::Engine.config.masq['use_ssl']
current_account.last_authenticated_with_yubikey? ? 3 : 2
else
0
end
end
def auth_time
current_account.last_authenticated_at
end
def auth_policies
current_account.last_authenticated_with_yubikey? ?
[OpenID::PAPE::AUTH_MULTI_FACTOR, OpenID::PAPE::AUTH_PHISHING_RESISTANT] :
[]
end
def site_params
authorized_params = params.require(:site).permit( :persona_id , :url )
additional_data = params[:site].slice(:ax_fetch, :sreg, :properties)
authorized_params.merge(additional_data)
end
end
end
Jump to Line
Something went wrong with that request. Please try again.