mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-15 17:17:02 +02:00
Merge branch 'main' into nmb/user-models
This commit is contained in:
commit
14cecf46ba
21 changed files with 1302 additions and 55 deletions
|
@ -6,10 +6,13 @@ name = "pypi"
|
|||
[packages]
|
||||
django = "*"
|
||||
cfenv = "*"
|
||||
pycryptodomex = "*"
|
||||
django-allow-cidr = "*"
|
||||
django-csp = "*"
|
||||
environs = {extras=["django"]}
|
||||
gunicorn = "*"
|
||||
oic = "*"
|
||||
pyjwkest = "*"
|
||||
psycopg2-binary = "*"
|
||||
whitenoise = "*"
|
||||
|
||||
|
|
309
src/Pipfile.lock
generated
309
src/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "a699f5429c7a64d18840baefdfc9731d7c73d57ed55fd701bcc411efeb6e4035"
|
||||
"sha256": "45645e181d935b55c0d58e163245cce64c11cfbcc86ad9cbb2d549d613d9069e"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -22,6 +22,20 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.5.2"
|
||||
},
|
||||
"beaker": {
|
||||
"hashes": [
|
||||
"sha256:ad5d1c05027ee3be3a482ea39f8cb70339b41e5d6ace0cb861382754076d187e"
|
||||
],
|
||||
"version": "==1.11.0"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5",
|
||||
"sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.9.14"
|
||||
},
|
||||
"cfenv": {
|
||||
"hashes": [
|
||||
"sha256:7815bffcc4a3db350f92517157fafc577c11b5a7ff172dc5632f1042b93073e8",
|
||||
|
@ -30,6 +44,123 @@
|
|||
"index": "pypi",
|
||||
"version": "==0.5.3"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
|
||||
"sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
|
||||
"sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
|
||||
"sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
|
||||
"sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
|
||||
"sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
|
||||
"sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
|
||||
"sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
|
||||
"sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
|
||||
"sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
|
||||
"sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
|
||||
"sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
|
||||
"sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
|
||||
"sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
|
||||
"sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
|
||||
"sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
|
||||
"sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
|
||||
"sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
|
||||
"sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
|
||||
"sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
|
||||
"sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
|
||||
"sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
|
||||
"sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
|
||||
"sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
|
||||
"sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
|
||||
"sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
|
||||
"sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
|
||||
"sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
|
||||
"sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
|
||||
"sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
|
||||
"sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
|
||||
"sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
|
||||
"sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
|
||||
"sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
|
||||
"sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
|
||||
"sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
|
||||
"sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
|
||||
"sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
|
||||
"sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
|
||||
"sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
|
||||
"sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
|
||||
"sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
|
||||
"sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
|
||||
"sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
|
||||
"sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
|
||||
"sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
|
||||
"sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
|
||||
"sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
|
||||
"sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
|
||||
"sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
|
||||
"sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
|
||||
"sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
|
||||
"sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
|
||||
"sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
|
||||
"sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
|
||||
"sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
|
||||
"sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
|
||||
"sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
|
||||
"sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
|
||||
"sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
|
||||
"sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
|
||||
"sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
|
||||
"sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
|
||||
"sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
|
||||
],
|
||||
"version": "==1.15.1"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
|
||||
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a",
|
||||
"sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f",
|
||||
"sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0",
|
||||
"sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407",
|
||||
"sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7",
|
||||
"sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6",
|
||||
"sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153",
|
||||
"sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750",
|
||||
"sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad",
|
||||
"sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6",
|
||||
"sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b",
|
||||
"sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5",
|
||||
"sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a",
|
||||
"sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d",
|
||||
"sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d",
|
||||
"sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294",
|
||||
"sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0",
|
||||
"sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a",
|
||||
"sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac",
|
||||
"sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61",
|
||||
"sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013",
|
||||
"sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e",
|
||||
"sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb",
|
||||
"sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9",
|
||||
"sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd",
|
||||
"sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==38.0.1"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
"sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
|
||||
"sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"dj-database-url": {
|
||||
"hashes": [
|
||||
"sha256:ccf3e8718f75ddd147a1e212fca88eecdaa721759ee48e38b485481c77bca3dc",
|
||||
|
@ -93,6 +224,13 @@
|
|||
],
|
||||
"version": "==2.1.3"
|
||||
},
|
||||
"future": {
|
||||
"hashes": [
|
||||
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.18.2"
|
||||
},
|
||||
"gunicorn": {
|
||||
"hashes": [
|
||||
"sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
|
||||
|
@ -101,6 +239,68 @@
|
|||
"index": "pypi",
|
||||
"version": "==20.1.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
|
||||
"sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==3.4"
|
||||
},
|
||||
"mako": {
|
||||
"hashes": [
|
||||
"sha256:3724869b363ba630a272a5f89f68c070352137b8fd1757650017b7e06fda163f",
|
||||
"sha256:8efcb8004681b5f71d09c983ad5a9e6f5c40601a6ec469148753292abc0da534"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.2.2"
|
||||
},
|
||||
"markupsafe": {
|
||||
"hashes": [
|
||||
"sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003",
|
||||
"sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88",
|
||||
"sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5",
|
||||
"sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7",
|
||||
"sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a",
|
||||
"sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603",
|
||||
"sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1",
|
||||
"sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135",
|
||||
"sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247",
|
||||
"sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6",
|
||||
"sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601",
|
||||
"sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77",
|
||||
"sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02",
|
||||
"sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e",
|
||||
"sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63",
|
||||
"sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f",
|
||||
"sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980",
|
||||
"sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b",
|
||||
"sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812",
|
||||
"sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff",
|
||||
"sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96",
|
||||
"sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1",
|
||||
"sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925",
|
||||
"sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a",
|
||||
"sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6",
|
||||
"sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e",
|
||||
"sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f",
|
||||
"sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4",
|
||||
"sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f",
|
||||
"sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3",
|
||||
"sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c",
|
||||
"sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a",
|
||||
"sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417",
|
||||
"sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a",
|
||||
"sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a",
|
||||
"sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37",
|
||||
"sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452",
|
||||
"sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933",
|
||||
"sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a",
|
||||
"sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"marshmallow": {
|
||||
"hashes": [
|
||||
"sha256:1172ce82765bf26c24a3f9299ed6dbeeca4d213f638eaa39a37772656d7ce408",
|
||||
|
@ -109,6 +309,14 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.17.1"
|
||||
},
|
||||
"oic": {
|
||||
"hashes": [
|
||||
"sha256:b82316c4b9633781b8fcb091a7d082ffc863f850a87d8725ead454746aeae677",
|
||||
"sha256:c1a46dd5f803349f1eea7393d70a3f2bdbc97e73b96f3ebb54843e1dc190f5e4"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"orderedmultidict": {
|
||||
"hashes": [
|
||||
"sha256:04070bbb5e87291cc9bfa51df413677faf2141c73c61d2a5f7b26bea3cd882ad",
|
||||
|
@ -186,6 +394,56 @@
|
|||
"index": "pypi",
|
||||
"version": "==2.9.3"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
|
||||
"sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
|
||||
],
|
||||
"version": "==2.21"
|
||||
},
|
||||
"pycryptodomex": {
|
||||
"hashes": [
|
||||
"sha256:04cc393045a8f19dd110c975e30f38ed7ab3faf21ede415ea67afebd95a22380",
|
||||
"sha256:0776bfaf2c48154ab54ea45392847c1283d2fcf64e232e85565f858baedfc1fa",
|
||||
"sha256:0fadb9f7fa3150577800eef35f62a8a24b9ddf1563ff060d9bd3af22d3952c8c",
|
||||
"sha256:18e2ab4813883ae63396c0ffe50b13554b32bb69ec56f0afaf052e7a7ae0d55b",
|
||||
"sha256:191e73bc84a8064ad1874dba0ebadedd7cce4dedee998549518f2c74a003b2e1",
|
||||
"sha256:35a8f7afe1867118330e2e0e0bf759c409e28557fb1fc2fbb1c6c937297dbe9a",
|
||||
"sha256:3709f13ca3852b0b07fc04a2c03b379189232b24007c466be0f605dd4723e9d4",
|
||||
"sha256:4540904c09704b6f831059c0dfb38584acb82cb97b0125cd52688c1f1e3fffa6",
|
||||
"sha256:463119d7d22d0fc04a0f9122e9d3e6121c6648bcb12a052b51bd1eed1b996aa2",
|
||||
"sha256:46b3f05f2f7ac7841053da4e0f69616929ca3c42f238c405f6c3df7759ad2780",
|
||||
"sha256:48697790203909fab02a33226fda546604f4e2653f9d47bc5d3eb40879fa7c64",
|
||||
"sha256:5676a132169a1c1a3712edf25250722ebc8c9102aa9abd814df063ca8362454f",
|
||||
"sha256:65204412d0c6a8e3c41e21e93a5e6054a74fea501afa03046a388cf042e3377a",
|
||||
"sha256:67e1e6a92151023ccdfcfbc0afb3314ad30080793b4c27956ea06ab1fb9bcd8a",
|
||||
"sha256:6f5b6ba8aefd624834bc177a2ac292734996bb030f9d1b388e7504103b6fcddf",
|
||||
"sha256:7341f1bb2dadb0d1a0047f34c3a58208a92423cdbd3244d998e4b28df5eac0ed",
|
||||
"sha256:78d9621cf0ea35abf2d38fa2ca6d0634eab6c991a78373498ab149953787e5e5",
|
||||
"sha256:8eecdf9cdc7343001d047f951b9cc805cd68cb6cd77b20ea46af5bffc5bd3dfb",
|
||||
"sha256:94c7b60e1f52e1a87715571327baea0733708ab4723346598beca4a3b6879794",
|
||||
"sha256:996e1ba717077ce1e6d4849af7a1426f38b07b3d173b879e27d5e26d2e958beb",
|
||||
"sha256:a07a64709e366c2041cd5cfbca592b43998bf4df88f7b0ca73dca37071ccf1bd",
|
||||
"sha256:b6306403228edde6e289f626a3908a2f7f67c344e712cf7c0a508bab3ad9e381",
|
||||
"sha256:b9279adc16e4b0f590ceff581f53a80179b02cba9056010d733eb4196134a870",
|
||||
"sha256:c4cb9cb492ea7dcdf222a8d19a1d09002798ea516aeae8877245206d27326d86",
|
||||
"sha256:dd452a5af7014e866206d41751886c9b4bf379a339fdf2dbfc7dd16c0fb4f8e0",
|
||||
"sha256:e2b12968522a0358b8917fc7b28865acac002f02f4c4c6020fcb264d76bfd06d",
|
||||
"sha256:e3164a18348bd53c69b4435ebfb4ac8a4076291ffa2a70b54f0c4b80c7834b1d",
|
||||
"sha256:e47bf8776a7e15576887f04314f5228c6527b99946e6638cf2f16da56d260cab",
|
||||
"sha256:f8be976cec59b11f011f790b88aca67b4ea2bd286578d0bd3e31bcd19afcd3e4",
|
||||
"sha256:fc9bc7a9b79fe5c750fc81a307052f8daabb709bdaabb0fb18fb136b66b653b5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.15.0"
|
||||
},
|
||||
"pyjwkest": {
|
||||
"hashes": [
|
||||
"sha256:5560fd5ba08655f29ff6ad1df1e15dc05abc9d976fcbcec8d2b5167f49b70222"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.4.2"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb",
|
||||
|
@ -202,6 +460,14 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.21.0"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
|
||||
"sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4'",
|
||||
"version": "==2.28.1"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82",
|
||||
|
@ -226,6 +492,22 @@
|
|||
"markers": "python_version >= '3.5'",
|
||||
"version": "==0.4.2"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
|
||||
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.3.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e",
|
||||
"sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
|
||||
"version": "==1.26.12"
|
||||
},
|
||||
"whitenoise": {
|
||||
"hashes": [
|
||||
"sha256:8e9c600a5c18bd17655ef668ad55b5edf6c24ce9bdca5bf607649ca4b1e8e2c2",
|
||||
|
@ -391,11 +673,11 @@
|
|||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:01eecd304ba0e6eeed188ae5fa568e99ef10265af7fd9ab737d6412b4ee0ab85",
|
||||
"sha256:aefa80ac32d5bf1f96139dca67cefb69a431beff4e6bf1168468f37d7ab87015"
|
||||
"sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93",
|
||||
"sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.10.0"
|
||||
"version": "==0.10.1"
|
||||
},
|
||||
"pbr": {
|
||||
"hashes": [
|
||||
|
@ -431,6 +713,7 @@
|
|||
},
|
||||
"pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf",
|
||||
"sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
|
||||
"sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
|
||||
"sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
|
||||
|
@ -442,26 +725,32 @@
|
|||
"sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
|
||||
"sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
|
||||
"sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
|
||||
"sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782",
|
||||
"sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
|
||||
"sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
|
||||
"sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
|
||||
"sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
|
||||
"sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
|
||||
"sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1",
|
||||
"sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
|
||||
"sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
|
||||
"sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
|
||||
"sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
|
||||
"sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
|
||||
"sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
|
||||
"sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d",
|
||||
"sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
|
||||
"sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
|
||||
"sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7",
|
||||
"sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
|
||||
"sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
|
||||
"sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
|
||||
"sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358",
|
||||
"sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
|
||||
"sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
|
||||
"sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
|
||||
"sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
|
||||
"sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f",
|
||||
"sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
|
||||
"sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
|
||||
],
|
||||
|
@ -510,18 +799,18 @@
|
|||
},
|
||||
"types-requests": {
|
||||
"hashes": [
|
||||
"sha256:86cb66d3de2f53eac5c09adc42cf6547eefbd0c7e1210beca1ee751c35d96083",
|
||||
"sha256:feaf581bd580497a47fe845d506fa3b91b484cf706ff27774e87659837de9962"
|
||||
"sha256:45b485725ed58752f2b23461252f1c1ad9205b884a1e35f786bb295525a3e16a",
|
||||
"sha256:97d8f40aa1ffe1e58c3726c77d63c182daea9a72d9f1fa2cafdea756b2a19f2c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.9"
|
||||
"version": "==2.28.10"
|
||||
},
|
||||
"types-urllib3": {
|
||||
"hashes": [
|
||||
"sha256:333e675b188a1c1fd980b4b352f9e40572413a4c1ac689c23cd546e96310070a",
|
||||
"sha256:b78e819f0e350221d0689a5666162e467ba3910737bafda14b5c2c85e9bb1e56"
|
||||
"sha256:a1b3aaea7dda3eb1b51699ee723aadd235488e4dc4648e030f09bc429ecff42f",
|
||||
"sha256:cf7918503d02d3576e503bbfb419b0e047c4617653bba09624756ab7175e15c9"
|
||||
],
|
||||
"version": "==1.26.23"
|
||||
"version": "==1.26.24"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
|
|
201
src/djangooidc/LICENSE
Normal file
201
src/djangooidc/LICENSE
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2014 Andrea Biancini <andrea.biancini@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
10
src/djangooidc/NOTICE
Normal file
10
src/djangooidc/NOTICE
Normal file
|
@ -0,0 +1,10 @@
|
|||
Python module to integrate pyoic into django
|
||||
Copyright 2014 Andrea Biancini
|
||||
|
||||
This product includes software developed by
|
||||
Andrea Biancini <andrea.biancini@gmail.com>
|
||||
|
||||
Portions of this software is derived from the pyoidc project
|
||||
avaialble from
|
||||
https://github.com/rohe/pyoidc
|
||||
|
30
src/djangooidc/README.rst
Normal file
30
src/djangooidc/README.rst
Normal file
|
@ -0,0 +1,30 @@
|
|||
Django OpenID Connect (OIDC) authentication provider
|
||||
====================================================
|
||||
|
||||
This module makes it easy to integrate OpenID Connect as an authentication source in a Django project.
|
||||
|
||||
Behind the scenes, it uses Roland Hedberg's great pyoidc library.
|
||||
|
||||
Modified by JHUAPL BOSS to support Python3
|
||||
|
||||
Modified by Thomas Frössman with fixes and additional modifications.
|
||||
|
||||
A note for anyone viewing this file from the .gov repository:
|
||||
|
||||
This code has been included from its upstream counterpart in order to minimize external dependencies. Here is an excerpt from setup.py::
|
||||
|
||||
name='django-oidc-tf',
|
||||
description="""A Django OpenID Connect (OIDC) authentication backend""",
|
||||
author='Thomas Frössman',
|
||||
author_email='thomasf@jossystem.se',
|
||||
url='https://github.com/py-pa/django-oidc',
|
||||
packages=[
|
||||
'djangooidc',
|
||||
],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'django>=1.10',
|
||||
'oic>=0.10.0',
|
||||
],
|
||||
|
||||
It was taken from https://github.com/koriaf/django-oidc at ae4a0ba5e6bfda1495f9447a507e6f54cc056980.
|
0
src/djangooidc/__init__.py
Normal file
0
src/djangooidc/__init__.py
Normal file
77
src/djangooidc/backends.py
Normal file
77
src/djangooidc/backends.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
from django.utils import timezone
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenIdConnectBackend(ModelBackend):
|
||||
"""
|
||||
This backend checks a previously performed OIDC authentication.
|
||||
If it is OK and the user already exists in the database, it is returned.
|
||||
If it is OK and user does not exist in the database, it is created and
|
||||
returned unless setting OIDC_CREATE_UNKNOWN_USER is False.
|
||||
In all other cases, None is returned.
|
||||
"""
|
||||
|
||||
def authenticate(self, request, **kwargs):
|
||||
logger.debug("kwargs %s" % kwargs)
|
||||
user = None
|
||||
if not kwargs or "sub" not in kwargs.keys():
|
||||
return user
|
||||
|
||||
UserModel = get_user_model()
|
||||
username = self.clean_username(kwargs["sub"])
|
||||
if "upn" in kwargs.keys():
|
||||
username = kwargs["upn"]
|
||||
|
||||
# Some OP may actually choose to withhold some information, so we must
|
||||
# test if it is present
|
||||
openid_data = {"last_login": timezone.now()}
|
||||
openid_data["first_name"] = kwargs.get("first_name", "")
|
||||
openid_data["first_name"] = kwargs.get("given_name", "")
|
||||
openid_data["first_name"] = kwargs.get("christian_name", "")
|
||||
openid_data["last_name"] = kwargs.get("family_name", "")
|
||||
openid_data["last_name"] = kwargs.get("last_name", "")
|
||||
openid_data["email"] = kwargs.get("email", "")
|
||||
|
||||
# Note that this could be accomplished in one try-except clause, but
|
||||
# instead we use get_or_create when creating unknown users since it has
|
||||
# built-in safeguards for multiple threads.
|
||||
if getattr(settings, "OIDC_CREATE_UNKNOWN_USER", True):
|
||||
args = {
|
||||
UserModel.USERNAME_FIELD: username,
|
||||
"defaults": openid_data,
|
||||
}
|
||||
user, created = UserModel.objects.update_or_create(**args)
|
||||
if created:
|
||||
user = self.configure_user(user, **kwargs)
|
||||
else:
|
||||
try:
|
||||
user = UserModel.objects.get_by_natural_key(username)
|
||||
except UserModel.DoesNotExist:
|
||||
try:
|
||||
user = UserModel.objects.get(email=kwargs["email"])
|
||||
except UserModel.DoesNotExist:
|
||||
return None
|
||||
return user
|
||||
|
||||
def clean_username(self, username):
|
||||
"""
|
||||
Performs any cleaning on the "username" prior to using it to get or
|
||||
create the user object. Returns the cleaned username.
|
||||
"""
|
||||
return username
|
||||
|
||||
def configure_user(self, user, **kwargs):
|
||||
"""
|
||||
Configures a user after creation and returns the updated user.
|
||||
"""
|
||||
user.set_unusable_password()
|
||||
return user
|
42
src/djangooidc/exceptions.py
Normal file
42
src/djangooidc/exceptions.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
from oic import rndstr
|
||||
from http import HTTPStatus as status
|
||||
|
||||
|
||||
class OIDCException(Exception):
|
||||
"""
|
||||
Base class for django oidc exceptions.
|
||||
Subclasses should provide `.status` and `.friendly_message` properties.
|
||||
`.locator`, if used, should be a useful, unique identifier for
|
||||
locating related log messages.
|
||||
"""
|
||||
|
||||
status = status.INTERNAL_SERVER_ERROR
|
||||
friendly_message = "A server error occurred."
|
||||
locator = None
|
||||
|
||||
def __init__(self, friendly_message=None, status=None, locator=None):
|
||||
if friendly_message is not None:
|
||||
self.friendly_message = friendly_message
|
||||
if status is not None:
|
||||
self.status = status
|
||||
if locator is not None:
|
||||
self.locator = locator
|
||||
else:
|
||||
self.locator = rndstr(size=12)
|
||||
|
||||
def __str__(self):
|
||||
return f"[{self.locator}] {self.friendly_message}"
|
||||
|
||||
|
||||
class AuthenticationFailed(OIDCException):
|
||||
status = status.UNAUTHORIZED
|
||||
friendly_message = "This login attempt didn't work."
|
||||
|
||||
|
||||
class InternalError(OIDCException):
|
||||
status = status.INTERNAL_SERVER_ERROR
|
||||
friendly_message = "The system broke while trying to log you in."
|
||||
|
||||
|
||||
class BannedUser(AuthenticationFailed):
|
||||
friendly_message = "Your user is not valid in this application."
|
289
src/djangooidc/oidc.py
Normal file
289
src/djangooidc/oidc.py
Normal file
|
@ -0,0 +1,289 @@
|
|||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponseRedirect
|
||||
from Cryptodome.PublicKey.RSA import importKey
|
||||
from jwkest.jwk import RSAKey
|
||||
from oic import oic, rndstr
|
||||
from oic.oauth2 import ErrorResponse
|
||||
from oic.oic import AuthorizationRequest, AuthorizationResponse, RegistrationResponse
|
||||
from oic.oic.message import AccessTokenResponse
|
||||
from oic.utils.authn.client import CLIENT_AUTHN_METHOD
|
||||
from oic.utils import keyio
|
||||
|
||||
from . import exceptions as o_e
|
||||
|
||||
__author__ = "roland"
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Client(oic.Client):
|
||||
def __init__(self, op):
|
||||
"""Step 1: Configure the OpenID Connect client."""
|
||||
logger.debug("Initializing the OpenID Connect client...")
|
||||
try:
|
||||
provider = settings.OIDC_PROVIDERS[op]
|
||||
verify_ssl = getattr(settings, "OIDC_VERIFY_SSL", True)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Configuration missing for OpenID Connect client")
|
||||
raise o_e.InternalError()
|
||||
|
||||
try:
|
||||
# prepare private key for authentication method of private_key_jwt
|
||||
key_bundle = keyio.KeyBundle()
|
||||
rsa_key = importKey(provider["client_registration"]["sp_private_key"])
|
||||
key = RSAKey(key=rsa_key, use="sig")
|
||||
key_bundle.append(key)
|
||||
keyjar = keyio.KeyJar(verify_ssl=verify_ssl)
|
||||
keyjar.add_kb("", key_bundle)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error(
|
||||
"Key jar preparation failed for %s",
|
||||
provider["srv_discovery_url"],
|
||||
)
|
||||
raise o_e.InternalError()
|
||||
|
||||
try:
|
||||
# create the oic client instance
|
||||
super().__init__(
|
||||
client_id=None,
|
||||
client_authn_method=CLIENT_AUTHN_METHOD,
|
||||
keyjar=keyjar,
|
||||
verify_ssl=verify_ssl,
|
||||
config=None,
|
||||
)
|
||||
# must be set after client is initialized
|
||||
self.behaviour = provider["behaviour"]
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error(
|
||||
"Client creation failed for %s",
|
||||
provider["srv_discovery_url"],
|
||||
)
|
||||
raise o_e.InternalError()
|
||||
|
||||
try:
|
||||
# discover and store the provider (OP) urls, etc
|
||||
self.provider_config(provider["srv_discovery_url"])
|
||||
self.store_registration_info(
|
||||
RegistrationResponse(**provider["client_registration"])
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error(
|
||||
"Provider info discovery failed for %s",
|
||||
provider["srv_discovery_url"],
|
||||
)
|
||||
raise o_e.InternalError()
|
||||
|
||||
def create_authn_request(
|
||||
self,
|
||||
session,
|
||||
extra_args=None,
|
||||
):
|
||||
"""Step 2: Construct a login URL at OP's domain and send the user to it."""
|
||||
logger.debug("Creating the OpenID Connect authn request...")
|
||||
state = rndstr(size=32)
|
||||
try:
|
||||
session["state"] = state
|
||||
session["nonce"] = rndstr(size=32)
|
||||
scopes = list(self.behaviour.get("scope", []))
|
||||
scopes.append("openid")
|
||||
request_args = {
|
||||
"response_type": self.behaviour.get("response_type"),
|
||||
"scope": " ".join(set(scopes)),
|
||||
"state": session["state"],
|
||||
"nonce": session["nonce"],
|
||||
"redirect_uri": self.registration_response["redirect_uris"][0],
|
||||
"acr_values": self.behaviour.get("acr_value"),
|
||||
}
|
||||
|
||||
if extra_args is not None:
|
||||
request_args.update(extra_args)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Failed to assemble request arguments for %s" % state)
|
||||
raise o_e.InternalError(locator=state)
|
||||
|
||||
logger.debug("request args: %s" % request_args)
|
||||
|
||||
try:
|
||||
# prepare the request for sending
|
||||
cis = self.construct_AuthorizationRequest(request_args=request_args)
|
||||
logger.debug("request: %s" % cis)
|
||||
|
||||
# obtain the url and headers from the prepared request
|
||||
url, body, headers, cis = self.uri_and_body(
|
||||
AuthorizationRequest,
|
||||
cis,
|
||||
method="GET",
|
||||
request_args=request_args,
|
||||
)
|
||||
logger.debug("body: %s" % body)
|
||||
logger.debug("URL: %s" % url)
|
||||
logger.debug("headers: %s" % headers)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Failed to prepare request for %s" % state)
|
||||
raise o_e.InternalError(locator=state)
|
||||
|
||||
try:
|
||||
# create the redirect object
|
||||
response = HttpResponseRedirect(str(url))
|
||||
# add headers to the object, if any
|
||||
if headers:
|
||||
for key, value in headers.items():
|
||||
response[key] = value
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Failed to create redirect object for %s" % state)
|
||||
raise o_e.InternalError(locator=state)
|
||||
|
||||
return response
|
||||
|
||||
def callback(self, unparsed_response, session):
|
||||
"""Step 3: Receive OP's response, request an access token, and user info."""
|
||||
logger.debug("Processing the OpenID Connect callback response...")
|
||||
state = session.get("state", "")
|
||||
try:
|
||||
# parse the response from OP
|
||||
authn_response = self.parse_response(
|
||||
AuthorizationResponse,
|
||||
unparsed_response,
|
||||
sformat="dict",
|
||||
keyjar=self.keyjar,
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Unable to parse response for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
# ErrorResponse is not raised, it is passed back...
|
||||
if isinstance(authn_response, ErrorResponse):
|
||||
error = authn_response.get("error", "")
|
||||
if error == "login_required":
|
||||
logger.warning(
|
||||
"User was not logged in (%s), trying again for %s" % (error, state)
|
||||
)
|
||||
return self.create_authn_request(session)
|
||||
else:
|
||||
logger.error("Unable to process response %s for %s" % (error, state))
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
logger.debug("authn_response %s" % authn_response)
|
||||
|
||||
if not authn_response.get("state", None):
|
||||
logger.error("State value not received from OP for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
if authn_response["state"] != session.get("state", None):
|
||||
# this most likely means the user's Django session vanished
|
||||
logger.error("Received state not the same as expected for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
if self.behaviour.get("response_type") == "code":
|
||||
# need an access token to get user info (and to log the user out later)
|
||||
self._request_token(
|
||||
authn_response["state"], authn_response["code"], session
|
||||
)
|
||||
|
||||
user_info = self._get_user_info(state, session)
|
||||
|
||||
return user_info
|
||||
|
||||
def _get_user_info(self, state, session):
|
||||
"""Get information from OP about the user."""
|
||||
scopes = list(self.behaviour.get("user_info_request", []))
|
||||
scopes.append("openid")
|
||||
try:
|
||||
# get info about the user from OP
|
||||
info_response = self.do_user_info_request(
|
||||
state=session["state"],
|
||||
method="GET",
|
||||
scope=" ".join(set(scopes)),
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Unable to request user info for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
# ErrorResponse is not raised, it is passed back...
|
||||
if isinstance(info_response, ErrorResponse):
|
||||
logger.error(
|
||||
"Unable to get user info (%s) for %s"
|
||||
% (info_response.get("error", ""), state)
|
||||
)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
logger.debug("user info: %s" % info_response)
|
||||
return info_response.to_dict()
|
||||
|
||||
def _request_token(self, state, code, session):
|
||||
"""Request a token from OP to allow us to then request user info."""
|
||||
try:
|
||||
token_response = self.do_access_token_request(
|
||||
scope="openid",
|
||||
state=state,
|
||||
request_args={
|
||||
"code": code,
|
||||
"redirect_uri": self.registration_response["redirect_uris"][0],
|
||||
"client_id": self.client_id,
|
||||
"client_secret": self.client_secret,
|
||||
},
|
||||
authn_method=self.registration_response["token_endpoint_auth_method"],
|
||||
)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Unable to obtain access token for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
# ErrorResponse is not raised, it is passed back...
|
||||
if isinstance(token_response, ErrorResponse):
|
||||
logger.error(
|
||||
"Unable to get token (%s) for %s"
|
||||
% (token_response.get("error", ""), state)
|
||||
)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
logger.debug("token response %s" % token_response)
|
||||
|
||||
try:
|
||||
# get the token and other bits of info
|
||||
id_token = token_response["id_token"]._dict
|
||||
|
||||
if id_token["nonce"] != session["nonce"]:
|
||||
logger.error("Received nonce not the same as expected for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
session["id_token"] = id_token
|
||||
session["id_token_raw"] = getattr(self, "id_token_raw", None)
|
||||
session["access_token"] = token_response["access_token"]
|
||||
session["refresh_token"] = token_response.get("refresh_token", "")
|
||||
session["expires_in"] = token_response.get("expires_in", "")
|
||||
self.id_token[state] = getattr(self, "id_token_raw", None)
|
||||
except Exception as err:
|
||||
logger.error(err)
|
||||
logger.error("Unable to parse access token response for %s" % state)
|
||||
raise o_e.AuthenticationFailed(locator=state)
|
||||
|
||||
def store_response(self, resp, info):
|
||||
"""Make raw ID token available for internal use."""
|
||||
if isinstance(resp, AccessTokenResponse):
|
||||
info = json.loads(info)
|
||||
self.id_token_raw = info["id_token"]
|
||||
|
||||
super(Client, self).store_response(resp, info)
|
||||
|
||||
def __repr__(self):
|
||||
return "Client {} {} {}".format(
|
||||
self.client_id,
|
||||
self.client_prefs,
|
||||
self.behaviour,
|
||||
)
|
12
src/djangooidc/urls.py
Normal file
12
src/djangooidc/urls.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# coding: utf-8
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("login/", views.openid, name="openid"),
|
||||
path("callback/login/", views.login_callback, name="openid_login_callback"),
|
||||
path("logout/", views.logout, name="logout"),
|
||||
path("callback/logout/", views.logout_callback, name="openid_logout_callback"),
|
||||
]
|
120
src/djangooidc/views.py
Normal file
120
src/djangooidc/views.py
Normal file
|
@ -0,0 +1,120 @@
|
|||
# coding: utf-8
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import logout as auth_logout
|
||||
from django.contrib.auth import authenticate, login
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import redirect, render
|
||||
from urllib.parse import parse_qs, urlencode
|
||||
|
||||
from djangooidc.oidc import Client
|
||||
from djangooidc import exceptions as o_e
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Initialize provider using pyOICD
|
||||
OP = getattr(settings, "OIDC_ACTIVE_PROVIDER")
|
||||
CLIENT = Client(OP)
|
||||
logger.debug("client initialized %s" % CLIENT)
|
||||
except Exception as err:
|
||||
logger.warning(err)
|
||||
logger.warning("Unable to configure OpenID Connect provider. Users cannot log in.")
|
||||
|
||||
|
||||
def error_page(request, error):
|
||||
"""Display a sensible message and log the error."""
|
||||
logger.error(error)
|
||||
if isinstance(error, o_e.AuthenticationFailed):
|
||||
return render(
|
||||
request,
|
||||
"401.html",
|
||||
context={
|
||||
"friendly_message": error.friendly_message,
|
||||
"log_identifier": error.locator,
|
||||
},
|
||||
status=401,
|
||||
)
|
||||
if isinstance(error, o_e.InternalError):
|
||||
return render(
|
||||
request,
|
||||
"500.html",
|
||||
context={
|
||||
"friendly_message": error.friendly_message,
|
||||
"log_identifier": error.locator,
|
||||
},
|
||||
status=500,
|
||||
)
|
||||
if isinstance(error, Exception):
|
||||
return render(request, "500.html", status=500)
|
||||
|
||||
|
||||
def openid(request):
|
||||
"""Redirect the user to an authentication provider (OP)."""
|
||||
request.session["next"] = request.GET.get("next", "/")
|
||||
|
||||
try:
|
||||
return CLIENT.create_authn_request(request.session)
|
||||
except Exception as err:
|
||||
return error_page(request, err)
|
||||
|
||||
|
||||
def login_callback(request):
|
||||
"""Analyze the token returned by the authentication provider (OP)."""
|
||||
try:
|
||||
query = parse_qs(request.GET.urlencode())
|
||||
userinfo = CLIENT.callback(query, request.session)
|
||||
user = authenticate(request=request, **userinfo)
|
||||
if user:
|
||||
login(request, user)
|
||||
logger.info("Successfully logged in user %s" % user)
|
||||
return redirect(request.session.get("next", "/"))
|
||||
else:
|
||||
raise o_e.BannedUser()
|
||||
except Exception as err:
|
||||
return error_page(request, err)
|
||||
|
||||
|
||||
def logout(request, next_page=None):
|
||||
"""Redirect the user to the authentication provider (OP) logout page."""
|
||||
try:
|
||||
username = request.user.username
|
||||
request_args = {
|
||||
# it is perfectly fine to send the token, even if it is expired
|
||||
"id_token_hint": request.session["id_token_raw"],
|
||||
"state": request.session["state"],
|
||||
}
|
||||
if (
|
||||
"post_logout_redirect_uris" in CLIENT.registration_response.keys()
|
||||
and len(CLIENT.registration_response["post_logout_redirect_uris"]) > 0
|
||||
):
|
||||
request_args.update(
|
||||
{
|
||||
"post_logout_redirect_uri": CLIENT.registration_response[
|
||||
"post_logout_redirect_uris"
|
||||
][0]
|
||||
}
|
||||
)
|
||||
|
||||
url = CLIENT.provider_info["end_session_endpoint"]
|
||||
url += "?" + urlencode(request_args)
|
||||
return HttpResponseRedirect(url)
|
||||
except Exception as err:
|
||||
return error_page(request, err)
|
||||
finally:
|
||||
# Always remove Django session stuff - even if not logged out from OP.
|
||||
# Don't wait for the callback as it may never come.
|
||||
auth_logout(request)
|
||||
logger.info("Successfully logged out user %s" % username)
|
||||
next_page = getattr(settings, "LOGOUT_REDIRECT_URL", None)
|
||||
if next_page:
|
||||
request.session["next"] = next_page
|
||||
|
||||
|
||||
def logout_callback(request):
|
||||
"""Simple redirection view: after logout, redirect to `next`."""
|
||||
next = request.session["next"] if "next" in request.session.keys() else "/"
|
||||
return redirect(next)
|
|
@ -25,6 +25,11 @@ services:
|
|||
- DJANGO_SECRET_KEY=feedabee
|
||||
# Run Django in debug mode on local
|
||||
- DJANGO_DEBUG=True
|
||||
# Tell Django where it is being hosted
|
||||
- DJANGO_BASE_URL="localhost:8080"
|
||||
# --- These keys are obtained from `.env` file ---
|
||||
# Set a private JWT signing key for Login.gov
|
||||
- DJANGO_SECRET_LOGIN_KEY
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ports:
|
||||
|
|
|
@ -17,6 +17,7 @@ $ docker-compose exec app python manage.py shell
|
|||
|
||||
"""
|
||||
import environs
|
||||
from base64 import b64decode
|
||||
from cfenv import AppEnv
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -43,7 +44,9 @@ path = Path(__file__)
|
|||
env_db_url = env.dj_db_url("DATABASE_URL")
|
||||
env_debug = env.bool("DJANGO_DEBUG", default=False)
|
||||
env_log_level = env.str("DJANGO_LOG_LEVEL", "DEBUG")
|
||||
env_base_url = env.str("DJANGO_BASE_URL")
|
||||
|
||||
secret_login_key = b64decode(secret("DJANGO_SECRET_LOGIN_KEY", ""))
|
||||
secret_key = secret("DJANGO_SECRET_KEY")
|
||||
|
||||
# region: Basic Django Config-----------------------------------------------###
|
||||
|
@ -78,6 +81,8 @@ INSTALLED_APPS = [
|
|||
# (and any other places you specify) into a single location
|
||||
# that can easily be served in production
|
||||
"django.contrib.staticfiles",
|
||||
# application used for integrating with Login.gov
|
||||
"djangooidc",
|
||||
# let's be sure to install our own application!
|
||||
"registrar",
|
||||
]
|
||||
|
@ -268,12 +273,32 @@ USE_TZ = True
|
|||
# endregion
|
||||
# region: Logging-----------------------------------------------------------###
|
||||
|
||||
# No file logger is configured, because containerized apps
|
||||
# do not log to the file system.
|
||||
# TODO: Configure better logging options
|
||||
# A Python logging configuration consists of four parts:
|
||||
# Loggers
|
||||
# Handlers
|
||||
# Filters
|
||||
# Formatters
|
||||
# https://docs.djangoproject.com/en/4.1/topics/logging/
|
||||
|
||||
# Log a message by doing this:
|
||||
#
|
||||
# import logging
|
||||
# logger = logging.getLogger(__name__)
|
||||
#
|
||||
# Then:
|
||||
#
|
||||
# logger.debug("We're about to execute function xyz. Wish us luck!")
|
||||
# logger.info("Oh! Here's something you might want to know.")
|
||||
# logger.warning("Something kinda bad happened.")
|
||||
# logger.error("Can't do this important task. Something is very wrong.")
|
||||
# logger.critical("Going to crash now.")
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
# Don't import Django's existing loggers
|
||||
"disable_existing_loggers": True,
|
||||
# define how to convert log messages into text;
|
||||
# each handler has its choice of format
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] "
|
||||
|
@ -283,29 +308,62 @@ LOGGING = {
|
|||
"simple": {
|
||||
"format": "%(levelname)s %(message)s",
|
||||
},
|
||||
"django.server": {
|
||||
"()": "django.utils.log.ServerFormatter",
|
||||
"format": "[{server_time}] {message}",
|
||||
"style": "{",
|
||||
},
|
||||
},
|
||||
# define where log messages will be sent;
|
||||
# each logger can have one or more handlers
|
||||
"handlers": {
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"level": env_log_level,
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
},
|
||||
"django.server": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "django.server",
|
||||
},
|
||||
# No file logger is configured,
|
||||
# because containerized apps
|
||||
# do not log to the file system.
|
||||
},
|
||||
# define loggers: these are "sinks" into which
|
||||
# messages are sent for processing
|
||||
"loggers": {
|
||||
# Django's generic logger
|
||||
"django": {
|
||||
"handlers": ["console"],
|
||||
"propagate": True,
|
||||
"level": env_log_level,
|
||||
"level": "INFO",
|
||||
},
|
||||
# Django's template processor
|
||||
"django.template": {
|
||||
"handlers": ["console"],
|
||||
"propagate": True,
|
||||
"level": "INFO",
|
||||
},
|
||||
# Django's runserver
|
||||
"django.server": {
|
||||
"handlers": ["django.server"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
# OpenID Connect logger
|
||||
"oic": {
|
||||
"handlers": ["console"],
|
||||
"level": "INFO",
|
||||
},
|
||||
# Django wrapper for OpenID Connect
|
||||
"djangooidc": {
|
||||
"handlers": ["console"],
|
||||
"level": "INFO",
|
||||
},
|
||||
# Our app!
|
||||
"registrar": {
|
||||
"handlers": ["console"],
|
||||
"propagate": True,
|
||||
"level": "INFO",
|
||||
"level": "DEBUG",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -313,41 +371,49 @@ LOGGING = {
|
|||
# endregion
|
||||
# region: Login-------------------------------------------------------------###
|
||||
|
||||
# TODO: FAC example for login.gov
|
||||
# SIMPLE_JWT = {
|
||||
# "ALGORITHM": "RS256",
|
||||
# "AUDIENCE": None,
|
||||
# "ISSUER": "https://idp.int.identitysandbox.gov/",
|
||||
# "JWK_URL": "https://idp.int.identitysandbox.gov/api/openid_connect/certs",
|
||||
# "LEEWAY": 0,
|
||||
# "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.UntypedToken",),
|
||||
# "USER_ID_CLAIM": "sub",
|
||||
# }
|
||||
# TOKEN_AUTH = {"TOKEN_TTL": 3600}
|
||||
# list of Python classes used when trying to authenticate a user
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"djangooidc.backends.OpenIdConnectBackend",
|
||||
]
|
||||
|
||||
# endregion
|
||||
# region: Rest Framework/API------------------------------------------------###
|
||||
# this is where unauthenticated requests are redirected when using
|
||||
# the login_required() decorator, LoginRequiredMixin, or AccessMixin
|
||||
LOGIN_URL = "openid/openid/login"
|
||||
|
||||
# Enable CORS if api is served at subdomain
|
||||
# https://github.com/adamchainz/django-cors-headers
|
||||
# TODO: FAC example for REST framework
|
||||
# API_VERSION = "0"
|
||||
# REST_FRAMEWORK = {
|
||||
# "DEFAULT_AUTHENTICATION_CLASSES": [
|
||||
# "rest_framework.authentication.BasicAuthentication",
|
||||
# "users.auth.ExpiringTokenAuthentication",
|
||||
# ],
|
||||
# "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
|
||||
# "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
|
||||
# "PAGE_SIZE": 10,
|
||||
# "TEST_REQUEST_RENDERER_CLASSES": [
|
||||
# "rest_framework.renderers.MultiPartRenderer",
|
||||
# "rest_framework.renderers.JSONRenderer",
|
||||
# "rest_framework.renderers.TemplateHTMLRenderer",
|
||||
# "rest_framework.renderers.BrowsableAPIRenderer",
|
||||
# ],
|
||||
# "TEST_REQUEST_DEFAULT_FORMAT": "api",
|
||||
# }
|
||||
# where to go after logging out
|
||||
LOGOUT_REDIRECT_URL = "home"
|
||||
|
||||
# disable dynamic client registration,
|
||||
# only the OP inside OIDC_PROVIDERS will be available
|
||||
OIDC_ALLOW_DYNAMIC_OP = False
|
||||
|
||||
# which provider to use if multiple are available
|
||||
# (code does not currently support user selection)
|
||||
OIDC_ACTIVE_PROVIDER = "login.gov"
|
||||
|
||||
|
||||
OIDC_PROVIDERS = {
|
||||
"login.gov": {
|
||||
"srv_discovery_url": "https://idp.int.identitysandbox.gov",
|
||||
"behaviour": {
|
||||
# the 'code' workflow requires direct connectivity from us to Login.gov
|
||||
"response_type": "code",
|
||||
"scope": ["email", "profile:name", "phone"],
|
||||
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
|
||||
},
|
||||
"client_registration": {
|
||||
"client_id": "cisa_dotgov_registrar",
|
||||
"redirect_uris": [f"https://{env_base_url}/openid/callback/login/"],
|
||||
"post_logout_redirect_uris": [
|
||||
f"https://{env_base_url}/openid/callback/logout/"
|
||||
],
|
||||
"token_endpoint_auth_method": ["private_key_jwt"],
|
||||
"sp_private_key": secret_login_key,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# endregion
|
||||
# region: Routing-----------------------------------------------------------###
|
||||
|
|
|
@ -10,10 +10,11 @@ from django.urls import include, path
|
|||
from registrar.views import health, index, profile
|
||||
|
||||
urlpatterns = [
|
||||
path("", index.index, name="home"),
|
||||
path("admin/", admin.site.urls),
|
||||
path("", index.index),
|
||||
path("health/", health.health),
|
||||
path("edit_profile/", profile.edit_profile, name="edit-profile"),
|
||||
path("openid/", include("djangooidc.urls")),
|
||||
# these views respect the DEBUG setting
|
||||
path("__debug__/", include("debug_toolbar.urls")),
|
||||
]
|
||||
|
|
27
src/registrar/templates/401.html
Normal file
27
src/registrar/templates/401.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate "Unauthorized" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Unauthorized" %}</h1>
|
||||
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "Authorization failed." %}</p>
|
||||
{% endif %}
|
||||
|
||||
<p><a href="{% url 'openid' %}">
|
||||
{% translate "Would you like to try logging in again?" %}
|
||||
</a></p>
|
||||
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<blockquote>{{ log_identifier }}</blockquote>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
|
||||
TODO: Content team to create a "how to contact us" footer for the error pages
|
||||
|
||||
{% endblock %}
|
13
src/registrar/templates/404.html
Normal file
13
src/registrar/templates/404.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate "Page not found" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h2>{% translate "Page not found" %}</h2>
|
||||
|
||||
<p>{% translate "The requested page could not be found." %}</p>
|
||||
|
||||
{% endblock %}
|
23
src/registrar/templates/500.html
Normal file
23
src/registrar/templates/500.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate "Server error" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% translate "Server Error" %}</h1>
|
||||
|
||||
{% if friendly_message %}
|
||||
<p>{{ friendly_message }}</p>
|
||||
{% else %}
|
||||
<p>{% translate "An internal server error occurred." %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if log_identifier %}
|
||||
<p>Here's a unique identifier for this error.</p>
|
||||
<blockquote>{{ log_identifier }}</blockquote>
|
||||
<p>{% translate "Please include it if you contact us." %}</p>
|
||||
{% endif %}
|
||||
|
||||
TODO: Content team to create a "how to contact us" footer for the error pages
|
||||
|
||||
{% endblock %}
|
26
src/registrar/templates/home.html
Normal file
26
src/registrar/templates/home.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!-- Test page -->
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block title %} Hello {% endblock %}
|
||||
{% block hero %}
|
||||
<section class="usa-hero">
|
||||
<div class="usa-grid">
|
||||
<div class="usa-hero-callout usa-section-dark">
|
||||
<h2>
|
||||
<span class="usa-hero-callout-alt">Welcome to the .gov registrar</span>
|
||||
</h2>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>This is the .gov registrar.</p>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<p><b>Hello {{ user.id }}</b></p>
|
||||
<p><a href="/openid/logout/">Click here to log out.</a></p>
|
||||
{% else %}
|
||||
<p><a href="/openid/login/">Click here to log in.</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue