diff --git a/.ci/certs/ca.p12 b/.ci/certs/ca.p12 deleted file mode 100644 index 5900f69..0000000 Binary files a/.ci/certs/ca.p12 and /dev/null differ diff --git a/.ci/certs/ca_certificate.pem b/.ci/certs/ca_certificate.pem index 77f380a..a67df16 100644 --- a/.ci/certs/ca_certificate.pem +++ b/.ci/certs/ca_certificate.pem @@ -1,21 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIDhjCCAm6gAwIBAgIUYqXLpnhFfIhE5o1qvs6gnL67IsQwDQYJKoZIhvcNAQEL -BQAwTDE7MDkGA1UEAwwyVExTR2VuU2VsZlNpZ25lZHRSb290Q0EgMjAyMy0wOS0x -MVQyMDo1MTozOS42MDMwMTMxDTALBgNVBAcMBCQkJCQwHhcNMjMwOTExMTg1MTM5 -WhcNMzMwOTA4MTg1MTM5WjBMMTswOQYDVQQDDDJUTFNHZW5TZWxmU2lnbmVkdFJv -b3RDQSAyMDIzLTA5LTExVDIwOjUxOjM5LjYwMzAxMzENMAsGA1UEBwwEJCQkJDCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJl6TmIdlTdZpd3ZJaTafrYi -0MAcHE5FEfDQgGkTJgbeXG83MTUD2NYjlGgrb1v793PiO3/iYSK2uGv5AAQdvUmI -jAP8yuJTFiIFpWvljERaDd3sg+RniUN4YaxI0xnM82A2UBWXpdAbS2ASMdPSY6+V -ZX+xbBaY/H7HDL7zhrQEkl1OGgybX+segjOTX1jkNJ7QQZ924DHLvJWDNIIBt8S8 -aYVbf6V2MFziwD98hsfIRgF22T2bgEMkI5M0H5jO4hLkeTYE7Mhpb7TfeZCSLeVD -/vmMbnOvgXJt0wvILwltH6MAviAQTjKIXiMbECTX81tmHInUQ+PKTiz8t5mVSLcC -AwEAAaNgMF4wDwYDVR0TAQH/BAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYE -FCjyUnXFpZI5+zz7PJxHxDuulqqZMB8GA1UdIwQYMBaAFCjyUnXFpZI5+zz7PJxH -xDuulqqZMA0GCSqGSIb3DQEBCwUAA4IBAQAw6PFWRnMqVd9rYXHHagxDSlIPj8xm -0RoeSHNgl+G8w2c6tXY2gWU0LWdmrdEX4/OpIBcw7USBSwIBCsv6vBU+HGosMhlk -/K6arqvxENu/zafU1P0RMZnjiTmmzRObIsJiijFsgZQC2q6IjZetpPo6UfdHx0Xm -PRrv+SnbkMk93/QCJJAOlodYwAhZqAkishR2fwlDnNDdR2Aj7qQLYuFr3t5Z92ej -M7OPKbd6XudeWVR3FOxi7/fcNu8mttOtfXkFcUPigs8RJDHMEH1mLMrCzQsRMfA+ -BVZiA+hifJn/9KgZXFlsANT+uLuAWmcEimDAcU/xlAf8eZLcntTB2Oep +MIIDhDCCAmygAwIBAgIUMNeYbv9MMCXx9e/o+BO7JYbdHJowDQYJKoZIhvcNAQEL +BQAwSzE6MDgGA1UEAwwxVExTR2VuU2VsZlNpZ25lZFJvb3RDQSAyMDI1LTAyLTI3 +VDE1OjQ0OjU4Ljg4MDUzMDENMAsGA1UEBwwEJCQkJDAeFw0yNTAyMjcxNDQ0NTha +Fw0zNTAyMjUxNDQ0NThaMEsxOjA4BgNVBAMMMVRMU0dlblNlbGZTaWduZWRSb290 +Q0EgMjAyNS0wMi0yN1QxNTo0NDo1OC44ODA1MzAxDTALBgNVBAcMBCQkJCQwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqAwl11OZzzMDh1oaaA/IbnU39 +VCDE3BsKKg3arhVGhYaSbYEtaJWhNbB12qkw2GEFeSl0mZCSorTJmQHmcUjcO0yH +zRIM5vzEscPOffUBfIxXiVehPyyNJa9P2IRE65i3d7mcmR62dG6EWtj1tW0VLKGc +d3STpmGoA9b8tuJZq9vt6ivDTv7OECCLmDR2IHKoAXKZQmsDgI1Dy0UuLCEWIzOq +r8WAq1at28AAiDL9Rh0bxyQ8oREx86zjLOXOsJ8CNNWFRheAFh65hWWMpM4SavEV +pW0kE9qCfopBGl4xpbBLE/gzkhMkUZVwri6cGakzvA+dmdCsS5D3t2ZHWIkzAgMB +AAGjYDBeMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQy +XdLfduEv+NHMZvy4ASFH6BIZWjAfBgNVHSMEGDAWgBQyXdLfduEv+NHMZvy4ASFH +6BIZWjANBgkqhkiG9w0BAQsFAAOCAQEAF0gI9aTScyOoImqvHQZXdfZlgphT8E/o +ks2DDY4ZC1KAIYxRj2y+M9zmrQqSbfhSSuEZ8IKaFKMiBPALBlEVrVJUGAoUAjrU +C9zxSg2TOjJqO2lJD3mMJ0u36cmv0sIPhlm0DRnxWg+1eKmAfEn/DPSj6V8xwHqH +7tpPEea19RgKwBCSOnVUmOwDnIEzCy9H/A8U4P2XzFFEIWSeGWlDHFRy8j4P5YyH +0TRpwR4JGh5t/5+bo4hfdHxSeY5wsWk2k+lfNszfau8qEDdFQVASXmJ6iTelSbqv +pSkMsWk9u8z7ENA+w3Qzhwg1OzOluDl8EVAJziSDfGZpWqeVWK1E4A== -----END CERTIFICATE----- diff --git a/.ci/certs/ca_key.pem b/.ci/certs/ca_key.pem index ebbda3c..ffd51ee 100644 --- a/.ci/certs/ca_key.pem +++ b/.ci/certs/ca_key.pem @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZek5iHZU3WaXd -2SWk2n62ItDAHBxORRHw0IBpEyYG3lxvNzE1A9jWI5RoK29b+/dz4jt/4mEitrhr -+QAEHb1JiIwD/MriUxYiBaVr5YxEWg3d7IPkZ4lDeGGsSNMZzPNgNlAVl6XQG0tg -EjHT0mOvlWV/sWwWmPx+xwy+84a0BJJdThoMm1/rHoIzk19Y5DSe0EGfduAxy7yV -gzSCAbfEvGmFW3+ldjBc4sA/fIbHyEYBdtk9m4BDJCOTNB+YzuIS5Hk2BOzIaW+0 -33mQki3lQ/75jG5zr4FybdMLyC8JbR+jAL4gEE4yiF4jGxAk1/NbZhyJ1EPjyk4s -/LeZlUi3AgMBAAECggEABfZ+hDsi5P69U3GpOofEcBVXh9EBjdx8rYKBnj4kNk1w -Ae6bdtC4x/kVdv+Drk7EPf94JovPSW37fvn5n4Smf142Tto9sJHR0sM7nhQ1fZQg -Vq9moGw8elhe/cTNq0mdaURr06rvUH4bbV3kC3rF+vFLbR6hxqffawvBoMtisbrY -xIp1MfsguOjHLhEDp9crJ+1N5XkqWKZPMpMgPI0mW4Yk+O409+hT5yg/ziNQ5a7o -o86tK3axtPNiaSTPkxoU+sCVu2ILZVTbfjMk2lh7OCgoAA3A9jQ3ulRz+Cl4sXqr -1Ze5pPuRseBL10xmOmoNHR5kvqSNG3Kp6bxyTGiqIQKBgQDPYT2VyaSD4NSA5Es3 -p1DjJa/gItWWIsSSDnpBm5zF908g9rAVxLvx3JLUI4YPFY82o57DCi34jDrE6O0k -SsjuZA2SiuqSqrHxP02RYdAh7/9S/LLM9kakj7QKUU2f92QoyQvKn8M3cygBBz3x -G0uwLE0EU2wgm58SBdnAR6zHuwKBgQC9deipcKT+OG6MZWGhjYsnBiaK8XxxPl9v -Kf2hBmImVDDdOkwthPLKJhP4VhjLETKo8Zi28Nxo6ueOpfn2Q5+0XrI18VNQZI/G -ip6adbezgdIKB0OcWvkXH0Fwl7P7fgNWAPdla9jznSvGj7UnSgt7tFp1xJkXzEfU -n1NEXDNdNQKBgDlOR7RimkGPGWncrCRe6e069tTbC2aHiQZLVeFXXQUfiBA12wbI -7J6zMyfIAT2d2Ythv3dqErYCGiNbslw7BjdKEq4SESwiWzWtJoQsIVWfelC2X7pf -u7mxtDC9stOni1fx5n5Bk7J48e8Gz0kXH905ALdXTiPcnSJf14JYzBgNAoGBAITB -dBAWkGZaYIwcFfc/2Tu1AZjmcY5gaDrar4//ixLUd5Ds4qgauo2PdPrUSXcxS9A5 -ygqWZ7tUroC0KJy48dVPbYyC1yBD9sLmKxCMX/Z2hxjj0ipjTJs5GX+trT4SJIBF -GRWGJnU9sojl9cfcCIPb8m8HHUchq0t/gLcr7AnpAoGARNnUh19fK+8ldKx/h9jN -svJZK1I615OXXRCAY2BWl4k7pbufseVVhtSHxkAfzhmv5gIqojfUGf4WCfcf0eZ9 -xWBXpuWgMwnWuMlPJLwIzlaU1phDsSaHdd2iuAYfZEKWmIIIxT7+vXDO4UaVpXs6 -kso6qBkSwQTymY4m5RATlK4= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDqAwl11OZzzMDh +1oaaA/IbnU39VCDE3BsKKg3arhVGhYaSbYEtaJWhNbB12qkw2GEFeSl0mZCSorTJ +mQHmcUjcO0yHzRIM5vzEscPOffUBfIxXiVehPyyNJa9P2IRE65i3d7mcmR62dG6E +Wtj1tW0VLKGcd3STpmGoA9b8tuJZq9vt6ivDTv7OECCLmDR2IHKoAXKZQmsDgI1D +y0UuLCEWIzOqr8WAq1at28AAiDL9Rh0bxyQ8oREx86zjLOXOsJ8CNNWFRheAFh65 +hWWMpM4SavEVpW0kE9qCfopBGl4xpbBLE/gzkhMkUZVwri6cGakzvA+dmdCsS5D3 +t2ZHWIkzAgMBAAECggEAZMk+D9PMFV/ASwQcIMVGRwJvDoZnPqIVu0D1ipOjciYc +GYC0PBxpJW98OqYcbH8k+jh+1Es3axBMkO8nVFrCKKgZg/ucpJXvk7+EN7EkDqnX +v/PVHAubYocyhE8aWJynv4z/EiUYhziKSNLf0qN7Ab2hNUR1nwnv0W8l7t3NixSY +4sIuGhm2QbqfHKvvG//GWOmvRIYLdJPZ69tJR8sOidIpNY2LI4tNXlC2fdPI+RaT +pOJcULSi+AZItyxHwELDR3u5xuWJ3KMcrBRiMees//dhg8Sga0tmBIW1vN33eKtW +wOkq48hBGUi8sfrRfVSiJBquZFURYrzC1J2EZQOcwQKBgQD3DuZl4NQiGaL3afY6 +fp2hstVjRm8Xdy2AdCPXx5w+R8CJUpLDpHfYavT7pFUx7W1ERfbsqHujMbb2Zaq9 +FyYdXvIpcqFjCJl16dLaDmzvDn98v9mWEB9I+NeqXSBVbbPhCFSuKElpx5OrrxeQ +CUMfofoTtQlHvSYUsz0vm/aiMQKBgQDye0HNNd41NiaXVYUQenNbwOBO+POUBxj2 +pccNTpQulZEXug8IomfDLQ+cA3Dsqlf16tran4wVPUO53n2By07nof/tHV6y0IF9 +oQPznCrbaKl35e4MlvDf0+FfGKNFWExwVeKJJGDVUWgBa+cXFvtUJHYDGb7Bo6+C +5NyqHw6EowKBgCyS056t4ZgFaBGbXIFRNr9ltHokywZAykTSr2TO7rGN4H7mFvSV +R8oUAf8ktvo7C+u1c8de3m+jGI976EIVWxsRdj9kHxnvA0Dy3sfYsm6u/vFS677X +Sc2wl7h09NB06m8/QYfqXNRo3YusG2QxR5r9blD/6Jy405YIgJGGYgkBAoGAGaAp +DhTZTOpSHcAt9dXbByFVE0OACm7NlpNie+eIBXxM/yLsn876BEho0+YRMxG1hgmx +41TlKwF0fNokjWj9B8G5GEf4UBF0/d/cWQxyAwoGjuM/yxjQj/cGZFRoPNXeDikl +bbTofuLBiRTsMSZ+nR/VUPKRlElGLSEeqOPrVt0CgYEAugfHj4AqmDfL1F3rbw+D +XwiyvRqwLBYzxZ0t8Hbm0uht0MKjTgJ/G2fkC7Y8aJgEn8jstfoVSh9sXUKgUnCA +MAuil2220ctEh04bp/na1z/9igJWGiJNbRVXjF+BRAbSJ25RY8BX5dDydcWbqzyr +eT5xfQPC5smWZlI0l0JK1bs= -----END PRIVATE KEY----- diff --git a/.ci/certs/client.p12 b/.ci/certs/client.p12 deleted file mode 100644 index b6b4a0f..0000000 Binary files a/.ci/certs/client.p12 and /dev/null differ diff --git a/.ci/certs/client_certificate.pem b/.ci/certs/client_certificate.pem deleted file mode 100644 index a217a9b..0000000 --- a/.ci/certs/client_certificate.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBMMTswOQYDVQQDDDJUTFNH -ZW5TZWxmU2lnbmVkdFJvb3RDQSAyMDIzLTA5LTExVDIwOjUxOjM5LjYwMzAxMzEN -MAsGA1UEBwwEJCQkJDAeFw0yMzA5MTExODUxNDBaFw0zMzA5MDgxODUxNDBaMDYx -IzAhBgNVBAMMGmdzYW50b21hZ2c2TFZETS52bXdhcmUuY29tMQ8wDQYDVQQKDAZj -bGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDQf+IB3RTjtvdY -3/Rii9zzrY3hCcFcG1k4aOwQAGnE3pgcpRzHaF+l6ZvFX8llP2hcix6ew/IZReDF -p8kuK93PxM0qsYxvCj5fywaGI2mL9sNibrs6CFtvPL+Rj57LSt5UJHSaH3LmY0CE -bV2OdBEuYEBR7eGtzmpupmA+PptHF/U0hTmfIaet6sVLjvJTmD2/3LcztNm/8ksH -iqeHgJDUE+ERWUVl7AEcBo1rHDJw+z/jsKEtKbmqoNxsfcdb2UdZw9cJkB5ojKMr -l73m35s9uIWZxf2iNd3/tqos7cXMLJcTpwr4x6n6F+PsMhBK5sVTw+kFkq+iyxsu -nSVpVT6nAgMBAAGjgb4wgbswTAYDVR0RBEUwQ4IaZ3NhbnRvbWFnZzZMVkRNLnZt -d2FyZS5jb22CGmdzYW50b21hZ2c2TFZETS52bXdhcmUuY29tgglsb2NhbGhvc3Qw -CQYDVR0TBAIwADAfBgNVHSMEGDAWgBQo8lJ1xaWSOfs8+zycR8Q7rpaqmTAdBgNV -HQ4EFgQUX5uJILPEBrNtrLU1pN8YNZrBTp0wCwYDVR0PBAQDAgWgMBMGA1UdJQQM -MAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAbfphg4JZuCmxoAzXhbDdB -48K98aPVv4ABa6L8BrJcWcXo9H/q0puC2+4LtLNAX7M/hri+UcIWDt2nG+9qkUYN -HXZCfpgZo99FbpfmPlk/m7mBqB6YBFYx94VzcHGTipsOVEfSmOMybf+XW8KPJrXd -ZhsbrAu1ev3k85P8jkv7psc+TKG+HjCG3Rqiy73gMpNvWRQriEO2yOa0sx5Pct+l -x0KBlXNLQc37gddV08BgcTn6fsS72FbjMh0GXbG1mZMCA4DxpRlhwkWqaHCbcfFq -3LKBCvuz/ysAEeJoWug8LKHly8p1Gpelot+tj/lFEYryOr6whJI5xoN6VxNHp67S ------END CERTIFICATE----- diff --git a/.ci/certs/client_key.pem b/.ci/certs/client_key.pem deleted file mode 100644 index ab60e2f..0000000 --- a/.ci/certs/client_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQf+IB3RTjtvdY -3/Rii9zzrY3hCcFcG1k4aOwQAGnE3pgcpRzHaF+l6ZvFX8llP2hcix6ew/IZReDF -p8kuK93PxM0qsYxvCj5fywaGI2mL9sNibrs6CFtvPL+Rj57LSt5UJHSaH3LmY0CE -bV2OdBEuYEBR7eGtzmpupmA+PptHF/U0hTmfIaet6sVLjvJTmD2/3LcztNm/8ksH -iqeHgJDUE+ERWUVl7AEcBo1rHDJw+z/jsKEtKbmqoNxsfcdb2UdZw9cJkB5ojKMr -l73m35s9uIWZxf2iNd3/tqos7cXMLJcTpwr4x6n6F+PsMhBK5sVTw+kFkq+iyxsu -nSVpVT6nAgMBAAECggEAI86Cit1j9AN9ERdNCguJA5Q/tHEPcvkDZoumVs0rXPL5 -XpoAikJjgsPOy6O6m5e7HYGK3as7DZoRkwBQrYw9CKx8q6NYQc2Zjqv4T9sPCklz -npjzsHC0+zKMl7v5gmI2Mm8cU0epXOWYs+VwVsGaHcPL6AYZZtb3Mk+CYc4wrpGg -BBzkFhMMRDHAhdz5O0tiV/zawZyqxAXrlgBkIWb+lQZm+cZiC/NGEQNnSErNoHiN -mjaC86jjGaI6TXmn0bte0H+KSUSCfWm3xHJGKFIxV74GzWeS3ZtBOMOTZRwZ9qYU -MS/7YeV26J2tGeC3RhWTyuZm7zrn7ZqGp9QzVWbrSQKBgQD+O24nMmveW9yQnahX -rPWwRZ+E0DDblB0M+CjHfI8bn6JEi56B+akkYjjAYi2JgVWwiWJS9TCbWkk4wEmu -o8IxEL0SH0SuBjLXWwMt4vDM5AmxNqdOyoO3J+Ewapw9zKY6uz8b/FV1eZNPQ9+J -bXQxQODDs5GC3QeR+3uJr3uIEwKBgQDR8wq9SgsBfwuqKxSX3rBA7jx/Xxfl3Qbz -OraCCl8o00C7P1jTVk32K+JmzRB2I6lVhDNNjh0hQkc39P1o6SXWH5OAC98hdZDh -q14qGit/oTtQ3Ps9Sw7dt1AAcSbEpKfkIS0T/uf2c1uTvC7tPzAqHViEG+WUwMuD -gVssX5FpnQKBgDnXzb0vVKmX3vwsUsP3/0Jm5N9z+tnKvj1YLPcOWQUg8euElMDf -y+MSUfU7oT665YMwDuXvEWsXRLeb0GfirGk7dLkt0hOCJ4kmFPgYvU7wx1/Bnpln -rEY81ZiNeRT6fgu41KgKZms/CQws1ixPcfNO3pTIQ2Ax5+oH/Niby5BvAoGALDzK -yYG9ee48FfoH18w7VSMdqjTuQyfkXAHGDPaEgISqwgmh/L3VpYYvqTuSOWJgPr2h -VbkZGDXv7bF4Z8+gglKa8MMPm+w6v+Is8DAddEITzoERiyOymTMT71PoOEz9d0sq -RWlTlRFPfXyMYr8KtgUC7qs2H7bT6vypqlrkt90CgYEA1FtcmoIdj0aUH1Ph3ReQ -HSfG19UDmI0o0CaD0d6k8cjDZnxAMe7rHW9oejIzEB+fLOuQeyGpnWKtUXLTHxYz -HmGAlmshG6r4G0Vs7En0syXB7Kd8ZLAj+oRAobzRVTOj2opiF47mK5z4rO9ymUUS -09Wnq4lVGnpvuTeviY+fA3Y= ------END PRIVATE KEY----- diff --git a/.ci/certs/client_localhost.p12 b/.ci/certs/client_localhost.p12 new file mode 100644 index 0000000..4d84a9b Binary files /dev/null and b/.ci/certs/client_localhost.p12 differ diff --git a/.ci/certs/client_localhost_certificate.pem b/.ci/certs/client_localhost_certificate.pem new file mode 100644 index 0000000..b049ba1 --- /dev/null +++ b/.ci/certs/client_localhost_certificate.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBLMTowOAYDVQQDDDFUTFNH +ZW5TZWxmU2lnbmVkUm9vdENBIDIwMjUtMDItMjdUMTU6NDQ6NTguODgwNTMwMQ0w +CwYDVQQHDAQkJCQkMB4XDTI1MDIyNzE0NDQ1OVoXDTM1MDIyNTE0NDQ1OVowJTES +MBAGA1UEAwwJbG9jYWxob3N0MQ8wDQYDVQQKDAZjbGllbnQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC7L/xjD4iHTCf2IfXd/fayxkX0+dI+Z2y+latM +UFvn4GpDIz0Acfqjp3/NhShbWoHqOhR/w5l20J9Ljt2RmecpybK717Flst8Q0g0C +xm3GaN7fVLAxoWAIbzU7cAZMv0SRuu2RIo2HTt5i2xBljA5Bf6wMZqMFxvnNWNGt +TIWVUzCjeqWqPUi84XdHu0GWyQ11rIjCnw5zY3D8EFc+HoTgI33y81EABps7ybmH +BdUtMsAFEXgk3lJplaLeIvlM/HzBk+ffkqpcwC6kTnoR7Nww8a2aE6wHq91Hj+R7 +mmAo8Hpx0grott/pmwWOd2Ld1w3gxC3I7D6yqjfT4Rjc6FyxAgMBAAGjgdAwgc0w +CQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwKwYD +VR0RBCQwIoIJbG9jYWxob3N0ggpGMjNOMDQ5MlhUgglsb2NhbGhvc3QwMQYDVR0f +BCowKDAmoCSgIoYgaHR0cDovL2NybC1zZXJ2ZXI6ODAwMC9iYXNpYy5jcmwwHQYD +VR0OBBYEFLmThoy0pKufr0QWZRwg1FJGdcFRMB8GA1UdIwQYMBaAFDJd0t924S/4 +0cxm/LgBIUfoEhlaMA0GCSqGSIb3DQEBCwUAA4IBAQCk4Ytqqtymc8h0M2HiIyhK +p2Dkf7GZRjBPvC6ULIxMEixslcDCkVTkLaYKRJL7xv37RNfc6kgi9K1IjPfDUtEm +IDm56hRhIvLkH/BsUbhhJsZnYBN1GbqmFNtNP7Zj2Yt6uAwFkFB6gnK7RflSwVaG +EYZhs8QEmZ1VhGymJorp5HGI6EcVkOhG3pScp5yaAqM2cKy7CLnZJfpCzQ12LZ7/ +2UEKRtfILvN8kWaWOaGCM7t3Z2i6bfEh/1WZBmZnyK+zDBxv/YDp2iave/i7r/dY +tOZA1KB2OMWZY4pHmiEior05yf0o7xNctPdwy3+IvRYAH6FJhMA29XoizPW8Cvtk +-----END CERTIFICATE----- diff --git a/.ci/certs/client_localhost_key.pem b/.ci/certs/client_localhost_key.pem new file mode 100644 index 0000000..9f6dcf4 --- /dev/null +++ b/.ci/certs/client_localhost_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7L/xjD4iHTCf2 +IfXd/fayxkX0+dI+Z2y+latMUFvn4GpDIz0Acfqjp3/NhShbWoHqOhR/w5l20J9L +jt2RmecpybK717Flst8Q0g0Cxm3GaN7fVLAxoWAIbzU7cAZMv0SRuu2RIo2HTt5i +2xBljA5Bf6wMZqMFxvnNWNGtTIWVUzCjeqWqPUi84XdHu0GWyQ11rIjCnw5zY3D8 +EFc+HoTgI33y81EABps7ybmHBdUtMsAFEXgk3lJplaLeIvlM/HzBk+ffkqpcwC6k +TnoR7Nww8a2aE6wHq91Hj+R7mmAo8Hpx0grott/pmwWOd2Ld1w3gxC3I7D6yqjfT +4Rjc6FyxAgMBAAECggEAAvFuHrGbFC4rRQMDg7NvRPV57/Awm84SUeHLtnC0rTiO ++ydrRAicEqV6zISu3dbWYD7RsoY6XA72KODiFMdjxiQCH9LJmtTSjd1mRSL7uAj5 +6MQtsVi9SDdVZy3rpMUaGHolAOlB6pIfzClFFfpQgWZMPSACVAAs+SClH/wqGoRU +LBt8uAwwx9QWqId8nuP4OorAFQYqmmzFb6Q42CanSNObaWcutmIuziOv/P9hRKcx +WbUei3Q5+DjU4ScFGXmyKzP4DxYMJM42jqocZdggHk+eL2yjdR5TmUpnR5WDcjtM +pf5lXzbPsCaFpVsQgHTZgA5OWcaztEtObnINrryb5QKBgQDtOpp5K0kO6HafC9CI +sPDmYyEtfg+IIv9rdEzHmpuj3uyeFBxO3fNE51m1pxGr2DmfekeWzG17lIFIrOkf +t9SAMVPiI5m5mKsPHt3bRE3CjJNjHTvj3tQyF7xDEsJdYyTvEGB5tTKl+t338Wq8 +1H2B0szEr8dEbt1Hf2WFz/lA1QKBgQDJ/7lQnXPC50+U0IhdFk9K/YPE1FOo3ck8 +EIi0S4A0F97N5bCoQ1n+PSCLMGDp3f/QQfX6dh/dnX+SXXlOVNbquKuy5/uIj1Lq +glA0Jj9wKFDVpZG6wziB79TQWQP6TltsQJ4NGwpuPUbyxUQBBg2t3cZy9BJpTJjQ +LUYbSnm6bQKBgQDBi6eOJjeT9ysYdc4sR5gzjzr5X7kSS+Nx6s/dphFHcFBCZIv3 ++HNKiyoQ3362YlIY/+26ZY0JX07fWVtVqmiwMg6LGJqJ5rnhO0CsbRy4FnMFUUuU +jS84s07AtmRnRsVSWl0rzx7Edll0ub1o1ECVk8PG0NbVyVG1zIWq19Q3BQKBgEOa +8LzIVawPmpTlzh3Jj7Q7cNR5c556zBTsO7SL6FaG/qzOiPdnw0DR2Ih9IpJjGHDt +ApRW4IddZQrpeeX7gwp/0AdKmOa1gTy3bHxnqKey9orqpQFqwQjL6d/pSumFPBfY +8IzWVgFbRNmPqBjnm8BrDzX99gOD/Uj/Pg14OZFpAoGAfDfDCsKbW6TJnvbIFkw3 +/V4v6WAwaUS9mECtRb00yzNtn7YbEveQ2/pKPLPZ5z8Vz9pMpuzXfoqFYUEJNsz/ +F2qNaYrvREsDbsLVqFdTTyNHPicilcM8bfmaspI72Tb/YkJNH0/cVhF6H8/J02rr +ValHWT50FbbgAY337QwDjO8= +-----END PRIVATE KEY----- diff --git a/.ci/certs/server.p12 b/.ci/certs/server.p12 deleted file mode 100644 index cc47a6c..0000000 Binary files a/.ci/certs/server.p12 and /dev/null differ diff --git a/.ci/certs/server_certificate.pem b/.ci/certs/server_certificate.pem deleted file mode 100644 index f24e28e..0000000 --- a/.ci/certs/server_certificate.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDzzCCAregAwIBAgIUeRECMRSdARujrzjF1SEBlULbo4IwDQYJKoZIhvcNAQEL -BQAwTDE7MDkGA1UEAwwyVExTR2VuU2VsZlNpZ25lZHRSb290Q0EgMjAyMy0wOS0x -MVQyMDo1MTozOS42MDMwMTMxDTALBgNVBAcMBCQkJCQwHhcNMjUwMjI2MTUyNDA1 -WhcNMjYwMjI2MTUyNDA1WjA2MSMwIQYDVQQDDBpnc2FudG9tYWdnNkxWRE0udm13 -YXJlLmNvbTEPMA0GA1UECgwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAuh/YJzKBgpoAzRl6aoC4iCQZg726OuD/hbDzryhGBTSo/z6M9lFV -WPepDiUQ0wV1t6eNgtBgpZf9s/+EXDsDeCXH+EyFPiLMrTskN/RWDVTAyaTMx+HU -D2Jb8fXrsiKsVHHE2Aol7R5K3ccOXFd4LOxz0C3JJULQYKFTZ5X9Xz0HD4GD2ucf -efqBJItsloUnF2vRD91uhvilxILgotXp6CGd/jH8WxJH3ZvgvWKOO74ei13KJMiq -YrjmJNkxq32sOSUG9bUcG+ATkDka4jAh4uvk5EH+fMkORkWbyebIwXiYC7Zgr0SV -23WV3MNuYylz8M++KktUO9vJA3NpO5YCrwIDAQABo4G+MIG7MEwGA1UdEQRFMEOC -GmdzYW50b21hZ2c2TFZETS52bXdhcmUuY29tghpnc2FudG9tYWdnNkxWRE0udm13 -YXJlLmNvbYIJbG9jYWxob3N0MAkGA1UdEwQCMAAwHwYDVR0jBBgwFoAUKPJSdcWl -kjn7PPs8nEfEO66WqpkwHQYDVR0OBBYEFG5VGCQucC7FqyOJOTzIYtclS9/SMAsG -A1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOC -AQEAemxaoori7vi3+drMpOSSmeqG/lmtupuoX7ysbYlhZenRPGAOiGTEMaafwp7E -bsBFG+vHajY9bLMesVCbKXPJS5KzvAHq4nIQeI7ravUAKUb4wGin6Hsqma6ac7vc -0U8jVGKStr+Ax9Xvu1YbZLIPyrWXL9d8pDsVogGySXKYU9fBfiohcwvdZTnXnHaY -FkpMcYORJywNulz53jHspIjKTf+jCG/LC4pWsyidmNsDUZlDePVB5wR/ycFUGzdi -OjGRZUvQ2+UNmbLdmzfqIHursON8I/7t22ogeCbY/hX1S29zMpNjzZ4XagWDtLUf -BDnD2qWuKVvdPm9YiRckNlWxNQ== ------END CERTIFICATE----- diff --git a/.ci/certs/server_key.pem b/.ci/certs/server_key.pem deleted file mode 100644 index b352f63..0000000 --- a/.ci/certs/server_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC6H9gnMoGCmgDN -GXpqgLiIJBmDvbo64P+FsPOvKEYFNKj/Poz2UVVY96kOJRDTBXW3p42C0GCll/2z -/4RcOwN4Jcf4TIU+IsytOyQ39FYNVMDJpMzH4dQPYlvx9euyIqxUccTYCiXtHkrd -xw5cV3gs7HPQLcklQtBgoVNnlf1fPQcPgYPa5x95+oEki2yWhScXa9EP3W6G+KXE -guCi1enoIZ3+MfxbEkfdm+C9Yo47vh6LXcokyKpiuOYk2TGrfaw5JQb1tRwb4BOQ -ORriMCHi6+TkQf58yQ5GRZvJ5sjBeJgLtmCvRJXbdZXcw25jKXPwz74qS1Q728kD -c2k7lgKvAgMBAAECggEAA45fNA8wJUgpqBbgCA/nNrFbNsarERFCdedLHfd1hbJG -C6vCHWSxbkPmcX7ozYyxp+17c9qPtipAw9h9IBaqkCLx/qmrnrkjKFN/HiiSH6q/ -t4/pWmT+BxXjLtd624pJDktRg6qHTfqsWy9rSVK3LkM4SpZ4B45kFSyP6jnrP49j -vmSHzDISiN4G4GqFK12IbA0nioF5tbuacZVGEAoKcUFxEHQmq1e8b6sjg6uKSexk -GFEpvcBm8XgSYjiBPit/AHKxGqzuO0/BbjvQK7gSeSLLaV6DUdKFi9GFsOQjmedq -DLbxy0CIleCnK0hYwp1SN9gvNBLrOIT5lqZfv4+X0QKBgQDrlRjiNJ6rWwQZcidp -ncStV2f/RVuztAacSYHz+0ZZY72uQh+8+W/7+BDkKsZIV2Bswv2wN9/8mPluvyI8 -OLWf1MOwRC5RUsBJqykRuPzFCoe4aZksBVnQsx82bBpfzQFqspCXw3UAcZcM+dHg -jTkFVsSOeCoQxg2DFN0bwz8/vwKBgQDKQWogOStxgnJisxffSQYH0pHbMmVV/V8+ -OTIhx87YHV+cnWT9JGv+Qsnz2s18E9zj1FEYwBJqAbHd5qytATsvMYB4y3voR03G -AzPeDOdFBNZqQaJyGqYPD+HEuVKYlPx8NC2ew+CEh+h5kPUoO/DgCQJrYA3ZXZKw -dOpieR75EQKBgApgtArq9H5p8QFJ9RCDAbH9IritDoAZEx15Y38i95NigG2Xvhwu -BM/duqjCdZ+kMbw4zsIfg/91oa9OPizW9rFGxyQRrNSqR4w3PQTp2EC52Qa3qCa3 -SaCW824LTxIfTsureBEnbBUL6/KHYsZ4kiV5EAmSo4+/mcLHfYIGlNezAoGAMcw5 -XQW2dJQxpauCzS8llPd7ggS+fpWLxb4/YaHYg813pQ/7tXgqPsgjAS92OH6LfGzi -Kr3fysnwCTqqeU48TDpb72HqeB5WP9K6CooSxyORx0exv3ZgPIUkiVM3yumj4NDY -CqcfuIHd81CFjAp2HDMbrWRRBJvNajTfIK/BuIECgYBJPClPwYzJaXYMlc5rmAgL -lJ9rlbjxRffyLOG4BAsapfGYS789MpoP8WzFPkCNcweXWnI9ftKAE57R/o+a+ov6 -hMlnqI4fij2N9BZuFbVWvpf3th72WU8CE1wpzY10Gp//iePXntJqsiwkr+eStPSL -9H/WjV5NwVwqe+YYC6ABMw== ------END PRIVATE KEY----- diff --git a/.ci/certs/server_localhost.p12 b/.ci/certs/server_localhost.p12 new file mode 100644 index 0000000..437d112 Binary files /dev/null and b/.ci/certs/server_localhost.p12 differ diff --git a/.ci/certs/server_localhost_certificate.pem b/.ci/certs/server_localhost_certificate.pem new file mode 100644 index 0000000..0e72b16 --- /dev/null +++ b/.ci/certs/server_localhost_certificate.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDvDCCAqSgAwIBAgIBATANBgkqhkiG9w0BAQsFADBLMTowOAYDVQQDDDFUTFNH +ZW5TZWxmU2lnbmVkUm9vdENBIDIwMjUtMDItMjdUMTU6NDQ6NTguODgwNTMwMQ0w +CwYDVQQHDAQkJCQkMB4XDTI1MDIyNzE0NDQ1OVoXDTM1MDIyNTE0NDQ1OVowJTES +MBAGA1UEAwwJbG9jYWxob3N0MQ8wDQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCn1MRZTV3ATEvS8jFXhci/HGup4acSa1AduNak +8fpGHSFFmrywY6cl00rmPa95nfGloqbkRydqOwMn1Pv3XfHc3UeaiBgU+FNRj9u6 +NOwJ0zR3QkqLxvQqbjrvxMN/IaZ2WL0Zem+j8YIY9yHytjkLEX2AH9AZLwHpdBLI +vSVeS3BNF/gKpXYExGNNfG47/Lo0fIgwboN069pHY/Ff80SAzUkzRcOxDplJoMWp +wym15ssmAnGzAzTrMhKIJ7rUyaE0ZNAIcid7KQ1VzB+yMpeYz5pdbx0G4U/DuVXf +j8FnwlGwGAw05CckDjZcgrWNgLz1kqEcMV/UEFlbQuEzl5kTAgMBAAGjgdAwgc0w +CQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwKwYD +VR0RBCQwIoIJbG9jYWxob3N0ggpGMjNOMDQ5MlhUgglsb2NhbGhvc3QwHQYDVR0O +BBYEFGv69aUODEtJA5QWU4KalMtGvuGYMB8GA1UdIwQYMBaAFDJd0t924S/40cxm +/LgBIUfoEhlaMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwtc2VydmVyOjgw +MDAvYmFzaWMuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQBQxX+IwLmt9emhC/of3riN +wQaLXGYKKMHcsimGkBsQbitWlwWtBZwR2F9aOlvcOAlFbQ2Enldbdpkens1YwR4k +Fsx2VdOnumSYbq6DKZg0mMrg3AqufYLBGVPSGNksQ6qERZVD5NGATLh0kA9R3q0h +eGKJbHyrdI6fkSELkmBGbuetjmGIfmYh+OjYZhqvU5mutjdOfY9k1t08eRvdNiIB +4HxFVEk/S0opA98LkjY0wjPSAMZAWPNxHD5vHoaI6VwYnxLadD1NcasfEpae6uLW +t7CT+v6rtfBXvczfdd9rmhCmcHR5ckrL/wbpnvgkloQqxclw5IpDt/JkPyGghWx3 +-----END CERTIFICATE----- diff --git a/.ci/certs/server_localhost_key.pem b/.ci/certs/server_localhost_key.pem new file mode 100644 index 0000000..93189d7 --- /dev/null +++ b/.ci/certs/server_localhost_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCn1MRZTV3ATEvS +8jFXhci/HGup4acSa1AduNak8fpGHSFFmrywY6cl00rmPa95nfGloqbkRydqOwMn +1Pv3XfHc3UeaiBgU+FNRj9u6NOwJ0zR3QkqLxvQqbjrvxMN/IaZ2WL0Zem+j8YIY +9yHytjkLEX2AH9AZLwHpdBLIvSVeS3BNF/gKpXYExGNNfG47/Lo0fIgwboN069pH +Y/Ff80SAzUkzRcOxDplJoMWpwym15ssmAnGzAzTrMhKIJ7rUyaE0ZNAIcid7KQ1V +zB+yMpeYz5pdbx0G4U/DuVXfj8FnwlGwGAw05CckDjZcgrWNgLz1kqEcMV/UEFlb +QuEzl5kTAgMBAAECggEAEaH/jRhdSLZbYwrSF011hWqxfxQ3ru46aR0B5CuOLW6j +D8KNn4Sgy48S9/S0KnVnLY1UtngpUnZnwvgUDu2+WwOeocQ5r35VlqSkI8Cqqe+Y +PA1pcp0RCyIwq/9CwOkiqZ1yJKqh7xoRHplcZjkx7hFE28C75uFy9Hme/ZstwWXF +E+6Puia3YcE1CAYiIzrdKDGL+uIVjMfXQue3JybST9CzSPk2mgTq4tGLDON82V3u +RC80YmhSrzgi9/CPBQwE2YtD3zO0RTqTE1s2efP1ApfWZDL09rBWj6P4lplFnjzk +IAW35SbP8zEtnFuMLEui2cSr0ewAPks5x5HflitxhQKBgQDSP6R2iDa1XsxsMCO+ +hAgvIKelzrI1vdOs5OmpQQonL6t0xfFbesAEKxhoRQe73nvgQechLrbTjcAMDemj +F98TC39f3TGMVi2XQMaAkJMt+3NGtYj7OTrfwIB7sFZXg9guO1EG4hEsQbP0P11S +aFEoRp+/0dRVDc5PvHz70sNz1wKBgQDMWiqENzhuk1Ha2XzRpmgLvDqZ5x1rS+2p +LvwcVAEFuK1EoOqcGy8KBYz2HHQg3dbdDlM/ptSaV1YFqB8DGW+pfAHFqiS47YQf +QHSgoYXfHm9rSDkS2gnPLK0V8rN8Ft5umWx5FkT8x7ormaxUVcSg2Dxhe3J4UCh+ +EwJhoXudJQKBgQCpceVIKkt9LOOvpbSJDLvTz4uNk+IIce6w/uRaJjLalg6m1AjK +40jxkxHepxOuk4ZenH58PbvXD/zhOi075jdAkBmd1xTht2qS5f+VCe+0NV0YdaHq +ZptOTUS/asSLT5Tg3alV1MhmVKWFibPagHw364NAAwoPaksF9DD+e0ROjQKBgH4q +VQGYTkEGt4zUphmSEb7dEZkfdaxfDnZbyc97lb4AjQlICFEk/1/CmYsBejkofZWx +WHh9+djofvWzHKJ/O895/mYZa9641c+trdPWpZ5hXgzwZDxdXZ0JSju4wlOkkuPZ +2XzQ4ProHOr6T8kpwuJDXtQYsU3Sv41HEztPxc/5AoGBAIbELsuD0WsEEmQu10hp +fCzzUan8tS5cFYEBXYI6XoUBTHv/TM5Mx+hpEqZFYOALVmYyLEC6eFKYFFZVDZ6t +Up6L91J0AhsInnON3RotUekx4l77woMufYmOSuARqU/+UjkXUfrCXnjK/064HNEO +rRdolD9MXCQidIrqwsPyHprv +-----END PRIVATE KEY----- diff --git a/.ci/conf/enabled_plugins b/.ci/conf/enabled_plugins deleted file mode 100644 index 9d0383d..0000000 --- a/.ci/conf/enabled_plugins +++ /dev/null @@ -1 +0,0 @@ -[rabbitmq_management, rabbitmq_stream, rabbitmq_stream_management]. \ No newline at end of file diff --git a/.ci/conf/rabbitmq.conf b/.ci/conf/rabbitmq.conf deleted file mode 100644 index 6d1d180..0000000 --- a/.ci/conf/rabbitmq.conf +++ /dev/null @@ -1,9 +0,0 @@ -loopback_users.guest = false - -ssl_options.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem -ssl_options.certfile = /etc/rabbitmq/certs/server_certificate.pem -ssl_options.keyfile = /etc/rabbitmq/certs/server_key.pem -listeners.ssl.default = 5671 -stream.listeners.ssl.default = 5551 -ssl_options.verify = verify_peer -ssl_options.fail_if_no_peer_cert = false diff --git a/.ci/publish-documentation-to-github-pages.sh b/.ci/publish-documentation-to-github-pages.sh new file mode 100755 index 0000000..76a54c2 --- /dev/null +++ b/.ci/publish-documentation-to-github-pages.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +. $(pwd)/release-versions.txt + +MESSAGE=$(git log -1 --pretty=%B) +./mvnw buildnumber:create pre-site --no-transfer-progress + +./mvnw javadoc:javadoc -Dmaven.javadoc.skip=false --no-transfer-progress + +if [ -e target/site/apidocs/element-list ] + then cp target/site/apidocs/element-list target/site/apidocs/package-list +fi + +RELEASE_VERSION=$(cat pom.xml | grep -oPm1 "(?<=)[^<]+") + +# GHA does shallow clones, so need the next 2 commands to have the gh-pages branch +git remote set-branches origin 'gh-pages' +git fetch -v + +git checkout gh-pages +mkdir -p $RELEASE_VERSION/htmlsingle +cp target/generated-docs/index.html $RELEASE_VERSION/htmlsingle +mkdir -p $RELEASE_VERSION/api +cp -r target/site/apidocs/* $RELEASE_VERSION/api/ +git add $RELEASE_VERSION/ + +if [[ $LATEST == "true" ]] + then + if [[ $RELEASE_VERSION == *[RCM]* ]] + then + DOC_DIR="milestone" + elif [[ $RELEASE_VERSION == *SNAPSHOT* ]] + then + DOC_DIR="snapshot" + else + DOC_DIR="stable" + fi + + mkdir -p $DOC_DIR/htmlsingle + cp target/generated-docs/index.html $DOC_DIR/htmlsingle + mkdir -p $DOC_DIR/api + cp -r target/site/apidocs/* $DOC_DIR/api/ + git add $DOC_DIR/ + +fi + +git commit -m "$MESSAGE" +git push origin gh-pages +git checkout main diff --git a/.ci/ubuntu/advanced.config b/.ci/ubuntu/advanced.config new file mode 100644 index 0000000..fd7d0fd --- /dev/null +++ b/.ci/ubuntu/advanced.config @@ -0,0 +1,13 @@ +[ + {rabbitmq_auth_backend_oauth2, [{key_config, + [{signing_keys, + #{<<"token-key">> => + {map, + #{<<"alg">> => <<"HS256">>, + <<"k">> => <<"abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH">>, + <<"kid">> => <<"token-key">>, + <<"kty">> => <<"oct">>, + <<"use">> => <<"sig">>, + <<"value">> => <<"token-key">>}}}}]}, + {resource_server_id,<<"rabbitmq">>}]} +]. \ No newline at end of file diff --git a/.ci/ubuntu/enabled_plugins b/.ci/ubuntu/enabled_plugins new file mode 100644 index 0000000..1f453bb --- /dev/null +++ b/.ci/ubuntu/enabled_plugins @@ -0,0 +1 @@ +[rabbitmq_auth_mechanism_ssl,rabbitmq_management,rabbitmq_stream,rabbitmq_stream_management,rabbitmq_top,rabbitmq_auth_backend_oauth2]. diff --git a/.ci/ubuntu/gha-log-check.sh b/.ci/ubuntu/gha-log-check.sh new file mode 100755 index 0000000..3b68178 --- /dev/null +++ b/.ci/ubuntu/gha-log-check.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -o errexit +set -o pipefail +set -o xtrace +set -o nounset + +readonly docker_name_prefix='rabbitmq-amqp-python-client' + +declare -r rabbitmq_docker_name="$docker_name_prefix-rabbitmq" + +if docker logs "$rabbitmq_docker_name" | grep -iF inet_error +then + echo '[ERROR] found inet_error in RabbitMQ logs' 1>&2 + exit 1 +fi diff --git a/.ci/ubuntu/gha-setup.sh b/.ci/ubuntu/gha-setup.sh new file mode 100755 index 0000000..6eb91c8 --- /dev/null +++ b/.ci/ubuntu/gha-setup.sh @@ -0,0 +1,174 @@ +#!/usr/bin/env bash + +set -o errexit +set -o pipefail +set -o xtrace + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir +echo "[INFO] script_dir: '$script_dir'" +readonly rabbitmq_image=rabbitmq:4.1.0-beta.4-management-alpine + + +readonly docker_name_prefix='rabbitmq-amqp-python-client' +readonly docker_network_name="$docker_name_prefix-network" + +if [[ ! -v GITHUB_ACTIONS ]] +then + GITHUB_ACTIONS='false' +fi + +if [[ -d $GITHUB_WORKSPACE ]] +then + echo "[INFO] GITHUB_WORKSPACE is set: '$GITHUB_WORKSPACE'" +else + GITHUB_WORKSPACE="$(cd "$script_dir/../.." && pwd)" + echo "[INFO] set GITHUB_WORKSPACE to: '$GITHUB_WORKSPACE'" +fi + +if [[ $1 == 'toxiproxy' ]] +then + readonly run_toxiproxy='true' +else + readonly run_toxiproxy='false' +fi + +if [[ $2 == 'pull' ]] +then + readonly docker_pull_args='--pull always' +else + readonly docker_pull_args='' +fi + +if [[ $1 == 'stop' ]] +then + docker stop "$rabbitmq_docker_name" + docker stop "$toxiproxy_docker_name" + exit 0 +fi + +set -o nounset + +declare -r rabbitmq_docker_name="$docker_name_prefix-rabbitmq" +declare -r toxiproxy_docker_name="$docker_name_prefix-toxiproxy" + +function start_toxiproxy +{ + if [[ $run_toxiproxy == 'true' ]] + then + # sudo ss -4nlp + echo "[INFO] starting Toxiproxy server docker container" + docker rm --force "$toxiproxy_docker_name" 2>/dev/null || echo "[INFO] $toxiproxy_docker_name was not running" + # shellcheck disable=SC2086 + docker run --detach $docker_pull_args \ + --name "$toxiproxy_docker_name" \ + --hostname "$toxiproxy_docker_name" \ + --publish 8474:8474 \ + --publish 55670-55680:55670-55680 \ + --network "$docker_network_name" \ + 'ghcr.io/shopify/toxiproxy:latest' + fi +} + +function start_rabbitmq +{ + echo "[INFO] starting RabbitMQ server docker container" + chmod 0777 "$GITHUB_WORKSPACE/.ci/ubuntu/log" + docker rm --force "$rabbitmq_docker_name" 2>/dev/null || echo "[INFO] $rabbitmq_docker_name was not running" + # shellcheck disable=SC2086 + docker run --detach $docker_pull_args \ + --name "$rabbitmq_docker_name" \ + --hostname "$rabbitmq_docker_name" \ + --publish 5671:5671 \ + --publish 5672:5672 \ + --publish 15672:15672 \ + --network "$docker_network_name" \ + --volume "$GITHUB_WORKSPACE/.ci/ubuntu/enabled_plugins:/etc/rabbitmq/enabled_plugins" \ + --volume "$GITHUB_WORKSPACE/.ci/ubuntu/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro" \ + --volume "$GITHUB_WORKSPACE/.ci/ubuntu/advanced.config:/etc/rabbitmq/advanced.config:ro" \ + --volume "$GITHUB_WORKSPACE/.ci/certs:/etc/rabbitmq/certs:ro" \ + --volume "$GITHUB_WORKSPACE/.ci/ubuntu/log:/var/log/rabbitmq" \ + "$rabbitmq_image" +} + +function wait_rabbitmq +{ + set +o errexit + set +o xtrace + + declare -i count=12 + while (( count > 0 )) && [[ "$(docker inspect --format='{{.State.Running}}' "$rabbitmq_docker_name")" != 'true' ]] + do + echo '[WARNING] RabbitMQ container is not yet running...' + sleep 5 + (( count-- )) + done + + declare -i count=12 + while (( count > 0 )) && ! docker exec "$rabbitmq_docker_name" epmd -names | grep -F 'name rabbit' + do + echo '[WARNING] epmd is not reporting rabbit name just yet...' + sleep 5 + (( count-- )) + done + + set -o xtrace + + docker exec "$rabbitmq_docker_name" rabbitmqctl await_startup + docker exec "$rabbitmq_docker_name" rabbitmq-diagnostics erlang_version + docker exec "$rabbitmq_docker_name" rabbitmqctl version + + set -o errexit +} + +function get_rabbitmq_id +{ + local rabbitmq_docker_id + rabbitmq_docker_id="$(docker inspect --format='{{.Id}}' "$rabbitmq_docker_name")" + echo "[INFO] '$rabbitmq_docker_name' docker id is '$rabbitmq_docker_id'" + if [[ -v GITHUB_OUTPUT ]] + then + if [[ -f $GITHUB_OUTPUT ]] + then + echo "[INFO] GITHUB_OUTPUT file: '$GITHUB_OUTPUT'" + fi + echo "id=$rabbitmq_docker_id" >> "$GITHUB_OUTPUT" + fi +} + +function install_ca_certificate +{ + set +o errexit + hostname + hostname -s + hostname -f + openssl version + openssl version -d + set -o errexit + + if [[ $GITHUB_ACTIONS == 'true' ]] + then + readonly openssl_store_dir='/usr/lib/ssl/certs' + sudo cp -vf "$GITHUB_WORKSPACE/.ci/certs/ca_certificate.pem" "$openssl_store_dir" + sudo ln -vsf "$openssl_store_dir/ca_certificate.pem" "$openssl_store_dir/$(openssl x509 -hash -noout -in $openssl_store_dir/ca_certificate.pem).0" + else + echo "[WARNING] you must install '$GITHUB_WORKSPACE/.ci/certs/ca_certificate.pem' manually into your trusted root store" + fi + + openssl s_client -connect localhost:5671 \ + -CAfile "$GITHUB_WORKSPACE/.ci/certs/ca_certificate.pem" \ + -cert "$GITHUB_WORKSPACE/.ci/certs/client_localhost_certificate.pem" \ + -key "$GITHUB_WORKSPACE/.ci/certs/client_localhost_key.pem" +} + +docker network create "$docker_network_name" || echo "[INFO] network '$docker_network_name' is already created" + +start_toxiproxy + +start_rabbitmq + +wait_rabbitmq + +get_rabbitmq_id + +install_ca_certificate diff --git a/.ci/ubuntu/log/.gitkeep b/.ci/ubuntu/log/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.ci/ubuntu/rabbitmq.conf b/.ci/ubuntu/rabbitmq.conf new file mode 100644 index 0000000..89b64f4 --- /dev/null +++ b/.ci/ubuntu/rabbitmq.conf @@ -0,0 +1,29 @@ +loopback_users = none +loopback_users.guest = true + +log.console = true +log.console.level = debug +log.file = /var/log/rabbitmq/rabbitmq.log +log.file.level = debug +log.exchange = false + +listeners.tcp.default = 5672 +listeners.ssl.default = 5671 +reverse_dns_lookups = false + +deprecated_features.permit.amqp_address_v1 = false + +ssl_options.cacertfile = /etc/rabbitmq/certs/ca_certificate.pem +ssl_options.certfile = /etc/rabbitmq/certs/server_localhost_certificate.pem +ssl_options.keyfile = /etc/rabbitmq/certs/server_localhost_key.pem +ssl_options.verify = verify_peer +ssl_options.depth = 1 +ssl_options.fail_if_no_peer_cert = false + +#auth_mechanisms.1 = PLAIN +#auth_mechanisms.2 = ANONYMOUS +#auth_mechanisms.3 = EXTERNAL + + +auth_backends.1 = internal +auth_backends.2 = rabbit_auth_backend_oauth2 diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index b6b9108..fdd9930 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -24,38 +24,9 @@ jobs: - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build and export - uses: docker/build-push-action@v6 - with: - context: . - tags: rabbitmq_tls:latest - outputs: type=docker,dest=/tmp/rabbitmq_tls.tar - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: rabbitmq_tls - path: /tmp/rabbitmq_tls.tar - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: rabbitmq_tls - path: /tmp - - name: Load image - run: | - docker load --input /tmp/rabbitmq_tls.tar - docker image ls -a - docker run -d --rm --name rabbitmq-stream-client-test \ - -p 5552:5552 -p 5672:5672 -p 5671:5671 -p 5551:5551 -p 15672:15672 \ - -e RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-rabbitmq_stream advertised_host localhost" \ - rabbitmq_tls - - name: wait for running - run: | - docker exec rabbitmq-stream-client-test /bin/bash -c 'ps -aux' - docker exec rabbitmq-stream-client-test /bin/bash -c 'sleep 10' - docker exec rabbitmq-stream-client-test /bin/bash -c 'rabbitmqctl status' - docker exec rabbitmq-stream-client-test /bin/bash -c 'rabbitmqctl wait --pid 1 --timeout 70' + - name: Start RabbitMQ + id: start-rabbitmq + run: ${{ github.workspace }}/.ci/ubuntu/gha-setup.sh - name: Install and configure Poetry uses: snok/install-poetry@v1 with: diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5adcbfb..0000000 --- a/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM rabbitmq:4-management -#FROM pivotalrabbitmq/rabbitmq:sha-ae9fbb7bd5982aff099293adbb1edcd616ef806f - - -COPY .ci/conf/rabbitmq.conf /etc/rabbitmq/rabbitmq.conf -COPY .ci/conf/enabled_plugins /etc/rabbitmq/enabled_plugins - -COPY .ci/certs /etc/rabbitmq/certs diff --git a/Makefile b/Makefile index 3cc9675..616547b 100644 --- a/Makefile +++ b/Makefile @@ -2,11 +2,10 @@ all: test build rabbitmq-server: - docker build -t rabbitmq-tls-test . - docker run -it --rm --name rabbitmq-tls-test \ - -p 5672:5672 -p 5671:5671 -p 15672:15672 \ - -e RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS="-rabbitmq_stream advertised_host localhost" \ - rabbitmq-tls-test + ./.ci/ubuntu/gha-setup.sh start pull + +rabbitmq-server-stop: + ./.ci/ubuntu/gha-setup.sh stop help: cat Makefile diff --git a/examples/README.md b/examples/README.md index 4c12d6b..123539c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,4 +3,5 @@ Client examples - [Getting started](./getting_started/getting_started.py) - Producer and Consumer example without reconnection - [Reconnection](./reconnection/reconnection_example.py) - Producer and Consumer example with reconnection - [TLS](./tls/tls_example.py) - Producer and Consumer using a TLS connection - - [Streams](./streams/example_with_streams.py) - Example supporting stream capabilities \ No newline at end of file + - [Streams](./streams/example_with_streams.py) - Example supporting stream capabilities + - [Oauth](./oauth/oauth.py) - Connection through Oauth token \ No newline at end of file diff --git a/examples/oauth/oaut.py b/examples/oauth/oaut.py new file mode 100644 index 0000000..56f16e8 --- /dev/null +++ b/examples/oauth/oaut.py @@ -0,0 +1,220 @@ +# type: ignore + + +import base64 +from datetime import datetime, timedelta + +import jwt + +from rabbitmq_amqp_python_client import ( # PosixSSlConfigurationContext,; PosixClientCert, + AddressHelper, + AMQPMessagingHandler, + Connection, + Environment, + Event, + ExchangeSpecification, + ExchangeToQueueBindingSpecification, + Message, + OAuth2Options, + OutcomeState, + QuorumQueueSpecification, +) + +MESSAGES_TO_PUBLISH = 100 + + +class MyMessageHandler(AMQPMessagingHandler): + + def __init__(self): + super().__init__() + self._count = 0 + + def on_amqp_message(self, event: Event): + print("received message: " + str(event.message.body)) + + # accepting + self.delivery_context.accept(event) + + # in case of rejection (+eventually deadlettering) + # self.delivery_context.discard(event) + + # in case of requeuing + # self.delivery_context.requeue(event) + + # annotations = {} + # annotations[symbol('x-opt-string')] = 'x-test1' + # in case of requeuing with annotations added + # self.delivery_context.requeue_with_annotations(event, annotations) + + # in case of rejection with annotations added + # self.delivery_context.discard_with_annotations(event) + + print("count " + str(self._count)) + + self._count = self._count + 1 + + if self._count == MESSAGES_TO_PUBLISH: + print("closing receiver") + # if you want you can add cleanup operations here + + def on_connection_closed(self, event: Event): + # if you want you can add cleanup operations here + print("connection closed") + + def on_link_closed(self, event: Event) -> None: + # if you want you can add cleanup operations here + print("link closed") + + +def create_connection(environment: Environment) -> Connection: + connection = environment.connection() + # in case of SSL enablement + # ca_cert_file = ".ci/certs/ca_certificate.pem" + # client_cert = ".ci/certs/client_certificate.pem" + # client_key = ".ci/certs/client_key.pem" + # connection = Connection( + # "amqps://guest:guest@localhost:5671/", + # ssl_context=PosixSslConfigurationContext( + # ca_cert=ca_cert_file, + # client_cert=ClientCert(client_cert=client_cert, client_key=client_key), + # ), + # ) + connection.dial() + + return connection + + +def main() -> None: + + exchange_name = "test-exchange" + queue_name = "example-queue" + routing_key = "routing-key" + + print("connection to amqp server") + oaut_token = token( + datetime.now() + timedelta(milliseconds=2500), + "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH", + ) + environment = Environment( + uri="amqp://localhost:5672", oauth2_options=OAuth2Options(token=oaut_token) + ) + connection = create_connection(environment) + + # you can refresh the oaut token with the connection api + oaut_token = token( + datetime.now() + timedelta(milliseconds=10000), + "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH", + ) + + connection.refresh_token( + oaut_token, + ) + + management = connection.management() + + print("declaring exchange and queue") + management.declare_exchange(ExchangeSpecification(name=exchange_name)) + + management.declare_queue( + QuorumQueueSpecification(name=queue_name) + # QuorumQueueSpecification(name=queue_name, dead_letter_exchange="dead-letter") + ) + + print("binding queue to exchange") + bind_name = management.bind( + ExchangeToQueueBindingSpecification( + source_exchange=exchange_name, + destination_queue=queue_name, + binding_key=routing_key, + ) + ) + + addr = AddressHelper.exchange_address(exchange_name, routing_key) + + addr_queue = AddressHelper.queue_address(queue_name) + + print("create a publisher and publish a test message") + publisher = connection.publisher(addr) + + print("purging the queue") + messages_purged = management.purge_queue(queue_name) + + print("messages purged: " + str(messages_purged)) + # management.close() + + # publish 10 messages + for i in range(MESSAGES_TO_PUBLISH): + print("publishing") + status = publisher.publish(Message(body="test")) + if status.remote_state == OutcomeState.ACCEPTED: + print("message accepted") + elif status.remote_state == OutcomeState.RELEASED: + print("message not routed") + elif status.remote_state == OutcomeState.REJECTED: + print("message not rejected") + + publisher.close() + + print( + "create a consumer and consume the test message - press control + c to terminate to consume" + ) + consumer = connection.consumer(addr_queue, message_handler=MyMessageHandler()) + + try: + consumer.run() + except KeyboardInterrupt: + pass + + print("cleanup") + consumer.close() + # once we finish consuming if we close the connection we need to create a new one + # connection = create_connection() + management = connection.management() + + print("unbind") + management.unbind(bind_name) + + print("delete queue") + management.delete_queue(queue_name) + + print("delete exchange") + management.delete_exchange(exchange_name) + + print("closing connections") + management.close() + print("after management closing") + environment.close() + print("after connection closing") + + +def token(duration: datetime, token: str) -> str: + # Decode the base64 key + decoded_key = base64.b64decode(token) + + # Define the claims + claims = { + "iss": "unit_test", + "aud": "rabbitmq", + "exp": int(duration.timestamp()), + "scope": ["rabbitmq.configure:*/*", "rabbitmq.write:*/*", "rabbitmq.read:*/*"], + "random": random_string(6), + } + + # Create the token with the claims and sign it + jwt_token = jwt.encode( + claims, decoded_key, algorithm="HS256", headers={"kid": "token-key"} + ) + + return jwt_token + + +# Helper function to generate a random string (replace with your implementation) +def random_string(length: int) -> str: + import random + import string + + return "".join(random.choices(string.ascii_letters + string.digits, k=length)) + + +if __name__ == "__main__": + main() diff --git a/poetry.lock b/poetry.lock index 7dafe77..2068e43 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "black" @@ -6,6 +6,7 @@ version = "24.10.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "black-24.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812"}, {file = "black-24.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea"}, @@ -52,6 +53,7 @@ version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -63,6 +65,7 @@ version = "1.17.1" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -142,6 +145,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -243,6 +247,7 @@ version = "8.1.8" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, @@ -257,6 +262,8 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\" or platform_system == \"Windows\"" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -268,6 +275,8 @@ version = "1.2.2" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, @@ -282,6 +291,7 @@ version = "7.1.2" description = "the modular source code checker: pep8 pyflakes and co" optional = false python-versions = ">=3.8.1" +groups = ["dev"] files = [ {file = "flake8-7.1.2-py2.py3-none-any.whl", hash = "sha256:1cbc62e65536f65e6d754dfe6f1bada7f5cf392d6f5db3c2b85892466c3e7c1a"}, {file = "flake8-7.1.2.tar.gz", hash = "sha256:c586ffd0b41540951ae41af572e6790dbd49fc12b3aa2541685d253d9bd504bd"}, @@ -298,6 +308,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -312,6 +323,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -323,6 +335,7 @@ version = "5.13.2" description = "A Python utility / library to sort Python imports." optional = false python-versions = ">=3.8.0" +groups = ["dev"] files = [ {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, @@ -337,6 +350,7 @@ version = "0.7.0" description = "McCabe checker, plugin for flake8" optional = false python-versions = ">=3.6" +groups = ["dev"] files = [ {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, @@ -348,6 +362,7 @@ version = "0.910" description = "Optional static typing for Python" optional = false python-versions = ">=3.5" +groups = ["dev"] files = [ {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, @@ -389,6 +404,7 @@ version = "0.4.4" description = "Experimental type system extensions for programs checked with the mypy typechecker." optional = false python-versions = ">=2.7" +groups = ["dev"] files = [ {file = "mypy_extensions-0.4.4.tar.gz", hash = "sha256:c8b707883a96efe9b4bb3aaf0dcc07e7e217d7d8368eec4db4049ee9e142f4fd"}, ] @@ -399,6 +415,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -410,6 +427,7 @@ version = "0.12.1" description = "Utility library for gitignore style pattern matching of file paths." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -421,6 +439,7 @@ version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, @@ -437,6 +456,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -452,6 +472,7 @@ version = "2.12.1" description = "Python style guide checker" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pycodestyle-2.12.1-py2.py3-none-any.whl", hash = "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3"}, {file = "pycodestyle-2.12.1.tar.gz", hash = "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"}, @@ -463,6 +484,7 @@ version = "2.22" description = "C parser in Python" optional = false python-versions = ">=3.8" +groups = ["main", "dev"] files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -474,20 +496,40 @@ version = "3.2.0" description = "passive checker of Python programs" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "pyflakes-3.2.0-py2.py3-none-any.whl", hash = "sha256:84b5be138a2dfbb40689ca07e2152deb896a65c3a3e24c251c5c62489568074a"}, {file = "pyflakes-3.2.0.tar.gz", hash = "sha256:1c61603ff154621fb2a9172037d84dca3500def8c8b630657d1701f026f8af3f"}, ] +[[package]] +name = "pyjwt" +version = "2.10.1" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, + {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pytest" -version = "8.3.4" +version = "8.3.5" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ - {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, - {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, + {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, + {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, ] [package.dependencies] @@ -507,6 +549,7 @@ version = "0.39.0" description = "An AMQP based messaging library." optional = false python-versions = "*" +groups = ["main", "dev"] files = [ {file = "python-qpid-proton-0.39.0.tar.gz", hash = "sha256:362055ae6ab4c7f1437247c602757f30328d55c0a6986d5b68ca9798de9fce02"}, {file = "python_qpid_proton-0.39.0-cp38-abi3-macosx_11_0_x86_64.whl", hash = "sha256:f69da296ffa9e3b22f88a53fe9e27c4f4844e088a9f041061bd4f75f74f2a0af"}, @@ -525,6 +568,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -546,6 +590,7 @@ version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +groups = ["dev"] files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -557,6 +602,8 @@ version = "2.2.1" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" +groups = ["dev"] +markers = "python_version < \"3.11\"" files = [ {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, @@ -598,6 +645,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -609,18 +657,19 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["dev"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, ] [package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] [metadata] -lock-version = "2.0" +lock-version = "2.1" python-versions = "^3.9" -content-hash = "9436dade4f0387b0b81013c2b05a4d42241fb0fd9274213567b078447611dd37" +content-hash = "3daaf4d89df7cd9bbd363a611db81f1339f4dfb78f4760b454d939b2b57bdfba" diff --git a/pyproject.toml b/pyproject.toml index 11a8e87..2a28892 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.9" python-qpid-proton = "^0.39.0" +pyjwt = "^2.10.1" [tool.poetry.group.dev.dependencies] flake8 = "^7.1.1" diff --git a/rabbitmq_amqp_python_client/__init__.py b/rabbitmq_amqp_python_client/__init__.py index a41a13d..cdb623a 100644 --- a/rabbitmq_amqp_python_client/__init__.py +++ b/rabbitmq_amqp_python_client/__init__.py @@ -10,6 +10,7 @@ ExchangeSpecification, ExchangeToExchangeBindingSpecification, ExchangeToQueueBindingSpecification, + OAuth2Options, OffsetSpecification, RecoveryConfiguration, StreamOptions, @@ -89,4 +90,5 @@ "Environment", "ExchangeCustomSpecification", "RecoveryConfiguration", + "OAuth2Options", ] diff --git a/rabbitmq_amqp_python_client/common.py b/rabbitmq_amqp_python_client/common.py index 1aa8f73..84a4346 100644 --- a/rabbitmq_amqp_python_client/common.py +++ b/rabbitmq_amqp_python_client/common.py @@ -18,6 +18,7 @@ class CommonValues(enum.Enum): key = "key" queue = "queues" bindings = "bindings" + path_tokens = "/auth/tokens" class ExchangeType(enum.Enum): diff --git a/rabbitmq_amqp_python_client/connection.py b/rabbitmq_amqp_python_client/connection.py index 884c82f..00e96c7 100644 --- a/rabbitmq_amqp_python_client/connection.py +++ b/rabbitmq_amqp_python_client/connection.py @@ -14,7 +14,11 @@ from .address_helper import validate_address from .consumer import Consumer -from .entities import RecoveryConfiguration, StreamOptions +from .entities import ( + OAuth2Options, + RecoveryConfiguration, + StreamOptions, +) from .exceptions import ( ArgumentOutOfRangeException, ValidationCodeException, @@ -60,6 +64,7 @@ def __init__( ssl_context: Union[ PosixSslConfigurationContext, WinSslConfigurationContext, None ] = None, + oauth2_options: Optional[OAuth2Options] = None, recovery_configuration: RecoveryConfiguration = RecoveryConfiguration(), ): """ @@ -93,6 +98,7 @@ def __init__( self._index: int = -1 self._publishers: list[Publisher] = [] self._consumers: list[Consumer] = [] + self._oauth2_options = oauth2_options # Some recovery_configuration validation if recovery_configuration.back_off_reconnect_interval < timedelta(seconds=1): @@ -109,19 +115,8 @@ def _set_environment_connection_list(self, connections: []): # type: ignore def _open_connections(self, reconnect_handlers: bool = False) -> None: logger.debug("inside connection._open_connections creating connection") - if self._recovery_configuration.active_recovery is False: - self._conn = BlockingConnection( - url=self._addr, - urls=self._addrs, - ssl_domain=self._ssl_domain, - ) - else: - self._conn = BlockingConnection( - url=self._addr, - urls=self._addrs, - ssl_domain=self._ssl_domain, - on_disconnection_handler=self._on_disconnection, - ) + + self._create_connection() if reconnect_handlers is True: logger.debug("reconnecting managements, publishers and consumers handlers") @@ -137,6 +132,40 @@ def _open_connections(self, reconnect_handlers: bool = False) -> None: # Update the broken connection and sender in the consumer self._consumers[i]._update_connection(self._conn) + def _create_connection(self) -> None: + + self._user = None + self._password = None + self._mechs = None + + if self._oauth2_options is not None: + # To investigate: normally in case of oauth user should be "" but the internal library gives error + self._user = "no" + self._password = self._oauth2_options.token + self._mechs = "PLAIN" + + if self._recovery_configuration.active_recovery is False: + self._conn = BlockingConnection( + url=self._addr, + urls=self._addrs, + oauth2_options=self._oauth2_options, + ssl_domain=self._ssl_domain, + allowed_mechs=self._mechs, + user=self._user, + password=self._password, + ) + else: + self._conn = BlockingConnection( + url=self._addr, + urls=self._addrs, + oauth2_options=self._oauth2_options, + ssl_domain=self._ssl_domain, + on_disconnection_handler=self._on_disconnection, + allowed_mechs=self._mechs, + user=self._user, + password=self._password, + ) + def dial(self) -> None: """ Establish a connection to the AMQP server. @@ -354,3 +383,24 @@ def active_producers(self) -> int: def active_consumers(self) -> int: """Returns the number of active consumers""" return len(self._consumers) + + def refresh_token(self, token: str) -> None: + """ + Refresh the oauth token + + Args: + token: the oauth token to refresh + + Raises: + ValidationCodeException: If oauth is not enabled + """ + + if self._oauth2_options is None: + raise ValidationCodeException("the connection is not oauth enabled") + + management = self.management() + management.refresh_token(token) + + # update credentials (for reconnection) + self._user = "no" + self._password = self._oauth2_options.token diff --git a/rabbitmq_amqp_python_client/entities.py b/rabbitmq_amqp_python_client/entities.py index 0577375..a9c6a65 100644 --- a/rabbitmq_amqp_python_client/entities.py +++ b/rabbitmq_amqp_python_client/entities.py @@ -257,3 +257,8 @@ class RecoveryConfiguration: active_recovery: bool = True back_off_reconnect_interval: timedelta = timedelta(seconds=5) MaxReconnectAttempts: int = 5 + + +@dataclass +class OAuth2Options: + token: str diff --git a/rabbitmq_amqp_python_client/environment.py b/rabbitmq_amqp_python_client/environment.py index c2adef9..657c7a2 100644 --- a/rabbitmq_amqp_python_client/environment.py +++ b/rabbitmq_amqp_python_client/environment.py @@ -9,7 +9,7 @@ ) from .connection import Connection -from .entities import RecoveryConfiguration +from .entities import OAuth2Options, RecoveryConfiguration from .ssl_configuration import ( PosixSslConfigurationContext, WinSslConfigurationContext, @@ -41,6 +41,7 @@ def __init__( ssl_context: Union[ PosixSslConfigurationContext, WinSslConfigurationContext, None ] = None, + oauth2_options: Optional[OAuth2Options] = None, recovery_configuration: RecoveryConfiguration = RecoveryConfiguration(), ): """ @@ -66,6 +67,7 @@ def __init__( self._ssl_context = ssl_context self._recovery_configuration = recovery_configuration self._connections: list[Connection] = [] + self._oauth2_options = oauth2_options def connection( self, @@ -86,6 +88,7 @@ def connection( uri=self._uri, uris=self._uris, ssl_context=self._ssl_context, + oauth2_options=self._oauth2_options, recovery_configuration=self._recovery_configuration, ) logger.debug("Environment: Creating and returning a new connection") diff --git a/rabbitmq_amqp_python_client/management.py b/rabbitmq_amqp_python_client/management.py index 1c6550b..9ba75ac 100644 --- a/rabbitmq_amqp_python_client/management.py +++ b/rabbitmq_amqp_python_client/management.py @@ -573,3 +573,14 @@ def queue_info(self, name: str) -> QueueInfo: message_count=queue_info["message_count"], consumer_count=queue_info["consumer_count"], ) + + def refresh_token(self, token: str) -> None: + + self.request( + token.encode(), + CommonValues.path_tokens.value, + CommonValues.command_put.value, + [ + CommonValues.response_code_204.value, + ], + ) diff --git a/rabbitmq_amqp_python_client/qpid/proton/_utils.py b/rabbitmq_amqp_python_client/qpid/proton/_utils.py index ff0b504..4be2ff2 100644 --- a/rabbitmq_amqp_python_client/qpid/proton/_utils.py +++ b/rabbitmq_amqp_python_client/qpid/proton/_utils.py @@ -467,7 +467,8 @@ def __init__( ) except ConnectionException: - self.conn.close() + if self.conn is not None: + self.conn.close() if attempt == len(urls): raise continue diff --git a/tests/conftest.py b/tests/conftest.py index 859340c..2552463 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ import os import sys +from datetime import datetime, timedelta from typing import Optional import pytest @@ -9,6 +10,7 @@ AMQPMessagingHandler, Environment, Event, + OAuth2Options, PKCS12Store, PosixClientCert, PosixSslConfigurationContext, @@ -22,6 +24,7 @@ ) from .http_requests import delete_all_connections +from .utils import token os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -36,6 +39,20 @@ def environment(pytestconfig): environment.close() +@pytest.fixture() +def environment_auth(pytestconfig): + token_string = token(datetime.now() + timedelta(milliseconds=2500)) + environment = Environment( + uri="amqp://localhost:5672", + oauth2_options=OAuth2Options(token=token_string), + ) + try: + yield environment + + finally: + environment.close() + + @pytest.fixture() def connection(pytestconfig): environment = Environment(uri="amqp://guest:guest@localhost:5672/") @@ -67,9 +84,9 @@ def connection_with_reconnect(pytestconfig): def ssl_context(pytestconfig): if sys.platform == "win32": return WinSslConfigurationContext( - ca_store=PKCS12Store(path=".ci/certs/ca.p12"), + ca_store=PKCS12Store(path=".ci/certs/server_localhost.p12"), client_cert=WinClientCert( - store=PKCS12Store(path=".ci/certs/client.p12"), + store=PKCS12Store(path=".ci/certs/client_localhost.p12"), disambiguation_method=FriendlyName(name="1"), ), ) @@ -77,8 +94,8 @@ def ssl_context(pytestconfig): return PosixSslConfigurationContext( ca_cert=".ci/certs/ca_certificate.pem", client_cert=PosixClientCert( - client_cert=".ci/certs/client_certificate.pem", - client_key=".ci/certs/client_key.pem", + client_cert=".ci/certs/client_localhost_certificate.pem", + client_key=".ci/certs/client_localhost_key.pem", ), ) diff --git a/tests/test_connection.py b/tests/test_connection.py index 0f18410..10f933b 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -1,15 +1,17 @@ import time -from datetime import timedelta +from datetime import datetime, timedelta from rabbitmq_amqp_python_client import ( ConnectionClosed, Environment, + QuorumQueueSpecification, RecoveryConfiguration, StreamSpecification, ValidationCodeException, ) from .http_requests import delete_all_connections +from .utils import token def on_disconnected(): @@ -43,6 +45,56 @@ def test_connection_ssl(ssl_context) -> None: environment.close() +def test_connection_oauth(environment_auth: Environment) -> None: + + connection = environment_auth.connection() + connection.dial() + management = connection.management() + management.declare_queue(QuorumQueueSpecification(name="test-queue")) + management.close() + connection.close() + + +def test_connection_oauth_with_timeout(environment_auth: Environment) -> None: + + connection = environment_auth.connection() + connection.dial() + # let the token expire + time.sleep(3) + raised = False + # token expired + try: + management = connection.management() + management.declare_queue(QuorumQueueSpecification(name="test-queue")) + management.close() + except Exception: + raised = True + + assert raised is True + + connection.close() + + +def test_connection_oauth_refresh_token(environment_auth: Environment) -> None: + + connection = environment_auth.connection() + connection.dial() + # let the token expire + time.sleep(1) + raised = False + # token expired, refresh + connection.refresh_token(token(datetime.now() + timedelta(milliseconds=5000))) + time.sleep(3) + try: + management = connection.management() + management.declare_queue(QuorumQueueSpecification(name="test-queue")) + except Exception: + raised = True + + assert raised is False + connection.close() + + def test_environment_connections_management() -> None: environment = Environment(uri="amqp://guest:guest@localhost:5672/") diff --git a/tests/utils.py b/tests/utils.py index bc4ecca..2b0a7f0 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,5 +1,9 @@ +import base64 +from datetime import datetime from typing import Optional +import jwt + from rabbitmq_amqp_python_client import ( AddressHelper, Connection, @@ -92,3 +96,32 @@ def cleanup_dead_lettering(management: Management, bind_path: str) -> None: management.unbind(bind_path) management.delete_exchange(exchange_dead_lettering) management.delete_queue(queue_dead_lettering) + + +def token(duration: datetime) -> str: + # Decode the base64 key + decoded_key = base64.b64decode("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGH") + + # Define the claims + claims = { + "iss": "unit_test", + "aud": "rabbitmq", + "exp": int(duration.timestamp()), + "scope": ["rabbitmq.configure:*/*", "rabbitmq.write:*/*", "rabbitmq.read:*/*"], + "random": random_string(6), + } + + # Create the token with the claims and sign it + jwt_token = jwt.encode( + claims, decoded_key, algorithm="HS256", headers={"kid": "token-key"} + ) + + return jwt_token + + +# Helper function to generate a random string (replace with your implementation) +def random_string(length: int) -> str: + import random + import string + + return "".join(random.choices(string.ascii_letters + string.digits, k=length))