module SamlIdp::Controller
Attributes
algorithm[RW]
saml_acs_url[RW]
secret_key[RW]
x509_certificate[RW]
Public Instance Methods
algorithm=(algorithm)
click to toggle source
# File lib/saml_idp/controller.rb, line 26 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
algorithm_name()
click to toggle source
# File lib/saml_idp/controller.rb, line 40 def algorithm_name algorithm.to_s.split('::').last.downcase end
Protected Instance Methods
decode_SAMLRequest(saml_request)
click to toggle source
# File lib/saml_idp/controller.rb, line 50 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
encode_SAMLResponse(nameID, opts = {})
click to toggle source
# File lib/saml_idp/controller.rb, line 59 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 = %[<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_#{reference_id}" IssueInstant="#{now.iso8601}" Version="2.0"><saml:Issuer Format="urn:oasis:names:SAML:2.0:nameid-format:entity">#{issuer_uri}</saml:Issuer><saml:Subject><saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">#{nameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData#{@saml_request_id.present? ? %[ InResponseTo="#{@saml_request_id}"] : ""} NotOnOrAfter="#{(now+3*60).iso8601}" Recipient="#{@saml_acs_url}"></saml:SubjectConfirmationData></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="#{(now-5).iso8601}" NotOnOrAfter="#{(now+60*60).iso8601}"><saml:AudienceRestriction><saml:Audience>#{audience_uri}</saml:Audience></saml:AudienceRestriction></saml:Conditions>#{attributes_statement}<saml:AuthnStatement AuthnInstant="#{now.iso8601}" SessionIndex="_#{reference_id}"><saml:AuthnContext><saml:AuthnContextClassRef>urn:federation:authentication:windows</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement></saml:Assertion>] digest_value = Base64.encode64(algorithm.digest(assertion)).gsub(/\n/, '') signed_info = %[<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-#{algorithm_name}"></ds:SignatureMethod><ds:Reference URI="#_#{reference_id}"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig##{algorithm_name}"></ds:DigestMethod><ds:DigestValue>#{digest_value}</ds:DigestValue></ds:Reference></ds:SignedInfo>] signature_value = sign(signed_info).gsub(/\n/, '') signature = %[<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{signed_info}<ds:SignatureValue>#{signature_value}</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>#{self.x509_certificate}</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>] assertion_and_signature = assertion.sub(/Issuer\>\<saml:Subject/, "Issuer>#{signature}<saml:Subject") xml = %[<samlp:Response ID="_#{response_id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{@saml_acs_url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified"#{@saml_request_id.present? ? %[ InResponseTo="#{@saml_request_id}"] : ""} xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">#{issuer_uri}</saml:Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status>#{assertion_and_signature}</samlp:Response>] Base64.encode64(xml) end
validate_saml_request(saml_request = params[:SAMLRequest])
click to toggle source
# File lib/saml_idp/controller.rb, line 46 def validate_saml_request(saml_request = params[:SAMLRequest]) decode_SAMLRequest(saml_request) rescue false end
Private Instance Methods
attributes(provider, nameID)
click to toggle source
# File lib/saml_idp/controller.rb, line 90 def attributes(provider, nameID) provider ? provider : %[<saml:AttributeStatement><saml:Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"><saml:AttributeValue>#{nameID}</saml:AttributeValue></saml:Attribute></saml:AttributeStatement>] end
sign(data)
click to toggle source
# File lib/saml_idp/controller.rb, line 85 def sign(data) key = OpenSSL::PKey::RSA.new(self.secret_key) Base64.encode64(key.sign(algorithm.new, data)) end