main
 1# frozen_string_literal: true
 2
 3class SessionsController < ApplicationController
 4  ALLOWED_SAML_PARAMS = [
 5    :RelayState,
 6    :SAMLEncoding,
 7    :SAMLRequest,
 8    :SAMLResponse,
 9    :SigAlg,
10    :Signature,
11  ].freeze
12  skip_before_action :verify_authenticity_token, only: [:new, :destroy]
13  skip_before_action :authenticate!, only: [:new, :show, :create, :destroy]
14  skip_before_action :authenticate_mfa!, only: [:show]
15
16  def new
17    binding = binding_for(
18      request.post? ? :http_post : :http_redirect, new_session_url
19    )
20    @saml = binding.deserialize(saml_params)
21    return render_error(:forbidden, model: @saml) if @saml.invalid?
22
23    session[:saml] = { params: saml_params.to_h, xml: @saml.to_xml }
24    redirect_to response_path if current_user?
25  rescue StandardError => error
26    logger.error(error)
27    redirect_to my_dashboard_path if current_user?
28  end
29
30  def show
31    expires_in UserSession::IDLE_TIMEOUT
32    render layout: nil
33  end
34
35  def create
36    user_params = params.require(:user).permit(:email, :password)
37    if (user = User.login(user_params[:email], user_params[:password]))
38      login(user)
39      redirect_to response_path
40    else
41      redirect_to new_session_path, error: "Invalid Credentials"
42    end
43  end
44
45  def destroy
46    binding = binding_for(:http_post, session_url)
47    if saml_params[:SAMLRequest].present?
48      saml = binding.deserialize(saml_params)
49      raise ActiveRecord::RecordInvalid.new(saml) if saml.invalid?
50      raise 'Unknown NameId' unless current_user.to_param == saml.name_id
51
52      session[:saml] = { params: saml_params.to_h, xml: saml.to_xml }
53      redirect_to response_path
54    elsif saml_params[:SAMLResponse].present?
55      saml = binding.deserialize(saml_params)
56      raise ActiveRecord::RecordInvalid.new(saml) if saml.invalid?
57
58      reset_session
59      redirect_to new_session_path
60    else
61      Current.user_session&.destroy
62      reset_session
63      redirect_to new_session_path
64    end
65  end
66
67  private
68
69  def login(user)
70    saml_data = session[:saml]
71    reset_session
72    session[:user_session_key] = user.sessions.build.access(request)
73    session[:saml] = saml_data
74  end
75
76  def binding_for(binding, location)
77    if binding == :http_post
78      Saml::Kit::Bindings::HttpPost.new(location: location)
79    else
80      Saml::Kit::Bindings::HttpRedirect.new(location: location)
81    end
82  end
83
84  def saml_params(allowed_params = ALLOWED_SAML_PARAMS)
85    @saml_params ||=
86      if request.post?
87        params.permit(*allowed_params)
88      else
89        query_string = request.query_string
90        on = query_string.include?("&amp;") ? "&amp;" : "&"
91        result = Hash[query_string.split(on).map { |x| x.split("=", 2) }]
92        result = result.symbolize_keys
93        result.select! { |key, _value| allowed_params.include?(key.to_sym) }
94        result
95      end
96  end
97end