diff --git a/.docker/certs/Cenobit_Root_CA.pem b/.docker/certs/Cenobit_Root_CA.pem new file mode 100644 index 00000000..f2f572fc --- /dev/null +++ b/.docker/certs/Cenobit_Root_CA.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFFzCCA3+gAwIBAgIQOWWFM0qvic8JSw9z/XMW+zANBgkqhkiG9w0BAQsFADCB +ozEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTwwOgYDVQQLDDNueWNo +b2xhc0BkcmVhbWVyLmxvY2FsIChOeWNob2xhcyBPbGl2ZWlyYSBPbGl2ZWlyYSkx +QzBBBgNVBAMMOm1rY2VydCBueWNob2xhc0BkcmVhbWVyLmxvY2FsIChOeWNob2xh +cyBPbGl2ZWlyYSBPbGl2ZWlyYSkwHhcNMjEwNjE3MTMwMjQ1WhcNMzEwNjE3MTMw +MjQ1WjCBozEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTwwOgYDVQQL +DDNueWNob2xhc0BkcmVhbWVyLmxvY2FsIChOeWNob2xhcyBPbGl2ZWlyYSBPbGl2 +ZWlyYSkxQzBBBgNVBAMMOm1rY2VydCBueWNob2xhc0BkcmVhbWVyLmxvY2FsIChO +eWNob2xhcyBPbGl2ZWlyYSBPbGl2ZWlyYSkwggGiMA0GCSqGSIb3DQEBAQUAA4IB +jwAwggGKAoIBgQC/VgcpUwHdpe05ivn12+a0q71JaCcbhrSJYXFa3zXTKjymOYS8 +88iEL9X671Dwl+i9phP8bkq1E9HNgFglm9IYbZ6cfdrfc8CH2L+XnZdL5glnHZdz +zTmA1iroWBTw70Y1eMUWjALK4Ib0A7qTHu5kM1gtGojYc1j1Yp4cBm6JdA/mk/Mh +/iWj+voSOeHrjYBbwUioXzVshCQsUgdc4SeH3IGp1WzvJeVI9Te+cFitssvAtI4C +IZboi/wlutjq66jhJp6JUOToXkJ/4kBrh1iTfYOWnQBP3gFG7PqAoiVc5uo+PQ/w +Yx2BilSdc0XfsLhepI1Xg1kp67pd+xf+Jw9mNdqHCYn17ldR2TXo1u8MVmEzt9qs +H7lKFh32RWOsvhFwlIIFdRGj5K6Hg16H3fZot6lpOO5yVyslettlItVWGW+bPEas +hFTX4quDscIJkRXA1yO4i+ZcsP6hWiX3KVbWVHzPd8hLOzQ3H8qvduZUROIej4b9 +pFoNdePFVkORs+ECAwEAAaNFMEMwDgYDVR0PAQH/BAQDAgIEMBIGA1UdEwEB/wQI +MAYBAf8CAQAwHQYDVR0OBBYEFIliX3yj0TrwRJ78zRyNCAev611aMA0GCSqGSIb3 +DQEBCwUAA4IBgQC3y+3/58Z3SZ3myaJWhrkJGdzFGVC4IHnYopptCZIJDXnt4dnX +nvTcoUZ9YVojFkhWVFo/XXecr/PJY/lgqOaPbw1t2Ah5pVgmAR8SD8FVH7vRmFWk +arlf0GXGonwTg/hhv+Fdt6HcjN+bg8XjRbCIJWGmuevnxoZru9HAcATRrhNRmsQ2 +xSUg/jU3k5is7yl/UFXVnElejD9wXMWFNkdpXCV2H81CQAwpSW0mTrq31FUpnHPX +qqVa77OQo2HFQdNwEnqPlBqR9wOGuSn2LrRCm1V+rAF8UhPybU3BesoKk+lWv7wc +0e37Er3sxTzBkdnWQy3/8JSrqyUzn+xlWQ3KkM9rrC28VGQkTx83Dp7ln0ad9vNd +qK0i7xDv4iX7lbeJ38/qAwB5Lfnaw5SNhmVkv+CZKYHPnleixPUrlhP3w7KhVz4r +nAEz1HYZ3pi/xqeWln0D+UfehUc42pcGIW1Ia+CxRut8DM1Mu1KJoSxh9dsk1n7I +cowv7zPb0KBbtJM= +-----END CERTIFICATE----- diff --git a/.docker/nginx/README.md b/.docker/nginx/README.md new file mode 100644 index 00000000..1d1a1adb --- /dev/null +++ b/.docker/nginx/README.md @@ -0,0 +1,18 @@ +# nginx + +### Creating the SSL Certificate + +``` +$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ssl/nginx-ssl.key -out ssl/nginx-ssl.crt +$ openssl dhparam -out ssl/ssl-dhparam.pem 4096 +``` + +### Creating Root CA and Domain Certifications + +See [here](https://github.com/FiloSottile/mkcert#installation) how to install mkcert. +``` +$ mkcert -install +$ mkcert -key-file ssl/flask-jsonrpc.cenobit.es.key -cert-file ssl/flask-jsonrpc.cenobit.es.crt *.flask-jsonrpc.cenobit.es +$ cp -rf "$(mkcert -CAROOT)/rootCA.pem" ../certs/ +``` + diff --git a/.docker/nginx/conf.d/app.flask-jsonrpc.cenobit.es.conf b/.docker/nginx/conf.d/app.flask-jsonrpc.cenobit.es.conf new file mode 100644 index 00000000..fd675cd5 --- /dev/null +++ b/.docker/nginx/conf.d/app.flask-jsonrpc.cenobit.es.conf @@ -0,0 +1,28 @@ +server { + listen 443 ssl; + listen [::]:443 ssl; + + ssl_certificate /etc/nginx/ssl/flask-jsonrpc.cenobit.es.crt; + ssl_certificate_key /etc/nginx/ssl/flask-jsonrpc.cenobit.es.key; + + include ssl/ssl-params.conf; + + server_name app.flask-jsonrpc.cenobit.es; + + location / { + proxy_pass http://app:5000/; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Prefix /; + } +} + +server { + listen 80; + listen [::]:80; + + server_name app.flask-jsonrpc.cenobit.es; + + return 302 https://$server_name$request_uri; +} \ No newline at end of file diff --git a/.docker/nginx/conf.d/async-app.flask-jsonrpc.cenobit.es.conf b/.docker/nginx/conf.d/async-app.flask-jsonrpc.cenobit.es.conf new file mode 100644 index 00000000..cf23722f --- /dev/null +++ b/.docker/nginx/conf.d/async-app.flask-jsonrpc.cenobit.es.conf @@ -0,0 +1,28 @@ +server { + listen 443 ssl; + listen [::]:443 ssl; + + ssl_certificate /etc/nginx/ssl/flask-jsonrpc.cenobit.es.crt; + ssl_certificate_key /etc/nginx/ssl/flask-jsonrpc.cenobit.es.key; + + include ssl/ssl-params.conf; + + server_name async-app.flask-jsonrpc.cenobit.es; + + location / { + proxy_pass http://async_app:5000/; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Prefix /; + } +} + +server { + listen 80; + listen [::]:80; + + server_name async-app.flask-jsonrpc.cenobit.es; + + return 302 https://$server_name$request_uri; +} \ No newline at end of file diff --git a/.docker/nginx/nginx.conf b/.docker/nginx/nginx.conf new file mode 100644 index 00000000..e2944099 --- /dev/null +++ b/.docker/nginx/nginx.conf @@ -0,0 +1,17 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 80; + listen [::]:80; + + server_name 127.0.0.1; + + root /opt/test-static/public; + index index.html; + } + + include /etc/nginx/conf.d/*.conf; +} diff --git a/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.crt b/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.crt new file mode 100644 index 00000000..78a01086 --- /dev/null +++ b/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEjjCCAvagAwIBAgIRAJdntdMRAvSoHdaDsHUxPPkwDQYJKoZIhvcNAQELBQAw +gaMxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE8MDoGA1UECwwzbnlj +aG9sYXNAZHJlYW1lci5sb2NhbCAoTnljaG9sYXMgT2xpdmVpcmEgT2xpdmVpcmEp +MUMwQQYDVQQDDDpta2NlcnQgbnljaG9sYXNAZHJlYW1lci5sb2NhbCAoTnljaG9s +YXMgT2xpdmVpcmEgT2xpdmVpcmEpMB4XDTIzMDMyOTExMTAxOFoXDTI1MDYyOTEx +MTAxOFowcDEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRl +MUUwQwYDVQQLDDxueWNob2xhc0BCUjM2MDAwMk1CUDE2SU40LmxvY2FsIChOeWNo +b2xhcyBPbGl2ZWlyYSBPbGl2ZWlyYSkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDAzjlsHOl7sq9GpCdeNcP9NYKfjBm6SZQviJ/xDJTFQEsQuJ9t85oW +SsLI0SgAXO+o2XndrgjCE+u5CEiRzHnTAb/OHQH4Sp3JizbXnDcRZHOp1ZJPb9Xl +ZiQ7tGFFkm5Dah1Vh5eV1wTfQejfiA4XoN5DY67HkAy1+TRQ7TTXu1C7cG5DMEfQ +7FJaDNKHwQjPtiEoGCoWH0iRy2Z8P0mu0plx8Yuqo5JKWox9vuFEnndhN7kat4I3 +QN6FEnOZSltKx9iUImew8iTWiTgMEYguqWiZqVc2lxE0U7gyQueMFGraPIJT23oS +j5W6eCxfQ93NKFwF4AmqCm/Hmh344TIjAgMBAAGjbzBtMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSJYl98o9E68ESe/M0c +jQgHr+tdWjAlBgNVHREEHjAcghoqLmZsYXNrLWpzb25ycGMuY2Vub2JpdC5lczAN +BgkqhkiG9w0BAQsFAAOCAYEAJ1b/TDRnPk1y0uRIhLdwjtnIdpoTK5sgii5uKXAN +SDdVM+5MPjqv53AeVw7SbylZtkQD3T3QTXHMhRbRl4cxWLV+H/uqkXuClUyWUK2V +AD66v/rlz6A6f2G/K2+JDLWnwuaVgBDOEOC5b4fFICLJMPmeY1VAz11VoMlCiYSC +uRhKubZYHvkGGGvLy/G23jp042+Z3B/X8ofZG8eBXgW/gOfzRKS63wKzK5u58AZ0 +/PzCWb33+KCPfRmwl7a6qCRbN8kiMJGfVYF7teKZ9RZ+LpSb1tCOUjzkEKtxlBkW +HManFwJL8hkhMoYct1UUobzXD4/uUtnCnS/kFHLfj2611dqjPTyE5/KGsSIEONAc +xgZim6dR/B9xT8ug6d7t7VS2VP10vckw6B9m47QOCyEYJZqHZgma3J1sAk6J5pI/ +fgoA2cgXCo+ybnqvrCUpf+COHjO1KP8m4OjuiGGBu01AAfqq7EVV1ypudZZZYWdx +4suMPBGRXjprL/iJu1qsnRlu +-----END CERTIFICATE----- diff --git a/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.key b/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.key new file mode 100644 index 00000000..2e2baee0 --- /dev/null +++ b/.docker/nginx/ssl/flask-jsonrpc.cenobit.es.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAzjlsHOl7sq9G +pCdeNcP9NYKfjBm6SZQviJ/xDJTFQEsQuJ9t85oWSsLI0SgAXO+o2XndrgjCE+u5 +CEiRzHnTAb/OHQH4Sp3JizbXnDcRZHOp1ZJPb9XlZiQ7tGFFkm5Dah1Vh5eV1wTf +QejfiA4XoN5DY67HkAy1+TRQ7TTXu1C7cG5DMEfQ7FJaDNKHwQjPtiEoGCoWH0iR +y2Z8P0mu0plx8Yuqo5JKWox9vuFEnndhN7kat4I3QN6FEnOZSltKx9iUImew8iTW +iTgMEYguqWiZqVc2lxE0U7gyQueMFGraPIJT23oSj5W6eCxfQ93NKFwF4AmqCm/H +mh344TIjAgMBAAECggEAPpbzxyf/l7ti8QC7EvdLP3jOhbK6a2IVa/TjTyIxbXM4 +qx30C4KII/JzFzPm029YEa0/xXoIt9A1tqR8wm/poFkyBdwe1paKcHH0BLxemBPP +BHQjggwhGM42tvcQmMA3cUUSbsCMsgTSPROtYFtPj9YWMTQ7+ebGaZCTri3C+MDy +uSJwB+muzvBXl+OFP3ltfdc+wRvOde4BtSrtmuY25Y8EYU7ZVqkMZ4YNKTWsvptK +r+nmkxxTapoCRLs0N2did0QhKgaKDv7A8Shg+LEX3NURjyfsRWFkzTn4F470S9GY +eXVTPM5kVSbaFv4SLDDTaonzYTY4D0mfJ01PYRZ78QKBgQDSg8/pzviw7xnkpomX +/RQ44o9RLy8MAvbDdhmCJrTV6OKAf6+PY412KvVdqXjqCImLE7+N/7o54zm1Dr1s +52qBEw835EfFbxgjWgvizYVjinNvcF0kBHlhMNAvAVElwh7mSVCj+hW3/tOIwEiD +fy8FELig6/PrEkCKyXguzoRrCwKBgQDqdtulJKWiNapaQNZyU4g7i6DLd74hDNXb +uArzPrdyGnVBGKC/sqr66r7tmH7uGSyBBt2EAR6t9YEhB8GQRY8c+vvmAbGBHLhm +DvAsZ1/RvFNaWS7MaDU4wpxwr5P8gArQAwH6Blc/C1VY0pJpZgszcp9JLw6UFo8h +7aE/BeeESQKBgAzx6hEYlxZaPZ9f1kowV9P/qox1eFEmxYHTe1ITiz80KOl58JPC +rWo8zgXKbBMIH10cA8V5mjwBCwVlOKS/vj7dqyvmr7smT7esy9pQhQlP9DVAFZon +ZEdjtwmM7S6DN1811u2ebUN5OgzItizP31ZSZugOWlt0Ld/a77cBIAdtAoGAS+fY +6hj4KpCXKwrsoiya4TQuBVEp04dVQQHPQbqfJyibp0xy1jzgj8UptL/279xkhQ0U +7+Rw6z6c5HWfW7a5Q1eZ73fC4JrMs/XIGYgKBFuk3I4c0X43JL4UaYLeNRO67Gbc +3/Xnjvh7U/sv/UFVBP3pxYafgJ/hDMnitwWRU0kCgYBm8HzY5Qk7C5kIS4Z8Luym +DDWpBZMviet/PW4lY8kGEBQf0ImtOC24HisncyWOUFnueNeGVXuMu0gXSMT891Z9 +MoboHEBEhBhhlzQN79RCZIrFS0n3Xt86oeOIzZyJJjCyenKEgjy92WjRpRSBNCyC +VxdcxQ1rL411Gba/sr+sEw== +-----END PRIVATE KEY----- diff --git a/.docker/nginx/ssl/nginx-ssl.crt b/.docker/nginx/ssl/nginx-ssl.crt new file mode 100644 index 00000000..f0c3f6e0 --- /dev/null +++ b/.docker/nginx/ssl/nginx-ssl.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDwDCCAqgCCQCoAmvkcyQMdjANBgkqhkiG9w0BAQsFADCBoTELMAkGA1UEBhMC +QlIxFTATBgNVBAgMDFPDg8KjbyBQYXVsbzEVMBMGA1UEBwwMU8ODwqNvIFBhdWxv +MSMwIQYDVQQKDBpDZW5vYml0IFRlY2hub2xvZ2llcywgSW5jLjEMMAoGA1UECwwD +UiZEMRMwEQYDVQQDDApjZW5vYml0LmVzMRwwGgYJKoZIhvcNAQkBFg1oaUBjZW5v +Yml0LmVzMB4XDTIzMDMyOTExMzMyOVoXDTI0MDMyODExMzMyOVowgaExCzAJBgNV +BAYTAkJSMRUwEwYDVQQIDAxTw4PCo28gUGF1bG8xFTATBgNVBAcMDFPDg8KjbyBQ +YXVsbzEjMCEGA1UECgwaQ2Vub2JpdCBUZWNobm9sb2dpZXMsIEluYy4xDDAKBgNV +BAsMA1ImRDETMBEGA1UEAwwKY2Vub2JpdC5lczEcMBoGCSqGSIb3DQEJARYNaGlA +Y2Vub2JpdC5lczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbjCXNz +FEDs86vNCtXZqUjoyEGOHw2ei0kcUAFmdp2LEpE1cWyKHu5xHQZPZed+5UzyoKKu +M/6L8oY5wlAZgeZc+yia3q0BL4XftVA90sjG9UGEIv4hXsuYJwiq8nGnXUdpYxKA +ZhuLTyp3l7M67/Q/RzgZ/1j8itm97iaDuL1mF4bRArFXj/KZw5yloDLMPo7dEr1y +JFLzxXC2rPbFOA9bEazym+/sQL1V1VBTc65HENeGcNUSBM1N62XnP9d+lUIJAe4N +pBetAyMQ0RHWkfJoDD8Jh+J6UlY4/0WEgcaLZsxDSyALHFkbzx1R1uMBaGfSWD8B +EoB/zGmiLBHu278CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAj+5Qwfxz02vnPon7 +2MzjmFE/GVbXf/NKjCP95cXnoJAJ0XiGjkZxeQu6K3+W8NSEdfiQJoe+3sRGlcsn +pKzOpwi1Ce9hQq3bBzNTF2lgk+opznT6VWCotBeTwczOENkk66hvr18eXSvOuTAs +yFSHJnjU86Eu0c4pyWyY5vyFrvdvt5OamlNSThL4K3mniIdPLYNyKHqPy9Y7UKiE +XtmaIFiqvhxqpBVBDmKGXlxIpi/8uFRfAgiT6aNZqStUesmCpFWM2L1Z50t+KcZK +LPce6Xj7r2BsGzODrTNPNAlk1F5J4cGBUmGcY1F/bT3cV3dtoOejALi3shumwEyI +14/yWQ== +-----END CERTIFICATE----- diff --git a/.docker/nginx/ssl/nginx-ssl.key b/.docker/nginx/ssl/nginx-ssl.key new file mode 100644 index 00000000..54fc82ce --- /dev/null +++ b/.docker/nginx/ssl/nginx-ssl.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDG4wlzcxRA7POr +zQrV2alI6MhBjh8NnotJHFABZnadixKRNXFsih7ucR0GT2XnfuVM8qCirjP+i/KG +OcJQGYHmXPsomt6tAS+F37VQPdLIxvVBhCL+IV7LmCcIqvJxp11HaWMSgGYbi08q +d5ezOu/0P0c4Gf9Y/IrZve4mg7i9ZheG0QKxV4/ymcOcpaAyzD6O3RK9ciRS88Vw +tqz2xTgPWxGs8pvv7EC9VdVQU3OuRxDXhnDVEgTNTetl5z/XfpVCCQHuDaQXrQMj +ENER1pHyaAw/CYfielJWOP9FhIHGi2bMQ0sgCxxZG88dUdbjAWhn0lg/ARKAf8xp +oiwR7tu/AgMBAAECggEAXF759rrN/1GN4o76p1POQeSLjZbaaVLnUe9qYRwbnxQG +bZaqtUR5FTlHx7R8mcXf7ZsKbEQINgL9gsW0r8sI/9rTc4pqPWO9lKnP5hKwoTt2 +kRUzw1/wTRK/nlD3BT48Hnyd3OvxgH11KT4eIMoQxBnXlADPDZ9NRc/mmdC3ro62 ++flucLgkF+2Hd9YoT+EWl09a265bXwAWgHR7gHApjVkDA0Svpac9D3d9m5T+y+5S +gKeC0owrpo5p5/Hpj+voyenKG2nkEg+rSXFIx5AEE75NwwBIQ3zxV3az7vZ3Qxcr +7KNIzWy9utbrM7mBuTkCjrC+NKqj4hKzY67t0MjrIQKBgQD9ogrQjCK8BH/HdMQk +HmR2OmVWm7Je/QUZk/nysMVGnB2izsBnvgXUWtcYg7L5I2U/B/XWicd/gpN3CZib +CIaHDhoI8tsNGjhK3nlCy5at8YHSiWyptpuAqYCNhwY6pddZhMKt15yQJn9hUN8y +BffIP1o3xHDpOl6rLxdLKwa2CQKBgQDIvjM1tcujiQbdT67ARaVPYg9B/DpWUmvX +VxXhNNynEkucNxkZYRe0AMKNEaOA17YdYTOexJz51swDCLDu+ViAZxTezCp4NThS +Hvmr1rgY23xszQb/i9uu74zdsBMCB83youXhygrFqXrrxCp4Kedc6PEhUyTsFvVM +cFVolU01hwKBgQDHNEsMKtRpGZuGe2ZF+/zpgsnu8RByXIVx5nAstbvrzlfUdFBX +6r5HYCWKYn3iAXPWwpjKLKKcumC54LaUJWkLj/7QwnVbYBmQbBWPablp2sIdKsRG +s16E+hw2jDNPQsvVyljhEvQrn6DyPQNgFaHcDC3+mPIZrDeIec7wwdVeiQKBgFTg +hsqI6WdmAq8EiADn8bOU5JqjyQ7CnrJYMfoc3ccOZ0XPzh76/IixYZFACf/qNShK +9waEX1MLzQrjW9xPNWplK1/gt3xIFF40lqBfk0t/Uia2seTO/74Br2/0iLTtE9kp +o0VKpi9ZL44MTWCtZxPTslDC1PO16VW+kX480n51AoGBAOnmGW4NQet8RQ+xrU97 +j4aDu7kCxAThWEYXeTeXnfayvmQxyLQLLaTasLCFdjg2ZugiGe2xoDOwYrNTKaqz +6ZiFf8NpcEelaBDZKlu6AuqnTSzHgnBNAeCaLd/6sbI8R9FDV2sftl77Wzqa/m0Q +SA498fqP2guqs8L+TLZ0A298 +-----END PRIVATE KEY----- diff --git a/.docker/nginx/ssl/ssl-dhparam.pem b/.docker/nginx/ssl/ssl-dhparam.pem new file mode 100644 index 00000000..af936e8c --- /dev/null +++ b/.docker/nginx/ssl/ssl-dhparam.pem @@ -0,0 +1,13 @@ +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEAl1mU+XPxrdQiHynGOOwwq/ruzp2+nrNslMzl082/thKDrovwDYB0 +BCftWYW+NsOLQKqrsty8guBVxR34v8HlEFD/vo1zsVw1eTrB/cohSQKKuxhJB30C +veQaJ0iQa2pxpW6YY3+M3SnrMcGedBWGoq39npgKPNBfCciUtwt1rmuY5R2R6ojS +CpU7FoBso13HMrQtaNx1YOJ1J+7bIUXvdR8/2zTsQ3ZiD3IAKc5n+D61BNBTtY/e +/Ck9Mw9ZOBuHyhXkWMpF/o8dvwM1/yac4NxYbxWIbGMGm8ls2YMALBC88wH56wcg +rjQQpM7d76F0cCYqfLnJml2vQFWrSiwcByU4UKIESWQqeKh2YfaWl82I/i6uR9Vr +yJI7FMvPKNoSkl+lFH2Wax6kVEHAsTgsmn89wXCja7AhoGhfvmPfXtbUOSmfzJvJ +gTScR0NoXJdcfpSCbbob6SQsc1r61nR/8JZy9QWJouOd48vb7Tn0+tqS1qbY7Cmj +20YGBDtyx0LKXTZ1nfD8wdENhRqOddOtSq+X5haENYapxMSTMOFlpFsZ3xz4ByHR +1EmcdML/obbkfPJ5EUzRgLv1OskCCFAbc/OqzN4AIbBTPteChZHqqaK3q51AVlup +0vyJ8MEfCjuZIanWFcYEMBja6utfBDDIZz76ue0pEt0P4kcxc0KGUlsCAQI= +-----END DH PARAMETERS----- diff --git a/.docker/nginx/ssl/ssl-params.conf b/.docker/nginx/ssl/ssl-params.conf new file mode 100644 index 00000000..ef866abc --- /dev/null +++ b/.docker/nginx/ssl/ssl-params.conf @@ -0,0 +1,12 @@ +ssl_protocols TLSv1.2; +ssl_prefer_server_ciphers on; +ssl_dhparam /etc/nginx/ssl/ssl-dhparam.pem; +ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384; +ssl_ecdh_curve secp384r1; +ssl_session_timeout 10m; +ssl_session_cache shared:SSL:10m; +ssl_session_tickets off; +ssl_stapling on; +ssl_stapling_verify on; +resolver 1.1.1.1 1.1.2.2 valid=300s; +resolver_timeout 5s; \ No newline at end of file diff --git a/Dockerfile.it b/Dockerfile.it index 6ed30638..b2c1ec8f 100644 --- a/Dockerfile.it +++ b/Dockerfile.it @@ -5,6 +5,7 @@ RUN apk add --no-cache --update --virtual .build-deps \ linux-headers \ ca-certificates \ python3-dev \ + git \ && rm -rf /var/cache/* \ && mkdir /var/cache/apk \ && ln -sf /lib/ld-musl-x86_64.so.1 /usr/bin/ldd \ @@ -14,16 +15,26 @@ WORKDIR /svc COPY requirements/test.txt /svc/ RUN pip install pip setuptools wheel --upgrade \ - && pip wheel --wheel-dir=/svc/wheels -r test.txt + && pip wheel --wheel-dir=/svc/wheels -r test.txt \ + poetry-core>=1.0.0 FROM python:3.10-alpine ENV PYTHONUNBUFFERED=1 \ DEBUG=0 \ + PYTEST_CACHE_DIR=".pytest_cache" \ SITE_DOMAIN="app" \ - SITE_PORT=5000 + SITE_PORT=5000 \ + WEB_URL="http://app:5000" \ + API_URL="http://app:5000/api" \ + BROWSABLE_API_URL="http://app:5000/api/browse" RUN apk add --no-cache --update \ + git \ + chromium \ + chromium-chromedriver \ + ca-certificates \ + openssl \ && rm -rf /var/cache/* \ && mkdir /var/cache/apk \ && ln -sf /lib/ld-musl-x86_64.so.1 /usr/bin/ldd \ @@ -31,6 +42,11 @@ RUN apk add --no-cache --update \ && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \ && echo $TZ > /etc/timezone +ADD .docker/certs/Cenobit_Root_CA.pem /usr/local/share/ca-certificates/Cenobit_Root_CA.pem +RUN openssl x509 -inform PEM -in /usr/local/share/ca-certificates/Cenobit_Root_CA.pem -out /usr/local/share/ca-certificates/Cenobit_Root_CA.crt \ + && chmod 644 /usr/local/share/ca-certificates/Cenobit_Root_CA.crt \ + && update-ca-certificates + WORKDIR /svc COPY --from=builder /svc /svc @@ -52,6 +68,8 @@ RUN pip install pip setuptools wheel --upgrade \ --no-create-home \ -s /bin/false \ flask_user \ + && mkdir -p /app/test-results \ + && mkdir -p /app/.pytest_cache/screenshots \ && chown flask_user:flask_user -R /app USER flask_user diff --git a/Dockerfile.local b/Dockerfile.local index d4a798cc..ad083f4e 100644 --- a/Dockerfile.local +++ b/Dockerfile.local @@ -26,7 +26,8 @@ RUN pip install pip setuptools wheel --upgrade \ FROM python:3.10-alpine ENV PYTHONUNBUFFERED=1 \ - DEBUG=0 + DEBUG=0 \ + FLASK_SERVER_NAME="app:5000" RUN apk add --no-cache --update \ && rm -rf /var/cache/* \ diff --git a/Dockerfile.py310.test b/Dockerfile.py310.test index a8b00434..1c3c46dd 100644 --- a/Dockerfile.py310.test +++ b/Dockerfile.py310.test @@ -11,11 +11,17 @@ COPY requirements/ /code/requirements/ RUN set -ex \ && apk add --no-cache --virtual .build-deps \ gcc \ + g++ \ + cmake \ + rust \ + cargo \ musl-dev \ python3-dev \ - && pip install pip setuptools wheel --upgrade \ + git \ + && pip install pip setuptools --upgrade \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ + poetry-core>=1.0.0 \ && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ diff --git a/Dockerfile.py37.test b/Dockerfile.py37.test index 1523da7e..5ade3340 100644 --- a/Dockerfile.py37.test +++ b/Dockerfile.py37.test @@ -11,11 +11,14 @@ COPY requirements/ /code/requirements/ RUN set -ex \ && apk add --no-cache --virtual .build-deps \ gcc \ + g++ \ musl-dev \ python3-dev \ + git \ && pip install pip setuptools wheel --upgrade \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ + poetry-core>=1.0.0 \ && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ diff --git a/Dockerfile.py38.test b/Dockerfile.py38.test index dfb57219..92f7fcb6 100644 --- a/Dockerfile.py38.test +++ b/Dockerfile.py38.test @@ -13,9 +13,11 @@ RUN set -ex \ gcc \ musl-dev \ python3-dev \ + git \ && pip install pip setuptools wheel --upgrade \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ + poetry-core>=1.0.0 \ && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ diff --git a/Dockerfile.py39.test b/Dockerfile.py39.test index 320f1a87..b2ecef81 100644 --- a/Dockerfile.py39.test +++ b/Dockerfile.py39.test @@ -13,9 +13,11 @@ RUN set -ex \ gcc \ musl-dev \ python3-dev \ + git \ && pip install pip setuptools wheel --upgrade \ && pip install -r requirements/base.txt \ && pip install -r requirements/test.txt \ + poetry-core>=1.0.0 \ && pip install -r requirements/ci.txt \ && apk del .build-deps \ && addgroup -S kuchulu \ diff --git a/docker-compose.it.yml b/docker-compose.it.yml index 2487e46f..8038f518 100644 --- a/docker-compose.it.yml +++ b/docker-compose.it.yml @@ -6,15 +6,20 @@ services: context: . dockerfile: Dockerfile.it environment: - - FLASK_ENV=TESTING - - SITE_DOMAIN=async_app - - SITE_PORT=5000 - - WEB_URL=http://async_app:5000 - - API_URL=http://async_app:5000/api - - BROWSABLE_API_URL=http://async_app:5000/api/browse + - PYTEST_CACHE_DIR=.pytest_cache + - SITE_DOMAIN=async-app.flask-jsonrpc.cenobit.es + - SITE_PORT=80 + - WEB_URL=https://async-app.flask-jsonrpc.cenobit.es + - API_URL=https://async-app.flask-jsonrpc.cenobit.es/api + - BROWSABLE_API_URL=https://async-app.flask-jsonrpc.cenobit.es/api/browse user: ${UID:-0}:${GID:-0} + volumes: + - .pytest_cache/test-results/async_app:/app/test-results + - .pytest_cache/screnshots/async_app:/app/.pytest_cache/screnshots depends_on: - - async_app + - nginx + networks: + - default async_app: build: @@ -23,38 +28,66 @@ services: args: - FLASK_ASYNC=1 environment: - - FLASK_ENV=TESTING - FLASK_SERVER_NAME=async_app:5000 user: ${UID:-0}:${GID:-0} command: > python async_app.py ports: - '5001:5000' + networks: + - default sut: build: context: . dockerfile: Dockerfile.it environment: - - FLASK_ENV=TESTING - - SITE_DOMAIN=app - - SITE_PORT=5000 - - WEB_URL=http://app:5000 - - API_URL=http://app:5000/api - - BROWSABLE_API_URL=http://app:5000/api/browse + - PYTEST_CACHE_DIR=.pytest_cache + - SITE_DOMAIN=app.flask-jsonrpc.cenobit.es + - SITE_PORT=80 + - WEB_URL=https://app.flask-jsonrpc.cenobit.es + - API_URL=https://app.flask-jsonrpc.cenobit.es/api + - BROWSABLE_API_URL=https://app.flask-jsonrpc.cenobit.es/api/browse user: ${UID:-0}:${GID:-0} + volumes: + - .pytest_cache/test-results/app:/app/test-results + - .pytest_cache/screnshots/app:/app/.pytest_cache/screnshots depends_on: - - app + - nginx + networks: + - default app: build: context: . dockerfile: Dockerfile.local environment: - - FLASK_ENV=TESTING - FLASK_SERVER_NAME=app:5000 user: ${UID:-0}:${GID:-0} command: > python app.py ports: - '5000:5000' + networks: + - default + + nginx: + image: nginx:mainline-alpine + ports: + - '80:80' + - '443:443' + volumes: + - .docker/nginx/conf.d:/etc/nginx/conf.d + - .docker/nginx/ssl:/etc/nginx/ssl + - .docker/nginx/nginx.conf:/etc/nginx/nginx.conf + depends_on: + - app + - async_app + networks: + default: + aliases: + - app.flask-jsonrpc.cenobit.es + - async-app.flask-jsonrpc.cenobit.es + +networks: + default: \ No newline at end of file diff --git a/docker-compose.test.yml b/docker-compose.test.yml index bacdd98b..435d469e 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -6,7 +6,6 @@ services: context: . dockerfile: Dockerfile.py310.test environment: - - FLASK_ENV=TESTING - PRAGMA_VERSION=py3.10 command: > sh -c "flake8 src/ tests/ && @@ -21,7 +20,6 @@ services: context: . dockerfile: Dockerfile.py39.test environment: - - FLASK_ENV=TESTING - PRAGMA_VERSION=py3.9 command: > sh -c "flake8 src/ tests/ && @@ -33,7 +31,6 @@ services: context: . dockerfile: Dockerfile.py38.test environment: - - FLASK_ENV=TESTING - PRAGMA_VERSION=py3.8 command: > sh -c "flake8 src/ tests/ && @@ -45,7 +42,6 @@ services: context: . dockerfile: Dockerfile.py37.test environment: - - FLASK_ENV=TESTING - PRAGMA_VERSION=py3.7 command: > sh -c "flake8 src/ tests/ && diff --git a/requirements/ci.txt b/requirements/ci.txt index a3b04c5f..8e55d853 100644 --- a/requirements/ci.txt +++ b/requirements/ci.txt @@ -24,15 +24,15 @@ pylint<2.14.0;python_version<="3.6" # Type check # ------------------------------------------------------------------------------ -mypy==1.1.1;python_version>"3.6" # https://github.com/python/mypy -pytype==2022.10.13;python_version>"3.6" # https://github.com/google/pytype +mypy==1.1.1;python_version>="3.10" # https://github.com/python/mypy +pytype==2022.10.13;python_version>="3.10" # https://github.com/google/pytype types_setuptools==67.6.0.5 # https://github.com/python/typeshed typeguard==2.13.3 # https://github.com/agronholm/typeguard # Security check # ------------------------------------------------------------------------------ -bandit==1.7.4;python_version>"3.6" # https://github.com/PyCQA/bandit -safety==2.3.5;python_version>"3.6" # https://github.com/pyupio/safety +bandit==1.7.4;python_version>="3.10" # https://github.com/PyCQA/bandit +safety==2.3.5;python_version>="3.10" # https://github.com/pyupio/safety # Code quality # ------------------------------------------------------------------------------ diff --git a/requirements/test.txt b/requirements/test.txt index 61877e70..506a1671 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -8,6 +8,7 @@ pytest-xdist<=3.0.2;python_version<="3.6" pytest-sugar==0.9.6 # https://github.com/Frozenball/pytest-sugar pytest-env==0.8.1;python_version>"3.6" # https://github.com/MobileDynasty/pytest-env pytest-env<0.7.0;python_version<="3.6" +pytest-selenium@git+https://github.com/pytest-dev/pytest-selenium.git;python_version>"3.7" # https://github.com/pytest-dev/pytest-selenium mock==5.0.1 # https://github.com/testing-cabal/mock # Type check diff --git a/tests/test_apps/conftest.py b/tests/test_apps/conftest.py index f2874ebe..90de5bc2 100644 --- a/tests/test_apps/conftest.py +++ b/tests/test_apps/conftest.py @@ -25,18 +25,48 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import os +import time import unittest +from pathlib import Path +import urllib3 import requests +from selenium import webdriver + +WEB_DRIVER_SCREENSHOT_DIR = Path(os.environ['PYTEST_CACHE_DIR']) / 'screenshots' class APITestCase(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: + urllib3.disable_warnings() session = requests.Session() session.headers.update( { 'Content-Type': 'application/json', } ) + session.verify = False self.requests = session - self.api_url = os.environ['API_URL'] + + +class WebDriverTestCase(unittest.TestCase): + def setUp(self) -> None: + chrome_prefs = {} + chrome_options = webdriver.ChromeOptions() + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--headless') + chrome_options.add_argument('--disable-gpu') + chrome_options.add_argument('--allow-insecure-localhost') + chrome_options.add_argument('--ignore-certificate-errors') + chrome_options.add_experimental_option('prefs', chrome_prefs) + chrome_options.accept_insecure_certs = True + self.driver = webdriver.Chrome(options=chrome_options) + self.driver.delete_all_cookies() + self.driver.implicitly_wait(10) + self.addCleanup(self.driver.quit) + self.addCleanup(self.take_screenshot) + if not WEB_DRIVER_SCREENSHOT_DIR.exists(): + WEB_DRIVER_SCREENSHOT_DIR.mkdir(parents=True) + + def take_screenshot(self): + self.driver.get_screenshot_as_file(str(WEB_DRIVER_SCREENSHOT_DIR / f'{self.id()}-{time.time()}.png')) diff --git a/tests/test_apps/pytest.ini b/tests/test_apps/pytest.ini index fba6a5da..bbb0972f 100644 --- a/tests/test_apps/pytest.ini +++ b/tests/test_apps/pytest.ini @@ -9,5 +9,3 @@ required_plugins = pytest-xdist pytest-sugar pytest-env -env = - FLASK_ENV=TESTING diff --git a/tests/test_apps/pytest.local.ini b/tests/test_apps/pytest.local.ini index 6f840503..e67e8520 100644 --- a/tests/test_apps/pytest.local.ini +++ b/tests/test_apps/pytest.local.ini @@ -10,7 +10,7 @@ required_plugins = pytest-sugar pytest-env env = - FLASK_ENV=TESTING + PYTEST_CACHE_DIR=.pytest_cache SITE_DOMAIN=localhost SITE_PORT=5000 WEB_URL=http://localhost:5000 diff --git a/tests/test_apps/test_app.py b/tests/test_apps/test_app.py index 3e03e788..e819f89a 100644 --- a/tests/test_apps/test_app.py +++ b/tests/test_apps/test_app.py @@ -25,32 +25,35 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # pylint: disable=duplicate-code,too-many-public-methods +import os import json from .conftest import APITestCase +API_URL = os.environ['API_URL'] + class APITest(APITestCase): def test_greeting(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'}, rv.json()) self.assertEqual(200, rv.status_code) def test_app_greeting_with_different_content_types(self): rv = self.requests.post( - self.api_url, + API_URL, data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}), headers={'Content-Type': 'application/json-rpc'}, ) @@ -58,7 +61,7 @@ def test_app_greeting_with_different_content_types(self): self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}), headers={'Content-Type': 'application/jsonrequest'}, ) @@ -66,7 +69,7 @@ def test_app_greeting_with_different_content_types(self): self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, data=json.dumps({'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 'Flask'}}), headers={'Content-Type': 'application/json'}, ) @@ -74,7 +77,7 @@ def test_app_greeting_with_different_content_types(self): self.assertEqual(200, rv.status_code) def test_greeting_raise_parse_error(self): - rv = self.requests.post(self.api_url, data={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) + rv = self.requests.post(API_URL, data={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) self.assertEqual( { 'id': None, @@ -91,7 +94,7 @@ def test_greeting_raise_parse_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, data="{'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}", headers={'Content-Type': 'application/json'}, ) @@ -115,7 +118,7 @@ def test_greeting_raise_parse_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, data="""[ {'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask'], 'id': '1'}, {'jsonrpc': '2.0', 'method' @@ -143,7 +146,7 @@ def test_greeting_raise_parse_error(self): self.assertEqual(400, rv.status_code) def test_greeting_raise_invalid_request_error(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0'}) self.assertEqual( { 'id': 1, @@ -161,7 +164,7 @@ def test_greeting_raise_invalid_request_error(self): def test_greeting_raise_invalid_params_error(self): rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': 'Wrong'} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': 'Wrong'} ) self.assertEqual( { @@ -180,9 +183,7 @@ def test_greeting_raise_invalid_params_error(self): ) self.assertEqual(400, rv.status_code) - rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]} - ) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': [1]}) self.assertEqual( { 'id': 1, @@ -199,7 +200,7 @@ def test_greeting_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 2}} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': {'name': 2}} ) self.assertEqual( { @@ -217,7 +218,7 @@ def test_greeting_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) def test_greeting_raise_method_not_found_error(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'method-not-found'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'method-not-found'}) self.assertEqual( { 'id': 1, @@ -235,21 +236,19 @@ def test_greeting_raise_method_not_found_error(self): def test_echo(self): rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': ['Python']} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': ['Python']} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'string': 'Flask'}} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'string': 'Flask'}} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Flask'}, rv.json()) self.assertEqual(200, rv.status_code) def test_echo_raise_invalid_params_error(self): - rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'} - ) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': 'Wrong'}) self.assertEqual( { 'id': 1, @@ -267,7 +266,7 @@ def test_echo_raise_invalid_params_error(self): ) self.assertEqual(400, rv.status_code) - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': [1]}) self.assertEqual( { 'id': 1, @@ -284,7 +283,7 @@ def test_echo_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'name': 2}} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo', 'params': {'name': 2}} ) self.assertEqual( { @@ -301,7 +300,7 @@ def test_echo_raise_invalid_params_error(self): ) self.assertEqual(400, rv.status_code) - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.echo'}) self.assertEqual( { 'id': 1, @@ -318,30 +317,28 @@ def test_echo_raise_invalid_params_error(self): self.assertEqual(400, rv.status_code) def test_notify(self): - rv = self.requests.post(self.api_url, json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify'}) + rv = self.requests.post(API_URL, json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify'}) self.assertEqual('', rv.text) self.assertEqual(204, rv.status_code) - rv = self.requests.post( - self.api_url, json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']} - ) + rv = self.requests.post(API_URL, json={'jsonrpc': '2.0', 'method': 'jsonrpc.notify', 'params': ['Some string']}) self.assertEqual('', rv.text) self.assertEqual(204, rv.status_code) def test_not_allow_notify(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify'}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']}, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not allow notify'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, json={'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']} + API_URL, json={'jsonrpc': '2.0', 'method': 'jsonrpc.not_allow_notify', 'params': ['Some string']} ) self.assertEqual( { @@ -362,15 +359,11 @@ def test_not_allow_notify(self): self.assertEqual(400, rv.status_code) def test_fails(self): - rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]} - ) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [2]}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 2}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post( - self.api_url, json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]} - ) + rv = self.requests.post(API_URL, json={'id': '1', 'jsonrpc': '2.0', 'method': 'jsonrpc.fails', 'params': [1]}) self.assertEqual( { 'id': '1', @@ -393,7 +386,7 @@ def test_strange_echo(self): 'method': 'jsonrpc.strangeEcho', 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask'], } - rv = self.requests.post(self.api_url, json=data) + rv = self.requests.post(API_URL, json=data) self.assertEqual( {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Flask']}, rv.json() ) @@ -405,7 +398,7 @@ def test_strange_echo(self): 'method': 'jsonrpc.strangeEcho', 'params': ['string', {'a': 1}, ['a', 'b', 'c'], 23], } - rv = self.requests.post(self.api_url, json=data) + rv = self.requests.post(API_URL, json=data) self.assertEqual( {'id': 1, 'jsonrpc': '2.0', 'result': ['string', {'a': 1}, ['a', 'b', 'c'], 23, 'Default']}, rv.json() ) @@ -413,31 +406,31 @@ def test_strange_echo(self): def test_sum(self): data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.sum', 'params': [1, 3]} - rv = self.requests.post(self.api_url, json=data) + rv = self.requests.post(API_URL, json=data) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 4}, rv.json()) self.assertEqual(200, rv.status_code) data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.sum', 'params': [0.5, 1.5]} - rv = self.requests.post(self.api_url, json=data) + rv = self.requests.post(API_URL, json=data) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 2.0}, rv.json()) self.assertEqual(200, rv.status_code) def test_decorators(self): data = {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.decorators', 'params': ['Python']} - rv = self.requests.post(self.api_url, json=data) + rv = self.requests.post(API_URL, json=data) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python from decorator, ;)'}, rv.json()) self.assertEqual(200, rv.status_code) def test_return_status_code(self): rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCode', 'params': ['OK']} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCode', 'params': ['OK']} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code OK'}, rv.json()) self.assertEqual(201, rv.status_code) def test_return_headers(self): rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnHeaders', 'params': ['OK']} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnHeaders', 'params': ['OK']} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Headers OK'}, rv.json()) self.assertEqual(200, rv.status_code) @@ -445,7 +438,7 @@ def test_return_headers(self): def test_return_status_code_and_headers(self): rv = self.requests.post( - self.api_url, + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.returnStatusCodeAndHeaders', 'params': ['OK']}, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Status Code and Headers OK'}, rv.json()) @@ -454,19 +447,19 @@ def test_return_status_code_and_headers(self): def test_not_validate_method(self): rv = self.requests.post( - self.api_url, + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.not_validate', 'params': ['OK']}, ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Not validate: OK'}, rv.json()) self.assertEqual(200, rv.status_code) def test_with_rcp_batch(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, json=[ {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, {'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, @@ -484,7 +477,7 @@ def test_with_rcp_batch(self): self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, + API_URL, json=[ {'id': 1, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Python']}, {'jsonrpc': '2.0', 'method': 'jsonrpc.greeting', 'params': ['Flask']}, @@ -511,37 +504,33 @@ def test_with_rcp_batch(self): ) self.assertEqual(200, rv.status_code) - rv = self.requests.post(self.api_url, json={'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) + rv = self.requests.post(API_URL, json={'id': 2, 'jsonrpc': '2.0', 'method': 'jsonrpc.greeting'}) self.assertEqual({'id': 2, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'}, rv.json()) self.assertEqual(200, rv.status_code) def test_class(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'classapp.index'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'classapp.index'}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask JSON-RPC'}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']} - ) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'greeting', 'params': ['Python']}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Python'}, rv.json()) self.assertEqual(200, rv.status_code) rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'hello', 'params': {'name': 'Flask'}} + API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'hello', 'params': {'name': 'Flask'}} ) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Hello Flask'}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post( - self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]} - ) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'echo', 'params': ['Python', 1]}) self.assertEqual({'id': 1, 'jsonrpc': '2.0', 'result': 'Python'}, rv.json()) self.assertEqual(200, rv.status_code) - rv = self.requests.post(self.api_url, json={'jsonrpc': '2.0', 'method': 'notify', 'params': ['Python']}) + rv = self.requests.post(API_URL, json={'jsonrpc': '2.0', 'method': 'notify', 'params': ['Python']}) self.assertEqual(204, rv.status_code) - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'fails', 'params': [13]}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'fails', 'params': [13]}) self.assertEqual( { 'id': 1, @@ -558,7 +547,7 @@ def test_class(self): self.assertEqual(500, rv.status_code) def test_system_describe(self): - rv = self.requests.post(self.api_url, json={'id': 1, 'jsonrpc': '2.0', 'method': 'system.describe'}) + rv = self.requests.post(API_URL, json={'id': 1, 'jsonrpc': '2.0', 'method': 'system.describe'}) json_data = rv.json() self.assertEqual(1, json_data['id']) self.assertEqual('2.0', json_data['jsonrpc']) diff --git a/tests/test_apps/test_browse.py b/tests/test_apps/test_browse.py new file mode 100644 index 00000000..cee9a7c1 --- /dev/null +++ b/tests/test_apps/test_browse.py @@ -0,0 +1,41 @@ +# Copyright (c) 2022-2022, Cenobit Technologies, Inc. http://cenobit.es/ +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# * Neither the name of the Cenobit Technologies nor the names of +# its contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# pylint: disable=duplicate-code,too-many-public-methods +import os + +from selenium.webdriver.common.by import By + +from .conftest import WebDriverTestCase + +BROWSABLE_API_URL = os.environ['BROWSABLE_API_URL'] + + +class WebBrowsableAPITest(WebDriverTestCase): + def test_index(self): + self.driver.get(BROWSABLE_API_URL) + logo_link = self.driver.find_element(By.XPATH, '//*[@id="logo-link"]') + self.assertEqual('Web browsable API', logo_link.text)