diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 711ef7056..5226fb9cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: restore-keys: | ${{ runner.os }}-pipenv- - name: Install dependencies - run: pip install -U pip pipenv && pipenv install --dev + run: pip install -U pip pipenv && pipenv install --dev && pipenv install --dev prospector --skip-lock - name: Lint with prospector run: pipenv run prospector bandit: diff --git a/Pipfile b/Pipfile index 878c53417..5230760fc 100644 --- a/Pipfile +++ b/Pipfile @@ -40,6 +40,7 @@ signxml = "*" structlog = "*" swagger-spec-validator = "*" urllib3 = {extras = ["secure"],version = "*"} +jinja2 = "*" [requires] python_version = "3.8" @@ -51,7 +52,6 @@ bumpversion = "*" colorama = "*" coverage = "*" django-debug-toolbar = "*" -prospector = "*" pylint = "*" pylint-django = "*" unittest-xml-reporting = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 68958c9fb..1060f6ac8 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3693ac70f66ada5c8c8784e6e2d5c2f0ee75219e7141dde1075347234366e314" + "sha256": "c9232d99b062ee14eda43d176481f16da78ce53f912c844db3f33f1b3e8a47b7" }, "pipfile-spec": 6, "requires": { @@ -23,6 +23,13 @@ ], "version": "==2.5.2" }, + "asgiref": { + "hashes": [ + "sha256:7e06d934a7718bf3975acbf87780ba678957b87c7adc056f13b6215d610695a0", + "sha256:ea448f92fc35a0ef4b1508f53a04c4670255a3f33d22a81c8fc9c872036adbe5" + ], + "version": "==3.2.3" + }, "asn1crypto": { "hashes": [ "sha256:5a215cb8dc12f892244e3a113fe05397ee23c5c4ca7a69cd6e69811755efc42d", @@ -46,18 +53,18 @@ }, "boto3": { "hashes": [ - "sha256:7aaccacc199cd633b5ac14f0d544c9d592c53275a79cf4d230a9f66215331665", - "sha256:ca36aabfa5e7fa9ee8f84f82c3faffb4ffe078923f935f572f70dc49154ef6a0" + "sha256:33462a79d57c9c4a215e075472509537d03545f54566fc4f776fb0f4cfa616f6", + "sha256:34f9a04f529dc849f0e427782d6f3c6b62f7fb734d8f4859b17e5dee0855323e" ], "index": "pypi", - "version": "==1.11.5" + "version": "==1.12.0" }, "botocore": { "hashes": [ - "sha256:2538065f9f5023eae3607e78fc5d8c595c9173ec02bc92410652078617c44ac1", - "sha256:554231b1690c8521e05a41e50184e43d62941fdf9351e658aea894649b879985" + "sha256:055da4826f6c9158e4a61549d57a2ce449c27d44ce34ab4c96c7bb7b5c993efc", + "sha256:1f7cecfcd38c7cac17b5386014eb04626d1c7559ee8d8ec1526058cd23f6d1d4" ], - "version": "==1.14.15" + "version": "==1.15.0" }, "celery": { "hashes": [ @@ -164,11 +171,11 @@ }, "django": { "hashes": [ - "sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a", - "sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038" + "sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283", + "sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a" ], "index": "pypi", - "version": "==2.2.10" + "version": "==3.0.3" }, "django-cors-middleware": { "hashes": [ @@ -195,11 +202,11 @@ }, "django-guardian": { "hashes": [ - "sha256:8cf4efd67a863eb32beafd4335a38ffb083630f8ab2045212d27f8f9c3abe5a6", - "sha256:e638c9a23eeac534bb68b133975539ed8782f733ab6f35c0b23b4c39cd06b1bb" + "sha256:8cacf49ebcc1e545f0a8997971eec0fe109f5ed31fc2a569a7bf5615453696e2", + "sha256:ac81e88372fdf1795d84ba065550e739b42e9c6d07cdf201cf5bbf9efa7f396c" ], "index": "pypi", - "version": "==2.1.0" + "version": "==2.2.0" }, "django-model-utils": { "hashes": [ @@ -225,27 +232,26 @@ }, "django-otp": { "hashes": [ - "sha256:1f16c2b93fe484706ff16ac6f5e64ecc73dd240318c333e0560384ba548d3837", - "sha256:cd4975539be478417033561e9832a1a69a583189f680e92a649f412c661f90aa" + "sha256:523a87f8d0c52ce9a9f0a5e248c59dab3e85cdda5bbcd106cf49138a8f3f3209", + "sha256:ad3206e4a6f461c8968a2366a7cccfb340b93294e604b278aa9ab8b26f1017a1" ], "index": "pypi", - "version": "==0.7.5" + "version": "==0.8.1" }, "django-prometheus": { "hashes": [ - "sha256:f0657d4b887309086b71b55f6aa4a95f967b35fe115128b501f95422c423b12c", - "sha256:f645016ae5270ac2025a70788cd2bd636244a0c5705b323cc086994bf828181e" + "sha256:362ea45e5ee26bdba85ce978aeb370659ca6bbc0d6bac69868a055179e053bd1", + "sha256:facaa677386899303ea26c45552371cc43f476e42a81c081011a49cb5564af0b" ], "index": "pypi", - "version": "==2.0.0.dev124" + "version": "==2.1.0.dev5" }, "django-recaptcha": { "hashes": [ - "sha256:3b19b9d972ca802b683eed5fd51ed84b0798f2a52f8d057a912eb7d99cff3779", - "sha256:b6ce959cd7c0af7501698fab5f52ca1bcb6d9402cb3b8b42a4c4cb13d89cfbfa" + "sha256:567784963fd5400feaf92e8951d8dbbbdb4b4c48a76e225d4baa63a2c9d2cd8c" ], "index": "pypi", - "version": "==2.0.5" + "version": "==2.0.6" }, "django-redis": { "hashes": [ @@ -264,11 +270,11 @@ }, "django-storages": { "hashes": [ - "sha256:0a9b7e620e969fb0797523695329ed223bf540bbfdf6cd163b061fc11dab2d1c", - "sha256:9322ab74ba6371e2e0fccc350c741686ade829e43085597b26b07ae8955a0a00" + "sha256:3103991c2ee8cef8a2ff096709973ffe7106183d211a79f22cf855f33533d924", + "sha256:a59e9923cbce7068792f75344ed7727021ee4ac20f227cf17297d0d03d141e91" ], "index": "pypi", - "version": "==1.8" + "version": "==1.9.1" }, "djangorestframework": { "hashes": [ @@ -295,11 +301,11 @@ }, "drf-yasg": { "hashes": [ - "sha256:4cfec631880ae527a91ec7cd3241aea2f82189f59e2f089119aa687761afb227", - "sha256:504cce09035cf1bace63b84d9d778b772f86bb37d8a71ed6f723346362e633b2" + "sha256:5572e9d5baab9f6b49318169df9789f7399d0e3c7bdac8fdb8dfccf1d5d2b1ca", + "sha256:7d7af27ad16e18507e9392b2afd6b218fbffc432ec8dbea053099a2241e184ff" ], "index": "pypi", - "version": "==1.17.0" + "version": "==1.17.1" }, "eight": { "hashes": [ @@ -338,6 +344,7 @@ "sha256:c10142f819c2d22bdcd17548c46fa9b77cf4fda45097854c689666bf425e7484", "sha256:c922560ac46888d47384de1dbdc3daaa2ea993af4b26a436dec31fa2c19ec668" ], + "index": "pypi", "version": "==3.0.0a1" }, "jmespath": { @@ -372,35 +379,36 @@ }, "lxml": { "hashes": [ - "sha256:00ac0d64949fef6b3693813fe636a2d56d97a5a49b5bbb86e4cc4cc50ebc9ea2", - "sha256:0571e607558665ed42e450d7bf0e2941d542c18e117b1ebbf0ba72f287ad841c", - "sha256:0e3f04a7615fdac0be5e18b2406529521d6dbdb0167d2a690ee328bef7807487", - "sha256:13cf89be53348d1c17b453867da68704802966c433b2bb4fa1f970daadd2ef70", - "sha256:217262fcf6a4c2e1c7cb1efa08bd9ebc432502abc6c255c4abab611e8be0d14d", - "sha256:223e544828f1955daaf4cefbb4853bc416b2ec3fd56d4f4204a8b17007c21250", - "sha256:277cb61fede2f95b9c61912fefb3d43fbd5f18bf18a14fae4911b67984486f5d", - "sha256:3213f753e8ae86c396e0e066866e64c6b04618e85c723b32ecb0909885211f74", - "sha256:4690984a4dee1033da0af6df0b7a6bde83f74e1c0c870623797cec77964de34d", - "sha256:4fcc472ef87f45c429d3b923b925704aa581f875d65bac80f8ab0c3296a63f78", - "sha256:61409bd745a265a742f2693e4600e4dbd45cc1daebe1d5fad6fcb22912d44145", - "sha256:678f1963f755c5d9f5f6968dded7b245dd1ece8cf53c1aa9d80e6734a8c7f41d", - "sha256:6c6d03549d4e2734133badb9ab1c05d9f0ef4bcd31d83e5d2b4747c85cfa21da", - "sha256:6e74d5f4d6ecd6942375c52ffcd35f4318a61a02328f6f1bd79fcb4ffedf969e", - "sha256:7b4fc7b1ecc987ca7aaf3f4f0e71bbfbd81aaabf87002558f5bc95da3a865bcd", - "sha256:7ed386a40e172ddf44c061ad74881d8622f791d9af0b6f5be20023029129bc85", - "sha256:8f54f0924d12c47a382c600c880770b5ebfc96c9fd94cf6f6bdc21caf6163ea7", - "sha256:ad9b81351fdc236bda538efa6879315448411a81186c836d4b80d6ca8217cdb9", - "sha256:bbd00e21ea17f7bcc58dccd13869d68441b32899e89cf6cfa90d624a9198ce85", - "sha256:c3c289762cc09735e2a8f8a49571d0e8b4f57ea831ea11558247b5bdea0ac4db", - "sha256:cf4650942de5e5685ad308e22bcafbccfe37c54aa7c0e30cd620c2ee5c93d336", - "sha256:cfcbc33c9c59c93776aa41ab02e55c288a042211708b72fdb518221cc803abc8", - "sha256:e301055deadfedbd80cf94f2f65ff23126b232b0d1fea28f332ce58137bcdb18", - "sha256:ebbfe24df7f7b5c6c7620702496b6419f6a9aa2fd7f005eb731cc80d7b4692b9", - "sha256:eff69ddbf3ad86375c344339371168640951c302450c5d3e9936e98d6459db06", - "sha256:f6ed60a62c5f1c44e789d2cf14009423cb1646b44a43e40a9cf6a21f077678a1" + "sha256:06d4e0bbb1d62e38ae6118406d7cdb4693a3fa34ee3762238bcb96c9e36a93cd", + "sha256:0701f7965903a1c3f6f09328c1278ac0eee8f56f244e66af79cb224b7ef3801c", + "sha256:1f2c4ec372bf1c4a2c7e4bb20845e8bcf8050365189d86806bad1e3ae473d081", + "sha256:4235bc124fdcf611d02047d7034164897ade13046bda967768836629bc62784f", + "sha256:5828c7f3e615f3975d48f40d4fe66e8a7b25f16b5e5705ffe1d22e43fb1f6261", + "sha256:585c0869f75577ac7a8ff38d08f7aac9033da2c41c11352ebf86a04652758b7a", + "sha256:5d467ce9c5d35b3bcc7172c06320dddb275fea6ac2037f72f0a4d7472035cea9", + "sha256:63dbc21efd7e822c11d5ddbedbbb08cd11a41e0032e382a0fd59b0b08e405a3a", + "sha256:7bc1b221e7867f2e7ff1933165c0cec7153dce93d0cdba6554b42a8beb687bdb", + "sha256:8620ce80f50d023d414183bf90cc2576c2837b88e00bea3f33ad2630133bbb60", + "sha256:8a0ebda56ebca1a83eb2d1ac266649b80af8dd4b4a3502b2c1e09ac2f88fe128", + "sha256:90ed0e36455a81b25b7034038e40880189169c308a3df360861ad74da7b68c1a", + "sha256:95e67224815ef86924fbc2b71a9dbd1f7262384bca4bc4793645794ac4200717", + "sha256:afdb34b715daf814d1abea0317b6d672476b498472f1e5aacbadc34ebbc26e89", + "sha256:b4b2c63cc7963aedd08a5f5a454c9f67251b1ac9e22fd9d72836206c42dc2a72", + "sha256:d068f55bda3c2c3fcaec24bd083d9e2eede32c583faf084d6e4b9daaea77dde8", + "sha256:d5b3c4b7edd2e770375a01139be11307f04341ec709cf724e0f26ebb1eef12c3", + "sha256:deadf4df349d1dcd7b2853a2c8796593cc346600726eff680ed8ed11812382a7", + "sha256:df533af6f88080419c5a604d0d63b2c33b1c0c4409aba7d0cb6de305147ea8c8", + "sha256:e4aa948eb15018a657702fee0b9db47e908491c64d36b4a90f59a64741516e77", + "sha256:e5d842c73e4ef6ed8c1bd77806bf84a7cb535f9c0cf9b2c74d02ebda310070e1", + "sha256:ebec08091a22c2be870890913bdadd86fcd8e9f0f22bcb398abd3af914690c15", + "sha256:edc15fcfd77395e24543be48871c251f38132bb834d9fdfdad756adb6ea37679", + "sha256:f2b74784ed7e0bc2d02bd53e48ad6ba523c9b36c194260b7a5045071abbb1012", + "sha256:fa071559f14bd1e92077b1b5f6c22cf09756c6de7139370249eb372854ce51e6", + "sha256:fd52e796fee7171c4361d441796b64df1acfceb51f29e545e812f16d023c4bbc", + "sha256:fe976a0f1ef09b3638778024ab9fb8cde3118f203364212c198f71341c0715ca" ], "index": "pypi", - "version": "==4.4.2" + "version": "==4.5.0" }, "markupsafe": { "hashes": [ @@ -450,11 +458,11 @@ }, "packaging": { "hashes": [ - "sha256:aec3fdbb8bc9e4bb65f0634b9f551ced63983a529d6a8931817d52fdd0816ddb", - "sha256:fe1d8331dfa7cc0a883b49d75fc76380b2ab2734b220fbb87d774e4fd4b851f8" + "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73", + "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334" ], "index": "pypi", - "version": "==20.0" + "version": "==20.1" }, "prometheus-client": { "hashes": [ @@ -522,41 +530,39 @@ }, "pycryptodome": { "hashes": [ - "sha256:042ae873baadd0c33b4d699a5c5b976ade3233a979d972f98ca82314632d868c", - "sha256:0502876279772b1384b660ccc91563d04490d562799d8e2e06b411e2d81128a9", - "sha256:2de33ed0a95855735d5a0fc0c39603314df9e78ee8bbf0baa9692fb46b3b8bbb", - "sha256:319e568baf86620b419d53063b18c216abf924875966efdfe06891b987196a45", - "sha256:4372ec7518727172e1605c0843cdc5375d4771e447b8148c787b860260aae151", - "sha256:48821950ffb9c836858d8fa09d7840b6df52eadd387a3c5acece55cb387743f9", - "sha256:4b9533d4166ca07abdd49ce9d516666b1df944997fe135d4b21ac376aa624aff", - "sha256:54456cf85130e01674d21fb1ab89ffccacb138a8ade88d72fa2b0ac898d2798b", - "sha256:56fdd0e425f1b8fd3a00b6d96351f86226674974814c50534864d0124d48871f", - "sha256:57b1b707363490c495ad0eeb38bd1b0e1697c497af25fad78d3a1ebf0477fd5b", - "sha256:5c485ed6e9718ebcaa81138fa70ace9c563d202b56a8cee119b4085b023931f5", - "sha256:63c103a22cbe9752f6ea9f1a0de129995bad91c4d03a66c67cffcf6ee0c9f1e1", - "sha256:68fab8455efcbfe87c5d75015476f9b606227ffe244d57bfd66269451706e899", - "sha256:6c2720696b10ae356040e888bde1239b8957fe18885ccf5e7b4e8dec882f0856", - "sha256:72166c2ac520a5dbd2d90208b9c279161ec0861662a621892bd52fb6ca13ab91", - "sha256:7c52308ac5b834331b2f107a490b2c27de024a229b61df4cdc5c131d563dfe98", - "sha256:87d8d85b4792ca5e730fb7a519fbc3ed976c59dcf79c5204589c59afd56b9926", - "sha256:896e9b6fd0762aa07b203c993fbbee7a1f1a4674c6886afd7bfa86f3d1be98a8", - "sha256:8a799bea3c6617736e914a2e77c409f52893d382f619f088f8a80e2e21f573c1", - "sha256:9d9945ac8375d5d8e60bd2a2e1df5882eaa315522eedf3ca868b1546dfa34eba", - "sha256:9ef966c727de942de3e41aa8462c4b7b4bca70f19af5a3f99e31376589c11aac", - "sha256:a168e73879619b467072509a223282a02c8047d932a48b74fbd498f27224aa04", - "sha256:a30f501bbb32e01a49ef9e09ca1260e5ab49bf33a257080ec553e08997acc487", - "sha256:a8ca2450394d3699c9f15ef25e8de9a24b401933716a1e39d37fa01f5fe3c58b", - "sha256:aec4d42deb836b8fb3ba32f2ba1ef0d33dd3dc9d430b1479ee7a914490d15b5e", - "sha256:b4af098f2a50f8d048ab12cabb59456585c0acf43d90ee79782d2d6d0ed59dba", - "sha256:b55c60c321ac91945c60a40ac9896ac7a3d432bb3e8c14006dfd82ad5871c331", - "sha256:c53348358408d94869059e16fba5ff3bef8c52c25b18421472aba272b9bb450f", - "sha256:cbfd97f9e060f0d30245cd29fa267a9a84de9da97559366fca0a3f7655acc63f", - "sha256:d3fe3f33ad52bf0c19ee6344b695ba44ffbfa16f3c29ca61116b48d97bd970fb", - "sha256:e3a79a30d15d9c7c284a7734036ee8abdb5ca3a6f5774d293cdc9e1358c1dc10", - "sha256:eec0689509389f19875f66ae8dedd59f982240cdab31b9f78a8dc266011df93a" + "sha256:012ca77c2105600e3c6aef43188101ac1d95052c633a4ae8fbebffab20c25f8a", + "sha256:05b4d865710f9a6378d3ada28195ff78e52642d3ecffe6fa9d379d870b9bf29d", + "sha256:07daddb98f98f771ba027f8f835bdb675aeb84effe41ed5221f520b267429354", + "sha256:09bf05a489fe10f9280a5e0163f195e7b9630cafb15f7d72fb9c8f5eb2afa84f", + "sha256:0a8d5f2dbb4bbe830ace54286b829bfa529f0853bedaab6225fcb2e6d1f7e356", + "sha256:1259b8ca49662b8a941177357f08147d858595c0042e63ff81e9628e925b5c9d", + "sha256:238d8b6dd27bd1a04816a68aa90a739e6dd23b192fcd83b50f9360958bff192a", + "sha256:2a57daef18a2022a5e4b6f7376c9ddd0c2d946e4b1f1e59b837f5bf295be7380", + "sha256:39e5ca2f66d1eac7abcba5ce1a03370d123dc6085620f1cd532dfee27e650178", + "sha256:3d516df693c195b8da3795e381429bd420e87081b7e6c2871c62c9897c812cda", + "sha256:3e486c5b7228e864665fc479e9f596b2547b5fe29c6f5c8ed3807784d06faed7", + "sha256:5029c46b0d41dfb763c3981c0af68eab029f06fe2b94f2299112fc18cf9e8d6d", + "sha256:5817c0b3c263025d851da96b90cbc7e95348008f88b990e90d10683dba376666", + "sha256:79320f1fc5c9ca682869087c565bb29ca6f334692e940d7365771e9a94382e12", + "sha256:887d08beca6368d3d70dc75126607ad76317a9fd07fe61323d8c3cb42add12b6", + "sha256:9163fec630495c10c767991e3f8dab32f4427bfb2dfeaa59bb28fe3e52ba66f2", + "sha256:95d324e603c5cec5d89e8595236bbf59ade5fe3a72d100ce61eebb323d598750", + "sha256:9927aa8a8cb4af681279b6f28a1dcb14e0eb556c1aea8413a1e27608a8516e0c", + "sha256:9948c2d5c5c0ee45ed44cee0e2eba2ce60a03be006ed3074521f3da3be162e72", + "sha256:a719bd708207fa219fcbf4c8ebbcbc52846045f78179d00445b429fdabdbc1c4", + "sha256:bc22ced26ebc46546798fa0141f4418f1db116dec517f0aeaecec87cf7b2416c", + "sha256:c41b7e10b72cef00cd63410f31fe50e72dc3a40eafbd146e288384fbe4208064", + "sha256:cdb0ad83a5d6bac986a37fcb7562bcbef0aabae8ea19505bab5cf83c4d18af12", + "sha256:d8e480f65ac7105cbc288eec2417dc61eaac6ed6e75595aa15b8c7c77c53a68b", + "sha256:da2d581da279bc7408d38e16ff77754f5448c4352f2acfe530a5d14d8fc6934a", + "sha256:de61091dd68326b600422cf731eb4810c4c6363f18a65bccd6061784b7454f5b", + "sha256:ec7d39589f9cfc2a8b83b1d2fc673441757c99d43283e97b2dd46e0e23730db8", + "sha256:f3204006869ab037604b1d9f045c4e84882ddd365e4ee8caa5eb1ff47a59188e", + "sha256:f4d2174e168d0eabd1fffaf88b4f62c2b6f30a67b8816f31024b8e48be3e2d75", + "sha256:fcff8c9d88d58880f7eda2139c7c444552a38f98a9e77ba5970b6e78f54ac358" ], "index": "pypi", - "version": "==3.9.4" + "version": "==3.9.6" }, "pycryptodomex": { "hashes": [ @@ -714,10 +720,10 @@ }, "ruamel.yaml": { "hashes": [ - "sha256:ee3264b83c3309b4ae7978afa185da6a1d278e3abc9fb942f1a0b57c622092f8", - "sha256:fd16843ff0ba45fa5e1ea9ea7038428b4a46f2c39deea9aa67f9eaa34823dc11" + "sha256:0962fd7999e064c4865f96fb1e23079075f4a2a14849bcdc5cdba53a24f9759b", + "sha256:099c644a778bf72ffa00524f78dd0b6476bca94a1da344130f4bf3381ce5b954" ], - "version": "==0.16.9" + "version": "==0.16.10" }, "ruamel.yaml.clib": { "hashes": [ @@ -753,11 +759,11 @@ }, "sentry-sdk": { "hashes": [ - "sha256:8e2d38dc58dc992280487e553ec3d97a424e4d179f4fad802ef3b08f64ccf4d8", - "sha256:9b59e155229ea7d46a52b5c025d8c3c6d591e9dd9bb5f5f47310b2bb430038a8" + "sha256:b06dd27391fd11fb32f84fe054e6a64736c469514a718a99fb5ce1dff95d6b28", + "sha256:e023da07cfbead3868e1e2ba994160517885a32dfd994fc455b118e37989479b" ], "index": "pypi", - "version": "==0.14.0" + "version": "==0.14.1" }, "service-identity": { "hashes": [ @@ -791,11 +797,11 @@ }, "structlog": { "hashes": [ - "sha256:4287058cf4ce1a59bc5dea290d6386d37f29a37529c9a51cdf7387e51710152b", - "sha256:6640e6690fc31d5949bc614c1a630464d3aaa625284aeb7c6e486c3010d73e12" + "sha256:7a48375db6274ed1d0ae6123c486472aa1d0890b08d314d2b016f3aa7f35990b", + "sha256:8a672be150547a93d90a7d74229a29e765be05bd156a35cdcc527ebf68e9af92" ], "index": "pypi", - "version": "==19.2.0" + "version": "==20.1.0" }, "swagger-spec-validator": { "hashes": [ @@ -817,12 +823,12 @@ "secure" ], "hashes": [ - "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", - "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" + "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", + "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" ], "index": "pypi", "markers": null, - "version": "==1.25.7" + "version": "==1.25.8" }, "vine": { "hashes": [ @@ -863,10 +869,10 @@ }, "autopep8": { "hashes": [ - "sha256:4d8eec30cc81bc5617dbf1218201d770dc35629363547f17577c61683ccfb3ee" + "sha256:0f592a0447acea0c2b0a9602be1e4e3d86db52badd2e3c84f0193bfd89fd3a43" ], "index": "pypi", - "version": "==1.4.4" + "version": "==1.5" }, "bandit": { "hashes": [ @@ -946,40 +952,33 @@ }, "django": { "hashes": [ - "sha256:1226168be1b1c7efd0e66ee79b0e0b58b2caa7ed87717909cd8a57bb13a7079a", - "sha256:9a4635813e2d498a3c01b10c701fe4a515d76dd290aaa792ccb65ca4ccb6b038" + "sha256:2f1ba1db8648484dd5c238fb62504777b7ad090c81c5f1fd8d5eb5ec21b5f283", + "sha256:c91c91a7ad6ef67a874a4f76f58ba534f9208412692a840e1d125eb5c279cb0a" ], "index": "pypi", - "version": "==2.2.10" + "version": "==3.0.3" }, "django-debug-toolbar": { "hashes": [ - "sha256:24c157bc6c0e1648e0a6587511ecb1b007a00a354ce716950bff2de12693e7a8", - "sha256:77cfba1d6e91b9bc3d36dc7dc74a9bb80be351948db5f880f2562a0cbf20b6c5" + "sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943", + "sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c" ], "index": "pypi", - "version": "==2.1" - }, - "dodgy": { - "hashes": [ - "sha256:28323cbfc9352139fdd3d316fa17f325cc0e9ac74438cbba51d70f9b48f86c3a", - "sha256:51f54c0fd886fa3854387f354b19f429d38c04f984f38bc572558b703c0542a6" - ], - "version": "==0.2.1" + "version": "==2.2" }, "gitdb2": { "hashes": [ - "sha256:1b6df1433567a51a4a9c1a5a0de977aa351a405cc56d7d35f3388bad1f630350", - "sha256:96bbb507d765a7f51eb802554a9cfe194a174582f772e0d89f4e87288c288b7b" + "sha256:0375d983fd887d03c8942e81b1b0abc6c320cfb500cd3fe0d9c0eac87fbf2b52", + "sha256:b2b3a67090c17dc61f8407ca485e79ae811225ab5ebcd98ac5ee01448e8987b5" ], - "version": "==2.0.6" + "version": "==3.0.2" }, "gitpython": { "hashes": [ - "sha256:99c77677f31f255e130f3fed4c8e0eebb35f1a09df98ff965fff6774f71688cf", - "sha256:99cd0403cecd8a13b95d2e045b9fcaa7837137fcc5ec3105f2c413305d82c143" + "sha256:620b3c729bbc143b498cfea77e302999deedc55faec5b1067086c9ef90e101bc", + "sha256:a43a5d88a5bbc3cf32bb5223e4b4e68fd716db5e9996cad6e561bbfee6e5f4af" ], - "version": "==3.0.7" + "version": "==3.0.8" }, "isort": { "hashes": [ @@ -1035,40 +1034,12 @@ ], "version": "==5.4.4" }, - "pep8-naming": { - "hashes": [ - "sha256:1b419fa45b68b61cd8c5daf4e0c96d28915ad14d3d5f35fcc1e7e95324a33a2e", - "sha256:4eedfd4c4b05e48796f74f5d8628c068ff788b9c2b08471ad408007fc6450e5a" - ], - "version": "==0.4.1" - }, - "prospector": { - "hashes": [ - "sha256:ea910794b53cfefcb5dfb6b4eb0323e42d1a88132e165b85b016cc7f0b6ae635" - ], - "index": "pypi", - "version": "==1.2.0" - }, "pycodestyle": { "hashes": [ - "sha256:cbc619d09254895b0d12c2c691e237b2e91e9b2ecf5e84c26b35400f93dcfb83", - "sha256:cbfca99bd594a10f674d0cd97a3d802a1fdef635d4361e1a2658de47ed261e3a" + "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", + "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" ], - "version": "==2.4.0" - }, - "pydocstyle": { - "hashes": [ - "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586", - "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5" - ], - "version": "==5.0.2" - }, - "pyflakes": { - "hashes": [ - "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", - "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" - ], - "version": "==2.1.1" + "version": "==2.5.0" }, "pylint": { "hashes": [ @@ -1078,25 +1049,13 @@ "index": "pypi", "version": "==2.4.4" }, - "pylint-celery": { - "hashes": [ - "sha256:41e32094e7408d15c044178ea828dd524beedbdbe6f83f712c5e35bde1de4beb" - ], - "version": "==0.3" - }, "pylint-django": { "hashes": [ - "sha256:9bdb0e022b19881218a25ffb8ad05e83b83bc5cdbc58e5ee8ffbe99965193f6c", - "sha256:9eea6a026eaa5ecfad5fed7a33faf77ef55a43cc78afbcaf2f6ddd071156b3f8" + "sha256:440beb814464928aedd2e21196bb6e47a83b63e2cbe886a701ba0f4a64206bbb", + "sha256:d5d113605a64cf0e638b707d4cb42106e626f8851bc30a44d5b22bd698ad8483" ], "index": "pypi", - "version": "==2.0.12" - }, - "pylint-flask": { - "hashes": [ - "sha256:f4d97de2216bf7bfce07c9c08b166e978fe9f2725de2a50a9845a97de7e31517" - ], - "version": "==0.6" + "version": "==2.0.13" }, "pylint-plugin-utils": { "hashes": [ @@ -1155,18 +1114,6 @@ ], "version": "==2020.1.8" }, - "requirements-detector": { - "hashes": [ - "sha256:9fbc4b24e8b7c3663aff32e3eba34596848c6b91bd425079b386973bd8d08931" - ], - "version": "==0.6" - }, - "setoptconf": { - "hashes": [ - "sha256:5b0b5d8e0077713f5d5152d4f63be6f048d9a1bb66be15d089a11c898c3cf49c" - ], - "version": "==0.2.0" - }, "six": { "hashes": [ "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", @@ -1181,13 +1128,6 @@ ], "version": "==2.0.5" }, - "snowballstemmer": { - "hashes": [ - "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", - "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" - ], - "version": "==2.0.0" - }, "sqlparse": { "hashes": [ "sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177", @@ -1237,11 +1177,11 @@ }, "unittest-xml-reporting": { "hashes": [ - "sha256:358bbdaf24a26d904cc1c26ef3078bca7fc81541e0a54c8961693cc96a6f35e0", - "sha256:9d28ddf6524cf0ff9293f61bd12e792de298f8561a5c945acea63fb437789e0e" + "sha256:6584562cde8226fc79fa29e38903c669a02799074a563bb0b70fcd3a8e87829c", + "sha256:dd8046a64dc62f3d30301523a54992e0be75a945194491e0a3b718130cb429e0" ], "index": "pypi", - "version": "==2.5.2" + "version": "==3.0.1" }, "wrapt": { "hashes": [ diff --git a/docs/reference/property-mappings/user-object.md b/docs/reference/property-mappings/user-object.md new file mode 100644 index 000000000..8cc35c162 --- /dev/null +++ b/docs/reference/property-mappings/user-object.md @@ -0,0 +1,20 @@ +# Passbook User Object + +The User object has the following attributes: + + - `username`: User's Username + - `email` User's E-Mail + - `name` User's Display Name + - `is_staff` Boolean field if user is staff + - `is_active` Boolean field if user is active + - `date_joined` Date User joined/was created + - `password_change_date` Date Password was last changed + - `attributes` Dynamic Attributes + +## Examples + +List all the User's Group Names + +```jinja2 +[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}] +``` diff --git a/mkdocs.yml b/mkdocs.yml index efda66a94..bd2251ab4 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,6 +19,9 @@ nav: - Rancher: integrations/services/rancher/index.md - Harbor: integrations/services/harbor/index.md - Sentry: integrations/services/sentry/index.md + - Reference: + - Property Mappings: + - User Object: reference/property-mappings/user-object.md repo_name: "BeryJu.org/passbook" repo_url: https://github.com/BeryJu/passbook diff --git a/passbook/admin/templates/generic/create.html b/passbook/admin/templates/generic/create.html index 43bce2814..c62000bf1 100644 --- a/passbook/admin/templates/generic/create.html +++ b/passbook/admin/templates/generic/create.html @@ -1,4 +1,4 @@ -{% extends "generic/form.html" %} +{% extends base_template|default:"generic/form.html" %} {% load utils %} {% load i18n %} diff --git a/passbook/admin/templates/generic/form.html b/passbook/admin/templates/generic/form.html index e99c1974c..a7a073f03 100644 --- a/passbook/admin/templates/generic/form.html +++ b/passbook/admin/templates/generic/form.html @@ -20,6 +20,7 @@ + {% endblock %} {% block content %} @@ -29,21 +30,33 @@
{% include 'partials/form.html' with form=form %} + {% block beneath_form %} + {% endblock %} {% trans "Cancel" %}
- {% block beneath_form %} - {% endblock %} {% endblock %} diff --git a/passbook/admin/templates/generic/update.html b/passbook/admin/templates/generic/update.html index b818ef34a..738a45e25 100644 --- a/passbook/admin/templates/generic/update.html +++ b/passbook/admin/templates/generic/update.html @@ -1,4 +1,4 @@ -{% extends "generic/form.html" %} +{% extends base_template|default:"generic/form.html" %} {% load utils %} {% load i18n %} diff --git a/passbook/admin/views/property_mapping.py b/passbook/admin/views/property_mapping.py index d87ca4d42..420f10dd7 100644 --- a/passbook/admin/views/property_mapping.py +++ b/passbook/admin/views/property_mapping.py @@ -66,6 +66,9 @@ class PropertyMappingCreateView( if x.__name__ == property_mapping_type ) kwargs["type"] = model._meta.verbose_name + form_cls = self.get_form_class() + if hasattr(form_cls, "template_name"): + kwargs["base_template"] = form_cls.template_name return kwargs def get_form_class(self): @@ -92,6 +95,12 @@ class PropertyMappingUpdateView( success_url = reverse_lazy("passbook_admin:property-mappings") success_message = _("Successfully updated Property Mapping") + def get_context_data(self, **kwargs): + form_cls = self.get_form_class() + if hasattr(form_cls, "template_name"): + kwargs["base_template"] = form_cls.template_name + return kwargs + def get_form_class(self): form_class_path = self.get_object().form form_class = path_to_class(form_class_path) diff --git a/passbook/core/migrations/0006_propertymapping_template.py b/passbook/core/migrations/0006_propertymapping_template.py new file mode 100644 index 000000000..d842b48ac --- /dev/null +++ b/passbook/core/migrations/0006_propertymapping_template.py @@ -0,0 +1,19 @@ +# Generated by Django 3.0.3 on 2020-02-17 16:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_core", "0005_merge_20191025_2022"), + ] + + operations = [ + migrations.AddField( + model_name="propertymapping", + name="template", + field=models.TextField(default=""), + preserve_default=False, + ), + ] diff --git a/passbook/core/migrations/0007_auto_20200217_1934.py b/passbook/core/migrations/0007_auto_20200217_1934.py new file mode 100644 index 000000000..4fa3282bb --- /dev/null +++ b/passbook/core/migrations/0007_auto_20200217_1934.py @@ -0,0 +1,16 @@ +# Generated by Django 3.0.3 on 2020-02-17 19:34 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_core", "0006_propertymapping_template"), + ] + + operations = [ + migrations.RenameField( + model_name="propertymapping", old_name="template", new_name="expression", + ), + ] diff --git a/passbook/core/models.py b/passbook/core/models.py index 17169cdb9..7f1e26c8e 100644 --- a/passbook/core/models.py +++ b/passbook/core/models.py @@ -2,15 +2,17 @@ from datetime import timedelta from random import SystemRandom from time import sleep -from typing import Optional +from typing import Optional, Any from uuid import uuid4 +from jinja2.nativetypes import NativeEnvironment from django.contrib.auth.models import AbstractUser from django.contrib.postgres.fields import JSONField from django.db import models from django.urls import reverse_lazy +from django.http import HttpRequest from django.utils.timezone import now -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ from django_prometheus.models import ExportModelOperationsMixin from guardian.mixins import GuardianUserMixin from model_utils.managers import InheritanceManager @@ -22,6 +24,7 @@ from passbook.policies.exceptions import PolicyException from passbook.policies.struct import PolicyRequest, PolicyResult LOGGER = get_logger() +NATIVE_ENVIRONMENT = NativeEnvironment() def default_nonce_duration(): @@ -293,10 +296,16 @@ class PropertyMapping(UUIDModel): """User-defined key -> x mapping which can be used by providers to expose extra data.""" name = models.TextField() + expression = models.TextField() form = "" objects = InheritanceManager() + def evaluate(self, user: User, request: HttpRequest, **kwargs) -> Any: + """Evaluate `self.expression` using `**kwargs` as Context.""" + expression = NATIVE_ENVIRONMENT.from_string(self.expression) + return expression.render(user=user, request=request, **kwargs) + def __str__(self): return f"Property Mapping {self.name}" diff --git a/passbook/providers/saml/api.py b/passbook/providers/saml/api.py index 38dc57545..1348c85ca 100644 --- a/passbook/providers/saml/api.py +++ b/passbook/providers/saml/api.py @@ -14,12 +14,16 @@ class SAMLProviderSerializer(ModelSerializer): fields = [ "pk", "name", - "property_mappings", + "processor_path", "acs_url", "audience", - "processor_path", "issuer", - "assertion_valid_for", + "assertion_valid_not_before", + "assertion_valid_not_on_or_after", + "session_valid_not_on_or_after", + "property_mappings", + "digest_algorithm", + "signature_algorithm", "signing", "signing_cert", "signing_key", @@ -39,7 +43,7 @@ class SAMLPropertyMappingSerializer(ModelSerializer): class Meta: model = SAMLPropertyMapping - fields = ["pk", "name", "saml_name", "friendly_name", "values"] + fields = ["pk", "name", "saml_name", "friendly_name", "expression"] class SAMLPropertyMappingViewSet(ModelViewSet): diff --git a/passbook/providers/saml/forms.py b/passbook/providers/saml/forms.py index ed7f79d6e..08dc95f89 100644 --- a/passbook/providers/saml/forms.py +++ b/passbook/providers/saml/forms.py @@ -4,7 +4,6 @@ from django import forms from django.contrib.admin.widgets import FilteredSelectMultiple from django.utils.translation import gettext as _ -from passbook.lib.fields import DynamicArrayField from passbook.providers.saml.models import ( SAMLPropertyMapping, SAMLProvider, @@ -60,13 +59,14 @@ class SAMLProviderForm(forms.ModelForm): class SAMLPropertyMappingForm(forms.ModelForm): """SAML Property Mapping form""" + template_name = "saml/idp/property_mapping_form.html" + class Meta: model = SAMLPropertyMapping - fields = ["name", "saml_name", "friendly_name", "values"] + fields = ["name", "saml_name", "friendly_name", "expression"] widgets = { "name": forms.TextInput(), "saml_name": forms.TextInput(), "friendly_name": forms.TextInput(), } - field_classes = {"values": DynamicArrayField} diff --git a/passbook/providers/saml/migrations/0003_auto_20200216_1109.py b/passbook/providers/saml/migrations/0003_auto_20200216_1109.py index a1076185b..a1cd56c65 100644 --- a/passbook/providers/saml/migrations/0003_auto_20200216_1109.py +++ b/passbook/providers/saml/migrations/0003_auto_20200216_1109.py @@ -4,46 +4,6 @@ import django.contrib.postgres.fields from django.db import migrations, models -def create_default_property_mappings(apps, schema_editor): - """Create default SAML Property Mappings""" - SAMLPropertyMapping = apps.get_model( - "passbook_providers_saml", "SAMLPropertyMapping" - ) - db_alias = schema_editor.connection.alias - defaults = [ - { - "FriendlyName": "eduPersonPrincipalName", - "Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", - "Value": "{user.email}", - }, - {"FriendlyName": "cn", "Name": "urn:oid:2.5.4.3", "Value": "{user.name}",}, - { - "FriendlyName": "mail", - "Name": "urn:oid:0.9.2342.19200300.100.1.3", - "Value": "{user.email}", - }, - { - "FriendlyName": "displayName", - "Name": "urn:oid:2.16.840.1.113730.3.1.241", - "Value": "{user.username}", - }, - { - "FriendlyName": "uid", - "Name": "urn:oid:0.9.2342.19200300.100.1.1", - "Value": "{user.pk}", - }, - ] - for default in defaults: - SAMLPropertyMapping.objects.using(db_alias).get_or_create( - saml_name=default["Name"], - friendly_name=default["FriendlyName"], - values=[default["Value"]], - defaults={ - "name": f"Autogenerated SAML Mapping: {default['FriendlyName']} -> {default['Value']}" - }, - ) - - class Migration(migrations.Migration): dependencies = [ @@ -75,5 +35,4 @@ class Migration(migrations.Migration): name="signing_cert", field=models.TextField(verbose_name="Singing Certificate"), ), - migrations.RunPython(create_default_property_mappings), ] diff --git a/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py b/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py new file mode 100644 index 000000000..6d7589bb2 --- /dev/null +++ b/passbook/providers/saml/migrations/0005_remove_samlpropertymapping_values.py @@ -0,0 +1,76 @@ +# Generated by Django 3.0.3 on 2020-02-17 16:15 + +from django.db import migrations + + +def cleanup_old_autogenerated(apps, schema_editor): + SAMLPropertyMapping = apps.get_model( + "passbook_providers_saml", "SAMLPropertyMapping" + ) + db_alias = schema_editor.connection.alias + SAMLPropertyMapping.objects.using(db_alias).filter( + name__startswith="Autogenerated" + ).delete() + + +def create_default_property_mappings(apps, schema_editor): + """Create default SAML Property Mappings""" + SAMLPropertyMapping = apps.get_model( + "passbook_providers_saml", "SAMLPropertyMapping" + ) + db_alias = schema_editor.connection.alias + defaults = [ + { + "FriendlyName": "eduPersonPrincipalName", + "Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6", + "Expression": "{{ user.email }}", + }, + { + "FriendlyName": "cn", + "Name": "urn:oid:2.5.4.3", + "Expression": "{{ user.name }}", + }, + { + "FriendlyName": "mail", + "Name": "urn:oid:0.9.2342.19200300.100.1.3", + "Expression": "{{ user.email }}", + }, + { + "FriendlyName": "displayName", + "Name": "urn:oid:2.16.840.1.113730.3.1.241", + "Expression": "{{ user.username }}", + }, + { + "FriendlyName": "uid", + "Name": "urn:oid:0.9.2342.19200300.100.1.1", + "Expression": "{{ user.pk }}", + }, + { + "FriendlyName": "member-of", + "Name": "member-of", + "Expression": "[{% for group in user.groups.all() %}'{{ group.name }}',{% endfor %}]", + }, + ] + for default in defaults: + SAMLPropertyMapping.objects.using(db_alias).get_or_create( + saml_name=default["Name"], + friendly_name=default["FriendlyName"], + expression=default["Expression"], + defaults={ + "name": f"Autogenerated SAML Mapping: {default['FriendlyName']} -> {default['Expression']}" + }, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_providers_saml", "0004_auto_20200217_1526"), + ("passbook_core", "0007_auto_20200217_1934"), + ] + + operations = [ + migrations.RunPython(cleanup_old_autogenerated), + migrations.RemoveField(model_name="samlpropertymapping", name="values",), + migrations.RunPython(create_default_property_mappings), + ] diff --git a/passbook/providers/saml/models.py b/passbook/providers/saml/models.py index 0b0d66870..bee0dd4ac 100644 --- a/passbook/providers/saml/models.py +++ b/passbook/providers/saml/models.py @@ -1,5 +1,4 @@ """passbook saml_idp Models""" -from django.contrib.postgres.fields import ArrayField from django.db import models from django.shortcuts import reverse from django.utils.translation import ugettext_lazy as _ @@ -118,15 +117,6 @@ class SAMLPropertyMapping(PropertyMapping): saml_name = models.TextField(verbose_name="SAML Name") friendly_name = models.TextField(default=None, blank=True, null=True) - values = ArrayField( - models.TextField(), - help_text=_( - ( - "This string can contain string substitutions delimited by {}." - " The following Variables are available: user, request" - ) - ), - ) form = "passbook.providers.saml.forms.SAMLPropertyMappingForm" diff --git a/passbook/providers/saml/processors/base.py b/passbook/providers/saml/processors/base.py index 067195ae0..12942f8b7 100644 --- a/passbook/providers/saml/processors/base.py +++ b/passbook/providers/saml/processors/base.py @@ -98,17 +98,19 @@ class Processor: for mapping in self._remote.property_mappings.all().select_subclasses(): if isinstance(mapping, SAMLPropertyMapping): + value = mapping.evaluate( + user=self._http_request.user, + request=self._http_request, + provider=self._remote, + ) mapping_payload = { "Name": mapping.saml_name, - "ValueArray": [], "FriendlyName": mapping.friendly_name, } - for value in mapping.values: - mapping_payload["ValueArray"].append( - value.format( - user=self._http_request.user, request=self._http_request - ) - ) + if isinstance(value, list): + mapping_payload["ValueArray"] = value + else: + mapping_payload["Value"] = value attributes.append(mapping_payload) self._assertion_params["ATTRIBUTES"] = attributes self._assertion_xml = get_assertion_xml( diff --git a/passbook/providers/saml/templates/saml/idp/property_mapping_form.html b/passbook/providers/saml/templates/saml/idp/property_mapping_form.html new file mode 100644 index 000000000..43ff158ff --- /dev/null +++ b/passbook/providers/saml/templates/saml/idp/property_mapping_form.html @@ -0,0 +1,20 @@ +{% extends "generic/form.html" %} + +{% load i18n %} + +{% block beneath_form %} +
+ +
+

+ Expression using Jinja. Following variables are available: +

+

+
+
+{% endblock %} diff --git a/passbook/sources/ldap/api.py b/passbook/sources/ldap/api.py index 50f65f098..a51a5ce12 100644 --- a/passbook/sources/ldap/api.py +++ b/passbook/sources/ldap/api.py @@ -35,7 +35,7 @@ class LDAPPropertyMappingSerializer(ModelSerializer): class Meta: model = LDAPPropertyMapping - fields = ["pk", "name", "ldap_property", "object_field"] + fields = ["pk", "name", "expression", "object_field"] class LDAPSourceViewSet(ModelViewSet): diff --git a/passbook/sources/ldap/connector.py b/passbook/sources/ldap/connector.py index 40904122d..dc33c53e4 100644 --- a/passbook/sources/ldap/connector.py +++ b/passbook/sources/ldap/connector.py @@ -6,7 +6,7 @@ import ldap3.core.exceptions from structlog import get_logger from passbook.core.models import Group, User -from passbook.sources.ldap.models import LDAPSource +from passbook.sources.ldap.models import LDAPSource, LDAPPropertyMapping LOGGER = get_logger() @@ -154,7 +154,10 @@ class Connector: ) -> Dict[str, Dict[Any, Any]]: properties = {"attributes": {}} for mapping in self._source.property_mappings.all().select_subclasses(): - properties[mapping.object_field] = attributes.get(mapping.ldap_property, "") + mapping: LDAPPropertyMapping + properties[mapping.object_field] = mapping.evaluate( + user=None, request=None, ldap=attributes + ) if self._source.object_uniqueness_field in attributes: properties["attributes"]["ldap_uniq"] = attributes.get( self._source.object_uniqueness_field diff --git a/passbook/sources/ldap/forms.py b/passbook/sources/ldap/forms.py index e940b132c..76e616f0f 100644 --- a/passbook/sources/ldap/forms.py +++ b/passbook/sources/ldap/forms.py @@ -50,10 +50,12 @@ class LDAPSourceForm(forms.ModelForm): class LDAPPropertyMappingForm(forms.ModelForm): """LDAP Property Mapping form""" + template_name = "ldap/property_mapping_form.html" + class Meta: model = LDAPPropertyMapping - fields = ["name", "ldap_property", "object_field"] + fields = ["name", "object_field", "expression"] widgets = { "name": forms.TextInput(), "ldap_property": forms.TextInput(), diff --git a/passbook/sources/ldap/migrations/0007_remove_ldappropertymapping_ldap_property.py b/passbook/sources/ldap/migrations/0007_remove_ldappropertymapping_ldap_property.py new file mode 100644 index 000000000..f6ac24740 --- /dev/null +++ b/passbook/sources/ldap/migrations/0007_remove_ldappropertymapping_ldap_property.py @@ -0,0 +1,46 @@ +# Generated by Django 3.0.3 on 2020-02-17 16:19 + +from django.apps.registry import Apps +from django.db import migrations + + +def cleanup_old_autogenerated(apps, schema_editor): + LDAPPropertyMapping = apps.get_model("passbook_sources_ldap", "LDAPPropertyMapping") + db_alias = schema_editor.connection.alias + LDAPPropertyMapping.objects.using(db_alias).filter( + name__startswith="Autogenerated" + ).delete() + + +def create_default_ad_property_mappings(apps: Apps, schema_editor): + LDAPPropertyMapping = apps.get_model("passbook_sources_ldap", "LDAPPropertyMapping") + mapping = { + "name": "{{ ldap.name }}", + "first_name": "{{ ldap.givenName }}", + "last_name": "{{ ldap.sn }}", + "username": "{{ ldap.sAMAccountName }}", + "email": "{{ ldap.mail }}", + } + db_alias = schema_editor.connection.alias + for object_field, expression in mapping.items(): + LDAPPropertyMapping.objects.using(db_alias).get_or_create( + expression=expression, + object_field=object_field, + defaults={ + "name": f"Autogenerated LDAP Mapping: {expression} -> {object_field}" + }, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("passbook_sources_ldap", "0006_auto_20200216_1116"), + ("passbook_core", "0007_auto_20200217_1934"), + ] + + operations = [ + migrations.RunPython(cleanup_old_autogenerated), + migrations.RemoveField(model_name="ldappropertymapping", name="ldap_property",), + migrations.RunPython(create_default_ad_property_mappings), + ] diff --git a/passbook/sources/ldap/models.py b/passbook/sources/ldap/models.py index 045864dc3..393cccfa2 100644 --- a/passbook/sources/ldap/models.py +++ b/passbook/sources/ldap/models.py @@ -59,13 +59,12 @@ class LDAPSource(Source): class LDAPPropertyMapping(PropertyMapping): """Map LDAP Property to User or Group object""" - ldap_property = models.TextField(verbose_name=_("LDAP Property")) object_field = models.TextField() form = "passbook.sources.ldap.forms.LDAPPropertyMappingForm" def __str__(self): - return f"LDAP Property Mapping {self.ldap_property} -> {self.object_field}" + return f"LDAP Property Mapping {self.expression} -> {self.object_field}" class Meta: diff --git a/passbook/sources/ldap/templates/ldap/property_mapping_form.html b/passbook/sources/ldap/templates/ldap/property_mapping_form.html new file mode 100644 index 000000000..ff41588ed --- /dev/null +++ b/passbook/sources/ldap/templates/ldap/property_mapping_form.html @@ -0,0 +1,18 @@ +{% extends "generic/form.html" %} + +{% load i18n %} + +{% block beneath_form %} +
+ +
+

+ Expression using Jinja. Following variables are available: +

+

+
+
+{% endblock %}