Commit 03079ff

mo <mo.khan@gmail.com>
2017-11-19 18:34:14
implement logout flow.
1 parent f3fcb44
Changed files (4)
airport
app
config
proof
app
airport/app/controllers/sessions_controller.rb
@@ -1,5 +1,5 @@
 class SessionsController < ApplicationController
-  skip_before_action :verify_authenticity_token, only: [:create]
+  skip_before_action :verify_authenticity_token, only: [:create, :destroy]
   skip_before_action :authenticate!
 
   def new
@@ -21,7 +21,7 @@ class SessionsController < ApplicationController
   end
 
   def create
-    saml_binding = binding_for(request)
+    saml_binding = request_binding_for(request)
     @saml_response = saml_binding.deserialize(params)
     return render :error, status: :forbidden if @saml_response.invalid?
 
@@ -31,12 +31,17 @@ class SessionsController < ApplicationController
 
   def destroy
     if params['SAMLRequest'].present?
+      # IDP initiated logout
+    elsif params['SAMLResponse'].present?
+      saml_binding = request_binding_for(request)
+      saml_response = saml_binding.deserialize(params)
+      raise ActiveRecordRecordInvalid.new(saml_response) if saml_response.invalid?
+      reset_session
       redirect_to new_session_path
     else
       saml_binding = idp_metadata.single_logout_service_for(binding: :post)
       builder = Saml::Kit::LogoutRequest::Builder.new(current_user, sign: true)
       @url, @saml_params = saml_binding.serialize(builder)
-      reset_session
       render layout: "spinner"
     end
   end
@@ -47,7 +52,7 @@ class SessionsController < ApplicationController
     Rails.configuration.x.idp_metadata
   end
 
-  def binding_for(request)
+  def request_binding_for(request)
     target_binding = request.post? ? :post : :http_redirect
     sp.single_logout_service_for(binding: target_binding)
   end
airport/app/models/sp.rb
@@ -7,7 +7,7 @@ class Sp
         builder = Saml::Kit::ServiceProviderMetadata::Builder.new
         builder.sign = false
         builder.add_assertion_consumer_service(url_helpers.session_url(host: host), binding: :post)
-        builder.add_single_logout_service(url_helpers.session_url(host: host), binding: :post)
+        builder.add_single_logout_service(url_helpers.logout_url(host: host), binding: :post)
         builder.build
       end
     end
airport/config/routes.rb
@@ -1,6 +1,7 @@
 Rails.application.routes.draw do
   get "dashboard", to: "dashboard#show", as: :dashboard
   resource :session, only: [:new, :create, :destroy]
+  post "/session/logout" => "sessions#destroy", as: :logout
   resource :metadata, only: [:show]
   resources :computers, only: [:index]
   root to: "sessions#new"
proof/app/controllers/sessions_controller.rb
@@ -1,6 +1,9 @@
 class SessionsController < ApplicationController
   skip_before_action :verify_authenticity_token, only: [:new, :destroy]
   before_action :load_saml_request, only: [:new, :create, :destroy]
+  rescue_from ActiveRecord::RecordInvalid do |record|
+    render_error(:forbidden, model: record)
+  end
 
   def new
     session[:SAMLRequest] ||= params[:SAMLRequest]
@@ -11,8 +14,8 @@ class SessionsController < ApplicationController
     if user = User.login(user_params[:email], user_params[:password])
       reset_session
       session[:user_id] = user.id
-      binding = @saml_request.provider.single_logout_service_for(binding: :post)
-      @url, @saml_params = binding.serialize(@saml_request.response_for(user), relay_state: session[:RelayState])
+      response_binding = @saml_request.provider.assertion_consumer_service_for(binding: :post)
+      @url, @saml_params = response_binding.serialize(@saml_request.response_for(user), relay_state: session[:RelayState])
       render layout: "spinner"
     else
       redirect_to new_session_path, error: "Invalid Credentials"
@@ -20,12 +23,17 @@ class SessionsController < ApplicationController
   end
 
   def destroy
-    user = User.find_by(uuid: @saml_request.name_id)
-
-    saml_binding = binding_for(request)
-    @url, @saml_params = saml_binding.serialize(@saml_request.response_for(user), relay_state: params[:RelayState])
-    reset_session
-    render layout: "spinner"
+    if params['SAMLRequest'].present?
+      saml_request = load_saml_request
+      user = User.find_by(uuid: saml_request.name_id)
+      response_binding = saml_request.provider.single_logout_service_for(binding: :post)
+      saml_response = saml_request.response_for(user)
+      @url, @saml_params = response_binding.serialize(saml_response, relay_state: params[:RelayState])
+      reset_session
+      render layout: "spinner"
+    elsif params['SAMLResponse'].present?
+    else
+    end
   end
 
   private
@@ -35,18 +43,16 @@ class SessionsController < ApplicationController
   end
 
   def load_saml_request(raw_saml_request = session[:SAMLRequest] || params[:SAMLRequest])
-    saml_binding = binding_for(request)
-    @saml_request = saml_binding.deserialize(params)
-    if @saml_request.invalid?
-      render_error(:forbidden, model: @saml_request)
-    end
+    @saml_request = request_binding_for(request).deserialize(params)
+    raise ActiveRecord::RecordInvalid.new(@saml_request) if @saml_request.invalid?
+    @saml_request
   end
 
   def idp
     Idp.default(request)
   end
 
-  def binding_for(request)
+  def request_binding_for(request)
     target_binding = request.post? ? :post : :http_redirect
     idp.single_sign_on_service_for(binding: target_binding)
   end