module SamlIdp module Controller require 'openssl' require 'base64' require 'time' 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) rescue false 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 = SecureRandom.uuid, SecureRandom.uuid audience_uri = opts[:audience_uri] || saml_acs_url[/^(.*?\/\/.*?\/)/, 1] issuer_uri = opts[:issuer_uri] || (defined?(request) && request.url) || "http://example.com" attributes_statement = attributes(opts[:attributes_provider], nameID) assertion = %[#{issuer_uri}#{nameID}#{audience_uri}#{attributes_statement}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 attributes(provider, nameID) provider ? provider : %[#{nameID}] end end end