kl-ruby-saml 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +14 -0
- data/.travis.yml +17 -0
- data/Gemfile +9 -0
- data/LICENSE +19 -0
- data/README.md +575 -0
- data/Rakefile +41 -0
- data/changelog.md +75 -0
- data/gemfiles/nokogiri-1.5.gemfile +5 -0
- data/lib/onelogin/ruby-saml.rb +17 -0
- data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
- data/lib/onelogin/ruby-saml/attributes.rb +128 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
- data/lib/onelogin/ruby-saml/http_error.rb +7 -0
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
- data/lib/onelogin/ruby-saml/logging.rb +30 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
- data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
- data/lib/onelogin/ruby-saml/metadata.rb +123 -0
- data/lib/onelogin/ruby-saml/response.rb +722 -0
- data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
- data/lib/onelogin/ruby-saml/settings.rb +165 -0
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
- data/lib/onelogin/ruby-saml/utils.rb +172 -0
- data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
- data/lib/onelogin/ruby-saml/version.rb +5 -0
- data/lib/ruby-saml.rb +1 -0
- data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
- data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/xenc-schema.xsd +136 -0
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/xmldsig-core-schema.xsd +309 -0
- data/lib/xml_security.rb +358 -0
- data/ruby-saml.gemspec +57 -0
- data/test/certificates/certificate1 +12 -0
- data/test/certificates/certificate_without_head_foot +1 -0
- data/test/certificates/formatted_certificate +14 -0
- data/test/certificates/formatted_private_key +12 -0
- data/test/certificates/formatted_rsa_private_key +12 -0
- data/test/certificates/invalid_certificate1 +1 -0
- data/test/certificates/invalid_certificate2 +1 -0
- data/test/certificates/invalid_certificate3 +12 -0
- data/test/certificates/invalid_private_key1 +1 -0
- data/test/certificates/invalid_private_key2 +1 -0
- data/test/certificates/invalid_private_key3 +10 -0
- data/test/certificates/invalid_rsa_private_key1 +1 -0
- data/test/certificates/invalid_rsa_private_key2 +1 -0
- data/test/certificates/invalid_rsa_private_key3 +10 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/idp_metadata_parser_test.rb +95 -0
- data/test/logging_test.rb +62 -0
- data/test/logout_requests/invalid_slo_request.xml +6 -0
- data/test/logout_requests/slo_request.xml +4 -0
- data/test/logout_requests/slo_request.xml.base64 +1 -0
- data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
- data/test/logout_requests/slo_request_with_session_index.xml +5 -0
- data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
- data/test/logoutrequest_test.rb +211 -0
- data/test/logoutresponse_test.rb +258 -0
- data/test/metadata_test.rb +203 -0
- data/test/request_test.rb +282 -0
- data/test/response_test.rb +1094 -0
- data/test/responses/adfs_response_sha1.xml +46 -0
- data/test/responses/adfs_response_sha256.xml +46 -0
- data/test/responses/adfs_response_sha384.xml +46 -0
- data/test/responses/adfs_response_sha512.xml +46 -0
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/attackxee.xml +13 -0
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
- data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
- data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
- data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_id.xml.base64 +1 -0
- data/test/responses/invalids/no_saml2.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/no_status.xml.base64 +1 -0
- data/test/responses/invalids/no_status_code.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
- data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
- data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
- data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
- data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
- data/test/responses/no_signature_ns.xml +48 -0
- data/test/responses/open_saml_response.xml +56 -0
- data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
- data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
- data/test/responses/response_eval.xml +7 -0
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_unsigned_xml_base64 +1 -0
- data/test/responses/response_with_ampersands.xml +139 -0
- data/test/responses/response_with_ampersands.xml.base64 +93 -0
- data/test/responses/response_with_multiple_attribute_values.xml +67 -0
- data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
- data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
- data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_without_attributes.xml.base64 +79 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/simple_saml_php.xml +71 -0
- data/test/responses/starfield_response.xml.base64 +1 -0
- data/test/responses/test_sign.xml +43 -0
- data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
- data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/saml_message_test.rb +56 -0
- data/test/settings_test.rb +218 -0
- data/test/slo_logoutrequest_test.rb +275 -0
- data/test/slo_logoutresponse_test.rb +185 -0
- data/test/test_helper.rb +252 -0
- data/test/utils_test.rb +145 -0
- data/test/xml_security_test.rb +329 -0
- metadata +415 -0
@@ -0,0 +1 @@
|
|
1
|
+
PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfZTU3YTRiZmMxMmMxMjZlNWQxZTE3MjY5MGJiMzNjYTVjZjRlNTQ1YTE3IiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0wMy0xOVQxNDowMTowOFoiIERlc3RpbmF0aW9uPSJodHRwOi8vcnVieXNhbWwuY29tOjMwMDAvc2FtbC9hY3MiIEluUmVzcG9uc2VUbz0iXzUwZjEzZGUwLWIwNmUtMDEzMi01YzNjLTAwOTBmNWRlZGQ3NyI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vaWRwLmV4YW1wbGUuY29tL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMTI4LWNiYyIvPjxkc2lnOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PHhlbmM6RW5jcnlwdGVkS2V5Pjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNyc2Etb2FlcC1tZ2YxcCIvPjx4ZW5jOkNpcGhlckRhdGE+PHhlbmM6Q2lwaGVyVmFsdWU+V0J6Nkt3bEtZRzBKcXM4cGwvK1pqeVRCT1lGaFlsRjdDdldqYXdITERqMUU0V0JLVnc1QmM1cVR3MVAvZnlSSU1DQXNpblA5TCtsZTRlR3ZRTEFxbTJXejlBUHZZaFJ5MlY2bFJLU04zY21VUm9QRGtOSFp5b0lYWmQ4TlZ0ei82SEk5akZvaWFYRmRENDhiUTVhNkFZV2lLaUdOZEdpT055WG9tc0dPUzNzPTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkS2V5PjwvZHNpZzpLZXlJbmZvPgogICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICA8eGVuYzpDaXBoZXJWYWx1ZT4rb2R4dGhoWFM1YzA3RHRIeUFTeEtGUEhmc3M4TE1mWW5vckp1eVdLWHRuLzBKR2prT1dvQzFSK0VQR0o5ZEpZR2IrekZmSzd1akF0Si9BMkwyM3NEMGV3TVh5RHVkQ28zTkplRm5LYUNJaTBVbmdTTDZCMzQ5cUtXVDlHZ0I3b2MzOHFSYzhtSHVsaHpYdnQ0YmFUVjZmZXE4cnlIb0tiVXkxMEVsRXZXZ2t1RjBweWw1R1dLempJSnZzakZZOFJZNHhRRUNmRFkxcGRYQ2ZxR2RhQzd3dGFOdE1DbWFpdzVKM21FZnBXSnpNMlREY3dUUUJDYnJLRDJVNktEZzZNYkdjUzJWeGtoMEJkVWc5eWtaT2NocXVWYmJJd0w1dFdjR2dpWS9zbnBuU2dOWEx0ZjBSSURvdExlendtOG9kL2hmaGwrQVA3eklkVVVVWHNDYXBWUlV6Uk01Y1ZHN0NOaHBlQ25XcTMxem1hckxRblBtbWdqdTI3QlE3aTN2aWRud25hV2s2U2lmL29JUVFPMExWTXBBUXcyUWNpUU9SRk1lWml0TnUvTTljNkFwaXlvalV6a1Q4UjJlSitlcTcydmpQRHpybDJrYWdMVU1zMXJ0dFNmVVFLM0Zac0Z3bVYyKzJIM0FqaWF4bmFMYzVLUE9qMEJGakNyRzNSQy9TTmpEazZpU202L0h5aUh4Qmd2VHJyNEE1Y2tid0ZudEgxb1lKYlltY1JiQUI0cUVqd0IrK0FFZjlPVWVCVmNDVUM0cDluZEdMQzZNTkcrMzIzM1N0Mkc5ZEQ1dk54SUk2WU9MUGhwYlQ0Y1ZpYkZmRVRTdlRDYVNLVDN5THNOTEVlbUZxWUhoR0dNSFFjSFYvRFkvNmxhbFJSRkZVQkVZNWJnNWp5MzF3VGI4bTJMQncwQTN5OCswWHlaUFhCNEdZMlFIVWU5UkdUYVZHVkFoZXlFaTZ4RTI2bks1NU5ocnRnbDl4b0l2aFpMVmEvdjNCQ2ZFbnBZTCtYMVhQdzZwQnoxbEFZWjErN05abGtqcGw0L0FUL3AyMVNkZVVpaDB3OGZsMzNTVEpjWDYxdWNtcW9TS3FOL2tWUllZejY2SHFjTlpNb2dVYmx3bVNnQ3hGYU51THBydy84WlZjSlk4cHkrWkhSZ0JFNFJUclpoNld0TkwyaEg2bzdpUy9XSEgzYlRiYkEweXowMi9idGFZd1JZaGpLOUNTaDRPYjZ2MkZuSU1rMzkvSzV5VmJtZHExMFVFclo4SFllTGl0N2IvT3pmUVJ2Wnp5TnZHaWhoc3FsYlhPNUZSL3ROOG8xZFVuaVkxeURlcHZzS29LNmFUN3FGYjZYazd2RHQzNlR4MkY4NDFobHM3dVh5OVdWWUhKN2lxUnBkRU1taHBOOEFBWG5nZ3RVS1V4QTU2MTkxOGcyNjVtTXZERGgyckVUK2xBVjVIdk1WUXVYR2Zxd29ST0REaU1WRjJTZW83eGd2VWM3bHgzZCtkVU1SNFFKeHROd3hMVTJTWW1xL0thbnlXWTRYVGxZNmJzOE1pRExTdFlhYjFGN3hQK1J5S0JuSTBxdHc3UUJ3YytBNTZOUkxjOVNkaE1vYlRkYnlwMVpRdEpJL2F6dzBsaFB3Q09mZHhFekl3bHJtUVJZRld2dXZqVXpNcE51dkxPTlhvejdkbW9hQUpsZDYyZm0rZHcrUW1lYVRtZjg3K1luMHU1U0FBdW5keGZsOUNwemJLRU9JTFU5NWNRN3BKYkNTcGlkZWRoYm9YWithU1VKdUpub0ZPWHppcStpc3F3Z0M3UkR5bm1pQ0oyalppTDBxVzNJa0Y2Y1lKcHloOWMwMEthSGJTd3NkUTRrMm9Nbk91VHVvbVFHY28zR0NpSzJHMjUvRC8rU0FFTzJIODQ1UncyQ3d4RzdQRDFwRVU1Ylp4Y0VuLzlaVkR3Q1hxaEE5UW15UzZQaGZHWTRvUTYvSEJyVEZsSnN3OWRJbkdFMzhSZlZ2SkVWU0hGcklmVlZ4TVRzbUR5V0hFb0ROeTR0MU1QYkl5VGRuSldqWk1LN0ZZL0k4cndoQ2FHLzFNWG9wWEUza0Fmd1h5bkI0ZEtBUnA3T1lwcFBnS2F0NVV5Y0Via1pBWlpIMmhmMVIveFRBeExidzdKaGRaQytQU2ExWUVNUjYxczB1ZGZsN1BOVHdhUDF2Z0VSY3R3NmdDaTMxM0kxNDkzeUExS1FwMTNaekFQRUk0RkZnTVcvUmdSVEppWVhrZTkzaVNmeUxTTk9GRGNPaGxMaXpSWldqWWoxRm9RbE9vN2IxbGZsZ1pUT05mQzJzbWFtSDFZNGVUUlVaVjdxQXgzSWkxTHh3azFSL1phNm9VNHA0OGdHVUowTENCVE9WOU82Z0JSUzYxdFdSZld1aGhrUWFiTVphS3NxdEx2aW1PSW5NcFl6WGhsSEptN0Z1c0s4ajBsUGpqdjBuZUtDY2t5L0c2Zm5LTERaQzgyVER2UE5vQmVLZkRJWmNvUm9vRDhyejU4bDd3TWFkSzBUZzFqdnZKYmZLU0xrcmpidUpHeGp2L2hmTzd5OWFYNW43U1NhZ2orYkV3OVNIdlpSaWx1Qmtjb0Y1bVFOTGhIcjRtempnNlRRT0VkMmYybXhJNjJkL09mVE94Y2VFQU1Ua1VWWUpQRVUyQ01KWlJka2h5TXFiVGFJZGJVSkRzRmY1SlNBaTNuMVlWbGtWWDEwWkwvVE1TUTBUVjVWdnozOXprS29IcFBQY2ZHY1Y0Q0o1Ym1pYkh2RVdrTnN0ZXovV09QQ2RMLzFLNnhUeFB2NkNWWHNBMHJqQ2RJc0tGd0FsMGFTMmtLQkVKUmdhcXVLL2t5Vk5oR0ZWaHEwUG12RUFZZWwvbklpKzRiejZRQjlyNVI5Z3JNaUQ5dUdGMG5HbHlWRHV5ZHRLSlZYS3VEajVwVHpvSU4vM3FGQWFvY3lkWmJSMHN1RUN4Sloxc3pIUVJVTWNMcFYvdVFmVDRsMkpIajdIeVlqQUc2ZnBFbzdQKy9XelNCSjd1dkdxQXJOeGJHbzljV3JTQ1RpaWw5ampYd3N6eG8zQzlGc1RmUFU4a2FnSHJsalBhWEduMS92SFdYVTlTTVkwVjl4T1p6bUdXUlI2QmNKSW5qNXJaOFBmaHNGVVY3YmY3emFpcDdxZkJJWWRVMlRvekQ2OElnTHQxM1ZFM0ZDT1lHR21mM0hUVDMrSDhCSTBjbGtJMkt0TzZVdWQ2Q2ZVcjVmR0lGWlhLQysyalNyNlBpWG9kSkRBMU92WHM4VlZpOFltSm5QMTMxeHBMU2g5QWRVUGxPR2gxODJ5WENPNlN5Y0hYc1ZuN0IrOVUxU3Y0NXZlSWxhSWFSRVMwU0ZENVpxU215WkgxMWNHSEVwTUtZZWZwMnU1TFFxVVVsTEtReDNsN09WNTlYWG5yUmFnS0tRdGFrT29JSVdwcGFCeUJIdENCWmxGdk5MNTV0NlMvcEdwSUMzMzA1QWdubTRhdlJzMjZ1UlV2ZnhralNRZ3ZSeGltM2pkQTBzbUFVV2FEWk9KekE4STFjaG9CZnBQazF2WHBQWEhQc2hRcGgwdEVXeHpaS3dnTjZBQklZSGM3SXcvUXJMMUFtQ1c3VmpVZXpnQU1hSDNRaGNndE1QdExVbmZCRG1DZWF0ZEdUYjBBMm9iNlZFV3JyY0VnR2FOTlh5M3JrNXFGWXdaTjZieEwxVHVnTDdnRjh1NERLN2U3TTdtUzltU0pFbEI2S2NmZ2lhank4TWFlaVlRSFBWeGI1ZkZhNjhPcU96S0J2MkVKTTNmKy9WQ2xibHE4Q1Y1NUIrckFuQlVYM01Ray9aZEprU2tBWHpHL2hWQXpzb0s4RnNSQmJubWEyaFJYV0lIY0M0eUk2Z0xKV1ZYQzFxZnNXSkZzMmQvSGN0ZEVXQVNkS0d0NnFranVpMitHZFFLbnpjRUpBSkptUXdGVklObUNUc1JJTWxkajNMLy81RkpBL05CN0VpREFZeUJMcmhiblAwUkNLblBJWXVyMEpLR3pzSUwvSFBqRkNtRXA0L0w3eEE0ZFQ1bXRBbUVKeURoUzN5d0hoZ3QzQlV2bmJDa0lxdmhEZ2R2Uk9LZ2Z5RlJkNTlpTFlZQUx2TTlPUnpySzM1Zm9VeEpxMmhwSUlqLzRabyt2blhjaTQxVlZsdUpiM2dqZ1VuMFVSbDZhN1ZBQ2RkQ2UxR1A1ZmI0NE91YlJBbzdHYVpySmFkWnpWN3RQVlhnZ0xFVmZiaVovWGZVMjR6YzR0aFIrV3NtMEJTSmlYOUxFQ1doM1UzWjlidHZQQmx6b1hvSVhxZmhFQ1V6eGIxRUtOQ1dUUjJBNDVyNWJwRzJoVysvTmVNK2dqNUN0bGhubGQ5QXFHZGlXMmQwLzljMFdxNjJSM3RJVklScTZ1WFk0cVBjcUlWcFNaeWtPcHVnM3dkeUJBZFYyUXp0MUVTTVFaenVXWEJTSnBMM0NIQ2YvemNDRnZwWEo3RFM2QzNLeStteEU0L1ZJUlNmYWJYWXFJcWpMQXg2eUNLREdLa1N2c0k2QWFxTTRNN1hVU2tHV2JiVTN1SmlDRm1CMDRKS1U0OGc4VG4yMlh4ZXNNMWpobnY1N2I2Wnl1VVVneDBJTWJacW1iNUtBN3BWcitCdTFhaUR3L2VLU3k4TGplbjRBd0xGS0xiZGRTd2luSFNzWGpIYm03NDV1VEVZSm5NV1NYalRhNG5PMjhmRGZLeVcyM1BwWVNuZmREMjlEN3NMdkt2VFpEVWowdjZmWVhFZTNQeGk0czRQVVJNYVVPSU1jTFNmYkN3ZStHUm5FWXhRRUUrQkF3K25PcHVrNnVjMWUycHZHMWJGRHJ6UWJUQThtTDFmOVVacER1MlpiUDZUREFZdTBWNVVGVFpUWWs4cWNDZFBxcy9PdlZwMy9wTWJ4eWNlQ21UNTdEeWpLNHhnNEdFOERWSk5GRHlxeEw1S0dUeENMeUVpdGtDa3JubVhGdTNKV1NZdVNOa2RhbUxFUitIMVExamQxNE94SWdoYXdKRksvMTJYZElKSjNzUUlxNHdWV1NSV1VEV1h2eHhyRlBOSktuNUdRT0NJUEVRRjYzL0NNaDdlZGU5WnhTTGVQZW5qb0N5VjZIbFFQSUpwOHZXTFFnYjR6dC9EdXNReHh0VmJpR1FNVTdVUVZPTEJFeGRGZUFteDdiTTNuditla280SVVKYWRnYmVFa2txOXhPZW10dEh1RnhmUWFiNWU4WFl4ZG52bDM3NnR6bXB5Tm5jenB0T3ZEdHkxWnJBampubWhlTkYxcHU0d3ZDZElWS1Y1YmZxb1BaWTJVbnhURHlZemR0NmRKNnJEOG1POGNwdGlmRUp2cTRGQXdNWU1kTlVveVNtdGZTWmQ0MFdXNzlBa2dPU1NwMXIyZ2NPN2h2TjF1YTRBU2trVjRPeEltYlRhSllVWTFnZVFScTJTTVp1UlZhU08wNEMrRmg0N01ZblhncGh2bU80RkxYenY1bWVlQUNRWko3bHZldjFIOHZSd2JjTGZxejNtRFl2ZWFoajVlMjkrSGtEVEFDYXI2d2UvaWxrZGttdXhnWmpoQXY0UUlSQ1ROejE5WERzcGdiQ0JHZGZWT2k5ck03aTk4dVpVY1RzcDlPK0doTit5MG5Ca0R6STlxdnRSK1JFSHZVYnhsUEhzNTZyYzNXMFZ0bFNRYkF0SVA3M2t0bkV1azIvVTFNUElTcm1YbWs0WXp5RHNIRTFNUHJaVFZHTzJDQ21BS2NCQm84V21TZm1jd3RHNGVmYzI2Mis3UE41SUtZMmp6eWRNNHRDVk9ObHlFaEJPTUVJNHdNUjFlZGtLZ2tBR25sbHRtMndoUTd3L1lpUkJFNURXN29BOGsra2ZFaTdGbXQzR2Y4Ly9qa3hOUmQ1YzRlZ1JrVWJaVFZDRXRyWDJUQTVzV3FNTXBNSWxha2hYZFdoZ3p0UEtZQ1kybWFrYmwreFkwVkMxSEhpSEdMSFVjeEd3WnJPblRicVJSUkEwYXNjYWkzcDh6VVc1TW1aWGJOaXJUY0tGY2JpcW9KZHc0T05PVGhFV2JCeDc0TVgvQXozZnlpRG5talpSVlZ0RjlWQ09GU0JpK2lWaW5lU0JGMm1HL3d2YzdDdnRITEcydC9BMUNnL2RieklvWnJZVElreWg2LzNBc0RDMS83T3RjZEwxNitNSXM1RXBDbSt3aU9pOEppRlRvejRVc1FRREhNVm9iMndlWnFpR29ITHJLVngyM05zZWd3ZW5hZjZ2cVhESXExa2QwUXNnbTl0YXpNSTNyTVplU1FxU1BIWG5DV1Y0RlRmdDNQNTd4OGNId2ZtUmUwU1RNRUE1QjlFMFZqVDBWVWZtUDloOXBKaDNKb0RaY3gyTmEyTytqRDVUTDF6SnRJcy83TlB3dkl4MDVlWVByelliNWQvOGtGemF4V1ExRzUrb0VLNm1KckdaYkxXZzJaZ0dmNGFXLzRGaXVySTdxNFZzMDc2UDYvTGlsKzlxRFdIWFRJWGZIOExITkRhWTh4eFpoazFhT0hZMzdRQjNQd3dqMTljYkJWMHhRcHIwQzdlb3J1NnZnWmpEZHN4cTRralZQdmFUWWhjM1lPQk9qczhRbC9xaTVxOGNKTlZGSXUvZk4yU0kxb2hWSWU0QXdYT3FmV0kzOHdpY2l3ZVpmaG9EWSsvb0xrM1ZKMGl3aEdnTW43dTQ4VWxhajZwN24rL1BlUEMrcWNhNVR5WUtNTkNxUHUrSXQ1TWxIK1M1S2N1eXU3WHFWeURRdmJPTGZhdE5JWFZ4RVZjQWcvQXpjQnpmUVVVMnU4VElUWUl4TVIyRHBrY1JidG1FMER3enRIRlRPWCt0NGQwem1neFlqR3EvcmYvenZYUnRyM3BsT05ON2RyM1RaTWpTWHB5WUljSHRBSGZsR1FBUlpRdVZDamR5QUc1VmszZ1dkZE9qYXM5Z1V1djRrUnptYjJQNk9sZTJlKy9WRjA0a0tWd0dXNGhtK0s2dlhDV0k1bnArRDNKaDQyUERtL2VjcnowWC92NXgweUFieFQxbWJBMnBoeG1uVUxPS0NuUkhzNHBoMk1iVUhCdTBXNlpqUi9EdVpwYnF0SDBzR1NBRHJDSWVzcDZuQ3VjelpmNE1ISVhCbUQ5azNYdDczSE4rbllCVDBlVG82L2cyZDl3TVNwVTVXU1p2cXVkcVBuM2t5eDZPRURueE1ad0FjTk1YMG95ZEJYY1ZodE9VM2ZYUmp3YlpsUjF3eXFBa0tETk8vRzFONHNQZEJBa3FhR1RHVGR3c3JzNXhqcnB6bWEzd0RycHY3Wm9CMWlkUjNtSGRKdnNtdnY0blZmU2lYaG5wN3ZoakI5L3B5MWdJZ01ScTVIalpmaTl4Yk1XUjJYdllhV2ZlRTJTbkQxRFhoR1pZekh3djREU2w4NU5CRUw5bFlSaTdpbEYwMnpRU2NDcnplRkR0Z3ZtQlVSNzQ5UHA4U0M2am4xVkswSWVLbUpmaHpqWDFNbURzV2duTVJUYVRTbVRzekVqU2RacVFWZVhXTzVjR3RRY3NYV2tZSHNEd09rUEE0U3NrUXAxbmVkam9BUWRhQ2xQcTRIN2Q5LzFEM2JueHEvdm5xb1ExNDBlVTIvV1RLWUplRVo1dlhNREMxUFVyRkdKbVUxeUJCR0VmMnp4ME42NG85YkxCczJORk9rNTQ3aU5CalhDOW1WcnpNaUkwWDZ5WWttczk4b3Z2M2Y5SDlnUGdtbGxEaFJkSzdadUgvWGsvMnVRZXkvclRwYkdKd1lOckpoMXNOb3cwL1dEYU4vNldMV1NvU0F6eEZwZXhKcjV6NEFNS0JBSWFrUHFFdjBTZ1RzOFFvZGtjR2ljSGFNdFg4NWVpUkdLZHlsN0pLV0krYjU5MWZMZlJJMVVsOGxjeVkwRXh6MkxhZTk3VVVwT1FuSHBpSys5dnNqU2prbEFDcndaMHZVRGFoNllHL0Q4OGMwbnZDU3NoKzNNYUJsc25Pc2VyT01aQWF0OTk2emRKRkpYRkhFOWt6dytrekw4N0VJV294Nk1ObXdKWlZ3dUxHRkgzZkY1bU0xRk8rdFdCdDZ3UFh2b0R5dGVnY1BGOCtpZ1pJYzN3VkpDZm1iTWlnaDZRY0E5d0YvU2p3SWYyZ3VpdVNORDUwOUFVN0U1aTZYK0FCSGs0OUdSSEdlQmZLbkMrQzc3T1pVT3JpeFErUk1aekhVdUZ0SmZKWWJJd2VxYkpZdHhoVWREVDNGK2pYV3krcWcwOW9ONldPNmRFYk55MHN0Y3BtSUFHLzFxSmwvd3o3b3R1aDY2bnV0QW9TaHJ3eElyWjVEd0ZOUGdsUnF3bzFTbENMV1EyNGc1QzAzS21Fb29NSHdFTURtYStac1E2aVJlbllFVkVLR09ET1lqbnZyK2psZktLQm1tWVFDSTA5WjNEMzZWWEdkM0JNV1NmZXVlMElLcUtsb2w3bS9YM29wdGlpY2R5Y3hQbmVUZlNxMVdpVzE5NWVscTlPMysxQTFabEZhVmlpdmpjcHovZzQ0ZTJBM0tJKzE0bWMxYzF2ZFM3cDUvUjJ3aEUydVcvSFN2enREdlZqQi9PUUVOdTBia01SZVVwTlNYeE8xNjVyTkViU2p3SjAxQld3QkxXSmZsTGp0ay9UdUVyb3N1cXVScGFHZGI0ZEhrTHZWWDM3Zjk4ZGpqeFdoU0hrb0NlakVaWGtVSmc5a1l5cE1hMnhVd214ZFRGNGMyaVZsREhkNllVcjRGbGRzNHNsTFdOVTNHRWs2RFprSytaaFk3emRCRVloMUFCMThmdVhacU9qWGMrczZrZk9GV1ZzbXQ2aCszNGd0anl4YXB6STJBMUR2TDlnTFV6ekRLR1ZyZ0VJNlA5QkJPVnhLWXdFakNZY0NZT1JJYUdxVTdUZ2Z6bzNtRE4vQTU3MllXSnNxc3VoRDVJRHZiblhVU3RCdHd5TithYjF5dzZjRlBEMzkyZUlFY2t1RklhWnlrQ3NZMlNvbERqWU1HMjRiRFo4bzBOL1Y0N05xancydlNjQlFkQkQzczRCVVp2SGhoa1NNYU1YRjhUY2I1NWMrSGhva05IVUNSOEVDT0lKSUZWSGdoTzVaNzliNDlOQkhrNHRVdXFHMUxBVFJnOVlia0c5dCtnTlUraG9hQm1QRVpPQWtQVkljaFN5RTg2YkZlbkxyb0VSeTM4R3NIM1Z4Y2hOK2F6OEtNMzg0SGFIUHdhQ0NOUDdLZjh6cjBZWkJuWDJtWDlVWkZHSHpYYlBtSUhBVFV1Nmtac0NwY3QyVElOaHdlNDJGWXpsS29LV2RMakFiRlN0K2dsb0JGbS9Dc2RiYm1zNjI5dDE1YTJzbEFnblMySXB0Z0oxQ0lONlo1cDFwcEw1TUl2TUhIbGRLUk4wODMwWlIzMnlZMHEvcWhPdnFzTHhpdTVxczdPUzV2emhVQlVNcURmc0ZWd3grSm5FS1RBOCswV3YvbWZSYnp5UE5EcTBGZW1RUWkrN0lBMFIzNkR5OVlubnVpd2RpM3BvU3VXcW5WajBCMG0zazVHMXRYTDliVUdQMnRjMTlWcSsrYWNyMDFSbytmWkRnSGhOVU9oTi9ZU2F4cWJDVHlGV2ZGcFBHSUEyTk84OXJOYk5adzBxbFB0OS9PR201c3ZOdmJ1V0pTb3ozZzhMVzJxZENYV1FVZDB2M0NjTGhnUmZiWjlPT21BUkJzUDlYejRLZzBUYXFld2tYd3JGQVFLTk5teHYzMnM0eWRyLy82QXRqalBWTzRhV2FEVmVSVzFFSFlsQXVqSkw4WWRBSU8zYTBzcTdGQW93UTFkbkpCK1YvVFl2VmRMRkVpRlJEUVJmcmlHSTZsVUluUHZMUXVLaXk3WUlrOExlUDU2L1VEQ0srRWlJcE5CNnpId3p3bU0vTU53dEtLendrTXorQmRZUFVMbVZ3R0ZoSW94MGJuY0ZXNzFJczhzaWwvYTFiZnEwVkk5TnMwSHpEVVFYamFTaVJxY0hXQlgzQkpDbFU5bXl1Z1dTb2VZeFdTd3ROVTJISzJ6b1JzQUVyMjZDNVVDazBJaXRzT1gwN0doaU5zOU1ZejFBc0ZWUVRLSjBtWDRFR2I3eURXU2czZC9UM3EzK3p1RjNqSVkzNHBqbjl1eWFBSkV6UnRnWmVCOXNZOS9qd2o5ZHhwVDBlb1pqazFFSHBhdkw3cVJkOUx2Y0U2ZllMTysxRWdsZkxFT3lzRm5ONkZjMHQ3UGkxVXdHU01NbXhuTEl2V2pBV0VLcFd1RGUrRUJZWW1CUjJhclhRZ2ZPRU9xbDNEeTR3N2NXbjA1Zz09PC94ZW5jOkNpcGhlclZhbHVlPgogICA8L3hlbmM6Q2lwaGVyRGF0YT4KPC94ZW5jOkVuY3J5cHRlZERhdGE+PC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
|
@@ -0,0 +1 @@
|
|
1
|
+
PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfOTM2NWZhYTNhNmY2YTIzYjVjZDc1YTc0Y2IwNzg2ZGMxOWU4NDUyMDBlIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxNS0wMy0xOVQxNDowMDozMVoiIERlc3RpbmF0aW9uPSJodHRwOi8vcnVieXNhbWwuY29tOjMwMDAvc2FtbC9hY3MiIEluUmVzcG9uc2VUbz0iXzNhYjdhZGIwLWIwNmUtMDEzMi01YzNiLTAwOTBmNWRlZGQ3NyI+PHNhbWw6SXNzdWVyPmh0dHBzOi8vaWRwLmV4YW1wbGUuY29tL3NpbXBsZXNhbWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6RW5jcnlwdGVkQXNzZXJ0aW9uPjx4ZW5jOkVuY3J5cHRlZERhdGEgeG1sbnM6eGVuYz0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjIiB4bWxuczpkc2lnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIiBUeXBlPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNFbGVtZW50Ij48eGVuYzpFbmNyeXB0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjYWVzMTI4LWNiYyIvPjxkc2lnOktleUluZm8geG1sbnM6ZHNpZz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+PHhlbmM6RW5jcnlwdGVkS2V5Pjx4ZW5jOkVuY3J5cHRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNyc2Etb2FlcC1tZ2YxcCIvPjx4ZW5jOkNpcGhlckRhdGE+PHhlbmM6Q2lwaGVyVmFsdWU+eGtKbjdMUUhyVFp6TGRyTmVjMzNoTVNDNWRjOEIwdU00Znc4SEtod3BrV0lOd0M5ZGVhajBRVno0Zlo4Mlp2MlFkUDN2NnIwd0ovOFZyVUlGOHB1SlRIU1UwdTJFVVUrWHFCSnh2aXZHcDJVaFVQQ3lXbWlyU2k2ZWZCbldxTUtoT3o1WUpvYUxFdTE3eXcra09uYXZMWlB2MmJCOGI1Ym8ybVpQaWFQRzZzPTwveGVuYzpDaXBoZXJWYWx1ZT48L3hlbmM6Q2lwaGVyRGF0YT48L3hlbmM6RW5jcnlwdGVkS2V5PjwvZHNpZzpLZXlJbmZvPgogICA8eGVuYzpDaXBoZXJEYXRhPgogICAgICA8eGVuYzpDaXBoZXJWYWx1ZT5FUmlUa2RKekpOODQxcUlSd1Q3OStsYVRRWld6ODNvc29Lb2hSQklhQkhOTGFzR3VCQXBSNmlKcXNTclFiMi82d1dkZERhTk9xYm5CRVkrNlplMW1TekRqWW9tcld0VkREMnBzcnprMVQyOUc1Nkx1VFFpT3RUdE9VbE9QT1ZVa1M5VHAxUmdDWG1pN05vNDlsSkpIeE04YjUzWTZoTmpKUWlTd3JFZHhocnY4M01TdXFZYWh1VDVuMnlCOWpHZy9abkY2SVAyWVhMVDdjZDRrWVJqLzBnMG1FQkdBSUhWM1pHUkxWU0tnMENNVlBuUVVDUExYcVZDS29SYkYybnQ1UjhRNmdHbUttLzJ6c3lFZkR3L3ZXMDc2ZlZnNkVvSytYVlovTS9nSEtjY0EraXllSEtNTjJrL3lXZUhuN0ZCMzJqWTlvUjhDdDZtVzlKbGdCYmxpTTFTQ0RNbnNodUpYbEtueVh1dk9mSlFkMTJOVXJ0OWxXQUN3bktRelNaM3kvYjVVdGhhbEFZVGdIUFZ0TDg3MWhYbkM5TVRlVGZBaytIcVY3bmZkUEpUay9qVkRJaTBXc2JxbGYrcHBDMW52UmFiOG92V21mcFVLRlloSGhpUTZLNlhRR283eFQvZEtxVjcrMnd2aEh0dllQdDhpVzc2UmtXdEhWRWpaYUhqZ0NGL1hHcE1yajVDdVg4S01RM2I2N0NJeGIyOFY1TXhuVVhySlYxcTVUbkVGNTZidmxHNE5OcVFUWVdNY3UrYmhjQTV3MUUvckpaeFNLZnBnN1ZkVDRSTUEzczBaQ1hzOURSN1JualFQOXN6cnl0YXJNUndGK0FJMDdBeHQyL3o3MXQ1NTFyTnpDZWROTGRRRm5GYkZoOUlnRFNtWWluN3kxbXhzYTdJaUxVWmNKd3RKVGgzM3RLZjBPYndNZmo2OHZXZ0VKMEFxOFpqbmhWQjdDN3NSSzR2aDdOT2dsZFMvQ1haMkw1RTVrWWgyaFZ6cWgwRUdKcmtPczNSYjMvbmJaQ3dPNHpkQVl2djc0cXJQVWtFVXhHMy9wOXdqY2crYlNGdWxjSkNwazdpMCtObnNvY1EzT3BEUUdVRmh0djZROTI1aDJMREFZUVNrUWdJdlBucWJ0K0E2VlJDbmc2ZyttTXdiYndQNm8vYWdZMkN5ZHVFRDBRSU5Bdlp6ODVDcHRzM3VmNHV4SkVuYXp0REdUMVJCUEZ3aE11SlZ0N1FlNG5xdXJSZnRDaCtUNktpVnZ3RTZDcWZ1UkZRbStTbFdGeVlxNXU3R2RSTkZzdVQzWUtqaTNtRitVZHFnTkphKytSNGJwT3RabTFRUXp2MXFTUzZVQ3RySkhiakJYaFlEaWczZEV6MWVya01XQ3d3amNpY3BlU3JqQkZjMGxjMVZ6WWZtMW1PWVdCSFRFMlJmRG5FVXp0T3pCVW1pQlZEM0dYbVM0eHJVRXJJcXozUkhlMWVyRzhId1NaYmZzWGZPcGZSNUs4UnNxTDVBOGwrdmpTTjZ3ZlFHQjF1SWYwN09QcGx6TEwvVWxaV2NQUXErajBHR1FBZC80S2JsNWdpV1NTYnd1ZWFXOTUvZFVKUUdjT3c3Z0l3OUxJUmFzQVdiNkpRMC9UUmd3VSthN0hvaitjRkFkV3hrZVY2M1Y5ZzRCdndPVzNacjAwcFNvMkc5ZytLVXNBdUZReGs5aDljREVxZEVNMnZQdGZlU2tqcFhPWm5HT3JSTlZVRzIzbGdKSEVRc1ROYitWUWI0Uk9GSkJWampkcEU3akhNVTlZYUs4RWZRb2tVQmZyKzhPdElkcDkrTEFVbmZaM0pLR01LT1JPdVEycnRqYjNOQ1o4c3k1dTZ1c29tMG1iY3hIaVZxQStYYkZZb05mZXI0SnlxSSt2YW5TQytkWG5VRUhuV3hGOXM2T3FtU0xVTkNRMFRsS2hYQS82U0VCVEM1TGIrTnVzdDgwdFlIU2dCbTJTNDlhRkQ1c1Iyc0krOFNDZDNmZnRjeVpFU1pDSDd6SC90MGM2Z3I3QUVJUEhhcm8wam1KUFgvMlN4Ym9mMFREbWQxVkNQMXZFUUlLby9qWGdpUHBsSVBNemsrWlRLTENuNVRHamxCWFFNTGtFZ1hRdUZjeGY2a3duV2d2Vis0d2ZrVjBkU3dlTUY0UklxN29VQk11UmNLTFp6c0kxeEsxK09nMW9yb3Mvb01nbWtmQXZSK0xIbi9xMnZsNzNIZ1RNS3l0aTV0QndXSkJER1ExdUtQNFVEemxPM0wzU1U3MnhUMlNlQWthdlU2V0owZno2WFhSVklZbmZrZnpKNm5VcFhZeEVBN0crOVgyeklCZTZ4OW95RzFyT0I4dE5HM0l5SEpYZlZ1ano1VGFNVnljUDVoRnFCeStFTjJXRjFWdTBxTVVnaTdaWTl1MnhEbnJJOVhrWUQrZDBoWGhDNk93L0V6TWkrVEhWNncyTXNPaHk1RTBGTVFQYmlpcFJTaXJ6eHMwYlFrS0xtZ3hreGF1bGFXUHVDQVRjMmxOWk9iMVhwM3kyNzdUWDVJa0pRZGJvMWg3NzJjVVRmL3RmVzZsYi9teTM0VmJGRUpFdXNNWFNQeHdYYTJMa05vcUR2SUJQanNNWW5vZ2FncmlWV2QrV0RJak0vU3FoK1JoUm8wT3dwL1Y0dzhLaHJDVTB0RkkvZzFwbjU5aTRtcmhuTVV5ZVpkWVNQazBURmROQk1la01CNUtxM0NjOWtIU3BlNFRDQWtXa2tuWjlxS0Ywc0g3a1FuRHpmdldzckxESGR2OWlqWC9LeitKNHd3b3BlU0tjTGJqT09ScG5OZDBzQXVGNEZod3VPaDl0OWJIZ3hJVk5waXBjZmlNVUt6eEkxSWNMUWpGQzdhTnVjL0srU2RXNG13MUw5ckk4SmloTkgyMXZpZDRoOUJaVWFZMThmZWRXdmR0SCswVzJUUHNIZ01LTTVLYkFEUzZpeGN4YUs5cUJBRUxEYVJySXdZMjA5cERnRElqNlRVL0lyaEI1U0tHc3JFb0VudnZ4RFhJZHM3dlpQZmJuNlE1S3BoeGNVNjZ6TGhJMUF0R21Za08xdUlSUnJqWDNBMXpjaGhIcEUyT21URmhDRExZTUl1R1BoWTU1STcxTm56bjZ5c0YySEhueFJCV0VJRVY4YUVGRkE4N1V6cWhkek5XSTNzTWN5N3lWc3lMeFdwZFNCK0dwc0NNOXZVMStCUlpoMTBLZU5ST1NRcnlZM2xVcC91WXBXQ3N1VFBNWHVZdnFTNlJTZWJGZ1Z1MzhiZ0dmYUJ2TElMZmIvWWtjSFdHUC81WW8vbmRFZ3NUSTJwRFRxZHBEQXJOYXhvSEpKK3VDMURoUEtzUWdjMU9MdGRjSVI4MWVIbjlKSGRVS0JSV2NIVSs4R0JFY01tRmpIcmJyN1Y4ZVZQT0V1VG9kTzFnSWpRdVVDME9qUkQ1TzdnYWRQTndNRzVKVjNRODc4VjhzdGtyUUZQeUZkZ3NDU2pyUFBnZVhpZm1zWkRlVnpWdytiam9BQ3FiVEhjWUdZT0VZVWtTZlllNEc0M1lQL3dlMHdyNGVoSlVvOW5qNHRhUDhJTjhubm44WlR2dWFpOExIVmdSWit1WFpJN3FKdk9Ub2oxczhxQmw3S1daVEhSdVZDdkFDdlpwN1Y2bXp6QnhLZFF3QjhBNS9tbnAzam5WaGpGa0RUemN6QkRhUHF5aHVyTjNwWVZkR2hWNjJUVHJFQ2YwNHUwdXZKWS84NHY1NnhIc25ET3cwUHRLWjNLYlJkYVFUNHJJM1lmUzdGbEk0amsvdUljRzVPaWpLeHplSzh2OVVDRG43dEFvREFUa1NRdmk5VG45Y0pmVWRMTVlxS1hOKzNDTEtPc0pIOTc0Yis3OFpoRnBsRDZ2Kys2UEh6WmFlZWxQa1AvOWE5dllVYkRQWDNSeDNieFZmY1ZnTzNNdm0vMW1CL0Q3MXEzM3pFVXJlUzBxQlF6THFXTFNPbUFjYTl1WEFzVE1ydlVCamwySXI1TlhjTHZwbTVrVU11M0IzU2F6SmlEVDIxMmZQdTAzeFQzUElZbEdNWHljMTBLbUZCeVhldktBY1JUc1M4b3Y3SUhuY3R4bzZBczIvVXNpUVUwUzJTYTJmeDB3N1Q5K3lMT1BrVVB0cGxDTEhqcVRxM1pEc21wUnBEVjNYWUZNVmdCTE02THFLakRlcDN1SkIzUTFQWDFwZXdIM0hrbFd5VjdXeUlXc29ENmhGQUQzdFJpbnJJT1oyR1l2VStGZlRmWnVKMkJsNlVHcEpWM3ZZYTRNblZ0UWxweU5Lc09vQ2Z4QU80eGkwY1dvdGxtSnB5T3o3UGxFK21La1lDS0RsN2kzZzFuYnRrMnZDNmwxWjdySmFQa1lMQnNqVzRXdWlmT3M5aC9EeXJtYWVTazJCR2NUMXc4dmdpRzl6c0hzUkVwY083SUhGbVB0VmlOZHd6UlNkKzB1ZnVicDFnNDJ2Ny8vV2dOVS9zd1QreGVVazVFdy9kbm5uVmpXRjdpaEtHK0xqQTZaRzVXdkNZcWV5VDFLZmZDSkMyYXdTTDhtZ0hUcGFCSGZPNkRiQStabmgrNW1OaUhOZEQ0S2RDcVVOdWFDVzNPTDNmdGovZmd5WExtNEpRbDg4czNpeFBrSFdETFFra3ZOSEpmNFpNRDBaZ3p3MEdoWjlkRUVsY3JHclRTcTJLY1gwd21BQzJiTzQxRkVmdklpNFlHVE1pNDRxMVVIN3NLaU9hbTRiRHJuV2Y5citlY1VodXRDaXZxQVk1Zys5T1B0M1ZMZ2RFSHY5SHlVazJQallCQVFKQStuT1NIbkJHeExKUVhEMHVYWW9YM1JyOGM0d0hDeXlDNXBuUnRXWDZmTGlhWHJydG02bWk0Qnh6VUcwRkdCV05qdmFDdmJnT3hGTjdpOHhzb2RlOEZFbERVRllYcDhNRzN5Rmx5RHA3RkhFWnNXd1VYNThFNlpqK0hqQlVFVGNZUVhValgzZ0JDMlJ4alpjejgwWnNTYUZFSWhzcGhJQXpSRXIvSmNmUmdwSkg3YjBGdmJHL0VJUUJHTXdTWjFMSzdaTTBvWEpZK1c2QkdTaEt0cDhlT3I2RWo4ZnhjeFFvRHhJRDFIL2pnS3d6bHRLcTZ2MWZwWVNiQ2pkK2xFdXozT0JSTGErYlZyOHJ6U1JlekoxWGJRQ3ZDRGsvS2VRUzJhdTNGT29HSWhhWVR6RGVjOEd2RXgveGUxZ1FqdXVtQ0tzZkRPQWRUazJSTkN3N3p2c1BHaytJTXRlTVVOSnlhK2JFSzhqMXprYi9FbHhjNnNtd0FTY290WjJtZUtlL2xoaDBUd1g4blZCSGpUUC81Z1BGR2w4ZmwzNmFKMURxUmlqOU82aXYyaDlkY2tUMXhiQ1dZL3BXOGdGNy9UeU9Rb0RqZS9KTFpnOTd4aFowUHhvUXlXSG9wWGxOYUtpdmZEWEY1c1Y3MmxjT3phelNQMzJtU2VsSnVoNGtFUzMzMmRwalFMc2lUMlRHSzd0c04zcjc3Uk0yaUtwQTNIRS9YeWhqeUZxa3dJNGM2RTd0Q1FtWTlDUFhlTldaeUxXWGRMLzRGZ3ZzN2VsbVFqaFZ2aHgrOUs1Q0x2NU8vRFlUZUIzMUw5b0YwekxWNk4vSyt2ekNPRmFGVDZ0STZCK2ljWFV0Y2N1S0djZ2dJREY0Ti96K0FLQ05LMW0yVFgwUDBZN1pQRVNkVnp2U29ZUXFZYkNSeCt5R1RBQ0VIZDZjNzJ5M1VsZ2VjaWZzY2RKOTgzRitRREZqZVpCUmY4QzROaFd4QUx5cndxQWcxdEtKUFg4UlhFcFFmSkRIb0dOZElGRnZlWmhrdS80aFlaRjZCMkdKOGwvZmdJdFFEY3dTbnFjdExOQmx3VkowUDVjZ2JVM1NDQVlRRi8rbGRCME5KaUo4Z3pZT1FDcDJOOGtKRUVsNGNjNWdxMjh2NUJ0bVVHaEE3R2lsUzRaU0hzNnRKSXVxZWdvOFpXS2lyNEtJS1pnNVFpaVRmSy9zTUU0dS9aYWRLSm5KNW1oQ1RFK003SEcrVHlybW42cmtnc0hIbjZzVGM0cHgyM3h4SUgzUElNNVlDUXdLMER4b3BhZmxYRFhVQndZWVVyN21POHp2K05Mekkzby9ua1F6d1gyK1dwM2NiOXFFUy84TjdxNmgzNjUzeThqZ0tZaSsxVHYrT0RMVEZheDlRa0FHQUtOc2JlVzlkT1dDR292RFNsY0Eraml6Qm1PK2FnMkR5MUFvWkVJL2lveTlyZm1OV2U3ck5DdlIvdWxWTnN5ZUM3Vk8wWTNvNmc3SkN0dGpjNStCZWg0L0hDVGcvQ242bDEvL0xodTFqVmVjU0NaUU9UdjV3TFFLcUlURjQvTnRuZUd5dFdrVXNyb0p3TXUxUGpDRkJDcDBJOTM4dEJrOG1DcHpmNTdpM0M1MEkrdklFRk5ETnEzem1vU2tjTWFlOFU2S3ZwMS9lN3A0d3R3andIQXZ5N1B3ZXd0WXpuZGhUNUN3cUVxMXVOL1Nya0RsWGFDQU1aR3Q5akhUek9OdmlYam94N2wwZnIvbDhISjd5L0dMbm83d2RFMDBhMEx4emx4TkdOZGNLdXFyRGpuZEUzUW1MVVhyelRJQ1NINysyUDVicWhvZVNqajQ0SEF1L1FrREhwQnFRbDBrOTJEaUt5NFVTYlVMbnJBbFJuOGl4MHU5dUFaTCthTXdyRVJ5RnRJb2V0TkpNcldwa0VIRUxnMlY3NDRxUjNRWThRbmp2TDlqbUc1SjFyQVhOQ2sycWJGSTRTTFlPK2JMSUFaYXhJY1hyUDVHTWYvb2pZVVpFbkJpbnkyRStKV1I5VEZSamgrOHl4WGs5L1B3SldJM1JESkZkVjZyWThyc0ttMU0yKzAxcU53akFZS2tXVGR5QmVnM0psdmI4NEJvcU93UFBvSkhYS0IwZkpzWTgyUi9Gb1ZRSzI2YUw2YkE1eGNBdmZWc3p3U1lzKzR5VEpBMnJvQT09PC94ZW5jOkNpcGhlclZhbHVlPgogICA8L3hlbmM6Q2lwaGVyRGF0YT4KPC94ZW5jOkVuY3J5cHRlZERhdGE+PC9zYW1sOkVuY3J5cHRlZEFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
|
@@ -0,0 +1 @@
|
|
1
|
+
pVdbk6LIEn4/EfsfDOfRsCkughrdvacEtVFRadBWX05AUQjKTQoF/fVb4mW6e3pmZ/c8aWZlfvllZl2Sxz+LMKgccEr8OHqqsg+g+ufzH/95JFYYJO1XTJI4IrhCjSLSPiufqvs0ascW8Uk7skJM2hlqG1AbtbkH0LYIwWlGoarvXJJf+yRpnMUoDqoVVXmqJm5hoyYnWq5Txy0X1x3etetOUxLqSJAAQDx2AWpWK/MbZ4pBXQnZYzUimRVlVAVYoQ7EOhBMwLU5rg24VbWiYJL5kZWVXl6WJW2GsZLkIdw71kMUM4TEDKLp7kNMAaNb8mb8VP2fiwSLF2xQlzClA1gO15Fl4brU5Gxks7xt883qc1m2dsklfT5HINcQcYSDeO1HDygOmbMR98i8t310SNvw15TcPr1V2yF3lnmeP+T8Q5yuGQ4AwIAWQ20c4q+/VWm3KpWbP3bUyI1LONmK4shHVuCfypQ1nHmxU4HBOk79zAt/As4yLDiD13GB6ogVom9VpgzxPUhJ8jfhPnBNiVUnnsVeEc94r9jFKY4Qrsxe1afqt99tf5mimVoRceM0JB/Ff8YKRwfanAQ7dXJLjhL8Z4BfVu2R+ZGj4q/pLvw3xbsW7jvI3Ar2+Fk9CSs9IDwcrBSbJ80Dmi0Y/hXVevpTSeC9cam4l/wifto29wZfPHRvOAjj6Eim7C6MLMmo4S47Qlw6hJuNzE36qT5ZG5tpYsZoH+qit3zZnkxjlUoZozJDc2IcXmR9p7CpJwQG2ChTprt/GUM527aWUhBws+N6L3Gjmt3LdiuupUStdLQ1nH7q11TpxStGvCg5LjQMT6yRo/SGD11nq7k5VnQOLughEjud/vokmd2nezrv+NOrjCqH+HhPcNEALcXKrLsgn28sl56UDD9rqir3T7IMl7os6/J4jPRFHPLcXIHjznq787Z+v5WDDtRnPah0DE0nuawvlbmu97v5YD47dUca3PYhO+vKHU02ZkLRM6HZWY/nHYhMpRvsV/3As8NebnNFgk7dVw02L/aepppcIzD7rch6EwpFgcOLHzEhmO9XJ4h7OSjGCgTaRismil5oSs8qdeZHndZTC/kEBxf/pQmDufmOq0q5DsxNd651tDJ2p9A0462xtd56++Wic7D7wUl77eZKXtoPu7k3tcO5ZnNOYm+6mgbjC2ca7fWtEWlq39Ug6MvGrm+oNq/o3XONIBT6Y6jIHV+nqehKLIY1fXVY6kwxAt1gtN4nQ1ZX0HIkuI0p2uZkvDZayewwl9yTuZNftk3TGxXmVuA0ndPQgEzwbDCfSlxz6B3DTU/eFeupMN/nr9tWmsAA1ELvKGpT57g5QrHPCptUeTPExpGMkDcUMuaQJ13lBAw96MZ9dqudgsAIpdPK4BcpXkNJ2Yyt2VLf5aoCddj5nFPnklMHjjTu0Jd9nTmENUs8CKA2X3AnZ/cCOZ1hDz3Wt/XToCFoA3kyfzuQ2kFfuKue54RgMtU4NVVmUrw7DKe7qVhMIB4MxRdwnEqasBzxrpUZB3UZhu5xIbe2GW8cTsz4mL6cpt4sGoAjZsjMWBTRSS9yFDKtaQ53qW5BXk/07UtDOto9ZhKnxymucV55LD5v9LvychSY94fkwzF6vo4CRkZF8lGSYwdXynP26xeelNZtY48QJqS8GX8EbcPb7HB9/4qfvX8ss9BGBvJwaFXvtv7fG9f9cj5A+Ifh4TJ3tBqsaAOXrwsNXqyzwBXrSHStestxGhyPWaHVRL81afwfc0ApGHt7g1F2lca0nqpS6dE3xMp+Xmj2gS01vlN3S9M2TdkPoOOk56I/Z/QV+O/7+NfIF/iPkeU4cv0zxrkbl8fq1x1GYdvGVorT6s+BzjutMo6zSTRJoZvh9Fw9HryrnnSZ014x8hMfn8ub3v7eN81XyJ+WrhSoheOfl8k5agfTquCyYez3kOw15G/Quu3SveOfH086HWapjy7hP6zcG07iENOCP+DCCpMAlz23rkZXxnefT/IHdOZTOvd4mRedTxAOaYEqpfg3E7BBdwJF+Em2jS9M1cjBBZ1/WdFtSMC1EeDZBgCSBfhGw3GxJdo8EkRUfU+KUs1wkX2hkgP6hUAHkOdffkSgNjrbUfWU/uRx6kzpNwLtLHbKcSqJ0+xesC/Av1j7oLsX7a69XT33m+k2/D//BQ==
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
|
3
|
+
class RubySamlTest < Minitest::Test
|
4
|
+
|
5
|
+
describe "SamlMessage" do
|
6
|
+
|
7
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
8
|
+
let(:saml_message) { OneLogin::RubySaml::SamlMessage.new }
|
9
|
+
let(:response_document) { read_response("response_unsigned_xml_base64") }
|
10
|
+
let(:response_document_xml) { read_response("adfs_response_xmlns.xml") }
|
11
|
+
|
12
|
+
it "return decoded raw saml" do
|
13
|
+
decoded_raw = saml_message.send(:decode_raw_saml, logout_request_deflated_base64)
|
14
|
+
assert logout_request_document, decoded_raw
|
15
|
+
end
|
16
|
+
|
17
|
+
it "return encoded raw saml" do
|
18
|
+
settings.compress_request = true
|
19
|
+
encoded_raw = saml_message.send(:encode_raw_saml, logout_request_document, settings)
|
20
|
+
assert logout_request_deflated_base64, encoded_raw
|
21
|
+
|
22
|
+
settings.compress_request = false
|
23
|
+
deflated = saml_message.send(:deflate, logout_request_deflated_base64)
|
24
|
+
encoded_raw = saml_message.send(:encode_raw_saml, deflated, settings)
|
25
|
+
assert logout_request_deflated_base64, encoded_raw
|
26
|
+
end
|
27
|
+
|
28
|
+
it "return decoded string" do
|
29
|
+
decoded = saml_message.send(:decode, response_document)
|
30
|
+
assert response_document_xml, decoded
|
31
|
+
|
32
|
+
decoded = saml_message.send(:decode, logout_request_base64)
|
33
|
+
assert logout_request_document, decoded
|
34
|
+
end
|
35
|
+
|
36
|
+
it "return encoded string" do
|
37
|
+
encoded = saml_message.send(:encode, response_document_xml)
|
38
|
+
assert response_document, encoded
|
39
|
+
|
40
|
+
encoded = saml_message.send(:encode, logout_request_document)
|
41
|
+
assert logout_request_base64, encoded
|
42
|
+
end
|
43
|
+
|
44
|
+
it "return deflated string" do
|
45
|
+
deflated = saml_message.send(:deflate, logout_request_document)
|
46
|
+
encoded_deflated = saml_message.send(:encode, deflated)
|
47
|
+
assert logout_request_deflated_base64, encoded_deflated
|
48
|
+
end
|
49
|
+
|
50
|
+
it "return inflated string" do
|
51
|
+
decoded = saml_message.send(:decode, logout_request_deflated_base64)
|
52
|
+
decoded_inflated = saml_message.send(:inflate, decoded)
|
53
|
+
assert response_document_xml, decoded_inflated
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
|
3
|
+
require 'onelogin/ruby-saml/settings'
|
4
|
+
|
5
|
+
class SettingsTest < Minitest::Test
|
6
|
+
|
7
|
+
describe "Settings" do
|
8
|
+
before do
|
9
|
+
@settings = OneLogin::RubySaml::Settings.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should provide getters and settings" do
|
13
|
+
accessors = [
|
14
|
+
:idp_entity_id, :idp_sso_target_url, :idp_slo_target_url, :idp_cert, :idp_cert_fingerprint,
|
15
|
+
:issuer, :assertion_consumer_service_url, :assertion_consumer_service_binding,
|
16
|
+
:single_logout_service_url, :single_logout_service_binding,
|
17
|
+
:sp_name_qualifier, :name_identifier_format, :name_identifier_value,
|
18
|
+
:sessionindex, :attributes_index, :passive, :force_authn,
|
19
|
+
:compress_request, :double_quote_xml_attribute_values, :protocol_binding,
|
20
|
+
:security, :certificate, :private_key,
|
21
|
+
:authn_context, :authn_context_comparison, :authn_context_decl_ref,
|
22
|
+
:assertion_consumer_logout_service_url,
|
23
|
+
:assertion_consumer_logout_service_binding
|
24
|
+
]
|
25
|
+
|
26
|
+
accessors.each do |accessor|
|
27
|
+
value = Kernel.rand
|
28
|
+
@settings.send("#{accessor}=".to_sym, value)
|
29
|
+
assert_equal value, @settings.send(accessor)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
it "create settings from hash" do
|
35
|
+
config = {
|
36
|
+
:assertion_consumer_service_url => "http://app.muda.no/sso",
|
37
|
+
:issuer => "http://muda.no",
|
38
|
+
:sp_name_qualifier => "http://sso.muda.no",
|
39
|
+
:idp_sso_target_url => "http://sso.muda.no/sso",
|
40
|
+
:idp_slo_target_url => "http://sso.muda.no/slo",
|
41
|
+
:idp_cert_fingerprint => "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
|
42
|
+
:name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
43
|
+
:attributes_index => 30,
|
44
|
+
:passive => true,
|
45
|
+
:protocol_binding => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
46
|
+
}
|
47
|
+
@settings = OneLogin::RubySaml::Settings.new(config)
|
48
|
+
|
49
|
+
config.each do |k,v|
|
50
|
+
assert_equal v, @settings.send(k)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "configure attribute service attributes correctly" do
|
55
|
+
@settings = OneLogin::RubySaml::Settings.new
|
56
|
+
@settings.attribute_consuming_service.configure do
|
57
|
+
service_name "Test Service"
|
58
|
+
add_attribute :name => "Name", :name_format => "Name Format", :friendly_name => "Friendly Name"
|
59
|
+
end
|
60
|
+
|
61
|
+
assert_equal @settings.attribute_consuming_service.configured?, true
|
62
|
+
assert_equal @settings.attribute_consuming_service.name, "Test Service"
|
63
|
+
assert_equal @settings.attribute_consuming_service.attributes, [{:name => "Name", :name_format => "Name Format", :friendly_name => "Friendly Name" }]
|
64
|
+
end
|
65
|
+
|
66
|
+
it "does not modify default security settings" do
|
67
|
+
settings = OneLogin::RubySaml::Settings.new
|
68
|
+
settings.security[:authn_requests_signed] = true
|
69
|
+
settings.security[:embed_sign] = true
|
70
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA256
|
71
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
72
|
+
|
73
|
+
new_settings = OneLogin::RubySaml::Settings.new
|
74
|
+
assert_equal new_settings.security[:authn_requests_signed], false
|
75
|
+
assert_equal new_settings.security[:embed_sign], false
|
76
|
+
assert_equal new_settings.security[:digest_method], XMLSecurity::Document::SHA1
|
77
|
+
assert_equal new_settings.security[:signature_method], XMLSecurity::Document::RSA_SHA1
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#single_logout_service_url" do
|
81
|
+
it "when single_logout_service_url is nil but assertion_consumer_logout_service_url returns its value" do
|
82
|
+
settings.single_logout_service_url = nil
|
83
|
+
settings.assertion_consumer_logout_service_url = "http://app.muda.no/sls"
|
84
|
+
|
85
|
+
assert_equal "http://app.muda.no/sls", settings.single_logout_service_url
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "#single_logout_service_binding" do
|
90
|
+
it "when single_logout_service_binding is nil but assertion_consumer_logout_service_binding returns its value" do
|
91
|
+
settings.single_logout_service_binding = nil
|
92
|
+
settings.assertion_consumer_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
93
|
+
|
94
|
+
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", settings.single_logout_service_binding
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#get_idp_cert" do
|
99
|
+
it "returns nil when the cert is an empty string" do
|
100
|
+
@settings = OneLogin::RubySaml::Settings.new
|
101
|
+
@settings.idp_cert = ""
|
102
|
+
assert_equal nil, @settings.get_idp_cert
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns nil when the cert is nil" do
|
106
|
+
@settings = OneLogin::RubySaml::Settings.new
|
107
|
+
@settings.idp_cert = nil
|
108
|
+
assert_equal nil, @settings.get_idp_cert
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns the certificate when it is valid" do
|
112
|
+
@settings = OneLogin::RubySaml::Settings.new
|
113
|
+
@settings.idp_cert = ruby_saml_cert_text
|
114
|
+
assert @settings.get_idp_cert.kind_of? OpenSSL::X509::Certificate
|
115
|
+
end
|
116
|
+
|
117
|
+
it "raises when the certificate is not valid" do
|
118
|
+
# formatted but invalid cert
|
119
|
+
@settings.idp_cert = read_certificate("formatted_certificate")
|
120
|
+
assert_raises(OpenSSL::X509::CertificateError) {
|
121
|
+
@settings.get_idp_cert
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "#get_sp_cert" do
|
127
|
+
it "returns nil when the cert is an empty string" do
|
128
|
+
@settings = OneLogin::RubySaml::Settings.new
|
129
|
+
@settings.certificate = ""
|
130
|
+
assert_equal nil, @settings.get_sp_cert
|
131
|
+
end
|
132
|
+
|
133
|
+
it "returns nil when the cert is nil" do
|
134
|
+
@settings = OneLogin::RubySaml::Settings.new
|
135
|
+
@settings.certificate = nil
|
136
|
+
assert_equal nil, @settings.get_sp_cert
|
137
|
+
end
|
138
|
+
|
139
|
+
it "returns the certificate when it is valid" do
|
140
|
+
@settings = OneLogin::RubySaml::Settings.new
|
141
|
+
@settings.certificate = ruby_saml_cert_text
|
142
|
+
assert @settings.get_sp_cert.kind_of? OpenSSL::X509::Certificate
|
143
|
+
end
|
144
|
+
|
145
|
+
it "raises when the certificate is not valid" do
|
146
|
+
# formatted but invalid cert
|
147
|
+
@settings.certificate = read_certificate("formatted_certificate")
|
148
|
+
assert_raises(OpenSSL::X509::CertificateError) {
|
149
|
+
@settings.get_sp_cert
|
150
|
+
}
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#get_sp_key" do
|
156
|
+
it "returns nil when the private key is an empty string" do
|
157
|
+
@settings = OneLogin::RubySaml::Settings.new
|
158
|
+
@settings.private_key = ""
|
159
|
+
assert_equal nil, @settings.get_sp_key
|
160
|
+
end
|
161
|
+
|
162
|
+
it "returns nil when the private key is nil" do
|
163
|
+
@settings = OneLogin::RubySaml::Settings.new
|
164
|
+
@settings.private_key = nil
|
165
|
+
assert_equal nil, @settings.get_sp_key
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns the private key when it is valid" do
|
169
|
+
@settings = OneLogin::RubySaml::Settings.new
|
170
|
+
@settings.private_key = ruby_saml_key_text
|
171
|
+
assert @settings.get_sp_key.kind_of? OpenSSL::PKey::RSA
|
172
|
+
end
|
173
|
+
|
174
|
+
it "raises when the private key is not valid" do
|
175
|
+
# formatted but invalid rsa private key
|
176
|
+
@settings.private_key = read_certificate("formatted_rsa_private_key")
|
177
|
+
assert_raises(OpenSSL::PKey::RSAError) {
|
178
|
+
@settings.get_sp_key
|
179
|
+
}
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#get_fingerprint" do
|
185
|
+
it "get the fingerprint value when cert and fingerprint in settings are nil" do
|
186
|
+
@settings = OneLogin::RubySaml::Settings.new
|
187
|
+
@settings.idp_cert_fingerprint = nil
|
188
|
+
@settings.idp_cert = nil
|
189
|
+
fingerprint = @settings.get_fingerprint
|
190
|
+
assert_nil fingerprint
|
191
|
+
end
|
192
|
+
|
193
|
+
it "get the fingerprint value when there is a cert at the settings" do
|
194
|
+
@settings = OneLogin::RubySaml::Settings.new
|
195
|
+
@settings.idp_cert_fingerprint = nil
|
196
|
+
@settings.idp_cert = ruby_saml_cert_text
|
197
|
+
fingerprint = @settings.get_fingerprint
|
198
|
+
assert fingerprint.downcase == ruby_saml_cert_fingerprint.downcase
|
199
|
+
end
|
200
|
+
|
201
|
+
it "get the fingerprint value when there is a fingerprint at the settings" do
|
202
|
+
@settings = OneLogin::RubySaml::Settings.new
|
203
|
+
@settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
204
|
+
@settings.idp_cert = nil
|
205
|
+
fingerprint = @settings.get_fingerprint
|
206
|
+
assert fingerprint.downcase == ruby_saml_cert_fingerprint.downcase
|
207
|
+
end
|
208
|
+
|
209
|
+
it "get the fingerprint value when there are cert and fingerprint at the settings" do
|
210
|
+
@settings = OneLogin::RubySaml::Settings.new
|
211
|
+
@settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
212
|
+
@settings.idp_cert = ruby_saml_cert_text
|
213
|
+
fingerprint = @settings.get_fingerprint
|
214
|
+
assert fingerprint.downcase == ruby_saml_cert_fingerprint.downcase
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,275 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
require 'logout_responses/logoutresponse_fixtures'
|
3
|
+
|
4
|
+
require 'onelogin/ruby-saml/slo_logoutrequest'
|
5
|
+
require 'timecop'
|
6
|
+
|
7
|
+
class RubySamlTest < Minitest::Test
|
8
|
+
|
9
|
+
describe "SloLogoutrequest" do
|
10
|
+
|
11
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
12
|
+
let(:logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) }
|
13
|
+
let(:invalid_logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(invalid_logout_request_document) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/SOMEACCOUNT'
|
17
|
+
settings.soft = true
|
18
|
+
logout_request.settings = settings
|
19
|
+
invalid_logout_request.settings = settings
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "initiator" do
|
23
|
+
it "raise an exception when logout request is initialized with nil" do
|
24
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::SloLogoutrequest.new(nil) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#is_valid?" do
|
29
|
+
it "return false when logout request is initialized with blank data" do
|
30
|
+
logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
|
31
|
+
assert !logout_request_blank.is_valid?
|
32
|
+
assert_includes logout_request_blank.errors, 'Blank logout request'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "return true when the logout request is initialized with valid data" do
|
36
|
+
assert logout_request.is_valid?
|
37
|
+
assert_empty logout_request.errors
|
38
|
+
assert_equal 'someone@example.org', logout_request.nameid
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be idempotent when the logout request is initialized with invalid data" do
|
42
|
+
assert !invalid_logout_request.is_valid?
|
43
|
+
assert_equal ['Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'], invalid_logout_request.errors
|
44
|
+
assert !invalid_logout_request.is_valid?
|
45
|
+
assert_equal ['Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'], invalid_logout_request.errors
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should be idempotent when the logout request is initialized with valid data" do
|
49
|
+
assert logout_request.is_valid?
|
50
|
+
assert_empty logout_request.errors
|
51
|
+
assert logout_request.is_valid?
|
52
|
+
assert_empty logout_request.errors
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raise error for invalid xml" do
|
56
|
+
invalid_logout_request.soft = false
|
57
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { invalid_logout_request.is_valid? }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#nameid" do
|
62
|
+
it "extract the value of the name id element" do
|
63
|
+
assert_equal "someone@example.org", logout_request.nameid
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#issuer" do
|
68
|
+
it "return the issuer inside the logout request" do
|
69
|
+
assert_equal "https://app.onelogin.com/saml/metadata/SOMEACCOUNT", logout_request.issuer
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#id" do
|
74
|
+
it "extract the value of the ID attribute" do
|
75
|
+
assert_equal "_c0348950-935b-0131-1060-782bcb56fcaa", logout_request.id
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#not_on_or_after" do
|
80
|
+
it "extract the value of the NotOnOrAfter attribute" do
|
81
|
+
time_value = '2014-07-17T01:01:48Z'
|
82
|
+
assert_equal nil, logout_request.not_on_or_after
|
83
|
+
logout_request.document.root.attributes['NotOnOrAfter'] = time_value
|
84
|
+
assert_equal Time.parse(time_value), logout_request.not_on_or_after
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#session_indexes' do
|
89
|
+
it "return empty array when no SessionIndex" do
|
90
|
+
assert_equal [], logout_request.session_indexes
|
91
|
+
end
|
92
|
+
|
93
|
+
it "return an Array with one SessionIndex" do
|
94
|
+
logout_request_with_session_index = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_xml_with_session_index)
|
95
|
+
assert_equal ['_ea853497-c58a-408a-bc23-c849752d9741'], logout_request_with_session_index.session_indexes
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#validate_id" do
|
100
|
+
it "return true when there is a valid ID in the logout request" do
|
101
|
+
assert logout_request.send(:validate_id)
|
102
|
+
assert_empty logout_request.errors
|
103
|
+
end
|
104
|
+
|
105
|
+
it "return false when there is an invalid ID in the logout request" do
|
106
|
+
logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
|
107
|
+
assert !logout_request_blank.send(:validate_id)
|
108
|
+
assert_includes logout_request_blank.errors, "Missing ID attribute on Logout Request"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#validate_version" do
|
113
|
+
it "return true when the logout request is SAML 2.0 Version" do
|
114
|
+
assert logout_request.send(:validate_version)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "return false when the logout request is not SAML 2.0 Version" do
|
118
|
+
logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
|
119
|
+
assert !logout_request_blank.send(:validate_version)
|
120
|
+
assert_includes logout_request_blank.errors, "Unsupported SAML version"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#validate_not_on_or_after" do
|
125
|
+
it "return true when the logout request has a valid NotOnOrAfter or does not contain any" do
|
126
|
+
assert logout_request.send(:validate_not_on_or_after)
|
127
|
+
assert_empty logout_request.errors
|
128
|
+
Timecop.freeze Time.parse('2011-06-14T18:25:01.516Z') do
|
129
|
+
time_value = '2014-07-17T01:01:48Z'
|
130
|
+
logout_request.document.root.attributes['NotOnOrAfter'] = time_value
|
131
|
+
assert logout_request.send(:validate_not_on_or_after)
|
132
|
+
assert_empty logout_request.errors
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it "return false when the logout request has an invalid NotOnOrAfter" do
|
137
|
+
logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z'
|
138
|
+
assert !logout_request.send(:validate_not_on_or_after)
|
139
|
+
assert /Current time is on or after NotOnOrAfter/.match(logout_request.errors[0])
|
140
|
+
end
|
141
|
+
|
142
|
+
it "raise when the logout request has an invalid NotOnOrAfter" do
|
143
|
+
logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z'
|
144
|
+
logout_request.soft = false
|
145
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "Current time is on or after NotOnOrAfter") do
|
146
|
+
logout_request.send(:validate_not_on_or_after)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#validate_request_state" do
|
152
|
+
it "return true when valid logout request xml" do
|
153
|
+
assert logout_request.send(:validate_request_state)
|
154
|
+
assert_empty logout_request.errors
|
155
|
+
assert logout_request.send(:validate_request_state)
|
156
|
+
assert_empty logout_request.errors
|
157
|
+
end
|
158
|
+
|
159
|
+
it "return false when invalid logout request xml" do
|
160
|
+
logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
|
161
|
+
logout_request_blank.soft = true
|
162
|
+
assert !logout_request_blank.send(:validate_request_state)
|
163
|
+
assert_includes logout_request_blank.errors, "Blank logout request"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raise error for invalid xml" do
|
167
|
+
logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
|
168
|
+
logout_request_blank.soft = false
|
169
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "Blank logout request") do
|
170
|
+
logout_request_blank.send(:validate_request_state)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#validate_structure" do
|
176
|
+
it "return true when encountering a valid Logout Request xml" do
|
177
|
+
assert logout_request.send(:validate_structure)
|
178
|
+
assert_empty logout_request.errors
|
179
|
+
end
|
180
|
+
|
181
|
+
it "return false when encountering a Logout Request bad formatted" do
|
182
|
+
assert !invalid_logout_request.send(:validate_structure)
|
183
|
+
assert_includes invalid_logout_request.errors, "Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd"
|
184
|
+
end
|
185
|
+
|
186
|
+
it "raise when encountering a Logout Request bad formatted" do
|
187
|
+
invalid_logout_request.soft = false
|
188
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "Element '{urn:oasis:names:tc:SAML:2.0:assertion}Issuer': This element is not expected") do
|
189
|
+
invalid_logout_request.send(:validate_structure)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
describe "#validate_issuer" do
|
195
|
+
it "return true when the issuer of the Logout Request matchs the IdP entityId" do
|
196
|
+
logout_request.settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/SOMEACCOUNT'
|
197
|
+
assert logout_request.send(:validate_issuer)
|
198
|
+
end
|
199
|
+
it "return false when the issuer of the Logout Request does not match the IdP entityId" do
|
200
|
+
logout_request.settings.idp_entity_id = 'http://idp.example.com/invalid'
|
201
|
+
assert !logout_request.send(:validate_issuer)
|
202
|
+
assert_includes logout_request.errors, "Doesn't match the issuer, expected: <#{logout_request.settings.idp_entity_id}>, but was: <https://app.onelogin.com/saml/metadata/SOMEACCOUNT>"
|
203
|
+
end
|
204
|
+
it "raise when the issuer of the Logout Request does not match the IdP entityId" do
|
205
|
+
logout_request.settings.idp_entity_id = 'http://idp.example.com/invalid'
|
206
|
+
logout_request.soft = false
|
207
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "Doesn't match the issuer, expected: <#{logout_request.settings.idp_entity_id}>, but was: <https://app.onelogin.com/saml/metadata/SOMEACCOUNT>") do
|
208
|
+
logout_request.send(:validate_issuer)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "#validate_signature" do
|
214
|
+
before do
|
215
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
216
|
+
settings.security[:logout_requests_signed] = true
|
217
|
+
settings.security[:embed_sign] = false
|
218
|
+
settings.certificate = ruby_saml_cert_text
|
219
|
+
settings.private_key = ruby_saml_key_text
|
220
|
+
settings.idp_cert = ruby_saml_cert_text
|
221
|
+
end
|
222
|
+
|
223
|
+
it "return true when valid RSA_SHA1 Signature" do
|
224
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
225
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
226
|
+
params['RelayState'] = params[:RelayState]
|
227
|
+
options = {}
|
228
|
+
options[:get_params] = params
|
229
|
+
logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
|
230
|
+
logout_request_sign_test.settings = settings
|
231
|
+
assert logout_request_sign_test.send(:validate_signature)
|
232
|
+
end
|
233
|
+
|
234
|
+
it "return true when valid RSA_SHA256 Signature" do
|
235
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
236
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
237
|
+
options = {}
|
238
|
+
options[:get_params] = params
|
239
|
+
logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
|
240
|
+
params['RelayState'] = params[:RelayState]
|
241
|
+
logout_request_sign_test.settings = settings
|
242
|
+
assert logout_request_sign_test.send(:validate_signature)
|
243
|
+
end
|
244
|
+
|
245
|
+
it "return false when invalid RSA_SHA1 Signature" do
|
246
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
247
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
248
|
+
params['RelayState'] = 'http://invalid.exampcle.com'
|
249
|
+
params[:RelayState] = params['RelayState']
|
250
|
+
options = {}
|
251
|
+
options[:get_params] = params
|
252
|
+
|
253
|
+
logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
|
254
|
+
logout_request_sign_test.settings = settings
|
255
|
+
assert !logout_request_sign_test.send(:validate_signature)
|
256
|
+
end
|
257
|
+
|
258
|
+
it "raise when invalid RSA_SHA1 Signature" do
|
259
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
260
|
+
settings.soft = false
|
261
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
262
|
+
params['RelayState'] = 'http://invalid.exampcle.com'
|
263
|
+
params[:RelayState] = params['RelayState']
|
264
|
+
options = {}
|
265
|
+
options[:get_params] = params
|
266
|
+
options[:settings] = settings
|
267
|
+
|
268
|
+
logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
|
269
|
+
assert_raises(OneLogin::RubySaml::ValidationError, "Invalid Signature on Logout Request") do
|
270
|
+
logout_request_sign_test.send(:validate_signature)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|