*/saml: fully migrate to xmlsec, remove signxml dependency
This commit is contained in:
		| @ -10,8 +10,7 @@ extension-pkg-whitelist=lxml,xmlsec | |||||||
| const-rgx=[a-zA-Z0-9_]{1,40}$ | const-rgx=[a-zA-Z0-9_]{1,40}$ | ||||||
|  |  | ||||||
| ignored-modules=django-otp | ignored-modules=django-otp | ||||||
| generated-members=xmlsec.constants.*,xmlsec.tree.* | generated-members=xmlsec.constants.*,xmlsec.tree.*,xmlsec.template.* | ||||||
| ignore=migrations | ignore=migrations | ||||||
| max-attributes=12 | max-attributes=12 | ||||||
|  | max-branches=20 | ||||||
| jobs=12 |  | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							| @ -35,7 +35,6 @@ qrcode = "*" | |||||||
| requests-oauthlib = "*" | requests-oauthlib = "*" | ||||||
| sentry-sdk = "*" | sentry-sdk = "*" | ||||||
| service_identity = "*" | service_identity = "*" | ||||||
| signxml = "*" |  | ||||||
| structlog = "*" | structlog = "*" | ||||||
| swagger-spec-validator = "*" | swagger-spec-validator = "*" | ||||||
| urllib3 = {extras = ["secure"],version = "*"} | urllib3 = {extras = ["secure"],version = "*"} | ||||||
|  | |||||||
							
								
								
									
										125
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										125
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|     "_meta": { |     "_meta": { | ||||||
|         "hash": { |         "hash": { | ||||||
|             "sha256": "e8817160c5045ec2c6a1b0f355fb3fb8d25733e2fe6872f3a333741e1c8d15f1" |             "sha256": "db46a3d60f8aaad487f4ec1f65c454fd03da204eceb9d2c98c412c3029941261" | ||||||
|         }, |         }, | ||||||
|         "pipfile-spec": 6, |         "pipfile-spec": 6, | ||||||
|         "requires": { |         "requires": { | ||||||
| @ -28,6 +28,7 @@ | |||||||
|                 "sha256:5b9062d5c0812335c75434bf17ce33d7a20ecfedaa0733faec7379868eb4068a", |                 "sha256:5b9062d5c0812335c75434bf17ce33d7a20ecfedaa0733faec7379868eb4068a", | ||||||
|                 "sha256:fcd5b3baeeb7fc19b3486ff6d10543099d40ae1f5c9196eae695d1cde1b2f784" |                 "sha256:fcd5b3baeeb7fc19b3486ff6d10543099d40ae1f5c9196eae695d1cde1b2f784" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==5.0.2" |             "version": "==5.0.2" | ||||||
|         }, |         }, | ||||||
|         "asgiref": { |         "asgiref": { | ||||||
| @ -35,6 +36,7 @@ | |||||||
|                 "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", |                 "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", | ||||||
|                 "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" |                 "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==3.3.1" |             "version": "==3.3.1" | ||||||
|         }, |         }, | ||||||
|         "async-timeout": { |         "async-timeout": { | ||||||
| @ -42,6 +44,7 @@ | |||||||
|                 "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", |                 "sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f", | ||||||
|                 "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" |                 "sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_full_version >= '3.5.3'", | ||||||
|             "version": "==3.0.1" |             "version": "==3.0.1" | ||||||
|         }, |         }, | ||||||
|         "attrs": { |         "attrs": { | ||||||
| @ -49,6 +52,7 @@ | |||||||
|                 "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", |                 "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", | ||||||
|                 "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" |                 "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==20.3.0" |             "version": "==20.3.0" | ||||||
|         }, |         }, | ||||||
|         "autobahn": { |         "autobahn": { | ||||||
| @ -56,6 +60,7 @@ | |||||||
|                 "sha256:24ce276d313e84d68241c3aef30d484f352b90a40168981b3640312c821df77b", |                 "sha256:24ce276d313e84d68241c3aef30d484f352b90a40168981b3640312c821df77b", | ||||||
|                 "sha256:86bbce30cdd407137c57670993a8f9bfdfe3f8e994b889181d85e844d5aa8dfb" |                 "sha256:86bbce30cdd407137c57670993a8f9bfdfe3f8e994b889181d85e844d5aa8dfb" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==20.7.1" |             "version": "==20.7.1" | ||||||
|         }, |         }, | ||||||
|         "automat": { |         "automat": { | ||||||
| @ -74,23 +79,25 @@ | |||||||
|         }, |         }, | ||||||
|         "boto3": { |         "boto3": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:b091cf6581dc137f100789240d628a105c989cf8f559b863fd15e18c1a29b714" |                 "sha256:51c419d890ae216b9b031be31f3182739dc3deb5b64351f286bffca2818ddb35", | ||||||
|  |                 "sha256:d70d21ea137d786e84124639a62be42f92f4b09472ebfb761156057c92dc5366" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==1.16.17" |             "version": "==1.16.18" | ||||||
|         }, |         }, | ||||||
|         "botocore": { |         "botocore": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:33f650b2d63cc1f2d5239947c9ecdadfd8ceeb4ab8bdefa0a711ac175a43bf44", |                 "sha256:288d43e85f12e3c1d6a0535a585a182ca04e8c6e742ebaaf15357a0e3b37ca7a", | ||||||
|                 "sha256:81184afc24d19d730c1ded84513fbfc9e88409c329de5df1151bb45ac30dfce4" |                 "sha256:bba18b5c4eef3eb2dc39b1b1f8959ba01ac27e7e12e413e281b0fb242990c0f5" | ||||||
|             ], |             ], | ||||||
|             "version": "==1.19.17" |             "version": "==1.19.18" | ||||||
|         }, |         }, | ||||||
|         "cachetools": { |         "cachetools": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:513d4ff98dd27f85743a8dc0e92f55ddb1b49e060c2d5961512855cda2c01a98", |                 "sha256:513d4ff98dd27f85743a8dc0e92f55ddb1b49e060c2d5961512855cda2c01a98", | ||||||
|                 "sha256:bbaa39c3dede00175df2dc2b03d0cf18dd2d32a7de7beb68072d13043c9edb20" |                 "sha256:bbaa39c3dede00175df2dc2b03d0cf18dd2d32a7de7beb68072d13043c9edb20" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version ~= '3.5'", | ||||||
|             "version": "==4.1.1" |             "version": "==4.1.1" | ||||||
|         }, |         }, | ||||||
|         "celery": { |         "celery": { | ||||||
| @ -177,6 +184,7 @@ | |||||||
|                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", |                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", | ||||||
|                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" |                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==7.1.2" |             "version": "==7.1.2" | ||||||
|         }, |         }, | ||||||
|         "click-didyoumean": { |         "click-didyoumean": { | ||||||
| @ -253,6 +261,7 @@ | |||||||
|                 "sha256:0052c9887600c57054a5867d4b0240159fa009faa3bcf6a1627271d9cdcb005a", |                 "sha256:0052c9887600c57054a5867d4b0240159fa009faa3bcf6a1627271d9cdcb005a", | ||||||
|                 "sha256:c22b692707f514de9013651ecb687f2abe4f35cf6fe292ece634e9f1737bc7e3" |                 "sha256:c22b692707f514de9013651ecb687f2abe4f35cf6fe292ece634e9f1737bc7e3" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==3.0.1" |             "version": "==3.0.1" | ||||||
|         }, |         }, | ||||||
|         "defusedxml": { |         "defusedxml": { | ||||||
| @ -380,13 +389,6 @@ | |||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==1.19.4" |             "version": "==1.19.4" | ||||||
|         }, |         }, | ||||||
|         "eight": { |  | ||||||
|             "hashes": [ |  | ||||||
|                 "sha256:0a7f0e7725f2a478a97676cf9c49266d95f922f8ed621ec314eeccb333927dc2", |  | ||||||
|                 "sha256:d148aa1fac6cafb5ff806ff634914b05e3f9357aa8dbd82cd7908821d7f93f43" |  | ||||||
|             ], |  | ||||||
|             "version": "==1.0.0" |  | ||||||
|         }, |  | ||||||
|         "facebook-sdk": { |         "facebook-sdk": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e", |                 "sha256:2e987b3e0f466a6f4ee77b935eb023dba1384134f004a2af21f1cfff7fe0806e", | ||||||
| @ -399,6 +401,7 @@ | |||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" |                 "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==0.18.2" |             "version": "==0.18.2" | ||||||
|         }, |         }, | ||||||
|         "google-auth": { |         "google-auth": { | ||||||
| @ -406,6 +409,7 @@ | |||||||
|                 "sha256:5176db85f1e7e837a646cd9cede72c3c404ccf2e3373d9ee14b2db88febad440", |                 "sha256:5176db85f1e7e837a646cd9cede72c3c404ccf2e3373d9ee14b2db88febad440", | ||||||
|                 "sha256:b728625ff5dfce8f9e56a499c8a4eb51443a67f20f6d28b67d5774c310ec4b6b" |                 "sha256:b728625ff5dfce8f9e56a499c8a4eb51443a67f20f6d28b67d5774c310ec4b6b" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.23.0" |             "version": "==1.23.0" | ||||||
|         }, |         }, | ||||||
|         "gunicorn": { |         "gunicorn": { | ||||||
| @ -472,6 +476,7 @@ | |||||||
|                 "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390", |                 "sha256:e64be68255234bb489a574c4f2f8df7029c98c81ec4d160d6cd836e7f0679390", | ||||||
|                 "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298" |                 "sha256:e82d6b930e02e80e5109b678c663a9ed210680ded81c1abaf54635d88d1da298" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.1.0" |             "version": "==1.1.0" | ||||||
|         }, |         }, | ||||||
|         "httptools": { |         "httptools": { | ||||||
| @ -517,6 +522,7 @@ | |||||||
|                 "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417", |                 "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417", | ||||||
|                 "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2" |                 "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==0.5.1" |             "version": "==0.5.1" | ||||||
|         }, |         }, | ||||||
|         "itypes": { |         "itypes": { | ||||||
| @ -531,6 +537,7 @@ | |||||||
|                 "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", |                 "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0", | ||||||
|                 "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" |                 "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==2.11.2" |             "version": "==2.11.2" | ||||||
|         }, |         }, | ||||||
|         "jmespath": { |         "jmespath": { | ||||||
| @ -538,6 +545,7 @@ | |||||||
|                 "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", |                 "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9", | ||||||
|                 "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f" |                 "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==0.10.0" |             "version": "==0.10.0" | ||||||
|         }, |         }, | ||||||
|         "jsonschema": { |         "jsonschema": { | ||||||
| @ -552,20 +560,24 @@ | |||||||
|                 "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006", |                 "sha256:6dc509178ac4269b0e66ab4881f70a2035c33d3a622e20585f965986a5182006", | ||||||
|                 "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c" |                 "sha256:f4965fba0a4718d47d470beeb5d6446e3357a62402b16c510b6a2f251e05ac3c" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==5.0.2" |             "version": "==5.0.2" | ||||||
|         }, |         }, | ||||||
|         "kubernetes": { |         "kubernetes": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:72f095a1cd593401ff26b3b8d71749340394ca6d8413770ea28ce18efd5bcf4c", |                 "sha256:23c85d8571df8f56e773f1a413bc081537536dc47e2b5e8dc2e6262edb2c57ca", | ||||||
|                 "sha256:9a339a32d6c79e6461cb6050c3662cb4e33058b508d8d34ee5d5206add395828" |                 "sha256:ec52ea01d52e2ec3da255992f7e859f3a76f2bdb51cf65ba8cd71dfc309d8daa" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==12.0.0" |             "version": "==12.0.1" | ||||||
|         }, |         }, | ||||||
|         "ldap3": { |         "ldap3": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  |                 "sha256:8f59a7b5399555b22db06f153daa76c77ded2dd84bc0f0ffe5b0b33901b6eac4", | ||||||
|  |                 "sha256:7c3738570766f5e5e74a56fade15470f339d5c436d821cf476ef27da0a4de8b0", | ||||||
|                 "sha256:37d633e20fa360c302b1263c96fe932d40622d0119f1bddcb829b03462eeeeb7", |                 "sha256:37d633e20fa360c302b1263c96fe932d40622d0119f1bddcb829b03462eeeeb7", | ||||||
|                 "sha256:7c3738570766f5e5e74a56fade15470f339d5c436d821cf476ef27da0a4de8b0" |                 "sha256:10bdd23b612e942ce90ea4dbc744dfd88735949833e46c5467a2dcf68e60f469", | ||||||
|  |                 "sha256:bed71c6ce2f70a00a330eed0c8370664c065239d45bcbe1b82517b6f6eed7f25" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==2.8.1" |             "version": "==2.8.1" | ||||||
| @ -649,6 +661,7 @@ | |||||||
|                 "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", |                 "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", | ||||||
|                 "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" |                 "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.1.1" |             "version": "==1.1.1" | ||||||
|         }, |         }, | ||||||
|         "msgpack": { |         "msgpack": { | ||||||
| @ -679,6 +692,7 @@ | |||||||
|                 "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", |                 "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", | ||||||
|                 "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" |                 "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==3.1.0" |             "version": "==3.1.0" | ||||||
|         }, |         }, | ||||||
|         "packaging": { |         "packaging": { | ||||||
| @ -701,6 +715,7 @@ | |||||||
|                 "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c", |                 "sha256:25c95d2ac813909f813c93fde734b6e44406d1477a9faef7c915ff37d39c0a8c", | ||||||
|                 "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63" |                 "sha256:7debb9a521e0b1ee7d2fe96ee4bd60ef03c6492784de0547337ca4433e46aa63" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_full_version >= '3.6.1'", | ||||||
|             "version": "==3.0.8" |             "version": "==3.0.8" | ||||||
|         }, |         }, | ||||||
|         "psycopg2-binary": { |         "psycopg2-binary": { | ||||||
| @ -746,15 +761,37 @@ | |||||||
|         }, |         }, | ||||||
|         "pyasn1": { |         "pyasn1": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  |                 "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba", | ||||||
|  |                 "sha256:78fa6da68ed2727915c4767bb386ab32cdba863caa7dbe473eaae45f9959da86", | ||||||
|  |                 "sha256:08c3c53b75eaa48d71cf8c710312316392ed40899cb34710d092e96745a358b7", | ||||||
|  |                 "sha256:6e7545f1a61025a4e58bb336952c5061697da694db1cae97b116e9c46abcf7c8", | ||||||
|  |                 "sha256:99fcc3c8d804d1bc6d9a099921e39d827026409a58f2a720dcdb89374ea0c776", | ||||||
|  |                 "sha256:e89bf84b5437b532b0803ba5c9a5e054d21fec423a89952a74f87fa2c9b7bce2", | ||||||
|  |                 "sha256:5c9414dcfede6e441f7e8f81b43b34e834731003427e5b09e4e00e3172a10f00", | ||||||
|                 "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", |                 "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d", | ||||||
|                 "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba" |                 "sha256:fec3e9d8e36808a28efb59b489e4528c10ad0f480e57dcc32b4de5c9d8c9fdf3", | ||||||
|  |                 "sha256:014c0e9976956a08139dc0712ae195324a75e142284d5f87f1a87ee1b068a359", | ||||||
|  |                 "sha256:7ab8a544af125fb704feadb008c99a88805126fb525280b2270bb25cc1d78a12", | ||||||
|  |                 "sha256:03840c999ba71680a131cfaee6fab142e1ed9bbd9c693e285cc6aca0d555e576", | ||||||
|  |                 "sha256:0458773cfe65b153891ac249bcf1b5f8f320b7c2ce462151f8fa74de8934becf" | ||||||
|             ], |             ], | ||||||
|             "version": "==0.4.8" |             "version": "==0.4.8" | ||||||
|         }, |         }, | ||||||
|         "pyasn1-modules": { |         "pyasn1-modules": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  |                 "sha256:65cebbaffc913f4fe9e4808735c95ea22d7a7775646ab690518c056784bc21b4", | ||||||
|  |                 "sha256:f39edd8c4ecaa4556e989147ebf219227e2cd2e8a43c7e7fcb1f1c18c5fd6a3d", | ||||||
|  |                 "sha256:a99324196732f53093a84c4369c996713eb8c89d360a496b599fb1a9c47fc3eb", | ||||||
|  |                 "sha256:426edb7a5e8879f1ec54a1864f16b882c2837bfd06eee62f2c982315ee2473ed", | ||||||
|  |                 "sha256:15b7c67fabc7fc240d87fb9aabf999cf82311a6d6fb2c70d00d3d0604878c811", | ||||||
|                 "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", |                 "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e", | ||||||
|                 "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74" |                 "sha256:b80486a6c77252ea3a3e9b1e360bc9cf28eaac41263d173c032581ad2f20fe45", | ||||||
|  |                 "sha256:fe0644d9ab041506b62782e92b06b8c68cca799e1a9636ec398675459e031405", | ||||||
|  |                 "sha256:0845a5582f6a02bb3e1bde9ecfc4bfcae6ec3210dd270522fee602365430c3f8", | ||||||
|  |                 "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74", | ||||||
|  |                 "sha256:c29a5e5cc7a3f05926aff34e097e84f8589cd790ce0ed41b67aed6857b26aafd", | ||||||
|  |                 "sha256:0fe1b68d1e486a1ed5473f1302bd991c1611d319bba158e98b106ff86e1d7199", | ||||||
|  |                 "sha256:cbac4bc38d117f2a49aeedec4407d23e8866ea4ac27ff2cf7fb3e5b570df19e0" | ||||||
|             ], |             ], | ||||||
|             "version": "==0.2.8" |             "version": "==0.2.8" | ||||||
|         }, |         }, | ||||||
| @ -763,6 +800,7 @@ | |||||||
|                 "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", |                 "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", | ||||||
|                 "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" |                 "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.20" |             "version": "==2.20" | ||||||
|         }, |         }, | ||||||
|         "pycryptodome": { |         "pycryptodome": { | ||||||
| @ -844,6 +882,7 @@ | |||||||
|                 "sha256:f20a62397e09704049ce9007bea4f6bad965ba9336a760c6f4ef1b4192e12d6d", |                 "sha256:f20a62397e09704049ce9007bea4f6bad965ba9336a760c6f4ef1b4192e12d6d", | ||||||
|                 "sha256:f81f7311250d9480e36dec819127897ae772e7e8de07abfabe931b8566770b8e" |                 "sha256:f81f7311250d9480e36dec819127897ae772e7e8de07abfabe931b8566770b8e" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==3.9.9" |             "version": "==3.9.9" | ||||||
|         }, |         }, | ||||||
|         "pyhamcrest": { |         "pyhamcrest": { | ||||||
| @ -851,6 +890,7 @@ | |||||||
|                 "sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316", |                 "sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316", | ||||||
|                 "sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29" |                 "sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==2.0.2" |             "version": "==2.0.2" | ||||||
|         }, |         }, | ||||||
|         "pyjwkest": { |         "pyjwkest": { | ||||||
| @ -872,12 +912,14 @@ | |||||||
|                 "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", |                 "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", | ||||||
|                 "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" |                 "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.4.7" |             "version": "==2.4.7" | ||||||
|         }, |         }, | ||||||
|         "pyrsistent": { |         "pyrsistent": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" |                 "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==0.17.3" |             "version": "==0.17.3" | ||||||
|         }, |         }, | ||||||
|         "python-dateutil": { |         "python-dateutil": { | ||||||
| @ -885,6 +927,7 @@ | |||||||
|                 "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", |                 "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", | ||||||
|                 "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" |                 "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.8.1" |             "version": "==2.8.1" | ||||||
|         }, |         }, | ||||||
|         "python-dotenv": { |         "python-dotenv": { | ||||||
| @ -931,6 +974,7 @@ | |||||||
|                 "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", |                 "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2", | ||||||
|                 "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" |                 "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==3.5.3" |             "version": "==3.5.3" | ||||||
|         }, |         }, | ||||||
|         "requests": { |         "requests": { | ||||||
| @ -938,10 +982,12 @@ | |||||||
|                 "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", |                 "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8", | ||||||
|                 "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" |                 "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==2.25.0" |             "version": "==2.25.0" | ||||||
|         }, |         }, | ||||||
|         "requests-oauthlib": { |         "requests-oauthlib": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|  |                 "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc", | ||||||
|                 "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", |                 "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", | ||||||
|                 "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" |                 "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" | ||||||
|             ], |             ], | ||||||
| @ -990,7 +1036,7 @@ | |||||||
|                 "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2", |                 "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2", | ||||||
|                 "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f" |                 "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f" | ||||||
|             ], |             ], | ||||||
|             "markers": "platform_python_implementation == 'CPython' and python_version < '3.9'", |             "markers": "python_version < '3.9' and platform_python_implementation == 'CPython'", | ||||||
|             "version": "==0.2.2" |             "version": "==0.2.2" | ||||||
|         }, |         }, | ||||||
|         "s3transfer": { |         "s3transfer": { | ||||||
| @ -1016,19 +1062,12 @@ | |||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "version": "==18.1.0" |             "version": "==18.1.0" | ||||||
|         }, |         }, | ||||||
|         "signxml": { |  | ||||||
|             "hashes": [ |  | ||||||
|                 "sha256:b70e151d10d99cbc74a50a3344f508ee481fe3c376d61cd1cae850912d303d19", |  | ||||||
|                 "sha256:bab03a6823c9a5b225d1e6266ce66b5d08c4ebfb42029fdb5d3e588b8128c86d" |  | ||||||
|             ], |  | ||||||
|             "index": "pypi", |  | ||||||
|             "version": "==2.8.1" |  | ||||||
|         }, |  | ||||||
|         "six": { |         "six": { | ||||||
|             "hashes": [ |             "hashes": [ | ||||||
|                 "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", |                 "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", | ||||||
|                 "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" |                 "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.15.0" |             "version": "==1.15.0" | ||||||
|         }, |         }, | ||||||
|         "sqlparse": { |         "sqlparse": { | ||||||
| @ -1036,6 +1075,7 @@ | |||||||
|                 "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", |                 "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", | ||||||
|                 "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" |                 "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==0.4.1" |             "version": "==0.4.1" | ||||||
|         }, |         }, | ||||||
|         "structlog": { |         "structlog": { | ||||||
| @ -1083,6 +1123,7 @@ | |||||||
|                 "sha256:f058bd0168271de4dcdc39845b52dd0a4a2fecf5f1246335f13f5e96eaebb467", |                 "sha256:f058bd0168271de4dcdc39845b52dd0a4a2fecf5f1246335f13f5e96eaebb467", | ||||||
|                 "sha256:f3c19e5bd42bbe4bf345704ad7c326c74d3fd7a1b3844987853bef180be638d4" |                 "sha256:f3c19e5bd42bbe4bf345704ad7c326c74d3fd7a1b3844987853bef180be638d4" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==20.3.0" |             "version": "==20.3.0" | ||||||
|         }, |         }, | ||||||
|         "txaio": { |         "txaio": { | ||||||
| @ -1090,6 +1131,7 @@ | |||||||
|                 "sha256:17938f2bca4a9cabce61346758e482ca4e600160cbc28e861493eac74a19539d", |                 "sha256:17938f2bca4a9cabce61346758e482ca4e600160cbc28e861493eac74a19539d", | ||||||
|                 "sha256:38a469daf93c37e5527cb062653d6393ae11663147c42fab7ddc3f6d00d434ae" |                 "sha256:38a469daf93c37e5527cb062653d6393ae11663147c42fab7ddc3f6d00d434ae" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==20.4.1" |             "version": "==20.4.1" | ||||||
|         }, |         }, | ||||||
|         "uritemplate": { |         "uritemplate": { | ||||||
| @ -1097,6 +1139,7 @@ | |||||||
|                 "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", |                 "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f", | ||||||
|                 "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" |                 "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==3.0.1" |             "version": "==3.0.1" | ||||||
|         }, |         }, | ||||||
|         "urllib3": { |         "urllib3": { | ||||||
| @ -1108,7 +1151,6 @@ | |||||||
|                 "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" |                 "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "markers": null, |  | ||||||
|             "version": "==1.26.2" |             "version": "==1.26.2" | ||||||
|         }, |         }, | ||||||
|         "uvicorn": { |         "uvicorn": { | ||||||
| @ -1141,6 +1183,7 @@ | |||||||
|                 "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", |                 "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30", | ||||||
|                 "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" |                 "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==5.0.0" |             "version": "==5.0.0" | ||||||
|         }, |         }, | ||||||
|         "watchgod": { |         "watchgod": { | ||||||
| @ -1265,6 +1308,7 @@ | |||||||
|                 "sha256:f37d45fab14ffef9d33a0dc3bc59ce0c5313e2253323312d47739192da94f5fd", |                 "sha256:f37d45fab14ffef9d33a0dc3bc59ce0c5313e2253323312d47739192da94f5fd", | ||||||
|                 "sha256:f44906f70205d456d503105023041f1e63aece7623b31c390a0103db4de17537" |                 "sha256:f44906f70205d456d503105023041f1e63aece7623b31c390a0103db4de17537" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==5.2.0" |             "version": "==5.2.0" | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
| @ -1281,6 +1325,7 @@ | |||||||
|                 "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", |                 "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", | ||||||
|                 "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" |                 "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==3.3.1" |             "version": "==3.3.1" | ||||||
|         }, |         }, | ||||||
|         "astroid": { |         "astroid": { | ||||||
| @ -1288,6 +1333,7 @@ | |||||||
|                 "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1", |                 "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1", | ||||||
|                 "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38" |                 "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==2.4.1" |             "version": "==2.4.1" | ||||||
|         }, |         }, | ||||||
|         "attrs": { |         "attrs": { | ||||||
| @ -1295,6 +1341,7 @@ | |||||||
|                 "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", |                 "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", | ||||||
|                 "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" |                 "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==20.3.0" |             "version": "==20.3.0" | ||||||
|         }, |         }, | ||||||
|         "autopep8": { |         "autopep8": { | ||||||
| @ -1324,6 +1371,7 @@ | |||||||
|                 "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", |                 "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410", | ||||||
|                 "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6" |                 "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==1.0.1" |             "version": "==1.0.1" | ||||||
|         }, |         }, | ||||||
|         "bumpversion": { |         "bumpversion": { | ||||||
| @ -1339,6 +1387,7 @@ | |||||||
|                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", |                 "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", | ||||||
|                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" |                 "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", | ||||||
|             "version": "==7.1.2" |             "version": "==7.1.2" | ||||||
|         }, |         }, | ||||||
|         "colorama": { |         "colorama": { | ||||||
| @ -1417,6 +1466,7 @@ | |||||||
|                 "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", |                 "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", | ||||||
|                 "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" |                 "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==3.8.4" |             "version": "==3.8.4" | ||||||
|         }, |         }, | ||||||
|         "flake8-polyfill": { |         "flake8-polyfill": { | ||||||
| @ -1431,6 +1481,7 @@ | |||||||
|                 "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac", |                 "sha256:91f36bfb1ab7949b3b40e23736db18231bf7593edada2ba5c3a174a7b23657ac", | ||||||
|                 "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9" |                 "sha256:c9e1f2d0db7ddb9a704c2a0217be31214e91a4fe1dea1efad19ae42ba0c285c9" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.4'", | ||||||
|             "version": "==4.0.5" |             "version": "==4.0.5" | ||||||
|         }, |         }, | ||||||
|         "gitpython": { |         "gitpython": { | ||||||
| @ -1438,6 +1489,7 @@ | |||||||
|                 "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b", |                 "sha256:6eea89b655917b500437e9668e4a12eabdcf00229a0df1762aabd692ef9b746b", | ||||||
|                 "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8" |                 "sha256:befa4d101f91bad1b632df4308ec64555db684c360bd7d2130b4807d49ce86b8" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.4'", | ||||||
|             "version": "==3.1.11" |             "version": "==3.1.11" | ||||||
|         }, |         }, | ||||||
|         "iniconfig": { |         "iniconfig": { | ||||||
| @ -1452,6 +1504,7 @@ | |||||||
|                 "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", |                 "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1", | ||||||
|                 "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" |                 "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==4.3.21" |             "version": "==4.3.21" | ||||||
|         }, |         }, | ||||||
|         "lazy-object-proxy": { |         "lazy-object-proxy": { | ||||||
| @ -1478,6 +1531,7 @@ | |||||||
|                 "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", |                 "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", | ||||||
|                 "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" |                 "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.4.3" |             "version": "==1.4.3" | ||||||
|         }, |         }, | ||||||
|         "mccabe": { |         "mccabe": { | ||||||
| @ -1514,6 +1568,7 @@ | |||||||
|                 "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9", |                 "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9", | ||||||
|                 "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00" |                 "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6'", | ||||||
|             "version": "==5.5.1" |             "version": "==5.5.1" | ||||||
|         }, |         }, | ||||||
|         "pep8-naming": { |         "pep8-naming": { | ||||||
| @ -1528,6 +1583,7 @@ | |||||||
|                 "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", |                 "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", | ||||||
|                 "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" |                 "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==0.13.1" |             "version": "==0.13.1" | ||||||
|         }, |         }, | ||||||
|         "prospector": { |         "prospector": { | ||||||
| @ -1542,6 +1598,7 @@ | |||||||
|                 "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", |                 "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2", | ||||||
|                 "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" |                 "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.9.0" |             "version": "==1.9.0" | ||||||
|         }, |         }, | ||||||
|         "pycodestyle": { |         "pycodestyle": { | ||||||
| @ -1549,6 +1606,7 @@ | |||||||
|                 "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", |                 "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", | ||||||
|                 "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" |                 "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.6.0" |             "version": "==2.6.0" | ||||||
|         }, |         }, | ||||||
|         "pydocstyle": { |         "pydocstyle": { | ||||||
| @ -1556,6 +1614,7 @@ | |||||||
|                 "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", |                 "sha256:19b86fa8617ed916776a11cd8bc0197e5b9856d5433b777f51a3defe13075325", | ||||||
|                 "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" |                 "sha256:aca749e190a01726a4fb472dd4ef23b5c9da7b9205c0a7857c06533de13fd678" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==5.1.1" |             "version": "==5.1.1" | ||||||
|         }, |         }, | ||||||
|         "pyflakes": { |         "pyflakes": { | ||||||
| @ -1563,6 +1622,7 @@ | |||||||
|                 "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", |                 "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", | ||||||
|                 "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" |                 "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.2.0" |             "version": "==2.2.0" | ||||||
|         }, |         }, | ||||||
|         "pylint": { |         "pylint": { | ||||||
| @ -1605,6 +1665,7 @@ | |||||||
|                 "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", |                 "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", | ||||||
|                 "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" |                 "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==2.4.7" |             "version": "==2.4.7" | ||||||
|         }, |         }, | ||||||
|         "pytest": { |         "pytest": { | ||||||
| @ -1718,6 +1779,7 @@ | |||||||
|                 "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", |                 "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", | ||||||
|                 "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" |                 "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==1.15.0" |             "version": "==1.15.0" | ||||||
|         }, |         }, | ||||||
|         "smmap": { |         "smmap": { | ||||||
| @ -1725,6 +1787,7 @@ | |||||||
|                 "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4", |                 "sha256:54c44c197c819d5ef1991799a7e30b662d1e520f2ac75c9efbeb54a742214cf4", | ||||||
|                 "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24" |                 "sha256:9c98bbd1f9786d22f14b3d4126894d56befb835ec90cef151af566c7e19b5d24" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==3.0.4" |             "version": "==3.0.4" | ||||||
|         }, |         }, | ||||||
|         "snowballstemmer": { |         "snowballstemmer": { | ||||||
| @ -1739,6 +1802,7 @@ | |||||||
|                 "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", |                 "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", | ||||||
|                 "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" |                 "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.5'", | ||||||
|             "version": "==0.4.1" |             "version": "==0.4.1" | ||||||
|         }, |         }, | ||||||
|         "stevedore": { |         "stevedore": { | ||||||
| @ -1746,6 +1810,7 @@ | |||||||
|                 "sha256:5e1ab03eaae06ef6ce23859402de785f08d97780ed774948ef16c4652c41bc62", |                 "sha256:5e1ab03eaae06ef6ce23859402de785f08d97780ed774948ef16c4652c41bc62", | ||||||
|                 "sha256:f845868b3a3a77a2489d226568abe7328b5c2d4f6a011cc759dfa99144a521f0" |                 "sha256:f845868b3a3a77a2489d226568abe7328b5c2d4f6a011cc759dfa99144a521f0" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '3.6'", | ||||||
|             "version": "==3.2.2" |             "version": "==3.2.2" | ||||||
|         }, |         }, | ||||||
|         "toml": { |         "toml": { | ||||||
| @ -1753,6 +1818,7 @@ | |||||||
|                 "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", |                 "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", | ||||||
|                 "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" |                 "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" | ||||||
|             ], |             ], | ||||||
|  |             "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", | ||||||
|             "version": "==0.10.2" |             "version": "==0.10.2" | ||||||
|         }, |         }, | ||||||
|         "typed-ast": { |         "typed-ast": { | ||||||
| @ -1807,7 +1873,6 @@ | |||||||
|                 "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" |                 "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" | ||||||
|             ], |             ], | ||||||
|             "index": "pypi", |             "index": "pypi", | ||||||
|             "markers": null, |  | ||||||
|             "version": "==1.26.2" |             "version": "==1.26.2" | ||||||
|         }, |         }, | ||||||
|         "wrapt": { |         "wrapt": { | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - | |||||||
| sudo apt-get install -y nodejs | sudo apt-get install -y nodejs | ||||||
| sudo npm install -g yarn | sudo npm install -g yarn | ||||||
| # Setup python | # Setup python | ||||||
| sudo apt install -y python3.8 python3-pip | sudo apt install -y python3.8 python3-pip libxmlsec1-dev pkg-config | ||||||
| # Setup docker | # Setup docker | ||||||
| sudo pip3 install pipenv | sudo pip3 install pipenv | ||||||
|  |  | ||||||
|  | |||||||
| @ -0,0 +1,69 @@ | |||||||
|  | # Generated by Django 3.1.3 on 2020-11-12 20:16 | ||||||
|  |  | ||||||
|  | from django.apps.registry import Apps | ||||||
|  | from django.db import migrations, models | ||||||
|  | from django.db.backends.base.schema import BaseDatabaseSchemaEditor | ||||||
|  |  | ||||||
|  | from passbook.sources.saml.processors import constants | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def update_algorithms(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): | ||||||
|  |     SAMLProvider = apps.get_model("passbook_providers_saml", "SAMLProvider") | ||||||
|  |     signature_translation_map = { | ||||||
|  |         "rsa-sha1": constants.RSA_SHA1, | ||||||
|  |         "rsa-sha256": constants.RSA_SHA256, | ||||||
|  |         "ecdsa-sha256": constants.RSA_SHA256, | ||||||
|  |         "dsa-sha1": constants.DSA_SHA1, | ||||||
|  |     } | ||||||
|  |     digest_translation_map = { | ||||||
|  |         "sha1": constants.SHA1, | ||||||
|  |         "sha256": constants.SHA256, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for source in SAMLProvider.objects.all(): | ||||||
|  |         source.signature_algorithm = signature_translation_map.get( | ||||||
|  |             source.signature_algorithm, constants.RSA_SHA256 | ||||||
|  |         ) | ||||||
|  |         source.digest_algorithm = digest_translation_map.get( | ||||||
|  |             source.digest_algorithm, constants.SHA256 | ||||||
|  |         ) | ||||||
|  |         source.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("passbook_providers_saml", "0008_auto_20201112_1036"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="samlprovider", | ||||||
|  |             name="digest_algorithm", | ||||||
|  |             field=models.CharField( | ||||||
|  |                 choices=[ | ||||||
|  |                     (constants.SHA1, "SHA1"), | ||||||
|  |                     (constants.SHA256, "SHA256"), | ||||||
|  |                     (constants.SHA384, "SHA384"), | ||||||
|  |                     (constants.SHA512, "SHA512"), | ||||||
|  |                 ], | ||||||
|  |                 default=constants.SHA256, | ||||||
|  |                 max_length=50, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="samlprovider", | ||||||
|  |             name="signature_algorithm", | ||||||
|  |             field=models.CharField( | ||||||
|  |                 choices=[ | ||||||
|  |                     (constants.RSA_SHA1, "RSA-SHA1"), | ||||||
|  |                     (constants.RSA_SHA256, "RSA-SHA256"), | ||||||
|  |                     (constants.RSA_SHA384, "RSA-SHA384"), | ||||||
|  |                     (constants.RSA_SHA512, "RSA-SHA512"), | ||||||
|  |                     (constants.DSA_SHA1, "DSA-SHA1"), | ||||||
|  |                 ], | ||||||
|  |                 default=constants.RSA_SHA256, | ||||||
|  |                 max_length=50, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
| @ -13,6 +13,17 @@ from passbook.core.models import PropertyMapping, Provider | |||||||
| from passbook.crypto.models import CertificateKeyPair | from passbook.crypto.models import CertificateKeyPair | ||||||
| from passbook.lib.utils.template import render_to_string | from passbook.lib.utils.template import render_to_string | ||||||
| from passbook.lib.utils.time import timedelta_string_validator | from passbook.lib.utils.time import timedelta_string_validator | ||||||
|  | from passbook.sources.saml.processors.constants import ( | ||||||
|  |     DSA_SHA1, | ||||||
|  |     RSA_SHA1, | ||||||
|  |     RSA_SHA256, | ||||||
|  |     RSA_SHA384, | ||||||
|  |     RSA_SHA512, | ||||||
|  |     SHA1, | ||||||
|  |     SHA256, | ||||||
|  |     SHA384, | ||||||
|  |     SHA512, | ||||||
|  | ) | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
|  |  | ||||||
| @ -80,20 +91,23 @@ class SAMLProvider(Provider): | |||||||
|     digest_algorithm = models.CharField( |     digest_algorithm = models.CharField( | ||||||
|         max_length=50, |         max_length=50, | ||||||
|         choices=( |         choices=( | ||||||
|             ("sha1", _("SHA1")), |             (SHA1, _("SHA1")), | ||||||
|             ("sha256", _("SHA256")), |             (SHA256, _("SHA256")), | ||||||
|  |             (SHA384, _("SHA384")), | ||||||
|  |             (SHA512, _("SHA512")), | ||||||
|         ), |         ), | ||||||
|         default="sha256", |         default=SHA256, | ||||||
|     ) |     ) | ||||||
|     signature_algorithm = models.CharField( |     signature_algorithm = models.CharField( | ||||||
|         max_length=50, |         max_length=50, | ||||||
|         choices=( |         choices=( | ||||||
|             ("rsa-sha1", _("RSA-SHA1")), |             (RSA_SHA1, _("RSA-SHA1")), | ||||||
|             ("rsa-sha256", _("RSA-SHA256")), |             (RSA_SHA256, _("RSA-SHA256")), | ||||||
|             ("ecdsa-sha256", _("ECDSA-SHA256")), |             (RSA_SHA384, _("RSA-SHA384")), | ||||||
|             ("dsa-sha1", _("DSA-SHA1")), |             (RSA_SHA512, _("RSA-SHA512")), | ||||||
|  |             (DSA_SHA1, _("DSA-SHA1")), | ||||||
|         ), |         ), | ||||||
|         default="rsa-sha256", |         default=RSA_SHA256, | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     verification_kp = models.ForeignKey( |     verification_kp = models.ForeignKey( | ||||||
|  | |||||||
| @ -2,10 +2,10 @@ | |||||||
| from hashlib import sha256 | from hashlib import sha256 | ||||||
| from types import GeneratorType | from types import GeneratorType | ||||||
|  |  | ||||||
|  | import xmlsec | ||||||
| from django.http import HttpRequest | from django.http import HttpRequest | ||||||
| from lxml import etree  # nosec | from lxml import etree  # nosec | ||||||
| from lxml.etree import Element, SubElement  # nosec | from lxml.etree import Element, SubElement  # nosec | ||||||
| from signxml import XMLSigner, XMLVerifier, strip_pem_header |  | ||||||
| from structlog import get_logger | from structlog import get_logger | ||||||
|  |  | ||||||
| from passbook.core.exceptions import PropertyMappingExpressionException | from passbook.core.exceptions import PropertyMappingExpressionException | ||||||
| @ -16,14 +16,15 @@ from passbook.providers.saml.utils import get_random_id | |||||||
| from passbook.providers.saml.utils.time import get_time_string | from passbook.providers.saml.utils.time import get_time_string | ||||||
| from passbook.sources.saml.exceptions import UnsupportedNameIDFormat | from passbook.sources.saml.exceptions import UnsupportedNameIDFormat | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|  |     DIGEST_ALGORITHM_TRANSLATION_MAP, | ||||||
|     NS_MAP, |     NS_MAP, | ||||||
|     NS_SAML_ASSERTION, |     NS_SAML_ASSERTION, | ||||||
|     NS_SAML_PROTOCOL, |     NS_SAML_PROTOCOL, | ||||||
|     NS_SIGNATURE, |  | ||||||
|     SAML_NAME_ID_FORMAT_EMAIL, |     SAML_NAME_ID_FORMAT_EMAIL, | ||||||
|     SAML_NAME_ID_FORMAT_PERSISTENT, |     SAML_NAME_ID_FORMAT_PERSISTENT, | ||||||
|     SAML_NAME_ID_FORMAT_TRANSIENT, |     SAML_NAME_ID_FORMAT_TRANSIENT, | ||||||
|     SAML_NAME_ID_FORMAT_X509, |     SAML_NAME_ID_FORMAT_X509, | ||||||
|  |     SIGN_ALGORITHM_TRANSFORM_MAP, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| LOGGER = get_logger() | LOGGER = get_logger() | ||||||
| @ -186,12 +187,16 @@ class AssertionProcessor: | |||||||
|         assertion.append(self.get_issuer()) |         assertion.append(self.get_issuer()) | ||||||
|  |  | ||||||
|         if self.provider.signing_kp: |         if self.provider.signing_kp: | ||||||
|             # We need a placeholder signature as SAML requires the signature to be between |             sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get( | ||||||
|             # Issuer and subject |                 self.provider.signature_algorithm, xmlsec.constants.TransformRsaSha1 | ||||||
|             signature_placeholder = SubElement( |  | ||||||
|                 assertion, f"{{{NS_SIGNATURE}}}Signature", nsmap=NS_MAP |  | ||||||
|             ) |             ) | ||||||
|             signature_placeholder.attrib["Id"] = "placeholder" |             signature = xmlsec.template.create( | ||||||
|  |                 assertion, | ||||||
|  |                 xmlsec.constants.TransformExclC14N, | ||||||
|  |                 sign_algorithm_transform, | ||||||
|  |                 ns="ds",  # type: ignore | ||||||
|  |             ) | ||||||
|  |             assertion.append(signature) | ||||||
|  |  | ||||||
|         assertion.append(self.get_assertion_subject()) |         assertion.append(self.get_assertion_subject()) | ||||||
|         assertion.append(self.get_assertion_conditions()) |         assertion.append(self.get_assertion_conditions()) | ||||||
| @ -223,20 +228,36 @@ class AssertionProcessor: | |||||||
|         """Build string XML Response and sign if signing is enabled.""" |         """Build string XML Response and sign if signing is enabled.""" | ||||||
|         root_response = self.get_response() |         root_response = self.get_response() | ||||||
|         if self.provider.signing_kp: |         if self.provider.signing_kp: | ||||||
|             signer = XMLSigner( |             digest_algorithm_transform = DIGEST_ALGORITHM_TRANSLATION_MAP.get( | ||||||
|                 c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#", |                 self.provider.digest_algorithm, xmlsec.constants.TransformSha1 | ||||||
|                 signature_algorithm=self.provider.signature_algorithm, |  | ||||||
|                 digest_algorithm=self.provider.digest_algorithm, |  | ||||||
|             ) |             ) | ||||||
|             x509_data = strip_pem_header( |             assertion = root_response.xpath("//saml:Assertion", namespaces=NS_MAP)[0] | ||||||
|                 self.provider.signing_kp.certificate_data |             xmlsec.tree.add_ids(assertion, ["ID"]) | ||||||
|             ).replace("\n", "") |             signature_node = xmlsec.tree.find_node( | ||||||
|             signed = signer.sign( |                 assertion, xmlsec.constants.NodeSignature | ||||||
|                 root_response, |  | ||||||
|                 key=self.provider.signing_kp.private_key, |  | ||||||
|                 cert=[x509_data], |  | ||||||
|                 reference_uri=self._assertion_id, |  | ||||||
|             ) |             ) | ||||||
|             XMLVerifier().verify(signed, x509_cert=x509_data) |             ref = xmlsec.template.add_reference( | ||||||
|             return etree.tostring(signed).decode("utf-8")  # nosec |                 signature_node, | ||||||
|  |                 digest_algorithm_transform, | ||||||
|  |                 uri="#" + self._assertion_id, | ||||||
|  |             ) | ||||||
|  |             xmlsec.template.add_transform(ref, xmlsec.constants.TransformEnveloped) | ||||||
|  |             xmlsec.template.add_transform(ref, xmlsec.constants.TransformExclC14N) | ||||||
|  |             key_info = xmlsec.template.ensure_key_info(signature_node) | ||||||
|  |             xmlsec.template.add_x509_data(key_info) | ||||||
|  |  | ||||||
|  |             ctx = xmlsec.SignatureContext() | ||||||
|  |  | ||||||
|  |             key = xmlsec.Key.from_memory( | ||||||
|  |                 self.provider.signing_kp.key_data, | ||||||
|  |                 xmlsec.constants.KeyDataFormatPem, | ||||||
|  |                 None, | ||||||
|  |             ) | ||||||
|  |             key.load_cert_from_memory( | ||||||
|  |                 self.provider.signing_kp.certificate_data, | ||||||
|  |                 xmlsec.constants.KeyDataFormatCertPem, | ||||||
|  |             ) | ||||||
|  |             ctx.key = key | ||||||
|  |             ctx.sign(signature_node) | ||||||
|  |  | ||||||
|         return etree.tostring(root_response).decode("utf-8")  # nosec |         return etree.tostring(root_response).decode("utf-8")  # nosec | ||||||
|  | |||||||
| @ -4,9 +4,9 @@ from typing import Iterator, Optional | |||||||
| from django.http import HttpRequest | from django.http import HttpRequest | ||||||
| from django.shortcuts import reverse | from django.shortcuts import reverse | ||||||
| from lxml.etree import Element, SubElement, tostring  # nosec | from lxml.etree import Element, SubElement, tostring  # nosec | ||||||
| from signxml.util import strip_pem_header |  | ||||||
|  |  | ||||||
| from passbook.providers.saml.models import SAMLProvider | from passbook.providers.saml.models import SAMLProvider | ||||||
|  | from passbook.providers.saml.utils.encoding import strip_pem_header | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|     NS_MAP, |     NS_MAP, | ||||||
|     NS_SAML_METADATA, |     NS_SAML_METADATA, | ||||||
| @ -42,7 +42,7 @@ class MetadataProcessor: | |||||||
|             ) |             ) | ||||||
|             x509_certificate.text = strip_pem_header( |             x509_certificate.text = strip_pem_header( | ||||||
|                 self.provider.signing_kp.certificate_data.replace("\r", "") |                 self.provider.signing_kp.certificate_data.replace("\r", "") | ||||||
|             ).replace("\n", "") |             ) | ||||||
|             return key_descriptor |             return key_descriptor | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from passbook.providers.saml.models import SAMLProvider | |||||||
| from passbook.providers.saml.utils.encoding import decode_base64_and_inflate | from passbook.providers.saml.utils.encoding import decode_base64_and_inflate | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|     DSA_SHA1, |     DSA_SHA1, | ||||||
|  |     NS_MAP, | ||||||
|     NS_SAML_PROTOCOL, |     NS_SAML_PROTOCOL, | ||||||
|     RSA_SHA1, |     RSA_SHA1, | ||||||
|     RSA_SHA256, |     RSA_SHA256, | ||||||
| @ -77,18 +78,24 @@ class AuthNRequestParser: | |||||||
|  |  | ||||||
|     def parse(self, saml_request: str, relay_state: Optional[str]) -> AuthNRequest: |     def parse(self, saml_request: str, relay_state: Optional[str]) -> AuthNRequest: | ||||||
|         """Validate and parse raw request with enveloped signautre.""" |         """Validate and parse raw request with enveloped signautre.""" | ||||||
|         decoded_xml = decode_base64_and_inflate(saml_request) |         decoded_xml = b64decode(saml_request.encode()).decode() | ||||||
|  |  | ||||||
|         verifier = self.provider.verification_kp |         verifier = self.provider.verification_kp | ||||||
|  |  | ||||||
|         root = etree.fromstring(decoded_xml)  # nosec |         root = etree.fromstring(decoded_xml)  # nosec | ||||||
|         xmlsec.tree.add_ids(root, ["ID"]) |         xmlsec.tree.add_ids(root, ["ID"]) | ||||||
|         signature_node = xmlsec.tree.find_node(root, xmlsec.constants.NodeSignature) |         signature_nodes = root.xpath( | ||||||
|  |             "/samlp:AuthnRequest/ds:Signature", namespaces=NS_MAP | ||||||
|         if verifier and not signature_node: |         ) | ||||||
|  |         if len(signature_nodes) != 1: | ||||||
|             raise CannotHandleAssertion(ERROR_SIGNATURE_REQUIRED_BUT_ABSENT) |             raise CannotHandleAssertion(ERROR_SIGNATURE_REQUIRED_BUT_ABSENT) | ||||||
|  |  | ||||||
|         if signature_node: |         signature_node = signature_nodes[0] | ||||||
|  |  | ||||||
|  |         if verifier and signature_node is None: | ||||||
|  |             raise CannotHandleAssertion(ERROR_SIGNATURE_REQUIRED_BUT_ABSENT) | ||||||
|  |  | ||||||
|  |         if signature_node is not None: | ||||||
|             if not verifier: |             if not verifier: | ||||||
|                 raise CannotHandleAssertion(ERROR_SIGNATURE_EXISTS_BUT_NO_VERIFIER) |                 raise CannotHandleAssertion(ERROR_SIGNATURE_EXISTS_BUT_NO_VERIFIER) | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| """Test AuthN Request generator and parser""" | """Test AuthN Request generator and parser""" | ||||||
|  | from base64 import b64encode | ||||||
|  |  | ||||||
| from django.contrib.sessions.middleware import SessionMiddleware | from django.contrib.sessions.middleware import SessionMiddleware | ||||||
| from django.http.request import HttpRequest, QueryDict | from django.http.request import HttpRequest, QueryDict | ||||||
| from django.test import RequestFactory, TestCase | from django.test import RequestFactory, TestCase | ||||||
| @ -6,10 +8,9 @@ from guardian.utils import get_anonymous_user | |||||||
|  |  | ||||||
| from passbook.crypto.models import CertificateKeyPair | from passbook.crypto.models import CertificateKeyPair | ||||||
| from passbook.flows.models import Flow | from passbook.flows.models import Flow | ||||||
| from passbook.providers.saml.models import SAMLProvider | from passbook.providers.saml.models import SAMLPropertyMapping, SAMLProvider | ||||||
| from passbook.providers.saml.processors.assertion import AssertionProcessor | from passbook.providers.saml.processors.assertion import AssertionProcessor | ||||||
| from passbook.providers.saml.processors.request_parser import AuthNRequestParser | from passbook.providers.saml.processors.request_parser import AuthNRequestParser | ||||||
| from passbook.providers.saml.utils.encoding import deflate_and_base64_encode |  | ||||||
| from passbook.sources.saml.exceptions import MismatchedRequestID | from passbook.sources.saml.exceptions import MismatchedRequestID | ||||||
| from passbook.sources.saml.models import SAMLSource | from passbook.sources.saml.models import SAMLSource | ||||||
| from passbook.sources.saml.processors.constants import SAML_NAME_ID_FORMAT_EMAIL | from passbook.sources.saml.processors.constants import SAML_NAME_ID_FORMAT_EMAIL | ||||||
| @ -67,7 +68,7 @@ class TestAuthNRequest(TestCase): | |||||||
|  |  | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         cert = CertificateKeyPair.objects.first() |         cert = CertificateKeyPair.objects.first() | ||||||
|         self.provider = SAMLProvider.objects.create( |         self.provider: SAMLProvider = SAMLProvider.objects.create( | ||||||
|             authorization_flow=Flow.objects.get( |             authorization_flow=Flow.objects.get( | ||||||
|                 slug="default-provider-authorization-implicit-consent" |                 slug="default-provider-authorization-implicit-consent" | ||||||
|             ), |             ), | ||||||
| @ -75,6 +76,8 @@ class TestAuthNRequest(TestCase): | |||||||
|             signing_kp=cert, |             signing_kp=cert, | ||||||
|             verification_kp=cert, |             verification_kp=cert, | ||||||
|         ) |         ) | ||||||
|  |         self.provider.property_mappings.set(SAMLPropertyMapping.objects.all()) | ||||||
|  |         self.provider.save() | ||||||
|         self.source = SAMLSource.objects.create( |         self.source = SAMLSource.objects.create( | ||||||
|             slug="provider", |             slug="provider", | ||||||
|             issuer="passbook", |             issuer="passbook", | ||||||
| @ -95,11 +98,39 @@ class TestAuthNRequest(TestCase): | |||||||
|         request = request_proc.build_auth_n() |         request = request_proc.build_auth_n() | ||||||
|         # Now we check the ID and signature |         # Now we check the ID and signature | ||||||
|         parsed_request = AuthNRequestParser(self.provider).parse( |         parsed_request = AuthNRequestParser(self.provider).parse( | ||||||
|             deflate_and_base64_encode(request), "test_state" |             b64encode(request.encode()).decode(), "test_state" | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(parsed_request.id, request_proc.request_id) |         self.assertEqual(parsed_request.id, request_proc.request_id) | ||||||
|         self.assertEqual(parsed_request.relay_state, "test_state") |         self.assertEqual(parsed_request.relay_state, "test_state") | ||||||
|  |  | ||||||
|  |     def test_request_full_signed(self): | ||||||
|  |         """Test full SAML Request/Response flow, fully signed""" | ||||||
|  |         http_request = self.factory.get("/") | ||||||
|  |         http_request.user = get_anonymous_user() | ||||||
|  |  | ||||||
|  |         middleware = SessionMiddleware(dummy_get_response) | ||||||
|  |         middleware.process_request(http_request) | ||||||
|  |         http_request.session.save() | ||||||
|  |  | ||||||
|  |         # First create an AuthNRequest | ||||||
|  |         request_proc = RequestProcessor(self.source, http_request, "test_state") | ||||||
|  |         request = request_proc.build_auth_n() | ||||||
|  |  | ||||||
|  |         # To get an assertion we need a parsed request (parsed by provider) | ||||||
|  |         parsed_request = AuthNRequestParser(self.provider).parse( | ||||||
|  |             b64encode(request.encode()).decode(), "test_state" | ||||||
|  |         ) | ||||||
|  |         # Now create a response and convert it to string (provider) | ||||||
|  |         response_proc = AssertionProcessor(self.provider, http_request, parsed_request) | ||||||
|  |         response = response_proc.build_response() | ||||||
|  |  | ||||||
|  |         # Now parse the response (source) | ||||||
|  |         http_request.POST = QueryDict(mutable=True) | ||||||
|  |         http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode() | ||||||
|  |  | ||||||
|  |         response_parser = ResponseProcessor(self.source) | ||||||
|  |         response_parser.parse(http_request) | ||||||
|  |  | ||||||
|     def test_request_id_invalid(self): |     def test_request_id_invalid(self): | ||||||
|         """Test generated AuthNRequest with invalid request ID""" |         """Test generated AuthNRequest with invalid request ID""" | ||||||
|         http_request = self.factory.get("/") |         http_request = self.factory.get("/") | ||||||
| @ -119,7 +150,7 @@ class TestAuthNRequest(TestCase): | |||||||
|  |  | ||||||
|         # To get an assertion we need a parsed request (parsed by provider) |         # To get an assertion we need a parsed request (parsed by provider) | ||||||
|         parsed_request = AuthNRequestParser(self.provider).parse( |         parsed_request = AuthNRequestParser(self.provider).parse( | ||||||
|             deflate_and_base64_encode(request), "test_state" |             b64encode(request.encode()).decode(), "test_state" | ||||||
|         ) |         ) | ||||||
|         # Now create a response and convert it to string (provider) |         # Now create a response and convert it to string (provider) | ||||||
|         response_proc = AssertionProcessor(self.provider, http_request, parsed_request) |         response_proc = AssertionProcessor(self.provider, http_request, parsed_request) | ||||||
| @ -127,7 +158,7 @@ class TestAuthNRequest(TestCase): | |||||||
|  |  | ||||||
|         # Now parse the response (source) |         # Now parse the response (source) | ||||||
|         http_request.POST = QueryDict(mutable=True) |         http_request.POST = QueryDict(mutable=True) | ||||||
|         http_request.POST["SAMLResponse"] = deflate_and_base64_encode(response) |         http_request.POST["SAMLResponse"] = b64encode(response.encode()).decode() | ||||||
|  |  | ||||||
|         response_parser = ResponseProcessor(self.source) |         response_parser = ResponseProcessor(self.source) | ||||||
|  |  | ||||||
|  | |||||||
| @ -2,6 +2,9 @@ | |||||||
| import base64 | import base64 | ||||||
| import zlib | import zlib | ||||||
|  |  | ||||||
|  | PEM_HEADER = "-----BEGIN CERTIFICATE-----" | ||||||
|  | PEM_FOOTER = "-----END CERTIFICATE-----" | ||||||
|  |  | ||||||
|  |  | ||||||
| def decode_base64_and_inflate(encoded: str, encoding="utf-8") -> str: | def decode_base64_and_inflate(encoded: str, encoding="utf-8") -> str: | ||||||
|     """Base64 decode and ZLib decompress b64string""" |     """Base64 decode and ZLib decompress b64string""" | ||||||
| @ -22,3 +25,8 @@ def deflate_and_base64_encode(inflated: str, encoding="utf-8"): | |||||||
| def nice64(src: str) -> str: | def nice64(src: str) -> str: | ||||||
|     """Returns src base64-encoded and formatted nicely for our XML. """ |     """Returns src base64-encoded and formatted nicely for our XML. """ | ||||||
|     return base64.b64encode(src.encode()).decode("utf-8").replace("\n", "") |     return base64.b64encode(src.encode()).decode("utf-8").replace("\n", "") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def strip_pem_header(cert: str) -> str: | ||||||
|  |     """Remove PEM Headers""" | ||||||
|  |     return cert.replace(PEM_HEADER, "").replace(PEM_FOOTER, "").replace("\n", "") | ||||||
|  | |||||||
| @ -127,7 +127,7 @@ class SAMLSSOBindingPOSTView(SAMLSSOView): | |||||||
|     def check_saml_request(self) -> Optional[HttpRequest]: |     def check_saml_request(self) -> Optional[HttpRequest]: | ||||||
|         """Handle POST bindings""" |         """Handle POST bindings""" | ||||||
|         if REQUEST_KEY_SAML_REQUEST not in self.request.POST: |         if REQUEST_KEY_SAML_REQUEST not in self.request.POST: | ||||||
|             LOGGER.info("handle_saml_request: SAML payload missing") |             LOGGER.info("check_saml_request: SAML payload missing") | ||||||
|             return bad_request_message( |             return bad_request_message( | ||||||
|                 self.request, "The SAML request payload is missing." |                 self.request, "The SAML request payload is missing." | ||||||
|             ) |             ) | ||||||
|  | |||||||
| @ -12,3 +12,7 @@ class UnsupportedNameIDFormat(SentryIgnoredException): | |||||||
|  |  | ||||||
| class MismatchedRequestID(SentryIgnoredException): | class MismatchedRequestID(SentryIgnoredException): | ||||||
|     """Exception raised when the returned request ID doesn't match the saved ID.""" |     """Exception raised when the returned request ID doesn't match the saved ID.""" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class InvalidSignature(SentryIgnoredException): | ||||||
|  |     """Signature of XML Object is either missing or invalid""" | ||||||
|  | |||||||
							
								
								
									
										70
									
								
								passbook/sources/saml/migrations/0008_auto_20201112_2016.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								passbook/sources/saml/migrations/0008_auto_20201112_2016.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,70 @@ | |||||||
|  | # Generated by Django 3.1.3 on 2020-11-12 20:16 | ||||||
|  |  | ||||||
|  | from django.apps.registry import Apps | ||||||
|  | from django.db import migrations, models | ||||||
|  | from django.db.backends.base.schema import BaseDatabaseSchemaEditor | ||||||
|  |  | ||||||
|  | from passbook.sources.saml.processors import constants | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def update_algorithms(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): | ||||||
|  |     SAMLSource = apps.get_model("passbook_sources_saml", "SAMLSource") | ||||||
|  |     signature_translation_map = { | ||||||
|  |         "rsa-sha1": constants.RSA_SHA1, | ||||||
|  |         "rsa-sha256": constants.RSA_SHA256, | ||||||
|  |         "ecdsa-sha256": constants.RSA_SHA256, | ||||||
|  |         "dsa-sha1": constants.DSA_SHA1, | ||||||
|  |     } | ||||||
|  |     digest_translation_map = { | ||||||
|  |         "sha1": constants.SHA1, | ||||||
|  |         "sha256": constants.SHA256, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for source in SAMLSource.objects.all(): | ||||||
|  |         source.signature_algorithm = signature_translation_map.get( | ||||||
|  |             source.signature_algorithm, constants.RSA_SHA256 | ||||||
|  |         ) | ||||||
|  |         source.digest_algorithm = digest_translation_map.get( | ||||||
|  |             source.digest_algorithm, constants.SHA256 | ||||||
|  |         ) | ||||||
|  |         source.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ("passbook_sources_saml", "0007_auto_20201112_1055"), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="samlsource", | ||||||
|  |             name="signature_algorithm", | ||||||
|  |             field=models.CharField( | ||||||
|  |                 choices=[ | ||||||
|  |                     (constants.RSA_SHA1, "RSA-SHA1"), | ||||||
|  |                     (constants.RSA_SHA256, "RSA-SHA256"), | ||||||
|  |                     (constants.RSA_SHA384, "RSA-SHA384"), | ||||||
|  |                     (constants.RSA_SHA512, "RSA-SHA512"), | ||||||
|  |                     (constants.DSA_SHA1, "DSA-SHA1"), | ||||||
|  |                 ], | ||||||
|  |                 default=constants.RSA_SHA256, | ||||||
|  |                 max_length=50, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |         migrations.AlterField( | ||||||
|  |             model_name="samlsource", | ||||||
|  |             name="digest_algorithm", | ||||||
|  |             field=models.CharField( | ||||||
|  |                 choices=[ | ||||||
|  |                     (constants.SHA1, "SHA1"), | ||||||
|  |                     (constants.SHA256, "SHA256"), | ||||||
|  |                     (constants.SHA384, "SHA384"), | ||||||
|  |                     (constants.SHA512, "SHA512"), | ||||||
|  |                 ], | ||||||
|  |                 default=constants.SHA256, | ||||||
|  |                 max_length=50, | ||||||
|  |             ), | ||||||
|  |         ), | ||||||
|  |         migrations.RunPython(update_algorithms), | ||||||
|  |     ] | ||||||
| @ -13,11 +13,20 @@ from passbook.core.types import UILoginButton | |||||||
| from passbook.crypto.models import CertificateKeyPair | from passbook.crypto.models import CertificateKeyPair | ||||||
| from passbook.lib.utils.time import timedelta_string_validator | from passbook.lib.utils.time import timedelta_string_validator | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|  |     DSA_SHA1, | ||||||
|  |     RSA_SHA1, | ||||||
|  |     RSA_SHA256, | ||||||
|  |     RSA_SHA384, | ||||||
|  |     RSA_SHA512, | ||||||
|     SAML_NAME_ID_FORMAT_EMAIL, |     SAML_NAME_ID_FORMAT_EMAIL, | ||||||
|     SAML_NAME_ID_FORMAT_PERSISTENT, |     SAML_NAME_ID_FORMAT_PERSISTENT, | ||||||
|     SAML_NAME_ID_FORMAT_TRANSIENT, |     SAML_NAME_ID_FORMAT_TRANSIENT, | ||||||
|     SAML_NAME_ID_FORMAT_WINDOWS, |     SAML_NAME_ID_FORMAT_WINDOWS, | ||||||
|     SAML_NAME_ID_FORMAT_X509, |     SAML_NAME_ID_FORMAT_X509, | ||||||
|  |     SHA1, | ||||||
|  |     SHA256, | ||||||
|  |     SHA384, | ||||||
|  |     SHA512, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -109,20 +118,23 @@ class SAMLSource(Source): | |||||||
|     digest_algorithm = models.CharField( |     digest_algorithm = models.CharField( | ||||||
|         max_length=50, |         max_length=50, | ||||||
|         choices=( |         choices=( | ||||||
|             ("sha1", _("SHA1")), |             (SHA1, _("SHA1")), | ||||||
|             ("sha256", _("SHA256")), |             (SHA256, _("SHA256")), | ||||||
|  |             (SHA384, _("SHA384")), | ||||||
|  |             (SHA512, _("SHA512")), | ||||||
|         ), |         ), | ||||||
|         default="sha256", |         default=SHA256, | ||||||
|     ) |     ) | ||||||
|     signature_algorithm = models.CharField( |     signature_algorithm = models.CharField( | ||||||
|         max_length=50, |         max_length=50, | ||||||
|         choices=( |         choices=( | ||||||
|             ("rsa-sha1", _("RSA-SHA1")), |             (RSA_SHA1, _("RSA-SHA1")), | ||||||
|             ("rsa-sha256", _("RSA-SHA256")), |             (RSA_SHA256, _("RSA-SHA256")), | ||||||
|             ("ecdsa-sha256", _("ECDSA-SHA256")), |             (RSA_SHA384, _("RSA-SHA384")), | ||||||
|             ("dsa-sha1", _("DSA-SHA1")), |             (RSA_SHA512, _("RSA-SHA512")), | ||||||
|  |             (DSA_SHA1, _("DSA-SHA1")), | ||||||
|         ), |         ), | ||||||
|         default="rsa-sha256", |         default=RSA_SHA256, | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|  | |||||||
| @ -1,4 +1,6 @@ | |||||||
| """SAML Source processor constants""" | """SAML Source processor constants""" | ||||||
|  | import xmlsec | ||||||
|  |  | ||||||
| NS_SAML_PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol" | NS_SAML_PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol" | ||||||
| NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" | NS_SAML_ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" | ||||||
| NS_SAML_METADATA = "urn:oasis:names:tc:SAML:2.0:metadata" | NS_SAML_METADATA = "urn:oasis:names:tc:SAML:2.0:metadata" | ||||||
| @ -27,3 +29,23 @@ RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" | |||||||
| RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" | RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" | ||||||
| RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" | RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384" | ||||||
| RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" | RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" | ||||||
|  |  | ||||||
|  | SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1" | ||||||
|  | SHA256 = "http://www.w3.org/2001/04/xmlenc#sha256" | ||||||
|  | SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384" | ||||||
|  | SHA512 = "http://www.w3.org/2001/04/xmlenc#sha512" | ||||||
|  |  | ||||||
|  | SIGN_ALGORITHM_TRANSFORM_MAP = { | ||||||
|  |     DSA_SHA1: xmlsec.constants.TransformDsaSha1, | ||||||
|  |     RSA_SHA1: xmlsec.constants.TransformRsaSha1, | ||||||
|  |     RSA_SHA256: xmlsec.constants.TransformRsaSha256, | ||||||
|  |     RSA_SHA384: xmlsec.constants.TransformRsaSha384, | ||||||
|  |     RSA_SHA512: xmlsec.constants.TransformRsaSha512, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | DIGEST_ALGORITHM_TRANSLATION_MAP = { | ||||||
|  |     SHA1: xmlsec.constants.TransformSha1, | ||||||
|  |     SHA256: xmlsec.constants.TransformSha256, | ||||||
|  |     SHA384: xmlsec.constants.TransformSha384, | ||||||
|  |     SHA512: xmlsec.constants.TransformSha512, | ||||||
|  | } | ||||||
|  | |||||||
| @ -3,8 +3,8 @@ from typing import Iterator, Optional | |||||||
|  |  | ||||||
| from django.http import HttpRequest | from django.http import HttpRequest | ||||||
| from lxml.etree import Element, SubElement, tostring  # nosec | from lxml.etree import Element, SubElement, tostring  # nosec | ||||||
| from signxml.util import strip_pem_header |  | ||||||
|  |  | ||||||
|  | from passbook.providers.saml.utils.encoding import strip_pem_header | ||||||
| from passbook.sources.saml.models import SAMLSource | from passbook.sources.saml.models import SAMLSource | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|     NS_MAP, |     NS_MAP, | ||||||
|  | |||||||
| @ -7,21 +7,17 @@ import xmlsec | |||||||
| from django.http import HttpRequest | from django.http import HttpRequest | ||||||
| from lxml import etree  # nosec | from lxml import etree  # nosec | ||||||
| from lxml.etree import Element  # nosec | from lxml.etree import Element  # nosec | ||||||
| from signxml import XMLSigner |  | ||||||
|  |  | ||||||
| from passbook.providers.saml.utils import get_random_id | from passbook.providers.saml.utils import get_random_id | ||||||
| from passbook.providers.saml.utils.encoding import deflate_and_base64_encode | from passbook.providers.saml.utils.encoding import deflate_and_base64_encode | ||||||
| from passbook.providers.saml.utils.time import get_time_string | from passbook.providers.saml.utils.time import get_time_string | ||||||
| from passbook.sources.saml.models import SAMLSource | from passbook.sources.saml.models import SAMLSource | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|     DSA_SHA1, |     DIGEST_ALGORITHM_TRANSLATION_MAP, | ||||||
|     NS_MAP, |     NS_MAP, | ||||||
|     NS_SAML_ASSERTION, |     NS_SAML_ASSERTION, | ||||||
|     NS_SAML_PROTOCOL, |     NS_SAML_PROTOCOL, | ||||||
|     RSA_SHA1, |     SIGN_ALGORITHM_TRANSFORM_MAP, | ||||||
|     RSA_SHA256, |  | ||||||
|     RSA_SHA384, |  | ||||||
|     RSA_SHA512, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| SESSION_REQUEST_ID = "passbook_source_saml_request_id" | SESSION_REQUEST_ID = "passbook_source_saml_request_id" | ||||||
| @ -71,6 +67,19 @@ class RequestProcessor: | |||||||
|         auth_n_request.attrib["Version"] = "2.0" |         auth_n_request.attrib["Version"] = "2.0" | ||||||
|         # Create issuer object |         # Create issuer object | ||||||
|         auth_n_request.append(self.get_issuer()) |         auth_n_request.append(self.get_issuer()) | ||||||
|  |  | ||||||
|  |         if self.source.signing_kp: | ||||||
|  |             sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get( | ||||||
|  |                 self.source.signature_algorithm, xmlsec.constants.TransformRsaSha1 | ||||||
|  |             ) | ||||||
|  |             signature = xmlsec.template.create( | ||||||
|  |                 auth_n_request, | ||||||
|  |                 xmlsec.constants.TransformExclC14N, | ||||||
|  |                 sign_algorithm_transform, | ||||||
|  |                 ns="ds",  # type: ignore | ||||||
|  |             ) | ||||||
|  |             auth_n_request.append(signature) | ||||||
|  |  | ||||||
|         # Create NameID Policy Object |         # Create NameID Policy Object | ||||||
|         auth_n_request.append(self.get_name_id_policy()) |         auth_n_request.append(self.get_name_id_policy()) | ||||||
|         return auth_n_request |         return auth_n_request | ||||||
| @ -81,16 +90,38 @@ class RequestProcessor: | |||||||
|         auth_n_request = self.get_auth_n() |         auth_n_request = self.get_auth_n() | ||||||
|  |  | ||||||
|         if self.source.signing_kp: |         if self.source.signing_kp: | ||||||
|             signed_request = XMLSigner( |             xmlsec.tree.add_ids(auth_n_request, ["ID"]) | ||||||
|                 c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#", |  | ||||||
|                 signature_algorithm=self.source.signature_algorithm, |             ctx = xmlsec.SignatureContext() | ||||||
|                 digest_algorithm=self.source.digest_algorithm, |  | ||||||
|             ).sign( |             key = xmlsec.Key.from_memory( | ||||||
|                 auth_n_request, |                 self.source.signing_kp.key_data, xmlsec.constants.KeyDataFormatPem, None | ||||||
|                 cert=self.source.signing_kp.certificate_data, |  | ||||||
|                 key=self.source.signing_kp.key_data, |  | ||||||
|             ) |             ) | ||||||
|             return etree.tostring(signed_request).decode() |             key.load_cert_from_memory( | ||||||
|  |                 self.source.signing_kp.certificate_data, | ||||||
|  |                 xmlsec.constants.KeyDataFormatCertPem, | ||||||
|  |             ) | ||||||
|  |             ctx.key = key | ||||||
|  |  | ||||||
|  |             digest_algorithm_transform = DIGEST_ALGORITHM_TRANSLATION_MAP.get( | ||||||
|  |                 self.source.digest_algorithm, xmlsec.constants.TransformSha1 | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             signature_node = xmlsec.tree.find_node( | ||||||
|  |                 auth_n_request, xmlsec.constants.NodeSignature | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             ref = xmlsec.template.add_reference( | ||||||
|  |                 signature_node, | ||||||
|  |                 digest_algorithm_transform, | ||||||
|  |                 uri="#" + auth_n_request.attrib["ID"], | ||||||
|  |             ) | ||||||
|  |             xmlsec.template.add_transform(ref, xmlsec.constants.TransformEnveloped) | ||||||
|  |             xmlsec.template.add_transform(ref, xmlsec.constants.TransformExclC14N) | ||||||
|  |             key_info = xmlsec.template.ensure_key_info(signature_node) | ||||||
|  |             xmlsec.template.add_x509_data(key_info) | ||||||
|  |  | ||||||
|  |             ctx.sign(signature_node) | ||||||
|  |  | ||||||
|         return etree.tostring(auth_n_request).decode() |         return etree.tostring(auth_n_request).decode() | ||||||
|  |  | ||||||
| @ -111,14 +142,7 @@ class RequestProcessor: | |||||||
|             response_dict["RelayState"] = self.relay_state |             response_dict["RelayState"] = self.relay_state | ||||||
|  |  | ||||||
|         if self.source.signing_kp: |         if self.source.signing_kp: | ||||||
|             sign_algorithm_transform_map = { |             sign_algorithm_transform = SIGN_ALGORITHM_TRANSFORM_MAP.get( | ||||||
|                 DSA_SHA1: xmlsec.constants.TransformDsaSha1, |  | ||||||
|                 RSA_SHA1: xmlsec.constants.TransformRsaSha1, |  | ||||||
|                 RSA_SHA256: xmlsec.constants.TransformRsaSha256, |  | ||||||
|                 RSA_SHA384: xmlsec.constants.TransformRsaSha384, |  | ||||||
|                 RSA_SHA512: xmlsec.constants.TransformRsaSha512, |  | ||||||
|             } |  | ||||||
|             sign_algorithm_transform = sign_algorithm_transform_map.get( |  | ||||||
|                 self.source.signature_algorithm, xmlsec.constants.TransformRsaSha1 |                 self.source.signature_algorithm, xmlsec.constants.TransformRsaSha1 | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| """passbook saml source processor""" | """passbook saml source processor""" | ||||||
| from typing import TYPE_CHECKING, Dict | from base64 import b64decode | ||||||
|  | from typing import TYPE_CHECKING, Any, Dict | ||||||
|  |  | ||||||
| from defusedxml import ElementTree | import xmlsec | ||||||
|  | from defusedxml.lxml import fromstring | ||||||
| from django.core.cache import cache | from django.core.cache import cache | ||||||
| from django.core.exceptions import SuspiciousOperation | from django.core.exceptions import SuspiciousOperation | ||||||
| from django.http import HttpRequest, HttpResponse | from django.http import HttpRequest, HttpResponse | ||||||
| from signxml import XMLVerifier |  | ||||||
| from structlog import get_logger | from structlog import get_logger | ||||||
|  |  | ||||||
| from passbook.core.models import User | from passbook.core.models import User | ||||||
| @ -18,14 +19,15 @@ from passbook.flows.planner import ( | |||||||
| from passbook.flows.views import SESSION_KEY_PLAN | from passbook.flows.views import SESSION_KEY_PLAN | ||||||
| from passbook.lib.utils.urls import redirect_with_qs | from passbook.lib.utils.urls import redirect_with_qs | ||||||
| from passbook.policies.utils import delete_none_keys | from passbook.policies.utils import delete_none_keys | ||||||
| from passbook.providers.saml.utils.encoding import decode_base64_and_inflate |  | ||||||
| from passbook.sources.saml.exceptions import ( | from passbook.sources.saml.exceptions import ( | ||||||
|  |     InvalidSignature, | ||||||
|     MismatchedRequestID, |     MismatchedRequestID, | ||||||
|     MissingSAMLResponse, |     MissingSAMLResponse, | ||||||
|     UnsupportedNameIDFormat, |     UnsupportedNameIDFormat, | ||||||
| ) | ) | ||||||
| from passbook.sources.saml.models import SAMLSource | from passbook.sources.saml.models import SAMLSource | ||||||
| from passbook.sources.saml.processors.constants import ( | from passbook.sources.saml.processors.constants import ( | ||||||
|  |     NS_MAP, | ||||||
|     SAML_NAME_ID_FORMAT_EMAIL, |     SAML_NAME_ID_FORMAT_EMAIL, | ||||||
|     SAML_NAME_ID_FORMAT_PERSISTENT, |     SAML_NAME_ID_FORMAT_PERSISTENT, | ||||||
|     SAML_NAME_ID_FORMAT_TRANSIENT, |     SAML_NAME_ID_FORMAT_TRANSIENT, | ||||||
| @ -49,7 +51,7 @@ class ResponseProcessor: | |||||||
|  |  | ||||||
|     _source: SAMLSource |     _source: SAMLSource | ||||||
|  |  | ||||||
|     _root: "Element" |     _root: Any | ||||||
|     _root_xml: str |     _root_xml: str | ||||||
|  |  | ||||||
|     def __init__(self, source: SAMLSource): |     def __init__(self, source: SAMLSource): | ||||||
| @ -61,20 +63,36 @@ class ResponseProcessor: | |||||||
|         raw_response = request.POST.get("SAMLResponse", None) |         raw_response = request.POST.get("SAMLResponse", None) | ||||||
|         if not raw_response: |         if not raw_response: | ||||||
|             raise MissingSAMLResponse("Request does not contain 'SAMLResponse'") |             raise MissingSAMLResponse("Request does not contain 'SAMLResponse'") | ||||||
|         # relay_state = request.POST.get('RelayState', None) |  | ||||||
|         # Check if response is compressed, b64 decode it |         # Check if response is compressed, b64 decode it | ||||||
|         self._root_xml = decode_base64_and_inflate(raw_response) |         self._root_xml = b64decode(raw_response.encode()).decode() | ||||||
|         self._root = ElementTree.fromstring(self._root_xml) |         self._root = fromstring(self._root_xml) | ||||||
|  |  | ||||||
|         self._verify_signed() |         if self._source.signing_kp: | ||||||
|  |             self._verify_signed() | ||||||
|         self._verify_request_id(request) |         self._verify_request_id(request) | ||||||
|  |  | ||||||
|     def _verify_signed(self): |     def _verify_signed(self): | ||||||
|         """Verify SAML Response's Signature""" |         """Verify SAML Response's Signature""" | ||||||
|         verifier = XMLVerifier() |         signature_nodes = self._root.xpath( | ||||||
|         verifier.verify( |             "/samlp:Response/saml:Assertion/ds:Signature", namespaces=NS_MAP | ||||||
|             self._root_xml, x509_cert=self._source.signing_kp.certificate_data |  | ||||||
|         ) |         ) | ||||||
|  |         if len(signature_nodes) != 1: | ||||||
|  |             raise InvalidSignature() | ||||||
|  |         signature_node = signature_nodes[0] | ||||||
|  |         xmlsec.tree.add_ids(self._root, ["ID"]) | ||||||
|  |  | ||||||
|  |         ctx = xmlsec.SignatureContext() | ||||||
|  |         key = xmlsec.Key.from_memory( | ||||||
|  |             self._source.signing_kp.certificate_data, | ||||||
|  |             xmlsec.constants.KeyDataFormatCertPem, | ||||||
|  |         ) | ||||||
|  |         ctx.key = key | ||||||
|  |  | ||||||
|  |         ctx.set_enabled_key_data([xmlsec.constants.KeyDataX509]) | ||||||
|  |         try: | ||||||
|  |             ctx.verify(signature_node) | ||||||
|  |         except (xmlsec.InternalError, xmlsec.VerificationError) as exc: | ||||||
|  |             raise InvalidSignature from exc | ||||||
|         LOGGER.debug("Successfully verified signautre") |         LOGGER.debug("Successfully verified signautre") | ||||||
|  |  | ||||||
|     def _verify_request_id(self, request: HttpRequest): |     def _verify_request_id(self, request: HttpRequest): | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ from django.utils.http import urlencode | |||||||
| from django.utils.translation import gettext_lazy as _ | from django.utils.translation import gettext_lazy as _ | ||||||
| from django.views import View | from django.views import View | ||||||
| from django.views.decorators.csrf import csrf_exempt | from django.views.decorators.csrf import csrf_exempt | ||||||
| from signxml import InvalidSignature | from xmlsec import VerificationError | ||||||
|  |  | ||||||
| from passbook.lib.views import bad_request_message | from passbook.lib.views import bad_request_message | ||||||
| from passbook.providers.saml.utils.encoding import nice64 | from passbook.providers.saml.utils.encoding import nice64 | ||||||
| @ -77,7 +77,7 @@ class ACSView(View): | |||||||
|             processor.parse(request) |             processor.parse(request) | ||||||
|         except MissingSAMLResponse as exc: |         except MissingSAMLResponse as exc: | ||||||
|             return bad_request_message(request, str(exc)) |             return bad_request_message(request, str(exc)) | ||||||
|         except InvalidSignature as exc: |         except VerificationError as exc: | ||||||
|             return bad_request_message(request, str(exc)) |             return bad_request_message(request, str(exc)) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|  | |||||||
							
								
								
									
										45
									
								
								swagger.yaml
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								swagger.yaml
									
									
									
									
									
								
							| @ -7485,16 +7485,19 @@ definitions: | |||||||
|         title: Digest algorithm |         title: Digest algorithm | ||||||
|         type: string |         type: string | ||||||
|         enum: |         enum: | ||||||
|           - sha1 |           - http://www.w3.org/2000/09/xmldsig#sha1 | ||||||
|           - sha256 |           - http://www.w3.org/2001/04/xmlenc#sha256 | ||||||
|  |           - http://www.w3.org/2001/04/xmldsig-more#sha384 | ||||||
|  |           - http://www.w3.org/2001/04/xmlenc#sha512 | ||||||
|       signature_algorithm: |       signature_algorithm: | ||||||
|         title: Signature algorithm |         title: Signature algorithm | ||||||
|         type: string |         type: string | ||||||
|         enum: |         enum: | ||||||
|           - rsa-sha1 |           - http://www.w3.org/2000/09/xmldsig#rsa-sha1 | ||||||
|           - rsa-sha256 |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 | ||||||
|           - ecdsa-sha256 |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha384 | ||||||
|           - dsa-sha1 |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 | ||||||
|  |           - http://www.w3.org/2000/09/xmldsig#dsa-sha1 | ||||||
|       signing_kp: |       signing_kp: | ||||||
|         title: Signing Keypair |         title: Signing Keypair | ||||||
|         description: Keypair used to sign outgoing Responses going to the Service |         description: Keypair used to sign outgoing Responses going to the Service | ||||||
| @ -7779,7 +7782,6 @@ definitions: | |||||||
|       - name |       - name | ||||||
|       - slug |       - slug | ||||||
|       - sso_url |       - sso_url | ||||||
|       - signing_kp |  | ||||||
|     type: object |     type: object | ||||||
|     properties: |     properties: | ||||||
|       name: |       name: | ||||||
| @ -7851,6 +7853,30 @@ definitions: | |||||||
|           - REDIRECT |           - REDIRECT | ||||||
|           - POST |           - POST | ||||||
|           - POST_AUTO |           - POST_AUTO | ||||||
|  |       signing_kp: | ||||||
|  |         title: Singing Keypair | ||||||
|  |         description: Keypair which is used to sign outgoing requests. Leave empty | ||||||
|  |           to disable signing. | ||||||
|  |         type: string | ||||||
|  |         format: uuid | ||||||
|  |         x-nullable: true | ||||||
|  |       digest_algorithm: | ||||||
|  |         title: Digest algorithm | ||||||
|  |         type: string | ||||||
|  |         enum: | ||||||
|  |           - http://www.w3.org/2000/09/xmldsig#sha1 | ||||||
|  |           - http://www.w3.org/2001/04/xmlenc#sha256 | ||||||
|  |           - http://www.w3.org/2001/04/xmldsig-more#sha384 | ||||||
|  |           - http://www.w3.org/2001/04/xmlenc#sha512 | ||||||
|  |       signature_algorithm: | ||||||
|  |         title: Signature algorithm | ||||||
|  |         type: string | ||||||
|  |         enum: | ||||||
|  |           - http://www.w3.org/2000/09/xmldsig#rsa-sha1 | ||||||
|  |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 | ||||||
|  |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha384 | ||||||
|  |           - http://www.w3.org/2001/04/xmldsig-more#rsa-sha512 | ||||||
|  |           - http://www.w3.org/2000/09/xmldsig#dsa-sha1 | ||||||
|       temporary_user_delete_after: |       temporary_user_delete_after: | ||||||
|         title: Delete temporary users after |         title: Delete temporary users after | ||||||
|         description: "Time offset when temporary users should be deleted. This only\ |         description: "Time offset when temporary users should be deleted. This only\ | ||||||
| @ -7858,11 +7884,6 @@ definitions: | |||||||
|           \ log out manually. (Format: hours=1;minutes=2;seconds=3)." |           \ log out manually. (Format: hours=1;minutes=2;seconds=3)." | ||||||
|         type: string |         type: string | ||||||
|         minLength: 1 |         minLength: 1 | ||||||
|       signing_kp: |  | ||||||
|         title: Singing Keypair |  | ||||||
|         description: Keypair which is used to sign outgoing requests. |  | ||||||
|         type: string |  | ||||||
|         format: uuid |  | ||||||
|   Stage: |   Stage: | ||||||
|     description: Stage Serializer |     description: Stage Serializer | ||||||
|     required: |     required: | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens Langhammer
					Jens Langhammer