diff --git a/app/controllers/saml_idp/idp_controller.rb b/app/controllers/saml_idp/idp_controller.rb index 603c249..1080fc7 100644 --- a/app/controllers/saml_idp/idp_controller.rb +++ b/app/controllers/saml_idp/idp_controller.rb @@ -8,6 +8,8 @@ class IdpController < ActionController::Base protect_from_forgery before_filter :validate_saml_request + skip_before_filter :validate_saml_request, :only => [:logout] + before_filter :validate_saml_slo_request, :only => [:logout] def new render :template => "saml_idp/idp/new" @@ -27,6 +29,18 @@ def create render :template => "saml_idp/idp/new" end + def logout + _person, _logout = idp_slo_authenticate(params[:name_id]) + if _person && _logout + @saml_slo_response = idp_make_saml_slo_response(_person) + else + @saml_idp_fail_msg = 'User not found' + logger.error "User with email #{params[:name_id]} not found" + @saml_slo_response = encode_SAML_SLO_Response(params[:name_id]) + end + render :template => "saml_idp/idp/saml_slo_post", :layout => false + end + protected def idp_authenticate(email, password) @@ -37,5 +51,13 @@ def idp_make_saml_response(person) raise "Not implemented" end + def idp_slo_authenticate(email) + raise "Not implemented" + end + + def idp_make_saml_slo_response(person) + raise "Not implemented" + end + end end diff --git a/app/views/saml_idp/idp/saml_slo_post.html.erb b/app/views/saml_idp/idp/saml_slo_post.html.erb new file mode 100644 index 0000000..dc5fb46 --- /dev/null +++ b/app/views/saml_idp/idp/saml_slo_post.html.erb @@ -0,0 +1,13 @@ + + + + + + + +<%= form_tag(@saml_slo_acs_url) do %> + <%= hidden_field_tag("SAMLResponse", @saml_slo_response) %> + <%= submit_tag "Submit" %> +<% end %> + + diff --git a/lib/saml_idp/controller.rb b/lib/saml_idp/controller.rb index 6eda6be..41dc9dc 100644 --- a/lib/saml_idp/controller.rb +++ b/lib/saml_idp/controller.rb @@ -81,12 +81,48 @@ def encode_SAMLResponse(nameID, opts = {}) Base64.encode64(xml) end + def validate_saml_slo_request(saml_request = params[:SAMLRequest]) + decode_SAML_SLO_Request(saml_request) + end + + def decode_SAML_SLO_Request(saml_request) + zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) + @saml_slo_request = zstream.inflate(Base64.decode64(saml_request)) + zstream.finish + zstream.close + @saml_slo_request_id = @saml_slo_request[/ID=['"](.+?)['"]/, 1] + @saml_slo_acs_url = @saml_slo_request[/AssertionConsumerLogoutServiceURL=['"](.+?)['"]/, 1] + end + + def encode_SAML_SLO_Response(nameID, opts = {}) + now = Time.now.utc + response_id, reference_id = UUID.generate, UUID.generate + audience_uri = opts[:audience_uri] || @saml_slo_acs_url[/^(.*?\/\/.*?\/)/, 1] + issuer_uri = opts[:issuer_uri] || (defined?(request) && request.url.split("?")[0]) || "http://example.com" + + assertion = %[#{issuer_uri}#{nameID}#{audience_uri}#{nameID}urn:federation:authentication:windows] + + digest_value = Base64.encode64(algorithm.digest(assertion)).gsub(/\n/, '') + + signed_info = %[#{digest_value}] + + signature_value = sign(signed_info).gsub(/\n/, '') + + signature = %[#{signed_info}#{signature_value}#{self.x509_certificate}] + + assertion_and_signature = assertion.sub(/Issuer\>\#{signature}#{issuer_uri}#{assertion_and_signature}] + + Base64.encode64(xml) + end + private - def sign(data) - key = OpenSSL::PKey::RSA.new(self.secret_key) - Base64.encode64(key.sign(algorithm.new, data)) - end + def sign(data) + key = OpenSSL::PKey::RSA.new(self.secret_key) + Base64.encode64(key.sign(algorithm.new, data)) + end end end \ No newline at end of file