diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:04:21 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:04:21 +0000 |
commit | 8a754e0858d922e955e71b253c139e071ecec432 (patch) | |
tree | 527d16e74bfd1840c85efd675fdecad056c54107 /test/units/module_utils/urls | |
parent | Initial commit. (diff) | |
download | ansible-core-8a754e0858d922e955e71b253c139e071ecec432.tar.xz ansible-core-8a754e0858d922e955e71b253c139e071ecec432.zip |
Adding upstream version 2.14.3.upstream/2.14.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'test/units/module_utils/urls')
27 files changed, 1955 insertions, 0 deletions
diff --git a/test/units/module_utils/urls/__init__.py b/test/units/module_utils/urls/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/units/module_utils/urls/__init__.py diff --git a/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha256.pem b/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha256.pem new file mode 100644 index 0000000..fcc6f7a --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha256.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBjzCCATWgAwIBAgIQeNQTxkMgq4BF9tKogIGXUTAKBggqhkjOPQQ +DAjAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MDMxN1 +oXDTE4MDUzMDA4MjMxN1owFTETMBEGA1UEAwwKU0VSVkVSMjAxNjBZM +BMGByqGSM49AgEGCCqGSM49AwEHA0IABDAfXTLOaC3ElgErlgk2tBlM +wf9XmGlGBw4vBtMJap1hAqbsdxFm6rhK3QU8PFFpv8Z/AtRG7ba3UwQ +prkssClejZzBlMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBg +EFBQcDAgYIKwYBBQUHAwEwFQYDVR0RBA4wDIIKU0VSVkVSMjAxNjAdB +gNVHQ4EFgQUnFDE8824TYAiBeX4fghEEg33UgYwCgYIKoZIzj0EAwID +SAAwRQIhAK3rXA4/0i6nm/U7bi6y618Ci2Is8++M3tYIXnEsA7zSAiA +w2s6bJoI+D7Xaey0Hp0gkks9z55y976keIEI+n3qkzw== +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha512.pem b/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha512.pem new file mode 100644 index 0000000..1b45be5 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/ecdsa_sha512.pem @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBjjCCATWgAwIBAgIQHVj2AGEwd6pOOSbcf0skQDAKBggqhkjOPQQ +DBDAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA3NTUzOV +oXDTE4MDUzMDA4MTUzOVowFTETMBEGA1UEAwwKU0VSVkVSMjAxNjBZM +BMGByqGSM49AgEGCCqGSM49AwEHA0IABL8d9S++MFpfzeH8B3vG/PjA +AWg8tGJVgsMw9nR+OfC9ltbTUwhB+yPk3JPcfW/bqsyeUgq4//LhaSp +lOWFNaNqjZzBlMA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBg +EFBQcDAgYIKwYBBQUHAwEwFQYDVR0RBA4wDIIKU0VSVkVSMjAxNjAdB +gNVHQ4EFgQUKUkCgLlxoeai0EtQrZth1/BSc5kwCgYIKoZIzj0EAwQD +RwAwRAIgRrV7CLpDG7KueyFA3ZDced9dPOcv2Eydx/hgrfxYEcYCIBQ +D35JvzmqU05kSFV5eTvkhkaDObd7V55vokhm31+Li +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha256.pem b/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha256.pem new file mode 100644 index 0000000..fcbe01f --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha256.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZDCCAhugAwIBAgIUbo9YpgkeniC5jRmOgFYX3mcVJn4wPgYJKoZIhvcNAQEK +MDGgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogQC +AgDeMBMxETAPBgNVBAMMCE9NSSBSb290MB4XDTIwMDkwNDE4NTMyNloXDTIxMDkw +NDE4NTMyNlowGDEWMBQGA1UEAwwNREMwMS5vbWkudGVzdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANN++3POgcKcPILMdHWIEPiVENtKoJQ8iqKKeL+/ +j5oUULVuIn15H/RYMNFmStRIvj0dIL1JAq4W411wG2Tf/6niU2YSKPOAOtrVREef +gNvMZ06TYlC8UcGCLv4dBkU3q/FELV66lX9x6LcVwf2f8VWfDg4VNuwyg/eQUIgc +/yd5VV+1VXTf39QufVV+/hOtPptu+fBKOIuiuKm6FIqroqLri0Ysp6tWrSd7P6V4 +6zT2yd17981vaEv5Zek2t39PoLYzJb3rvqQmumgFBIUQ1eMPLFCXX8YYYC/9ByK3 +mdQaEnkD2eIOARLnojr2A228EgPpdM8phQkDzeWeYnhLiysCAwEAAaNJMEcwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGAYDVR0R +BBEwD4INREMwMS5vbWkudGVzdDA+BgkqhkiG9w0BAQowMaANMAsGCWCGSAFlAwQC +AaEaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiBAICAN4DggEBAA66cbtiysjq +sCaDiYyRWCS9af2DGxJ6NAyku2aX+xgmRQzUzFAN5TihcPau+zzpk2zQKHDVMhNx +ouhTkIe6mq9KegpUuesWwkJ5KEeuBT949tIru2wAtlSVDvDcau5T9pRI/RLlnsWg +0sWaUAh/ujL+VKk6AanC4MRV69llwJcAVxlS/tYjwC74Dr8tMT1TQcVDvywB85e9 +mA3uz8mGKfiMk2TKD6+6on0UwBMB8NbKSB5bcgp+CJ2ceeblmCOgOcOcV5PCGoFj +fgAppr7HjfNPYaIV5l59LfKo2Bj9kXPMqA6/D4gJ3hwoJdY/NOtuNyk8cxWMnWUe ++E2Mm6ZnB3Y= +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha512.pem b/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha512.pem new file mode 100644 index 0000000..add3109 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa-pss_sha512.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDZDCCAhugAwIBAgIUbo9YpgkeniC5jRmOgFYX3mcVJoEwPgYJKoZIhvcNAQEK +MDGgDTALBglghkgBZQMEAgOhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIDogQC +AgC+MBMxETAPBgNVBAMMCE9NSSBSb290MB4XDTIwMDkwNDE4NTMyN1oXDTIxMDkw +NDE4NTMyN1owGDEWMBQGA1UEAwwNREMwMS5vbWkudGVzdDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBANZMAyRDBn54RfveeVtikepqsyKVKooAc471snl5 +mEEeo6ZvlOrK1VGGmo/VlF4R9iW6f5iqxG792KXk+lDtx8sbapZWk/aQa+6I9wml +p17ocW4Otl7XyQ74UTQlxmrped0rgOk+I2Wu3IC7k64gmf/ZbL9mYN/+v8TlYYyO +l8DQbO61XWOJpWt7yf18OxZtPcHH0dkoTEyIxIQcv6FDFNvPjmJzubpDgsfnly7R +C0Rc2yPU702vmAfF0SGQbd6KoXUqlfy26C85vU0Fqom1Qo22ehKrfU50vZrXdaJ2 +gX14pm2kuubMjHtX/+bhNyWTxq4anCOl9/aoObZCM1D3+Y8CAwEAAaNJMEcwCQYD +VR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwGAYDVR0R +BBEwD4INREMwMS5vbWkudGVzdDA+BgkqhkiG9w0BAQowMaANMAsGCWCGSAFlAwQC +A6EaMBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgOiBAICAL4DggEBAHgTDTn8onIi +XFLZ3sWJ5xCBvXypqC37dKALvXxNSo75SQZpOioG4GSdW3zjJWCiudGs7BselkFv +sHK7+5sLKTl1RvxeUoyTxnPZZmVlD3yLq8JBPxu5NScvcRwAcgm3stkq0irRnh7M +4Clw6oSKCKI7Lc3gnbvR3QLSYHeZpUcQgVCad6O/Hi+vxFMJT8PVigG0YUoTW010 +pDpi5uh18RxCqRJnnEC7aDrVarxD9aAvqp1wqwWShfP4FZ9m57DH81RTGD2ZzGgP +MsZU5JHVYKkO7IKKIBKuLu+O+X2aZZ4OMlMNBt2DUIJGzEBYV41+3TST9bBPD8xt +AAIFCBcgUYY= +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_md5.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_md5.pem new file mode 100644 index 0000000..6671b73 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_md5.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQJzshhViMG5hLHIJHxa+TcTANBgkqhkiG9w0 +BAQQFADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MD +MxNloXDTE4MDUzMDA4MjMxNlowFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN9N5GAzI7uq +AVlI6vUqhY5+EZWCWWGRwR3FT2DEXE5++AiJxXO0i0ZfAkLu7UggtBe +QwVNkaPD27EYzVUhy1iDo37BrFcLNpfjsjj8wVjaSmQmqvLvrvEh/BT +C5SBgDrk2+hiMh9PrpJoB3QAMDinz5aW0rEXMKitPBBiADrczyYrliF +AlEU6pTlKEKDUAeP7dKOBlDbCYvBxKnR3ddVH74I5T2SmNBq5gzkbKP +nlCXdHLZSh74USu93rKDZQF8YzdTO5dcBreJDJsntyj1o49w9WCt6M7 ++pg6vKvE+tRbpCm7kXq5B9PDi42Nb6//MzNaMYf9V7v5MHapvVSv3+y +sCAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBTh4L2Clr9ber6yfY3JFS3wiECL4DANBgkqhkiG9w0BAQQ +FAAOCAQEA0JK/SL7SP9/nvqWp52vnsxVefTFehThle5DLzagmms/9gu +oSE2I9XkQIttFMprPosaIZWt7WP42uGcZmoZOzU8kFFYJMfg9Ovyca+ +gnG28jDUMF1E74KrC7uynJiQJ4vPy8ne7F3XJ592LsNJmK577l42gAW +u08p3TvEJFNHy2dBk/IwZp0HIPr9+JcPf7v0uL6lK930xHJHP56XLzN +YG8vCMpJFR7wVZp3rXkJQUy3GxyHPJPjS8S43I9j+PoyioWIMEotq2+ +q0IpXU/KeNFkdGV6VPCmzhykijExOMwO6doUzIUM8orv9jYLHXYC+i6 +IFKSb6runxF1MAik+GCSA== +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_sha.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_sha.pem new file mode 100644 index 0000000..2ed2b45 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_sha.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQUDHcKGevZohJV+TkIIYC1DANBgkqhkiG9w0 +BAQ0FADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MD +MxN1oXDTE4MDUzMDA4MjMxN1owFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKr9bo/XXvHt +D6Qnhb1wyLg9lDQxxe/enH49LQihtVTZMwGf2010h81QrRUe/bkHTvw +K22s2lqj3fUpGxtEbYFWLAHxv6IFnIKd+Zi1zaCPGfas9ekqCSj3vZQ +j7lCJVGUGuuqnSDvsed6g2Pz/g6mJUa+TzjxN+8wU5oj5YVUK+aing1 +zPSA2MDCfx3+YzjxVwNoGixOz6Yx9ijT4pUsAYQAf1o9R+6W1/IpGgu +oax714QILT9heqIowwlHzlUZc1UAYs0/JA4CbDZaw9hlJyzMqe/aE46 +efqPDOpO3vCpOSRcSyzh02WijPvEEaPejQRWg8RX93othZ615MT7dqp +ECAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBTgod3R6vejt6kOASAApA19xIG6kTANBgkqhkiG9w0BAQ0 +FAAOCAQEAVfz0okK2bh3OQE8cWNbJ5PjJRSAJEqVUvYaTlS0Nqkyuaj +gicP3hb/pF8FvaVaB6r7LqgBxyW5NNL1xwdNLt60M2zaULL6Fhm1vzM +sSMc2ynkyN4++ODwii674YcQAnkUh+ZGIx+CTdZBWJfVM9dZb7QjgBT +nVukeFwN2EOOBSpiQSBpcoeJEEAq9csDVRhEfcB8Wtz7TTItgOVsilY +dQY56ON5XszjCki6UA3GwdQbBEHjWF2WERqXWrojrSSNOYDvxM5mrEx +sG1npzUTsaIr9w8ty1beh/2aToCMREvpiPFOXnVV/ovHMU1lFQTNeQ0 +OI7elR0nJ0peai30eMpQQ==' +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_sha1.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_sha1.pem new file mode 100644 index 0000000..de21a67 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_sha1.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQJg/Mf5sR55xApJRK+kabbTANBgkqhkiG9w0 +BAQUFADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MD +MxNloXDTE4MDUzMDA4MjMxNlowFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPKwYikjbzL +Lo6JtS6cyytdMMjSrggDoTnRUKauC5/izoYJd+2YVR5YqnluBJZpoFp +hkCgFFohUOU7qUsI1SkuGnjI8RmWTrrDsSy62BrfX+AXkoPlXo6IpHz +HaEPxjHJdUACpn8QVWTPmdAhwTwQkeUutrm3EOVnKPX4bafNYeAyj7/ +AGEplgibuXT4/ehbzGKOkRN3ds/pZuf0xc4Q2+gtXn20tQIUt7t6iwh +nEWjIgopFL/hX/r5q5MpF6stc1XgIwJjEzqMp76w/HUQVqaYneU4qSG +f90ANK/TQ3aDbUNtMC/ULtIfHqHIW4POuBYXaWBsqalJL2VL3YYkKTU +sCAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBS1jgojcjPu9vqeP1uSKuiIonGwAjANBgkqhkiG9w0BAQU +FAAOCAQEAKjHL6k5Dv/Zb7dvbYEZyx0wVhjHkCTpT3xstI3+TjfAFsu +3zMmyFqFqzmr4pWZ/rHc3ObD4pEa24kP9hfB8nmr8oHMLebGmvkzh5h +0GYc4dIH7Ky1yfQN51hi7/X5iN7jnnBoCJTTlgeBVYDOEBXhfXi3cLT +u3d7nz2heyNq07gFP8iN7MfqdPZndVDYY82imLgsgar9w5d+fvnYM+k +XWItNNCUH18M26Obp4Es/Qogo/E70uqkMHost2D+tww/7woXi36X3w/ +D2yBDyrJMJKZLmDgfpNIeCimncTOzi2IhzqJiOY/4XPsVN/Xqv0/dzG +TDdI11kPLq4EiwxvPanCg== +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_sha256.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_sha256.pem new file mode 100644 index 0000000..fb17018 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_sha256.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQWkeAtqoFg6pNWF7xC4YXhTANBgkqhkiG9w0 +BAQsFADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUyNzA5MD +I0NFoXDTE4MDUyNzA5MjI0NFowFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALIPKM5uykFy +NmVoLyvPSXGk15ZDqjYi3AbUxVFwCkVImqhefLATit3PkTUYFtAT+TC +AwK2E4lOu1XHM+Tmp2KIOnq2oUR8qMEvfxYThEf1MHxkctFljFssZ9N +vASDD4lzw8r0Bhl+E5PhR22Eu1Wago5bvIldojkwG+WBxPQv3ZR546L +MUZNaBXC0RhuGj5w83lbVz75qM98wvv1ekfZYAP7lrVyHxqCTPDomEU +I45tQQZHCZl5nRx1fPCyyYfcfqvFlLWD4Q3PZAbnw6mi0MiWJbGYKME +1XGicjqyn/zM9XKA1t/JzChS2bxf6rsyA9I7ibdRHUxsm1JgKry2jfW +0CAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBQabLGWg1sn7AXPwYPyfE0ER921ZDANBgkqhkiG9w0BAQs +FAAOCAQEAnRohyl6ZmOsTWCtxOJx5A8yr//NweXKwWWmFQXRmCb4bMC +xhD4zqLDf5P6RotGV0I/SHvqz+pAtJuwmr+iyAF6WTzo3164LCfnQEu +psfrrfMkf3txgDwQkA0oPAw3HEwOnR+tzprw3Yg9x6UoZEhi4XqP9AX +R49jU92KrNXJcPlz5MbkzNo5t9nr2f8q39b5HBjaiBJxzdM1hxqsbfD +KirTYbkUgPlVOo/NDmopPPb8IX8ubj/XETZG2jixD0zahgcZ1vdr/iZ ++50WSXKN2TAKBO2fwoK+2/zIWrGRxJTARfQdF+fGKuj+AERIFNh88HW +xSDYjHQAaFMcfdUpa9GGQ== +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_sha384.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_sha384.pem new file mode 100644 index 0000000..c17f9ff --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_sha384.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQEmj1prSSQYRL2zYBEjsm5jANBgkqhkiG9w0 +BAQwFADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MD +MxN1oXDTE4MDUzMDA4MjMxN1owFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsK5NvHi4xO +081fRLMmPqKsKaHvXgPRykLA0SmKxpGJHfTAZzxojHVeVwOm87IvQj2 +JUh/yrRwSi5Oqrvqx29l2IC/qQt2xkAQsO51/EWkMQ5OSJsl1MN3NXW +eRTKVoUuJzBs8XLmeraxQcBPyyLhq+WpMl/Q4ZDn1FrUEZfxV0POXgU +dI3ApuQNRtJOb6iteBIoQyMlnof0RswBUnkiWCA/+/nzR0j33j47IfL +nkmU4RtqkBlO13f6+e1GZ4lEcQVI2yZq4Zgu5VVGAFU2lQZ3aEVMTu9 +8HEqD6heyNp2on5G/K/DCrGWYCBiASjnX3wiSz0BYv8f3HhCgIyVKhJ +8CAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBQS/SI61S2UE8xwSgHxbkCTpZXo4TANBgkqhkiG9w0BAQw +FAAOCAQEAMVV/WMXd9w4jtDfSrIsKaWKGtHtiMPpAJibXmSakBRwLOn +5ZGXL2bWI/Ac2J2Y7bSzs1im2ifwmEqwzzqnpVKShIkZmtij0LS0SEr +6Fw5IrK8tD6SH+lMMXUTvp4/lLQlgRCwOWxry/YhQSnuprx8IfSPvil +kwZ0Ysim4Aa+X5ojlhHpWB53edX+lFrmR1YWValBnQ5DvnDyFyLR6II +Ialp4vmkzI9e3/eOgSArksizAhpXpC9dxQBiHXdhredN0X+1BVzbgzV +hQBEwgnAIPa+B68oDILaV0V8hvxrP6jFM4IrKoGS1cq0B+Ns0zkG7ZA +2Q0W+3nVwSxIr6bd6hw7g== +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/cbt/rsa_sha512.pem b/test/units/module_utils/urls/fixtures/cbt/rsa_sha512.pem new file mode 100644 index 0000000..2ed2b45 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/cbt/rsa_sha512.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIQUDHcKGevZohJV+TkIIYC1DANBgkqhkiG9w0 +BAQ0FADAVMRMwEQYDVQQDDApTRVJWRVIyMDE2MB4XDTE3MDUzMDA4MD +MxN1oXDTE4MDUzMDA4MjMxN1owFTETMBEGA1UEAwwKU0VSVkVSMjAxN +jCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKr9bo/XXvHt +D6Qnhb1wyLg9lDQxxe/enH49LQihtVTZMwGf2010h81QrRUe/bkHTvw +K22s2lqj3fUpGxtEbYFWLAHxv6IFnIKd+Zi1zaCPGfas9ekqCSj3vZQ +j7lCJVGUGuuqnSDvsed6g2Pz/g6mJUa+TzjxN+8wU5oj5YVUK+aing1 +zPSA2MDCfx3+YzjxVwNoGixOz6Yx9ijT4pUsAYQAf1o9R+6W1/IpGgu +oax714QILT9heqIowwlHzlUZc1UAYs0/JA4CbDZaw9hlJyzMqe/aE46 +efqPDOpO3vCpOSRcSyzh02WijPvEEaPejQRWg8RX93othZ615MT7dqp +ECAwEAAaNnMGUwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGA +QUFBwMCBggrBgEFBQcDATAVBgNVHREEDjAMggpTRVJWRVIyMDE2MB0G +A1UdDgQWBBTgod3R6vejt6kOASAApA19xIG6kTANBgkqhkiG9w0BAQ0 +FAAOCAQEAVfz0okK2bh3OQE8cWNbJ5PjJRSAJEqVUvYaTlS0Nqkyuaj +gicP3hb/pF8FvaVaB6r7LqgBxyW5NNL1xwdNLt60M2zaULL6Fhm1vzM +sSMc2ynkyN4++ODwii674YcQAnkUh+ZGIx+CTdZBWJfVM9dZb7QjgBT +nVukeFwN2EOOBSpiQSBpcoeJEEAq9csDVRhEfcB8Wtz7TTItgOVsilY +dQY56ON5XszjCki6UA3GwdQbBEHjWF2WERqXWrojrSSNOYDvxM5mrEx +sG1npzUTsaIr9w8ty1beh/2aToCMREvpiPFOXnVV/ovHMU1lFQTNeQ0 +OI7elR0nJ0peai30eMpQQ==' +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/client.key b/test/units/module_utils/urls/fixtures/client.key new file mode 100644 index 0000000..0e90d95 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/client.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTyiVxrsSyZ+Qr +iMT6sFYCqQtkLqlIWfbpTg9B6fZc793uoMzLUGq3efiZUhhxI78dQ3gNPgs1sK3W +heFpk1n4IL8ll1MS1uJKk2vYqzZVhjgcvQpeV9gm7bt0ndPzGj5h4fh7proPntSy +eBvMKVoqTT7tEnapRKy3anbwRPgTt7B5jEvJkPazuIc+ooMsYOHWfvj4oVsev0N2 +SsP0o6cHcsRujFMhz/JTJ1STQxacaVuyKpXacX7Eu1MJgGt/jU/QKNREcV9LdneO +NgqY9tNv0h+9s7DfHYXm8U3POr+bdcW6Yy4791KGCaUNtiNqT1lvu/4yd4WRkXbF +Fm5hJUUpAgMBAAECggEBAJYOac1MSK0nEvENbJM6ERa9cwa+UM6kf176IbFP9XAP +u6zxXWjIR3RMBSmMkyjGbQhs30hypzqZPfH61aUZ8+rsOMKHnyKAAcFZBlZzqIGc +IXGrNwd1Mf8S/Xg4ww1BkOWFV6s0jCu5G3Z/xyI2Ql4qcOVD6bMwpzclRbQjCand +dvqyCdMD0sRDyeOIK5hBhUY60JnWbMCu6pBU+qPoRukbRieaeDLIN1clwEqIQV78 +LLnv4n9fuGozH0JdHHfyXFytCgIJvEspZUja/5R4orADhr3ZB010RLzYvs2ndE3B +4cF9RgxspJZeJ/P+PglViZuzj37pXy+7GAcJLR9ka4kCgYEA/l01XKwkCzMgXHW4 +UPgl1+on42BsN7T9r3S5tihOjHf4ZJWkgYzisLVX+Nc1oUI3HQfM9PDJZXMMNm7J +ZRvERcopU26wWqr6CFPblGv8oqXHqcpeta8i3xZKoPASsTW6ssuPCEajiLZbQ1rH +H/HP+OZIVLM/WCPgA2BckTU9JnsCgYEA1SbXllXnlwGqmjitmY1Z07rUxQ3ah/fB +iccbbg3E4onontYXIlI5zQms3u+qBdi0ZuwaDm5Y4BetOq0a3UyxAsugqVFnzTba +1w/sFb3fw9KeQ/il4CXkbq87nzJfDmEyqHGCCYXbijHBxnq99PkqwVpaAhHHEW0m +vWyMUvPRY6sCgYAbtUWR0cKfYbNdvwkT8OQWcBBmSWOgcdvMmBd+y0c7L/pj4pUn +85PiEe8CUVcrOM5OIEJoUC5wGacz6r+PfwXTYGE+EGmvhr5z18aslVLQ2OQ2D7Bf +dDOFP6VjgKNYoHS0802iZid8RfkNDj9wsGOqRlOMvnXhAQ9u7rlGrBj8LwKBgFfo +ph99nH8eE9N5LrfWoUZ+loQS258aInsFYB26lgnsYMEpgO8JxIb4x5BGffPdVUHh +fDmZbxQ1D5/UhvDgUVzayI8sYMg1KHpsOa0Z2zCzK8zSvu68EgNISCm3J5cRpUft +UHlG+K19KfMG6lMfdG+8KMUTuetI/iI/o3wOzLvzAoGAIrOh30rHt8wit7ELARyx +wPkp2ARYXrKfX3NES4c67zSAi+3dCjxRqywqTI0gLicyMlj8zEu9YE9Ix/rl8lRZ +nQ9LZmqv7QHzhLTUCPGgZYnemvBzo7r0eW8Oag52dbcJO6FBszfWrxskm/fX25Rb +WPxih2vdRy814dNPW25rgdw= +-----END PRIVATE KEY----- diff --git a/test/units/module_utils/urls/fixtures/client.pem b/test/units/module_utils/urls/fixtures/client.pem new file mode 100644 index 0000000..c8c7b82 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/client.pem @@ -0,0 +1,81 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4099 (0x1003) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=North Carolina, L=Durham, O=Ansible, CN=ansible.http.tests + Validity + Not Before: Mar 21 18:22:47 2018 GMT + Not After : Mar 18 18:22:47 2028 GMT + Subject: C=US, ST=North Carolina, O=Ansible, CN=client.ansible.http.tests + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:d3:ca:25:71:ae:c4:b2:67:e4:2b:88:c4:fa:b0: + 56:02:a9:0b:64:2e:a9:48:59:f6:e9:4e:0f:41:e9: + f6:5c:ef:dd:ee:a0:cc:cb:50:6a:b7:79:f8:99:52: + 18:71:23:bf:1d:43:78:0d:3e:0b:35:b0:ad:d6:85: + e1:69:93:59:f8:20:bf:25:97:53:12:d6:e2:4a:93: + 6b:d8:ab:36:55:86:38:1c:bd:0a:5e:57:d8:26:ed: + bb:74:9d:d3:f3:1a:3e:61:e1:f8:7b:a6:ba:0f:9e: + d4:b2:78:1b:cc:29:5a:2a:4d:3e:ed:12:76:a9:44: + ac:b7:6a:76:f0:44:f8:13:b7:b0:79:8c:4b:c9:90: + f6:b3:b8:87:3e:a2:83:2c:60:e1:d6:7e:f8:f8:a1: + 5b:1e:bf:43:76:4a:c3:f4:a3:a7:07:72:c4:6e:8c: + 53:21:cf:f2:53:27:54:93:43:16:9c:69:5b:b2:2a: + 95:da:71:7e:c4:bb:53:09:80:6b:7f:8d:4f:d0:28: + d4:44:71:5f:4b:76:77:8e:36:0a:98:f6:d3:6f:d2: + 1f:bd:b3:b0:df:1d:85:e6:f1:4d:cf:3a:bf:9b:75: + c5:ba:63:2e:3b:f7:52:86:09:a5:0d:b6:23:6a:4f: + 59:6f:bb:fe:32:77:85:91:91:76:c5:16:6e:61:25: + 45:29 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + AF:F3:E5:2A:EB:CF:C7:7E:A4:D6:49:92:F9:29:EE:6A:1B:68:AB:0F + X509v3 Authority Key Identifier: + keyid:13:2E:30:F0:04:EA:41:5F:B7:08:BD:34:31:D7:11:EA:56:A6:99:F0 + + Signature Algorithm: sha256WithRSAEncryption + 29:62:39:25:79:58:eb:a4:b3:0c:ea:aa:1d:2b:96:7c:6e:10: + ce:16:07:b7:70:7f:16:da:fd:20:e6:a2:d9:b4:88:e0:f9:84: + 87:f8:b0:0d:77:8b:ae:27:f5:ee:e6:4f:86:a1:2d:74:07:7c: + c7:5d:c2:bd:e4:70:e7:42:e4:14:ee:b9:b7:63:b8:8c:6d:21: + 61:56:0b:96:f6:15:ba:7a:ae:80:98:ac:57:99:79:3d:7a:a9: + d8:26:93:30:17:53:7c:2d:02:4b:64:49:25:65:e7:69:5a:08: + cf:84:94:8e:6a:42:a7:d1:4f:ba:39:4b:7c:11:67:31:f7:1b: + 2b:cd:79:c2:28:4d:d9:88:66:d6:7f:56:4c:4b:37:d1:3d:a8: + d9:4a:6b:45:1d:4d:a7:12:9f:29:77:6a:55:c1:b5:1d:0e:a5: + b9:4f:38:16:3c:7d:85:ae:ff:23:34:c7:2c:f6:14:0f:55:ef: + b8:00:89:f1:b2:8a:75:15:41:81:72:d0:43:a6:86:d1:06:e6: + ce:81:7e:5f:33:e6:f4:19:d6:70:00:ba:48:6e:05:fd:4c:3c: + c3:51:1b:bd:43:1a:24:c5:79:ea:7a:f0:85:a5:40:10:85:e9: + 23:09:09:80:38:9d:bc:81:5e:59:8c:5a:4d:58:56:b9:71:c2: + 78:cd:f3:b0 +-----BEGIN CERTIFICATE----- +MIIDuTCCAqGgAwIBAgICEAMwDQYJKoZIhvcNAQELBQAwZjELMAkGA1UEBhMCVVMx +FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMQ8wDQYDVQQHDAZEdXJoYW0xEDAOBgNV +BAoMB0Fuc2libGUxGzAZBgNVBAMMEmFuc2libGUuaHR0cC50ZXN0czAeFw0xODAz +MjExODIyNDdaFw0yODAzMTgxODIyNDdaMFwxCzAJBgNVBAYTAlVTMRcwFQYDVQQI +DA5Ob3J0aCBDYXJvbGluYTEQMA4GA1UECgwHQW5zaWJsZTEiMCAGA1UEAwwZY2xp +ZW50LmFuc2libGUuaHR0cC50ZXN0czCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBANPKJXGuxLJn5CuIxPqwVgKpC2QuqUhZ9ulOD0Hp9lzv3e6gzMtQard5 ++JlSGHEjvx1DeA0+CzWwrdaF4WmTWfggvyWXUxLW4kqTa9irNlWGOBy9Cl5X2Cbt +u3Sd0/MaPmHh+Humug+e1LJ4G8wpWipNPu0SdqlErLdqdvBE+BO3sHmMS8mQ9rO4 +hz6igyxg4dZ++PihWx6/Q3ZKw/SjpwdyxG6MUyHP8lMnVJNDFpxpW7IqldpxfsS7 +UwmAa3+NT9Ao1ERxX0t2d442Cpj202/SH72zsN8dhebxTc86v5t1xbpjLjv3UoYJ +pQ22I2pPWW+7/jJ3hZGRdsUWbmElRSkCAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg +hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O +BBYEFK/z5Srrz8d+pNZJkvkp7mobaKsPMB8GA1UdIwQYMBaAFBMuMPAE6kFftwi9 +NDHXEepWppnwMA0GCSqGSIb3DQEBCwUAA4IBAQApYjkleVjrpLMM6qodK5Z8bhDO +Fge3cH8W2v0g5qLZtIjg+YSH+LANd4uuJ/Xu5k+GoS10B3zHXcK95HDnQuQU7rm3 +Y7iMbSFhVguW9hW6eq6AmKxXmXk9eqnYJpMwF1N8LQJLZEklZedpWgjPhJSOakKn +0U+6OUt8EWcx9xsrzXnCKE3ZiGbWf1ZMSzfRPajZSmtFHU2nEp8pd2pVwbUdDqW5 +TzgWPH2Frv8jNMcs9hQPVe+4AInxsop1FUGBctBDpobRBubOgX5fM+b0GdZwALpI +bgX9TDzDURu9QxokxXnqevCFpUAQhekjCQmAOJ28gV5ZjFpNWFa5ccJ4zfOw +-----END CERTIFICATE----- diff --git a/test/units/module_utils/urls/fixtures/client.txt b/test/units/module_utils/urls/fixtures/client.txt new file mode 100644 index 0000000..380330f --- /dev/null +++ b/test/units/module_utils/urls/fixtures/client.txt @@ -0,0 +1,3 @@ +client.pem and client.key were retrieved from httptester docker image: + +ansible/ansible@sha256:fa5def8c294fc50813af131c0b5737594d852abac9cbe7ba38e17bf1c8476f3f diff --git a/test/units/module_utils/urls/fixtures/multipart.txt b/test/units/module_utils/urls/fixtures/multipart.txt new file mode 100644 index 0000000..c80a1b8 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/multipart.txt @@ -0,0 +1,166 @@ +--===============3996062709511591449==
+Content-Type: text/plain
+Content-Disposition: form-data; name="file1"; filename="fake_file1.txt"
+
+file_content_1
+--===============3996062709511591449==
+Content-Type: text/html
+Content-Disposition: form-data; name="file2"; filename="fake_file2.html"
+
+<html></html>
+--===============3996062709511591449==
+Content-Type: application/json
+Content-Disposition: form-data; name="file3"; filename="fake_file3.json"
+
+{"foo": "bar"}
+--===============3996062709511591449==
+Content-Transfer-Encoding: base64
+Content-Type: text/plain
+Content-Disposition: form-data; name="file4"; filename="client.pem"
+
+Q2VydGlmaWNhdGU6CiAgICBEYXRhOgogICAgICAgIFZlcnNpb246IDMgKDB4MikKICAgICAgICBT
+ZXJpYWwgTnVtYmVyOiA0MDk5ICgweDEwMDMpCiAgICBTaWduYXR1cmUgQWxnb3JpdGhtOiBzaGEy
+NTZXaXRoUlNBRW5jcnlwdGlvbgogICAgICAgIElzc3VlcjogQz1VUywgU1Q9Tm9ydGggQ2Fyb2xp
+bmEsIEw9RHVyaGFtLCBPPUFuc2libGUsIENOPWFuc2libGUuaHR0cC50ZXN0cwogICAgICAgIFZh
+bGlkaXR5CiAgICAgICAgICAgIE5vdCBCZWZvcmU6IE1hciAyMSAxODoyMjo0NyAyMDE4IEdNVAog
+ICAgICAgICAgICBOb3QgQWZ0ZXIgOiBNYXIgMTggMTg6MjI6NDcgMjAyOCBHTVQKICAgICAgICBT
+dWJqZWN0OiBDPVVTLCBTVD1Ob3J0aCBDYXJvbGluYSwgTz1BbnNpYmxlLCBDTj1jbGllbnQuYW5z
+aWJsZS5odHRwLnRlc3RzCiAgICAgICAgU3ViamVjdCBQdWJsaWMgS2V5IEluZm86CiAgICAgICAg
+ICAgIFB1YmxpYyBLZXkgQWxnb3JpdGhtOiByc2FFbmNyeXB0aW9uCiAgICAgICAgICAgICAgICBQ
+dWJsaWMtS2V5OiAoMjA0OCBiaXQpCiAgICAgICAgICAgICAgICBNb2R1bHVzOgogICAgICAgICAg
+ICAgICAgICAgIDAwOmQzOmNhOjI1OjcxOmFlOmM0OmIyOjY3OmU0OjJiOjg4OmM0OmZhOmIwOgog
+ICAgICAgICAgICAgICAgICAgIDU2OjAyOmE5OjBiOjY0OjJlOmE5OjQ4OjU5OmY2OmU5OjRlOjBm
+OjQxOmU5OgogICAgICAgICAgICAgICAgICAgIGY2OjVjOmVmOmRkOmVlOmEwOmNjOmNiOjUwOjZh
+OmI3Ojc5OmY4Ojk5OjUyOgogICAgICAgICAgICAgICAgICAgIDE4OjcxOjIzOmJmOjFkOjQzOjc4
+OjBkOjNlOjBiOjM1OmIwOmFkOmQ2Ojg1OgogICAgICAgICAgICAgICAgICAgIGUxOjY5OjkzOjU5
+OmY4OjIwOmJmOjI1Ojk3OjUzOjEyOmQ2OmUyOjRhOjkzOgogICAgICAgICAgICAgICAgICAgIDZi
+OmQ4OmFiOjM2OjU1Ojg2OjM4OjFjOmJkOjBhOjVlOjU3OmQ4OjI2OmVkOgogICAgICAgICAgICAg
+ICAgICAgIGJiOjc0OjlkOmQzOmYzOjFhOjNlOjYxOmUxOmY4OjdiOmE2OmJhOjBmOjllOgogICAg
+ICAgICAgICAgICAgICAgIGQ0OmIyOjc4OjFiOmNjOjI5OjVhOjJhOjRkOjNlOmVkOjEyOjc2OmE5
+OjQ0OgogICAgICAgICAgICAgICAgICAgIGFjOmI3OjZhOjc2OmYwOjQ0OmY4OjEzOmI3OmIwOjc5
+OjhjOjRiOmM5OjkwOgogICAgICAgICAgICAgICAgICAgIGY2OmIzOmI4Ojg3OjNlOmEyOjgzOjJj
+OjYwOmUxOmQ2OjdlOmY4OmY4OmExOgogICAgICAgICAgICAgICAgICAgIDViOjFlOmJmOjQzOjc2
+OjRhOmMzOmY0OmEzOmE3OjA3OjcyOmM0OjZlOjhjOgogICAgICAgICAgICAgICAgICAgIDUzOjIx
+OmNmOmYyOjUzOjI3OjU0OjkzOjQzOjE2OjljOjY5OjViOmIyOjJhOgogICAgICAgICAgICAgICAg
+ICAgIDk1OmRhOjcxOjdlOmM0OmJiOjUzOjA5OjgwOjZiOjdmOjhkOjRmOmQwOjI4OgogICAgICAg
+ICAgICAgICAgICAgIGQ0OjQ0OjcxOjVmOjRiOjc2Ojc3OjhlOjM2OjBhOjk4OmY2OmQzOjZmOmQy
+OgogICAgICAgICAgICAgICAgICAgIDFmOmJkOmIzOmIwOmRmOjFkOjg1OmU2OmYxOjRkOmNmOjNh
+OmJmOjliOjc1OgogICAgICAgICAgICAgICAgICAgIGM1OmJhOjYzOjJlOjNiOmY3OjUyOjg2OjA5
+OmE1OjBkOmI2OjIzOjZhOjRmOgogICAgICAgICAgICAgICAgICAgIDU5OjZmOmJiOmZlOjMyOjc3
+Ojg1OjkxOjkxOjc2OmM1OjE2OjZlOjYxOjI1OgogICAgICAgICAgICAgICAgICAgIDQ1OjI5CiAg
+ICAgICAgICAgICAgICBFeHBvbmVudDogNjU1MzcgKDB4MTAwMDEpCiAgICAgICAgWDUwOXYzIGV4
+dGVuc2lvbnM6CiAgICAgICAgICAgIFg1MDl2MyBCYXNpYyBDb25zdHJhaW50czogCiAgICAgICAg
+ICAgICAgICBDQTpGQUxTRQogICAgICAgICAgICBOZXRzY2FwZSBDb21tZW50OiAKICAgICAgICAg
+ICAgICAgIE9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlCiAgICAgICAgICAgIFg1MDl2MyBT
+dWJqZWN0IEtleSBJZGVudGlmaWVyOiAKICAgICAgICAgICAgICAgIEFGOkYzOkU1OjJBOkVCOkNG
+OkM3OjdFOkE0OkQ2OjQ5OjkyOkY5OjI5OkVFOjZBOjFCOjY4OkFCOjBGCiAgICAgICAgICAgIFg1
+MDl2MyBBdXRob3JpdHkgS2V5IElkZW50aWZpZXI6IAogICAgICAgICAgICAgICAga2V5aWQ6MTM6
+MkU6MzA6RjA6MDQ6RUE6NDE6NUY6Qjc6MDg6QkQ6MzQ6MzE6RDc6MTE6RUE6NTY6QTY6OTk6RjAK
+CiAgICBTaWduYXR1cmUgQWxnb3JpdGhtOiBzaGEyNTZXaXRoUlNBRW5jcnlwdGlvbgogICAgICAg
+ICAyOTo2MjozOToyNTo3OTo1ODplYjphNDpiMzowYzplYTphYToxZDoyYjo5Njo3Yzo2ZToxMDoK
+ICAgICAgICAgY2U6MTY6MDc6Yjc6NzA6N2Y6MTY6ZGE6ZmQ6MjA6ZTY6YTI6ZDk6YjQ6ODg6ZTA6
+Zjk6ODQ6CiAgICAgICAgIDg3OmY4OmIwOjBkOjc3OjhiOmFlOjI3OmY1OmVlOmU2OjRmOjg2OmEx
+OjJkOjc0OjA3OjdjOgogICAgICAgICBjNzo1ZDpjMjpiZDplNDo3MDplNzo0MjplNDoxNDplZTpi
+OTpiNzo2MzpiODo4Yzo2ZDoyMToKICAgICAgICAgNjE6NTY6MGI6OTY6ZjY6MTU6YmE6N2E6YWU6
+ODA6OTg6YWM6NTc6OTk6Nzk6M2Q6N2E6YTk6CiAgICAgICAgIGQ4OjI2OjkzOjMwOjE3OjUzOjdj
+OjJkOjAyOjRiOjY0OjQ5OjI1OjY1OmU3OjY5OjVhOjA4OgogICAgICAgICBjZjo4NDo5NDo4ZTo2
+YTo0MjphNzpkMTo0ZjpiYTozOTo0Yjo3YzoxMTo2NzozMTpmNzoxYjoKICAgICAgICAgMmI6Y2Q6
+Nzk6YzI6Mjg6NGQ6ZDk6ODg6NjY6ZDY6N2Y6NTY6NGM6NGI6Mzc6ZDE6M2Q6YTg6CiAgICAgICAg
+IGQ5OjRhOjZiOjQ1OjFkOjRkOmE3OjEyOjlmOjI5Ojc3OjZhOjU1OmMxOmI1OjFkOjBlOmE1Ogog
+ICAgICAgICBiOTo0ZjozODoxNjozYzo3ZDo4NTphZTpmZjoyMzozNDpjNzoyYzpmNjoxNDowZjo1
+NTplZjoKICAgICAgICAgYjg6MDA6ODk6ZjE6YjI6OGE6NzU6MTU6NDE6ODE6NzI6ZDA6NDM6YTY6
+ODY6ZDE6MDY6ZTY6CiAgICAgICAgIGNlOjgxOjdlOjVmOjMzOmU2OmY0OjE5OmQ2OjcwOjAwOmJh
+OjQ4OjZlOjA1OmZkOjRjOjNjOgogICAgICAgICBjMzo1MToxYjpiZDo0MzoxYToyNDpjNTo3OTpl
+YTo3YTpmMDo4NTphNTo0MDoxMDo4NTplOToKICAgICAgICAgMjM6MDk6MDk6ODA6Mzg6OWQ6YmM6
+ODE6NWU6NTk6OGM6NWE6NGQ6NTg6NTY6Yjk6NzE6YzI6CiAgICAgICAgIDc4OmNkOmYzOmIwCi0t
+LS0tQkVHSU4gQ0VSVElGSUNBVEUtLS0tLQpNSUlEdVRDQ0FxR2dBd0lCQWdJQ0VBTXdEUVlKS29a
+SWh2Y05BUUVMQlFBd1pqRUxNQWtHQTFVRUJoTUNWVk14CkZ6QVZCZ05WQkFnTURrNXZjblJvSUVO
+aGNtOXNhVzVoTVE4d0RRWURWUVFIREFaRWRYSm9ZVzB4RURBT0JnTlYKQkFvTUIwRnVjMmxpYkdV
+eEd6QVpCZ05WQkFNTUVtRnVjMmxpYkdVdWFIUjBjQzUwWlhOMGN6QWVGdzB4T0RBegpNakV4T0RJ
+eU5EZGFGdzB5T0RBek1UZ3hPREl5TkRkYU1Gd3hDekFKQmdOVkJBWVRBbFZUTVJjd0ZRWURWUVFJ
+CkRBNU9iM0owYUNCRFlYSnZiR2x1WVRFUU1BNEdBMVVFQ2d3SFFXNXphV0pzWlRFaU1DQUdBMVVF
+QXd3WlkyeHAKWlc1MExtRnVjMmxpYkdVdWFIUjBjQzUwWlhOMGN6Q0NBU0l3RFFZSktvWklodmNO
+QVFFQkJRQURnZ0VQQURDQwpBUW9DZ2dFQkFOUEtKWEd1eExKbjVDdUl4UHF3VmdLcEMyUXVxVWha
+OXVsT0QwSHA5bHp2M2U2Z3pNdFFhcmQ1CitKbFNHSEVqdngxRGVBMCtDeld3cmRhRjRXbVRXZmdn
+dnlXWFV4TFc0a3FUYTlpck5sV0dPQnk5Q2w1WDJDYnQKdTNTZDAvTWFQbUhoK0h1bXVnK2UxTEo0
+Rzh3cFdpcE5QdTBTZHFsRXJMZHFkdkJFK0JPM3NIbU1TOG1ROXJPNApoejZpZ3l4ZzRkWisrUGlo
+V3g2L1EzWkt3L1NqcHdkeXhHNk1VeUhQOGxNblZKTkRGcHhwVzdJcWxkcHhmc1M3ClV3bUFhMytO
+VDlBbzFFUnhYMHQyZDQ0MkNwajIwMi9TSDcyenNOOGRoZWJ4VGM4NnY1dDF4YnBqTGp2M1VvWUoK
+cFEyMkkycFBXVys3L2pKM2haR1Jkc1VXYm1FbFJTa0NBd0VBQWFON01Ia3dDUVlEVlIwVEJBSXdB
+REFzQmdsZwpoa2dCaHZoQ0FRMEVIeFlkVDNCbGJsTlRUQ0JIWlc1bGNtRjBaV1FnUTJWeWRHbG1h
+V05oZEdVd0hRWURWUjBPCkJCWUVGSy96NVNycno4ZCtwTlpKa3ZrcDdtb2JhS3NQTUI4R0ExVWRJ
+d1FZTUJhQUZCTXVNUEFFNmtGZnR3aTkKTkRIWEVlcFdwcG53TUEwR0NTcUdTSWIzRFFFQkN3VUFB
+NElCQVFBcFlqa2xlVmpycExNTTZxb2RLNVo4YmhETwpGZ2UzY0g4VzJ2MGc1cUxadElqZytZU0gr
+TEFOZDR1dUovWHU1aytHb1MxMEIzekhYY0s5NUhEblF1UVU3cm0zClk3aU1iU0ZoVmd1VzloVzZl
+cTZBbUt4WG1YazllcW5ZSnBNd0YxTjhMUUpMWkVrbFplZHBXZ2pQaEpTT2FrS24KMFUrNk9VdDhF
+V2N4OXhzcnpYbkNLRTNaaUdiV2YxWk1TemZSUGFqWlNtdEZIVTJuRXA4cGQycFZ3YlVkRHFXNQpU
+emdXUEgyRnJ2OGpOTWNzOWhRUFZlKzRBSW54c29wMUZVR0JjdEJEcG9iUkJ1Yk9nWDVmTStiMEdk
+WndBTHBJCmJnWDlURHpEVVJ1OVF4b2t4WG5xZXZDRnBVQVFoZWtqQ1FtQU9KMjhnVjVaakZwTldG
+YTVjY0o0emZPdwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+
+--===============3996062709511591449==
+Content-Transfer-Encoding: base64
+Content-Type: application/octet-stream
+Content-Disposition: form-data; name="file5"; filename="client.key"
+
+LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZB
+QVNDQktjd2dnU2pBZ0VBQW9JQkFRRFR5aVZ4cnNTeVorUXIKaU1UNnNGWUNxUXRrTHFsSVdmYnBU
+ZzlCNmZaYzc5M3VvTXpMVUdxM2VmaVpVaGh4STc4ZFEzZ05QZ3Mxc0szVwpoZUZwazFuNElMOGxs
+MU1TMXVKS2sydllxelpWaGpnY3ZRcGVWOWdtN2J0MG5kUHpHajVoNGZoN3Byb1BudFN5CmVCdk1L
+Vm9xVFQ3dEVuYXBSS3kzYW5id1JQZ1R0N0I1akV2SmtQYXp1SWMrb29Nc1lPSFdmdmo0b1ZzZXYw
+TjIKU3NQMG82Y0hjc1J1akZNaHovSlRKMVNUUXhhY2FWdXlLcFhhY1g3RXUxTUpnR3QvalUvUUtO
+UkVjVjlMZG5lTwpOZ3FZOXROdjBoKzlzN0RmSFlYbThVM1BPcitiZGNXNll5NDc5MUtHQ2FVTnRp
+TnFUMWx2dS80eWQ0V1JrWGJGCkZtNWhKVVVwQWdNQkFBRUNnZ0VCQUpZT2FjMU1TSzBuRXZFTmJK
+TTZFUmE5Y3dhK1VNNmtmMTc2SWJGUDlYQVAKdTZ6eFhXaklSM1JNQlNtTWt5akdiUWhzMzBoeXB6
+cVpQZkg2MWFVWjgrcnNPTUtIbnlLQUFjRlpCbFp6cUlHYwpJWEdyTndkMU1mOFMvWGc0d3cxQmtP
+V0ZWNnMwakN1NUczWi94eUkyUWw0cWNPVkQ2Yk13cHpjbFJiUWpDYW5kCmR2cXlDZE1EMHNSRHll
+T0lLNWhCaFVZNjBKbldiTUN1NnBCVStxUG9SdWtiUmllYWVETElOMWNsd0VxSVFWNzgKTExudjRu
+OWZ1R296SDBKZEhIZnlYRnl0Q2dJSnZFc3BaVWphLzVSNG9yQURocjNaQjAxMFJMell2czJuZEUz
+Qgo0Y0Y5Umd4c3BKWmVKL1ArUGdsVmladXpqMzdwWHkrN0dBY0pMUjlrYTRrQ2dZRUEvbDAxWEt3
+a0N6TWdYSFc0ClVQZ2wxK29uNDJCc043VDlyM1M1dGloT2pIZjRaSldrZ1l6aXNMVlgrTmMxb1VJ
+M0hRZk05UERKWlhNTU5tN0oKWlJ2RVJjb3BVMjZ3V3FyNkNGUGJsR3Y4b3FYSHFjcGV0YThpM3ha
+S29QQVNzVFc2c3N1UENFYWppTFpiUTFySApIL0hQK09aSVZMTS9XQ1BnQTJCY2tUVTlKbnNDZ1lF
+QTFTYlhsbFhubHdHcW1qaXRtWTFaMDdyVXhRM2FoL2ZCCmljY2JiZzNFNG9ub250WVhJbEk1elFt
+czN1K3FCZGkwWnV3YURtNVk0QmV0T3EwYTNVeXhBc3VncVZGbnpUYmEKMXcvc0ZiM2Z3OUtlUS9p
+bDRDWGticTg3bnpKZkRtRXlxSEdDQ1lYYmlqSEJ4bnE5OVBrcXdWcGFBaEhIRVcwbQp2V3lNVXZQ
+Ulk2c0NnWUFidFVXUjBjS2ZZYk5kdndrVDhPUVdjQkJtU1dPZ2Nkdk1tQmQreTBjN0wvcGo0cFVu
+Cjg1UGlFZThDVVZjck9NNU9JRUpvVUM1d0dhY3o2citQZndYVFlHRStFR212aHI1ejE4YXNsVkxR
+Mk9RMkQ3QmYKZERPRlA2VmpnS05Zb0hTMDgwMmlaaWQ4UmZrTkRqOXdzR09xUmxPTXZuWGhBUTl1
+N3JsR3JCajhMd0tCZ0ZmbwpwaDk5bkg4ZUU5TjVMcmZXb1VaK2xvUVMyNThhSW5zRllCMjZsZ25z
+WU1FcGdPOEp4SWI0eDVCR2ZmUGRWVUhoCmZEbVpieFExRDUvVWh2RGdVVnpheUk4c1lNZzFLSHBz
+T2EwWjJ6Q3pLOHpTdnU2OEVnTklTQ20zSjVjUnBVZnQKVUhsRytLMTlLZk1HNmxNZmRHKzhLTVVU
+dWV0SS9pSS9vM3dPekx2ekFvR0FJck9oMzBySHQ4d2l0N0VMQVJ5eAp3UGtwMkFSWVhyS2ZYM05F
+UzRjNjd6U0FpKzNkQ2p4UnF5d3FUSTBnTGljeU1sajh6RXU5WUU5SXgvcmw4bFJaCm5ROUxabXF2
+N1FIemhMVFVDUEdnWlluZW12QnpvN3IwZVc4T2FnNTJkYmNKTzZGQnN6ZldyeHNrbS9mWDI1UmIK
+V1B4aWgydmRSeTgxNGROUFcyNXJnZHc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
+
+--===============3996062709511591449==
+Content-Transfer-Encoding: base64
+Content-Type: text/plain
+Content-Disposition: form-data; name="file6"; filename="client.txt"
+
+Y2xpZW50LnBlbSBhbmQgY2xpZW50LmtleSB3ZXJlIHJldHJpZXZlZCBmcm9tIGh0dHB0ZXN0ZXIg
+ZG9ja2VyIGltYWdlOgoKYW5zaWJsZS9hbnNpYmxlQHNoYTI1NjpmYTVkZWY4YzI5NGZjNTA4MTNh
+ZjEzMWMwYjU3Mzc1OTRkODUyYWJhYzljYmU3YmEzOGUxN2JmMWM4NDc2ZjNmCg==
+
+--===============3996062709511591449==
+Content-Type: text/plain
+Content-Disposition: form-data; name="form_field_1"
+
+form_value_1
+--===============3996062709511591449==
+Content-Type: application/octet-stream
+Content-Disposition: form-data; name="form_field_2"
+
+form_value_2
+--===============3996062709511591449==
+Content-Type: text/html
+Content-Disposition: form-data; name="form_field_3"
+
+<html></html>
+--===============3996062709511591449==
+Content-Type: application/json
+Content-Disposition: form-data; name="form_field_4"
+
+{"foo": "bar"}
+--===============3996062709511591449==--
diff --git a/test/units/module_utils/urls/fixtures/netrc b/test/units/module_utils/urls/fixtures/netrc new file mode 100644 index 0000000..8f12717 --- /dev/null +++ b/test/units/module_utils/urls/fixtures/netrc @@ -0,0 +1,3 @@ +machine ansible.com +login user +password passwd diff --git a/test/units/module_utils/urls/test_RedirectHandlerFactory.py b/test/units/module_utils/urls/test_RedirectHandlerFactory.py new file mode 100644 index 0000000..7bbe4b5 --- /dev/null +++ b/test/units/module_utils/urls/test_RedirectHandlerFactory.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +from ansible.module_utils.urls import HAS_SSLCONTEXT, RedirectHandlerFactory, urllib_request, urllib_error +from ansible.module_utils.six import StringIO + +import pytest + + +@pytest.fixture +def urllib_req(): + req = urllib_request.Request( + 'https://ansible.com/' + ) + return req + + +@pytest.fixture +def request_body(): + return StringIO('TESTS') + + +def test_no_redirs(urllib_req, request_body): + handler = RedirectHandlerFactory('none', False) + inst = handler() + with pytest.raises(urllib_error.HTTPError): + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + +def test_urllib2_redir(urllib_req, request_body, mocker): + redir_request_mock = mocker.patch('ansible.module_utils.urls.urllib_request.HTTPRedirectHandler.redirect_request') + + handler = RedirectHandlerFactory('urllib2', False) + inst = handler() + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + redir_request_mock.assert_called_once_with(inst, urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + +def test_all_redir(urllib_req, request_body, mocker): + req_mock = mocker.patch('ansible.module_utils.urls.RequestWithMethod') + handler = RedirectHandlerFactory('all', False) + inst = handler() + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + req_mock.assert_called_once_with('https://docs.ansible.com/', data=None, headers={}, method='GET', origin_req_host='ansible.com', unverifiable=True) + + +def test_all_redir_post(request_body, mocker): + handler = RedirectHandlerFactory('all', False) + inst = handler() + + req = urllib_request.Request( + 'https://ansible.com/', + 'POST' + ) + + req_mock = mocker.patch('ansible.module_utils.urls.RequestWithMethod') + inst.redirect_request(req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + req_mock.assert_called_once_with('https://docs.ansible.com/', data=None, headers={}, method='GET', origin_req_host='ansible.com', unverifiable=True) + + +def test_redir_headers_removal(urllib_req, request_body, mocker): + req_mock = mocker.patch('ansible.module_utils.urls.RequestWithMethod') + handler = RedirectHandlerFactory('all', False) + inst = handler() + + urllib_req.headers = { + 'Content-Type': 'application/json', + 'Content-Length': 100, + 'Foo': 'bar', + } + + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + req_mock.assert_called_once_with('https://docs.ansible.com/', data=None, headers={'Foo': 'bar'}, method='GET', origin_req_host='ansible.com', + unverifiable=True) + + +def test_redir_url_spaces(urllib_req, request_body, mocker): + req_mock = mocker.patch('ansible.module_utils.urls.RequestWithMethod') + handler = RedirectHandlerFactory('all', False) + inst = handler() + + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/foo bar') + + req_mock.assert_called_once_with('https://docs.ansible.com/foo%20bar', data=None, headers={}, method='GET', origin_req_host='ansible.com', + unverifiable=True) + + +def test_redir_safe(urllib_req, request_body, mocker): + req_mock = mocker.patch('ansible.module_utils.urls.RequestWithMethod') + handler = RedirectHandlerFactory('safe', False) + inst = handler() + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + req_mock.assert_called_once_with('https://docs.ansible.com/', data=None, headers={}, method='GET', origin_req_host='ansible.com', unverifiable=True) + + +def test_redir_safe_not_safe(request_body): + handler = RedirectHandlerFactory('safe', False) + inst = handler() + + req = urllib_request.Request( + 'https://ansible.com/', + 'POST' + ) + + with pytest.raises(urllib_error.HTTPError): + inst.redirect_request(req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + +def test_redir_no_error_on_invalid(urllib_req, request_body): + handler = RedirectHandlerFactory('invalid', False) + inst = handler() + + with pytest.raises(urllib_error.HTTPError): + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + +def test_redir_validate_certs(urllib_req, request_body, mocker): + opener_mock = mocker.patch('ansible.module_utils.urls.urllib_request._opener') + handler = RedirectHandlerFactory('all', True) + inst = handler() + inst.redirect_request(urllib_req, request_body, 301, '301 Moved Permanently', {}, 'https://docs.ansible.com/') + + assert opener_mock.add_handler.call_count == int(not HAS_SSLCONTEXT) + + +def test_redir_http_error_308_urllib2(urllib_req, request_body, mocker): + redir_mock = mocker.patch.object(urllib_request.HTTPRedirectHandler, 'redirect_request') + handler = RedirectHandlerFactory('urllib2', False) + inst = handler() + + inst.redirect_request(urllib_req, request_body, 308, '308 Permanent Redirect', {}, 'https://docs.ansible.com/') + + assert redir_mock.call_count == 1 diff --git a/test/units/module_utils/urls/test_Request.py b/test/units/module_utils/urls/test_Request.py new file mode 100644 index 0000000..d2c4ea3 --- /dev/null +++ b/test/units/module_utils/urls/test_Request.py @@ -0,0 +1,467 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import datetime +import os + +from ansible.module_utils.urls import (Request, open_url, urllib_request, HAS_SSLCONTEXT, cookiejar, RequestWithMethod, + UnixHTTPHandler, UnixHTTPSConnection, httplib) +from ansible.module_utils.urls import SSLValidationHandler, HTTPSClientAuthHandler, RedirectHandlerFactory + +import pytest +from units.compat.mock import call + + +if HAS_SSLCONTEXT: + import ssl + + +@pytest.fixture +def urlopen_mock(mocker): + return mocker.patch('ansible.module_utils.urls.urllib_request.urlopen') + + +@pytest.fixture +def install_opener_mock(mocker): + return mocker.patch('ansible.module_utils.urls.urllib_request.install_opener') + + +def test_Request_fallback(urlopen_mock, install_opener_mock, mocker): + here = os.path.dirname(__file__) + pem = os.path.join(here, 'fixtures/client.pem') + + cookies = cookiejar.CookieJar() + request = Request( + headers={'foo': 'bar'}, + use_proxy=False, + force=True, + timeout=100, + validate_certs=False, + url_username='user', + url_password='passwd', + http_agent='ansible-tests', + force_basic_auth=True, + follow_redirects='all', + client_cert='/tmp/client.pem', + client_key='/tmp/client.key', + cookies=cookies, + unix_socket='/foo/bar/baz.sock', + ca_path=pem, + ciphers=['ECDHE-RSA-AES128-SHA256'], + use_netrc=True, + ) + fallback_mock = mocker.spy(request, '_fallback') + + r = request.open('GET', 'https://ansible.com') + + calls = [ + call(None, False), # use_proxy + call(None, True), # force + call(None, 100), # timeout + call(None, False), # validate_certs + call(None, 'user'), # url_username + call(None, 'passwd'), # url_password + call(None, 'ansible-tests'), # http_agent + call(None, True), # force_basic_auth + call(None, 'all'), # follow_redirects + call(None, '/tmp/client.pem'), # client_cert + call(None, '/tmp/client.key'), # client_key + call(None, cookies), # cookies + call(None, '/foo/bar/baz.sock'), # unix_socket + call(None, pem), # ca_path + call(None, None), # unredirected_headers + call(None, True), # auto_decompress + call(None, ['ECDHE-RSA-AES128-SHA256']), # ciphers + call(None, True), # use_netrc + ] + fallback_mock.assert_has_calls(calls) + + assert fallback_mock.call_count == 18 # All but headers use fallback + + args = urlopen_mock.call_args[0] + assert args[1] is None # data, this is handled in the Request not urlopen + assert args[2] == 100 # timeout + + req = args[0] + assert req.headers == { + 'Authorization': b'Basic dXNlcjpwYXNzd2Q=', + 'Cache-control': 'no-cache', + 'Foo': 'bar', + 'User-agent': 'ansible-tests' + } + assert req.data is None + assert req.get_method() == 'GET' + + +def test_Request_open(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/') + args = urlopen_mock.call_args[0] + assert args[1] is None # data, this is handled in the Request not urlopen + assert args[2] == 10 # timeout + + req = args[0] + assert req.headers == {} + assert req.data is None + assert req.get_method() == 'GET' + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + if not HAS_SSLCONTEXT: + expected_handlers = ( + SSLValidationHandler, + RedirectHandlerFactory(), # factory, get handler + ) + else: + expected_handlers = ( + RedirectHandlerFactory(), # factory, get handler + ) + + found_handlers = [] + for handler in handlers: + if isinstance(handler, SSLValidationHandler) or handler.__class__.__name__ == 'RedirectHandler': + found_handlers.append(handler) + + assert len(found_handlers) == len(expected_handlers) + + +def test_Request_open_http(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://ansible.com/') + args = urlopen_mock.call_args[0] + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + found_handlers = [] + for handler in handlers: + if isinstance(handler, SSLValidationHandler): + found_handlers.append(handler) + + assert len(found_handlers) == 0 + + +def test_Request_open_unix_socket(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://ansible.com/', unix_socket='/foo/bar/baz.sock') + args = urlopen_mock.call_args[0] + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + found_handlers = [] + for handler in handlers: + if isinstance(handler, UnixHTTPHandler): + found_handlers.append(handler) + + assert len(found_handlers) == 1 + + +def test_Request_open_https_unix_socket(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/', unix_socket='/foo/bar/baz.sock') + args = urlopen_mock.call_args[0] + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + found_handlers = [] + for handler in handlers: + if isinstance(handler, HTTPSClientAuthHandler): + found_handlers.append(handler) + + assert len(found_handlers) == 1 + + inst = found_handlers[0]._build_https_connection('foo') + assert isinstance(inst, UnixHTTPSConnection) + + +def test_Request_open_ftp(urlopen_mock, install_opener_mock, mocker): + mocker.patch('ansible.module_utils.urls.ParseResultDottedDict.as_list', side_effect=AssertionError) + + # Using ftp scheme should prevent the AssertionError side effect to fire + r = Request().open('GET', 'ftp://foo@ansible.com/') + + +def test_Request_open_headers(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://ansible.com/', headers={'Foo': 'bar'}) + args = urlopen_mock.call_args[0] + req = args[0] + assert req.headers == {'Foo': 'bar'} + + +def test_Request_open_username(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://ansible.com/', url_username='user') + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + expected_handlers = ( + urllib_request.HTTPBasicAuthHandler, + urllib_request.HTTPDigestAuthHandler, + ) + + found_handlers = [] + for handler in handlers: + if isinstance(handler, expected_handlers): + found_handlers.append(handler) + assert len(found_handlers) == 2 + assert found_handlers[0].passwd.passwd[None] == {(('ansible.com', '/'),): ('user', None)} + + +def test_Request_open_username_in_url(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://user2@ansible.com/') + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + expected_handlers = ( + urllib_request.HTTPBasicAuthHandler, + urllib_request.HTTPDigestAuthHandler, + ) + + found_handlers = [] + for handler in handlers: + if isinstance(handler, expected_handlers): + found_handlers.append(handler) + assert found_handlers[0].passwd.passwd[None] == {(('ansible.com', '/'),): ('user2', '')} + + +def test_Request_open_username_force_basic(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://ansible.com/', url_username='user', url_password='passwd', force_basic_auth=True) + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + expected_handlers = ( + urllib_request.HTTPBasicAuthHandler, + urllib_request.HTTPDigestAuthHandler, + ) + + found_handlers = [] + for handler in handlers: + if isinstance(handler, expected_handlers): + found_handlers.append(handler) + + assert len(found_handlers) == 0 + + args = urlopen_mock.call_args[0] + req = args[0] + assert req.headers.get('Authorization') == b'Basic dXNlcjpwYXNzd2Q=' + + +def test_Request_open_auth_in_netloc(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'http://user:passwd@ansible.com/') + args = urlopen_mock.call_args[0] + req = args[0] + assert req.get_full_url() == 'http://ansible.com/' + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + expected_handlers = ( + urllib_request.HTTPBasicAuthHandler, + urllib_request.HTTPDigestAuthHandler, + ) + + found_handlers = [] + for handler in handlers: + if isinstance(handler, expected_handlers): + found_handlers.append(handler) + + assert len(found_handlers) == 2 + + +def test_Request_open_netrc(urlopen_mock, install_opener_mock, monkeypatch): + here = os.path.dirname(__file__) + + monkeypatch.setenv('NETRC', os.path.join(here, 'fixtures/netrc')) + r = Request().open('GET', 'http://ansible.com/') + args = urlopen_mock.call_args[0] + req = args[0] + assert req.headers.get('Authorization') == b'Basic dXNlcjpwYXNzd2Q=' + + r = Request().open('GET', 'http://foo.ansible.com/') + args = urlopen_mock.call_args[0] + req = args[0] + assert 'Authorization' not in req.headers + + monkeypatch.setenv('NETRC', os.path.join(here, 'fixtures/netrc.nonexistant')) + r = Request().open('GET', 'http://ansible.com/') + args = urlopen_mock.call_args[0] + req = args[0] + assert 'Authorization' not in req.headers + + +def test_Request_open_no_proxy(urlopen_mock, install_opener_mock, mocker): + build_opener_mock = mocker.patch('ansible.module_utils.urls.urllib_request.build_opener') + + r = Request().open('GET', 'http://ansible.com/', use_proxy=False) + + handlers = build_opener_mock.call_args[0] + found_handlers = [] + for handler in handlers: + if isinstance(handler, urllib_request.ProxyHandler): + found_handlers.append(handler) + + assert len(found_handlers) == 1 + + +@pytest.mark.skipif(not HAS_SSLCONTEXT, reason="requires SSLContext") +def test_Request_open_no_validate_certs(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/', validate_certs=False) + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + ssl_handler = None + for handler in handlers: + if isinstance(handler, HTTPSClientAuthHandler): + ssl_handler = handler + break + + assert ssl_handler is not None + + inst = ssl_handler._build_https_connection('foo') + assert isinstance(inst, httplib.HTTPSConnection) + + context = ssl_handler._context + # Differs by Python version + # assert context.protocol == ssl.PROTOCOL_SSLv23 + if ssl.OP_NO_SSLv2: + assert context.options & ssl.OP_NO_SSLv2 + assert context.options & ssl.OP_NO_SSLv3 + assert context.verify_mode == ssl.CERT_NONE + assert context.check_hostname is False + + +def test_Request_open_client_cert(urlopen_mock, install_opener_mock): + here = os.path.dirname(__file__) + + client_cert = os.path.join(here, 'fixtures/client.pem') + client_key = os.path.join(here, 'fixtures/client.key') + + r = Request().open('GET', 'https://ansible.com/', client_cert=client_cert, client_key=client_key) + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + ssl_handler = None + for handler in handlers: + if isinstance(handler, HTTPSClientAuthHandler): + ssl_handler = handler + break + + assert ssl_handler is not None + + assert ssl_handler.client_cert == client_cert + assert ssl_handler.client_key == client_key + + https_connection = ssl_handler._build_https_connection('ansible.com') + + assert https_connection.key_file == client_key + assert https_connection.cert_file == client_cert + + +def test_Request_open_cookies(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/', cookies=cookiejar.CookieJar()) + + opener = install_opener_mock.call_args[0][0] + handlers = opener.handlers + + cookies_handler = None + for handler in handlers: + if isinstance(handler, urllib_request.HTTPCookieProcessor): + cookies_handler = handler + break + + assert cookies_handler is not None + + +def test_Request_open_invalid_method(urlopen_mock, install_opener_mock): + r = Request().open('UNKNOWN', 'https://ansible.com/') + + args = urlopen_mock.call_args[0] + req = args[0] + + assert req.data is None + assert req.get_method() == 'UNKNOWN' + # assert r.status == 504 + + +def test_Request_open_custom_method(urlopen_mock, install_opener_mock): + r = Request().open('DELETE', 'https://ansible.com/') + + args = urlopen_mock.call_args[0] + req = args[0] + + assert isinstance(req, RequestWithMethod) + + +def test_Request_open_user_agent(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/', http_agent='ansible-tests') + + args = urlopen_mock.call_args[0] + req = args[0] + + assert req.headers.get('User-agent') == 'ansible-tests' + + +def test_Request_open_force(urlopen_mock, install_opener_mock): + r = Request().open('GET', 'https://ansible.com/', force=True, last_mod_time=datetime.datetime.now()) + + args = urlopen_mock.call_args[0] + req = args[0] + + assert req.headers.get('Cache-control') == 'no-cache' + assert 'If-modified-since' not in req.headers + + +def test_Request_open_last_mod(urlopen_mock, install_opener_mock): + now = datetime.datetime.now() + r = Request().open('GET', 'https://ansible.com/', last_mod_time=now) + + args = urlopen_mock.call_args[0] + req = args[0] + + assert req.headers.get('If-modified-since') == now.strftime('%a, %d %b %Y %H:%M:%S GMT') + + +def test_Request_open_headers_not_dict(urlopen_mock, install_opener_mock): + with pytest.raises(ValueError): + Request().open('GET', 'https://ansible.com/', headers=['bob']) + + +def test_Request_init_headers_not_dict(urlopen_mock, install_opener_mock): + with pytest.raises(ValueError): + Request(headers=['bob']) + + +@pytest.mark.parametrize('method,kwargs', [ + ('get', {}), + ('options', {}), + ('head', {}), + ('post', {'data': None}), + ('put', {'data': None}), + ('patch', {'data': None}), + ('delete', {}), +]) +def test_methods(method, kwargs, mocker): + expected = method.upper() + open_mock = mocker.patch('ansible.module_utils.urls.Request.open') + request = Request() + getattr(request, method)('https://ansible.com') + open_mock.assert_called_once_with(expected, 'https://ansible.com', **kwargs) + + +def test_open_url(urlopen_mock, install_opener_mock, mocker): + req_mock = mocker.patch('ansible.module_utils.urls.Request.open') + open_url('https://ansible.com/') + req_mock.assert_called_once_with('GET', 'https://ansible.com/', data=None, headers=None, use_proxy=True, + force=False, last_mod_time=None, timeout=10, validate_certs=True, + url_username=None, url_password=None, http_agent=None, + force_basic_auth=False, follow_redirects='urllib2', + client_cert=None, client_key=None, cookies=None, use_gssapi=False, + unix_socket=None, ca_path=None, unredirected_headers=None, decompress=True, + ciphers=None, use_netrc=True) diff --git a/test/units/module_utils/urls/test_RequestWithMethod.py b/test/units/module_utils/urls/test_RequestWithMethod.py new file mode 100644 index 0000000..0510519 --- /dev/null +++ b/test/units/module_utils/urls/test_RequestWithMethod.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.urls import RequestWithMethod + + +def test_RequestWithMethod(): + get = RequestWithMethod('https://ansible.com/', 'GET') + assert get.get_method() == 'GET' + + post = RequestWithMethod('https://ansible.com/', 'POST', data='foo', headers={'Bar': 'baz'}) + assert post.get_method() == 'POST' + assert post.get_full_url() == 'https://ansible.com/' + assert post.data == 'foo' + assert post.headers == {'Bar': 'baz'} + + none = RequestWithMethod('https://ansible.com/', '') + assert none.get_method() == 'GET' diff --git a/test/units/module_utils/urls/test_channel_binding.py b/test/units/module_utils/urls/test_channel_binding.py new file mode 100644 index 0000000..ea9cd01 --- /dev/null +++ b/test/units/module_utils/urls/test_channel_binding.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# (c) 2020 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import base64 +import os.path +import pytest + +from ansible.module_utils import urls + + +@pytest.mark.skipif(not urls.HAS_CRYPTOGRAPHY, reason='Requires cryptography to be installed') +@pytest.mark.parametrize('certificate, expected', [ + ('rsa_md5.pem', b'\x23\x34\xB8\x47\x6C\xBF\x4E\x6D' + b'\xFC\x76\x6A\x5D\x5A\x30\xD6\x64' + b'\x9C\x01\xBA\xE1\x66\x2A\x5C\x3A' + b'\x13\x02\xA9\x68\xD7\xC6\xB0\xF6'), + ('rsa_sha1.pem', b'\x14\xCF\xE8\xE4\xB3\x32\xB2\x0A' + b'\x34\x3F\xC8\x40\xB1\x8F\x9F\x6F' + b'\x78\x92\x6A\xFE\x7E\xC3\xE7\xB8' + b'\xE2\x89\x69\x61\x9B\x1E\x8F\x3E'), + ('rsa_sha256.pem', b'\x99\x6F\x3E\xEA\x81\x2C\x18\x70' + b'\xE3\x05\x49\xFF\x9B\x86\xCD\x87' + b'\xA8\x90\xB6\xD8\xDF\xDF\x4A\x81' + b'\xBE\xF9\x67\x59\x70\xDA\xDB\x26'), + ('rsa_sha384.pem', b'\x34\xF3\x03\xC9\x95\x28\x6F\x4B' + b'\x21\x4A\x9B\xA6\x43\x5B\x69\xB5' + b'\x1E\xCF\x37\x58\xEA\xBC\x2A\x14' + b'\xD7\xA4\x3F\xD2\x37\xDC\x2B\x1A' + b'\x1A\xD9\x11\x1C\x5C\x96\x5E\x10' + b'\x75\x07\xCB\x41\x98\xC0\x9F\xEC'), + ('rsa_sha512.pem', b'\x55\x6E\x1C\x17\x84\xE3\xB9\x57' + b'\x37\x0B\x7F\x54\x4F\x62\xC5\x33' + b'\xCB\x2C\xA5\xC1\xDA\xE0\x70\x6F' + b'\xAE\xF0\x05\x44\xE1\xAD\x2B\x76' + b'\xFF\x25\xCF\xBE\x69\xB1\xC4\xE6' + b'\x30\xC3\xBB\x02\x07\xDF\x11\x31' + b'\x4C\x67\x38\xBC\xAE\xD7\xE0\x71' + b'\xD7\xBF\xBF\x2C\x9D\xFA\xB8\x5D'), + ('rsa-pss_sha256.pem', b'\xF2\x31\xE6\xFF\x3F\x9E\x16\x1B' + b'\xC2\xDC\xBB\x89\x8D\x84\x47\x4E' + b'\x58\x9C\xD7\xC2\x7A\xDB\xEF\x8B' + b'\xD9\xC0\xC0\x68\xAF\x9C\x36\x6D'), + ('rsa-pss_sha512.pem', b'\x85\x85\x19\xB9\xE1\x0F\x23\xE2' + b'\x1D\x2C\xE9\xD5\x47\x2A\xAB\xCE' + b'\x42\x0F\xD1\x00\x75\x9C\x53\xA1' + b'\x7B\xB9\x79\x86\xB2\x59\x61\x27'), + ('ecdsa_sha256.pem', b'\xFE\xCF\x1B\x25\x85\x44\x99\x90' + b'\xD9\xE3\xB2\xC9\x2D\x3F\x59\x7E' + b'\xC8\x35\x4E\x12\x4E\xDA\x75\x1D' + b'\x94\x83\x7C\x2C\x89\xA2\xC1\x55'), + ('ecdsa_sha512.pem', b'\xE5\xCB\x68\xB2\xF8\x43\xD6\x3B' + b'\xF4\x0B\xCB\x20\x07\x60\x8F\x81' + b'\x97\x61\x83\x92\x78\x3F\x23\x30' + b'\xE5\xEF\x19\xA5\xBD\x8F\x0B\x2F' + b'\xAA\xC8\x61\x85\x5F\xBB\x63\xA2' + b'\x21\xCC\x46\xFC\x1E\x22\x6A\x07' + b'\x24\x11\xAF\x17\x5D\xDE\x47\x92' + b'\x81\xE0\x06\x87\x8B\x34\x80\x59'), +]) +def test_cbt_with_cert(certificate, expected): + with open(os.path.join(os.path.dirname(__file__), 'fixtures', 'cbt', certificate)) as fd: + cert_der = base64.b64decode("".join([l.strip() for l in fd.readlines()[1:-1]])) + + actual = urls.get_channel_binding_cert_hash(cert_der) + assert actual == expected + + +def test_cbt_no_cryptography(monkeypatch): + monkeypatch.setattr(urls, 'HAS_CRYPTOGRAPHY', False) + assert urls.get_channel_binding_cert_hash(None) is None diff --git a/test/units/module_utils/urls/test_fetch_file.py b/test/units/module_utils/urls/test_fetch_file.py new file mode 100644 index 0000000..ed11227 --- /dev/null +++ b/test/units/module_utils/urls/test_fetch_file.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import os + +from ansible.module_utils.urls import fetch_file + +import pytest +from units.compat.mock import MagicMock + + +class FakeTemporaryFile: + def __init__(self, name): + self.name = name + + +@pytest.mark.parametrize( + 'url, prefix, suffix, expected', ( + ('http://ansible.com/foo.tar.gz?foo=%s' % ('bar' * 100), 'foo', '.tar.gz', 'foo.tar.gz'), + ('https://www.gnu.org/licenses/gpl-3.0.txt', 'gpl-3.0', '.txt', 'gpl-3.0.txt'), + ('http://pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz', 'yaml-0.2.5', '.tar.gz', 'yaml-0.2.5.tar.gz'), + ( + 'https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz', + 'geckodriver-v0.26.0-linux64', + '.tar.gz', + 'geckodriver-v0.26.0-linux64.tar.gz' + ), + ) +) +def test_file_multiple_extensions(mocker, url, prefix, suffix, expected): + module = mocker.Mock() + module.tmpdir = '/tmp' + module.add_cleanup_file = mocker.Mock(side_effect=AttributeError('raised intentionally')) + + mock_NamedTemporaryFile = mocker.patch('ansible.module_utils.urls.tempfile.NamedTemporaryFile', + return_value=FakeTemporaryFile(os.path.join(module.tmpdir, expected))) + + with pytest.raises(AttributeError, match='raised intentionally'): + fetch_file(module, url) + + mock_NamedTemporaryFile.assert_called_with(dir=module.tmpdir, prefix=prefix, suffix=suffix, delete=False) diff --git a/test/units/module_utils/urls/test_fetch_url.py b/test/units/module_utils/urls/test_fetch_url.py new file mode 100644 index 0000000..5bfd66a --- /dev/null +++ b/test/units/module_utils/urls/test_fetch_url.py @@ -0,0 +1,230 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import socket +import sys + +from ansible.module_utils.six import StringIO +from ansible.module_utils.six.moves.http_cookiejar import Cookie +from ansible.module_utils.six.moves.http_client import HTTPMessage +from ansible.module_utils.urls import fetch_url, urllib_error, ConnectionError, NoSSLError, httplib + +import pytest +from units.compat.mock import MagicMock + + +class AnsibleModuleExit(Exception): + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + + +class ExitJson(AnsibleModuleExit): + pass + + +class FailJson(AnsibleModuleExit): + pass + + +@pytest.fixture +def open_url_mock(mocker): + return mocker.patch('ansible.module_utils.urls.open_url') + + +@pytest.fixture +def fake_ansible_module(): + return FakeAnsibleModule() + + +class FakeAnsibleModule: + def __init__(self): + self.params = {} + self.tmpdir = None + + def exit_json(self, *args, **kwargs): + raise ExitJson(*args, **kwargs) + + def fail_json(self, *args, **kwargs): + raise FailJson(*args, **kwargs) + + +def test_fetch_url_no_urlparse(mocker, fake_ansible_module): + mocker.patch('ansible.module_utils.urls.HAS_URLPARSE', new=False) + + with pytest.raises(FailJson): + fetch_url(fake_ansible_module, 'http://ansible.com/') + + +def test_fetch_url(open_url_mock, fake_ansible_module): + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + + dummy, kwargs = open_url_mock.call_args + + open_url_mock.assert_called_once_with('http://ansible.com/', client_cert=None, client_key=None, cookies=kwargs['cookies'], data=None, + follow_redirects='urllib2', force=False, force_basic_auth='', headers=None, + http_agent='ansible-httpget', last_mod_time=None, method=None, timeout=10, url_password='', url_username='', + use_proxy=True, validate_certs=True, use_gssapi=False, unix_socket=None, ca_path=None, unredirected_headers=None, + decompress=True, ciphers=None, use_netrc=True) + + +def test_fetch_url_params(open_url_mock, fake_ansible_module): + fake_ansible_module.params = { + 'validate_certs': False, + 'url_username': 'user', + 'url_password': 'passwd', + 'http_agent': 'ansible-test', + 'force_basic_auth': True, + 'follow_redirects': 'all', + 'client_cert': 'client.pem', + 'client_key': 'client.key', + } + + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + + dummy, kwargs = open_url_mock.call_args + + open_url_mock.assert_called_once_with('http://ansible.com/', client_cert='client.pem', client_key='client.key', cookies=kwargs['cookies'], data=None, + follow_redirects='all', force=False, force_basic_auth=True, headers=None, + http_agent='ansible-test', last_mod_time=None, method=None, timeout=10, url_password='passwd', url_username='user', + use_proxy=True, validate_certs=False, use_gssapi=False, unix_socket=None, ca_path=None, unredirected_headers=None, + decompress=True, ciphers=None, use_netrc=True) + + +def test_fetch_url_cookies(mocker, fake_ansible_module): + def make_cookies(*args, **kwargs): + cookies = kwargs['cookies'] + r = MagicMock() + try: + r.headers = HTTPMessage() + add_header = r.headers.add_header + except TypeError: + # PY2 + r.headers = HTTPMessage(StringIO()) + add_header = r.headers.addheader + r.info.return_value = r.headers + for name, value in (('Foo', 'bar'), ('Baz', 'qux')): + cookie = Cookie( + version=0, + name=name, + value=value, + port=None, + port_specified=False, + domain="ansible.com", + domain_specified=True, + domain_initial_dot=False, + path="/", + path_specified=True, + secure=False, + expires=None, + discard=False, + comment=None, + comment_url=None, + rest=None + ) + cookies.set_cookie(cookie) + add_header('Set-Cookie', '%s=%s' % (name, value)) + + return r + + mocker = mocker.patch('ansible.module_utils.urls.open_url', new=make_cookies) + + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert info['cookies'] == {'Baz': 'qux', 'Foo': 'bar'} + + if sys.version_info < (3, 11): + # Python sorts cookies in order of most specific (ie. longest) path first + # items with the same path are reversed from response order + assert info['cookies_string'] == 'Baz=qux; Foo=bar' + else: + # Python 3.11 and later preserve the Set-Cookie order. + # See: https://github.com/python/cpython/pull/22745/ + assert info['cookies_string'] == 'Foo=bar; Baz=qux' + + # The key here has a `-` as opposed to what we see in the `uri` module that converts to `_` + # Note: this is response order, which differs from cookies_string + assert info['set-cookie'] == 'Foo=bar, Baz=qux' + + +def test_fetch_url_nossl(open_url_mock, fake_ansible_module, mocker): + mocker.patch('ansible.module_utils.urls.get_distribution', return_value='notredhat') + + open_url_mock.side_effect = NoSSLError + with pytest.raises(FailJson) as excinfo: + fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert 'python-ssl' not in excinfo.value.kwargs['msg'] + + mocker.patch('ansible.module_utils.urls.get_distribution', return_value='redhat') + + open_url_mock.side_effect = NoSSLError + with pytest.raises(FailJson) as excinfo: + fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert 'python-ssl' in excinfo.value.kwargs['msg'] + assert 'http://ansible.com/' == excinfo.value.kwargs['url'] + assert excinfo.value.kwargs['status'] == -1 + + +def test_fetch_url_connectionerror(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = ConnectionError('TESTS') + with pytest.raises(FailJson) as excinfo: + fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert excinfo.value.kwargs['msg'] == 'TESTS' + assert 'http://ansible.com/' == excinfo.value.kwargs['url'] + assert excinfo.value.kwargs['status'] == -1 + + open_url_mock.side_effect = ValueError('TESTS') + with pytest.raises(FailJson) as excinfo: + fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert excinfo.value.kwargs['msg'] == 'TESTS' + assert 'http://ansible.com/' == excinfo.value.kwargs['url'] + assert excinfo.value.kwargs['status'] == -1 + + +def test_fetch_url_httperror(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = urllib_error.HTTPError( + 'http://ansible.com/', + 500, + 'Internal Server Error', + {'Content-Type': 'application/json'}, + StringIO('TESTS') + ) + + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + + assert info == {'msg': 'HTTP Error 500: Internal Server Error', 'body': 'TESTS', + 'status': 500, 'url': 'http://ansible.com/', 'content-type': 'application/json'} + + +def test_fetch_url_urlerror(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = urllib_error.URLError('TESTS') + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + assert info == {'msg': 'Request failed: <urlopen error TESTS>', 'status': -1, 'url': 'http://ansible.com/'} + + +def test_fetch_url_socketerror(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = socket.error('TESTS') + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + assert info == {'msg': 'Connection failure: TESTS', 'status': -1, 'url': 'http://ansible.com/'} + + +def test_fetch_url_exception(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = Exception('TESTS') + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + exception = info.pop('exception') + assert info == {'msg': 'An unknown error occurred: TESTS', 'status': -1, 'url': 'http://ansible.com/'} + assert "Exception: TESTS" in exception + + +def test_fetch_url_badstatusline(open_url_mock, fake_ansible_module): + open_url_mock.side_effect = httplib.BadStatusLine('TESTS') + r, info = fetch_url(fake_ansible_module, 'http://ansible.com/') + assert info == {'msg': 'Connection failure: connection was closed before a valid response was received: TESTS', 'status': -1, 'url': 'http://ansible.com/'} diff --git a/test/units/module_utils/urls/test_generic_urlparse.py b/test/units/module_utils/urls/test_generic_urlparse.py new file mode 100644 index 0000000..7753726 --- /dev/null +++ b/test/units/module_utils/urls/test_generic_urlparse.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.urls import generic_urlparse +from ansible.module_utils.six.moves.urllib.parse import urlparse, urlunparse + + +def test_generic_urlparse(): + url = 'https://ansible.com/blog' + parts = urlparse(url) + generic_parts = generic_urlparse(parts) + assert generic_parts.as_list() == list(parts) + + assert urlunparse(generic_parts.as_list()) == url + + +def test_generic_urlparse_netloc(): + url = 'https://ansible.com:443/blog' + parts = urlparse(url) + generic_parts = generic_urlparse(parts) + assert generic_parts.hostname == parts.hostname + assert generic_parts.hostname == 'ansible.com' + assert generic_parts.port == 443 + assert urlunparse(generic_parts.as_list()) == url + + +def test_generic_urlparse_no_netloc(): + url = 'https://user:passwd@ansible.com:443/blog' + parts = list(urlparse(url)) + generic_parts = generic_urlparse(parts) + assert generic_parts.hostname == 'ansible.com' + assert generic_parts.port == 443 + assert generic_parts.username == 'user' + assert generic_parts.password == 'passwd' + assert urlunparse(generic_parts.as_list()) == url + + +def test_generic_urlparse_no_netloc_no_auth(): + url = 'https://ansible.com:443/blog' + parts = list(urlparse(url)) + generic_parts = generic_urlparse(parts) + assert generic_parts.username is None + assert generic_parts.password is None + + +def test_generic_urlparse_no_netloc_no_host(): + url = '/blog' + parts = list(urlparse(url)) + generic_parts = generic_urlparse(parts) + assert generic_parts.username is None + assert generic_parts.password is None + assert generic_parts.port is None + assert generic_parts.hostname == '' diff --git a/test/units/module_utils/urls/test_gzip.py b/test/units/module_utils/urls/test_gzip.py new file mode 100644 index 0000000..c684032 --- /dev/null +++ b/test/units/module_utils/urls/test_gzip.py @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- +# (c) 2021 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import gzip +import io +import sys + +try: + from urllib.response import addinfourl +except ImportError: + from urllib import addinfourl + +from ansible.module_utils.six import PY3 +from ansible.module_utils.six.moves import http_client +from ansible.module_utils.urls import GzipDecodedReader, Request + +import pytest + + +def compress(data): + buf = io.BytesIO() + try: + f = gzip.GzipFile(fileobj=buf, mode='wb') + f.write(data) + finally: + f.close() + return buf.getvalue() + + +class Sock(io.BytesIO): + def makefile(self, *args, **kwds): + return self + + +@pytest.fixture +def urlopen_mock(mocker): + return mocker.patch('ansible.module_utils.urls.urllib_request.urlopen') + + +JSON_DATA = b'{"foo": "bar", "baz": "qux", "sandwich": "ham", "tech_level": "pickle", "pop": "corn", "ansible": "awesome"}' + + +RESP = b'''HTTP/1.1 200 OK +Content-Type: application/json; charset=utf-8 +Set-Cookie: foo +Set-Cookie: bar +Content-Length: 108 + +%s''' % JSON_DATA + +GZIP_RESP = b'''HTTP/1.1 200 OK +Content-Type: application/json; charset=utf-8 +Set-Cookie: foo +Set-Cookie: bar +Content-Encoding: gzip +Content-Length: 100 + +%s''' % compress(JSON_DATA) + + +def test_Request_open_gzip(urlopen_mock): + h = http_client.HTTPResponse( + Sock(GZIP_RESP), + method='GET', + ) + h.begin() + + if PY3: + urlopen_mock.return_value = h + else: + urlopen_mock.return_value = addinfourl( + h.fp, + h.msg, + 'http://ansible.com/', + h.status, + ) + urlopen_mock.return_value.msg = h.reason + + r = Request().open('GET', 'https://ansible.com/') + assert isinstance(r.fp, GzipDecodedReader) + assert r.read() == JSON_DATA + + +def test_Request_open_not_gzip(urlopen_mock): + h = http_client.HTTPResponse( + Sock(RESP), + method='GET', + ) + h.begin() + + if PY3: + urlopen_mock.return_value = h + else: + urlopen_mock.return_value = addinfourl( + h.fp, + h.msg, + 'http://ansible.com/', + h.status, + ) + urlopen_mock.return_value.msg = h.reason + + r = Request().open('GET', 'https://ansible.com/') + assert not isinstance(r.fp, GzipDecodedReader) + assert r.read() == JSON_DATA + + +def test_Request_open_decompress_false(urlopen_mock): + h = http_client.HTTPResponse( + Sock(RESP), + method='GET', + ) + h.begin() + + if PY3: + urlopen_mock.return_value = h + else: + urlopen_mock.return_value = addinfourl( + h.fp, + h.msg, + 'http://ansible.com/', + h.status, + ) + urlopen_mock.return_value.msg = h.reason + + r = Request().open('GET', 'https://ansible.com/', decompress=False) + assert not isinstance(r.fp, GzipDecodedReader) + assert r.read() == JSON_DATA + + +def test_GzipDecodedReader_no_gzip(monkeypatch, mocker): + monkeypatch.delitem(sys.modules, 'gzip') + monkeypatch.delitem(sys.modules, 'ansible.module_utils.urls') + + orig_import = __import__ + + def _import(*args): + if args[0] == 'gzip': + raise ImportError + return orig_import(*args) + + if PY3: + mocker.patch('builtins.__import__', _import) + else: + mocker.patch('__builtin__.__import__', _import) + + mod = __import__('ansible.module_utils.urls').module_utils.urls + assert mod.HAS_GZIP is False + pytest.raises(mod.MissingModuleError, mod.GzipDecodedReader, None) diff --git a/test/units/module_utils/urls/test_prepare_multipart.py b/test/units/module_utils/urls/test_prepare_multipart.py new file mode 100644 index 0000000..226d9ed --- /dev/null +++ b/test/units/module_utils/urls/test_prepare_multipart.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# (c) 2020 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import os + +from io import StringIO + +from email.message import Message + +import pytest + +from ansible.module_utils.urls import prepare_multipart + + +def test_prepare_multipart(): + fixture_boundary = b'===============3996062709511591449==' + + here = os.path.dirname(__file__) + multipart = os.path.join(here, 'fixtures/multipart.txt') + + client_cert = os.path.join(here, 'fixtures/client.pem') + client_key = os.path.join(here, 'fixtures/client.key') + client_txt = os.path.join(here, 'fixtures/client.txt') + fields = { + 'form_field_1': 'form_value_1', + 'form_field_2': { + 'content': 'form_value_2', + }, + 'form_field_3': { + 'content': '<html></html>', + 'mime_type': 'text/html', + }, + 'form_field_4': { + 'content': '{"foo": "bar"}', + 'mime_type': 'application/json', + }, + 'file1': { + 'content': 'file_content_1', + 'filename': 'fake_file1.txt', + }, + 'file2': { + 'content': '<html></html>', + 'mime_type': 'text/html', + 'filename': 'fake_file2.html', + }, + 'file3': { + 'content': '{"foo": "bar"}', + 'mime_type': 'application/json', + 'filename': 'fake_file3.json', + }, + 'file4': { + 'filename': client_cert, + 'mime_type': 'text/plain', + }, + 'file5': { + 'filename': client_key, + 'mime_type': 'application/octet-stream' + }, + 'file6': { + 'filename': client_txt, + }, + } + + content_type, b_data = prepare_multipart(fields) + + headers = Message() + headers['Content-Type'] = content_type + assert headers.get_content_type() == 'multipart/form-data' + boundary = headers.get_boundary() + assert boundary is not None + + with open(multipart, 'rb') as f: + b_expected = f.read().replace(fixture_boundary, boundary.encode()) + + # Depending on Python version, there may or may not be a trailing newline + assert b_data.rstrip(b'\r\n') == b_expected.rstrip(b'\r\n') + + +def test_wrong_type(): + pytest.raises(TypeError, prepare_multipart, 'foo') + pytest.raises(TypeError, prepare_multipart, {'foo': None}) + + +def test_empty(): + pytest.raises(ValueError, prepare_multipart, {'foo': {}}) + + +def test_unknown_mime(mocker): + fields = {'foo': {'filename': 'foo.boom', 'content': 'foo'}} + mocker.patch('mimetypes.guess_type', return_value=(None, None)) + content_type, b_data = prepare_multipart(fields) + assert b'Content-Type: application/octet-stream' in b_data + + +def test_bad_mime(mocker): + fields = {'foo': {'filename': 'foo.boom', 'content': 'foo'}} + mocker.patch('mimetypes.guess_type', side_effect=TypeError) + content_type, b_data = prepare_multipart(fields) + assert b'Content-Type: application/octet-stream' in b_data diff --git a/test/units/module_utils/urls/test_split.py b/test/units/module_utils/urls/test_split.py new file mode 100644 index 0000000..7fd5fc1 --- /dev/null +++ b/test/units/module_utils/urls/test_split.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import pytest + +from ansible.module_utils.urls import _split_multiext + + +@pytest.mark.parametrize( + 'name, expected', + ( + ('', ('', '')), + ('a', ('a', '')), + ('file.tar', ('file', '.tar')), + ('file.tar.', ('file.tar.', '')), + ('file.hidden', ('file.hidden', '')), + ('file.tar.gz', ('file', '.tar.gz')), + ('yaml-0.2.5.tar.gz', ('yaml-0.2.5', '.tar.gz')), + ('yaml-0.2.5.zip', ('yaml-0.2.5', '.zip')), + ('yaml-0.2.5.zip.hidden', ('yaml-0.2.5.zip.hidden', '')), + ('geckodriver-v0.26.0-linux64.tar', ('geckodriver-v0.26.0-linux64', '.tar')), + ('/var/lib/geckodriver-v0.26.0-linux64.tar', ('/var/lib/geckodriver-v0.26.0-linux64', '.tar')), + ('https://acme.com/drivers/geckodriver-v0.26.0-linux64.tar', ('https://acme.com/drivers/geckodriver-v0.26.0-linux64', '.tar')), + ('https://acme.com/drivers/geckodriver-v0.26.0-linux64.tar.bz', ('https://acme.com/drivers/geckodriver-v0.26.0-linux64', '.tar.bz')), + ) +) +def test__split_multiext(name, expected): + assert expected == _split_multiext(name) + + +@pytest.mark.parametrize( + 'args, expected', + ( + (('base-v0.26.0-linux64.tar.gz', 4, 4), ('base-v0.26.0-linux64.tar.gz', '')), + (('base-v0.26.0.hidden', 1, 7), ('base-v0.26', '.0.hidden')), + (('base-v0.26.0.hidden', 3, 4), ('base-v0.26.0.hidden', '')), + (('base-v0.26.0.hidden.tar', 1, 7), ('base-v0.26.0', '.hidden.tar')), + (('base-v0.26.0.hidden.tar.gz', 1, 7), ('base-v0.26.0.hidden', '.tar.gz')), + (('base-v0.26.0.hidden.tar.gz', 4, 7), ('base-v0.26.0.hidden.tar.gz', '')), + ) +) +def test__split_multiext_min_max(args, expected): + assert expected == _split_multiext(*args) + + +@pytest.mark.parametrize( + 'kwargs, expected', ( + (({'name': 'base-v0.25.0.tar.gz', 'count': 1}), ('base-v0.25.0.tar', '.gz')), + (({'name': 'base-v0.25.0.tar.gz', 'count': 2}), ('base-v0.25.0', '.tar.gz')), + (({'name': 'base-v0.25.0.tar.gz', 'count': 3}), ('base-v0.25.0', '.tar.gz')), + (({'name': 'base-v0.25.0.tar.gz', 'count': 4}), ('base-v0.25.0', '.tar.gz')), + (({'name': 'base-v0.25.foo.tar.gz', 'count': 3}), ('base-v0.25', '.foo.tar.gz')), + (({'name': 'base-v0.25.foo.tar.gz', 'count': 4}), ('base-v0', '.25.foo.tar.gz')), + ) +) +def test__split_multiext_count(kwargs, expected): + assert expected == _split_multiext(**kwargs) + + +@pytest.mark.parametrize( + 'name', + ( + list(), + tuple(), + dict(), + set(), + 1.729879, + 247, + ) +) +def test__split_multiext_invalid(name): + with pytest.raises((TypeError, AttributeError)): + _split_multiext(name) diff --git a/test/units/module_utils/urls/test_urls.py b/test/units/module_utils/urls/test_urls.py new file mode 100644 index 0000000..69c1b82 --- /dev/null +++ b/test/units/module_utils/urls/test_urls.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# (c) 2018 Matt Martz <matt@sivel.net> +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils import urls +from ansible.module_utils._text import to_native + +import pytest + + +def test_build_ssl_validation_error(mocker): + mocker.patch.object(urls, 'HAS_SSLCONTEXT', new=False) + mocker.patch.object(urls, 'HAS_URLLIB3_PYOPENSSLCONTEXT', new=False) + mocker.patch.object(urls, 'HAS_URLLIB3_SSL_WRAP_SOCKET', new=False) + with pytest.raises(urls.SSLValidationError) as excinfo: + urls.build_ssl_validation_error('hostname', 'port', 'paths', exc=None) + + assert 'python >= 2.7.9' in to_native(excinfo.value) + assert 'the python executable used' in to_native(excinfo.value) + assert 'urllib3' in to_native(excinfo.value) + assert 'python >= 2.6' in to_native(excinfo.value) + assert 'validate_certs=False' in to_native(excinfo.value) + + mocker.patch.object(urls, 'HAS_SSLCONTEXT', new=True) + with pytest.raises(urls.SSLValidationError) as excinfo: + urls.build_ssl_validation_error('hostname', 'port', 'paths', exc=None) + + assert 'validate_certs=False' in to_native(excinfo.value) + + mocker.patch.object(urls, 'HAS_SSLCONTEXT', new=False) + mocker.patch.object(urls, 'HAS_URLLIB3_PYOPENSSLCONTEXT', new=True) + mocker.patch.object(urls, 'HAS_URLLIB3_SSL_WRAP_SOCKET', new=True) + + mocker.patch.object(urls, 'HAS_SSLCONTEXT', new=True) + with pytest.raises(urls.SSLValidationError) as excinfo: + urls.build_ssl_validation_error('hostname', 'port', 'paths', exc=None) + + assert 'urllib3' not in to_native(excinfo.value) + + with pytest.raises(urls.SSLValidationError) as excinfo: + urls.build_ssl_validation_error('hostname', 'port', 'paths', exc='BOOM') + + assert 'BOOM' in to_native(excinfo.value) + + +def test_maybe_add_ssl_handler(mocker): + mocker.patch.object(urls, 'HAS_SSL', new=False) + with pytest.raises(urls.NoSSLError): + urls.maybe_add_ssl_handler('https://ansible.com/', True) + + mocker.patch.object(urls, 'HAS_SSL', new=True) + url = 'https://user:passwd@ansible.com/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == 'ansible.com' + assert handler.port == 443 + + url = 'https://ansible.com:4433/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == 'ansible.com' + assert handler.port == 4433 + + url = 'https://user:passwd@ansible.com:4433/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == 'ansible.com' + assert handler.port == 4433 + + url = 'https://ansible.com/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == 'ansible.com' + assert handler.port == 443 + + url = 'http://ansible.com/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler is None + + url = 'https://[2a00:16d8:0:7::205]:4443/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == '2a00:16d8:0:7::205' + assert handler.port == 4443 + + url = 'https://[2a00:16d8:0:7::205]/' + handler = urls.maybe_add_ssl_handler(url, True) + assert handler.hostname == '2a00:16d8:0:7::205' + assert handler.port == 443 + + +def test_basic_auth_header(): + header = urls.basic_auth_header('user', 'passwd') + assert header == b'Basic dXNlcjpwYXNzd2Q=' + + +def test_ParseResultDottedDict(): + url = 'https://ansible.com/blog' + parts = urls.urlparse(url) + dotted_parts = urls.ParseResultDottedDict(parts._asdict()) + assert parts[0] == dotted_parts.scheme + + assert dotted_parts.as_list() == list(parts) + + +def test_unix_socket_patch_httpconnection_connect(mocker): + unix_conn = mocker.patch.object(urls.UnixHTTPConnection, 'connect') + conn = urls.httplib.HTTPConnection('ansible.com') + with urls.unix_socket_patch_httpconnection_connect(): + conn.connect() + assert unix_conn.call_count == 1 |