# encoding: utf-8 module SamlIdp module Controller require 'openssl' require 'base64' require 'time' require 'uuid' attr_accessor :x509_certificate, :secret_key, :algorithm attr_accessor :saml_acs_url def x509_certificate return @x509_certificate if defined?(@x509_certificate) @x509_certificate = SamlIdp.config.x509_certificate end def secret_key return @secret_key if defined?(@secret_key) @secret_key = SamlIdp.config.secret_key end def algorithm return @algorithm if defined?(@algorithm) self.algorithm = SamlIdp.config.algorithm @algorithm end def algorithm=(algorithm) @algorithm = algorithm if algorithm.is_a?(Symbol) @algorithm = case algorithm when :sha256 then OpenSSL::Digest::SHA256 when :sha384 then OpenSSL::Digest::SHA384 when :sha512 then OpenSSL::Digest::SHA512 else OpenSSL::Digest::SHA1 end end @algorithm end def algorithm_name algorithm.to_s.split('::').last.downcase end protected def validate_saml_request(saml_request = params[:SAMLRequest]) decode_SAMLRequest(saml_request) end def decode_SAMLRequest(saml_request) zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) @saml_request = zstream.inflate(Base64.decode64(saml_request)) zstream.finish zstream.close @saml_request_id = @saml_request[/ID=['"](.+?)['"]/, 1] @saml_acs_url = @saml_request[/AssertionConsumerServiceURL=['"](.+?)['"]/, 1] end def encode_SAMLResponse(nameID, opts = {}) now = Time.now.utc response_id, reference_id = UUID.generate, UUID.generate audience_uri = opts[:audience_uri] || saml_acs_url[/^(.*?\/\/.*?\/)/, 1] issuer_uri = opts[:issuer_uri] || (defined?(request) && request.url) || "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 end end