Module: SAML2::Bindings::HTTPRedirect
- Defined in:
- lib/saml2/bindings/http_redirect.rb
Defined Under Namespace
Modules: SigAlgs
Constant Summary collapse
- URN =
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Class Method Summary collapse
-
.decode(url, public_key: nil, public_key_used: nil) {|message, sig_alg| ... } ⇒ [Message, String]
Decode, validate signature, and parse a compressed and Base64 encoded SAML message.
-
.encode(message, relay_state: nil, private_key: nil, sig_alg: SigAlgs::RSA_SHA1) ⇒ String
Encode a SAML message into Base64, compressed query params.
Class Method Details
.decode(url, public_key: nil, public_key_used: nil) {|message, sig_alg| ... } ⇒ [Message, String]
Decode, validate signature, and parse a compressed and Base64 encoded SAML message.
A signature, if present, will be verified only if public_key
is passed.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/saml2/bindings/http_redirect.rb', line 46 def decode(url, public_key: nil, public_key_used: nil) uri = begin URI.parse(url) rescue URI::InvalidURIError raise CorruptMessage end raise MissingMessage unless uri.query query = URI.decode_www_form(uri.query) base64 = query.assoc("SAMLRequest")&.last if base64 = "SAMLRequest" else base64 = query.assoc("SAMLResponse")&.last = "SAMLResponse" end encoding = query.assoc("SAMLEncoding")&.last relay_state = query.assoc("RelayState")&.last signature = query.assoc("Signature")&.last sig_alg = query.assoc("SigAlg")&.last raise MissingMessage unless base64 raise UnsupportedEncoding if encoding && encoding != Encodings::DEFLATE raise MessageTooLarge if base64.bytesize > SAML2.config[:max_message_size] deflated = begin Base64.strict_decode64(base64) rescue ArgumentError raise CorruptMessage end zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) xml = +"" begin # do it in 1K slices, so we can protect against bombs (0..deflated.bytesize / 1024).each do |i| xml.concat(zstream.inflate(deflated.byteslice(i * 1024, 1024))) raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size] end xml.concat(zstream.finish) raise MessageTooLarge if xml.bytesize > SAML2.config[:max_message_size] rescue Zlib::DataError, Zlib::BufError raise CorruptMessage end zstream.close = Message.parse(xml) # if a block is provided, it's to fetch the proper certificate # based on the contents of the message public_key ||= yield(, sig_alg) if block_given? public_key = public_key.call(, sig_alg) if public_key.is_a?(Proc) if public_key raise UnsignedMessage unless signature raise UnsupportedSignatureAlgorithm unless SigAlgs::RECOGNIZED.include?(sig_alg) begin signature = Base64.strict_decode64(signature) rescue ArgumentError raise CorruptMessage end base_string = find_raw_query_param(uri.query, ) base_string << "&" << find_raw_query_param(uri.query, "RelayState") if relay_state base_string << "&" << find_raw_query_param(uri.query, "SigAlg") valid_signature = false # there could be multiple certificates to try Array(public_key).each do |key| hash = ((sig_alg == SigAlgs::RSA_SHA256) ? OpenSSL::Digest::SHA256 : OpenSSL::Digest::SHA1) next unless key.verify(hash.new, signature, base_string) # notify the caller which certificate was used public_key_used&.call(key) valid_signature = true break end raise InvalidSignature unless valid_signature end [, relay_state] end |
.encode(message, relay_state: nil, private_key: nil, sig_alg: SigAlgs::RSA_SHA1) ⇒ String
Encode a SAML message into Base64, compressed query params.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/saml2/bindings/http_redirect.rb', line 144 def encode(, relay_state: nil, private_key: nil, sig_alg: SigAlgs::RSA_SHA1) result = URI.parse(.destination) original_query = URI.decode_www_form(result.query) if result.query original_query ||= [] # remove any SAML protocol parameters %w[SAMLEncoding SAMLRequest SAMLResponse RelayState SigAlg Signature].each do |param| original_query.delete_if { |(k, _v)| k == param } end xml = .to_s(pretty: false) zstream = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS) deflated = zstream.deflate(xml, Zlib::FINISH) zstream.close base64 = Base64.strict_encode64(deflated) query = [] query << [.is_a?(Request) ? "SAMLRequest" : "SAMLResponse", base64] query << ["RelayState", relay_state] if relay_state if private_key unless SigAlgs::RECOGNIZED.include?(sig_alg) raise ArgumentError, "Unsupported signature algorithm #{sig_alg}" end query << ["SigAlg", sig_alg] base_string = URI.encode_www_form(query) hash = ((sig_alg == SigAlgs::RSA_SHA256) ? OpenSSL::Digest::SHA256 : OpenSSL::Digest::SHA1) signature = private_key.sign(hash.new, base_string) query << ["Signature", Base64.strict_encode64(signature)] end result.query = URI.encode_www_form(original_query + query) result.to_s end |