Compare commits

..

27 Commits

Author SHA1 Message Date
04a5428148 new release: 0.7.16-beta 2020-02-17 21:02:54 +01:00
73b173b92a admin: fix form missing on update pages 2020-02-17 21:02:47 +01:00
7cbf20a71c admin: fix CodeMirror field not loading correctly 2020-02-17 21:02:35 +01:00
7a98e6d92b new release: 0.7.15-beta 2020-02-17 20:45:56 +01:00
49e915f98b Merge pull request #4 from BeryJu/propertymapping-jinja
PropertyMappings using Jinja
2020-02-17 20:45:04 +01:00
3aa2f1e892 *: propertymapping template -> expression 2020-02-17 20:38:14 +01:00
bc4b7ef44d providers/saml: add custom help text for templates, add docs for User Object reference 2020-02-17 20:30:14 +01:00
9400b01a55 admin: parameterise generic from's base template 2020-02-17 20:29:41 +01:00
e57da71dcf sources/ldap: update LDAP source to use new property mappings 2020-02-17 17:55:48 +01:00
7268afaaf9 providers/saml: update to new PropertyMappings 2020-02-17 17:50:11 +01:00
205183445c admin: add support for template field and Jinja2 highlighting 2020-02-17 17:48:53 +01:00
a08bdfdbcd root: remove prospector from Pipfile as it causes lock issues, install in CI 2020-02-17 17:48:18 +01:00
e6c47fee26 core: add template field to PropertyMapping 2020-02-17 17:47:51 +01:00
a5629c5155 providers/saml: add changeable signature and digest algorithm 2020-02-17 16:28:18 +01:00
41689fe3ce sources/* add missing migrations 2020-02-17 16:27:35 +01:00
8e84208e2c new release: 0.7.14-beta 2020-02-17 15:42:14 +01:00
32a48fa07a providers/saml: more typehints 2020-02-17 15:40:49 +01:00
773a9c0692 policies/engine: fix cached policy results being ignored 2020-02-17 15:37:51 +01:00
8808e3afe0 policies/engine: set mp start method to fork to fix issues under macOS 2020-02-17 15:20:30 +01:00
ecea85f8ca lib/config: remove autoreload handler as this API is gone in django 3 2020-02-17 15:20:11 +01:00
5dfa141e35 root/wsgi: log requests with event name of request 2020-02-16 14:36:31 +01:00
447e81d0b8 providers/saml: handle uncompressed SAML AuthNRequest 2020-02-16 14:08:35 +01:00
e138076e1d sources/saml: move labels from forms to models 2020-02-16 12:34:46 +01:00
721d133dc3 sources/oauth: move labels from form to models 2020-02-16 12:34:33 +01:00
75b687ecbe sources/ldap: move labels from form to models 2020-02-16 12:30:45 +01:00
bdd1863177 providers/saml: move field labels from Form into models 2020-02-16 12:30:26 +01:00
e5b85e8e6a providers/saml: move default saml properties to DB 2020-02-16 12:29:53 +01:00
47 changed files with 763 additions and 373 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.7.13-beta
current_version = 0.7.16-beta
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

View File

@ -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:

View File

@ -16,11 +16,11 @@ jobs:
- name: Building Docker Image
run: docker build
--no-cache
-t beryju/passbook:0.7.13-beta
-t beryju/passbook:0.7.16-beta
-t beryju/passbook:latest
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook:0.7.13-beta
run: docker push beryju/passbook:0.7.16-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook:latest
build-gatekeeper:
@ -37,11 +37,11 @@ jobs:
cd gatekeeper
docker build \
--no-cache \
-t beryju/passbook-gatekeeper:0.7.13-beta \
-t beryju/passbook-gatekeeper:0.7.16-beta \
-t beryju/passbook-gatekeeper:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-gatekeeper:0.7.13-beta
run: docker push beryju/passbook-gatekeeper:0.7.16-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-gatekeeper:latest
build-static:
@ -66,11 +66,11 @@ jobs:
run: docker build
--no-cache
--network=$(docker network ls | grep github | awk '{print $1}')
-t beryju/passbook-static:0.7.13-beta
-t beryju/passbook-static:0.7.16-beta
-t beryju/passbook-static:latest
-f static.Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/passbook-static:0.7.13-beta
run: docker push beryju/passbook-static:0.7.16-beta
- name: Push Docker Container to Registry (latest)
run: docker push beryju/passbook-static:latest
test-release:

View File

@ -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 = "*"

324
Pipfile.lock generated
View File

@ -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": [

View File

@ -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 %}]
```

View File

@ -1,6 +1,6 @@
apiVersion: v1
appVersion: "0.7.13-beta"
appVersion: "0.7.16-beta"
description: A Helm chart for passbook.
name: passbook
version: "0.7.13-beta"
version: "0.7.16-beta"
icon: https://git.beryju.org/uploads/-/system/project/avatar/108/logo.png

View File

@ -2,7 +2,7 @@
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
tag: 0.7.13-beta
tag: 0.7.16-beta
nameOverride: ""

View File

@ -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

View File

@ -1,2 +1,2 @@
"""passbook"""
__version__ = "0.7.13-beta"
__version__ = "0.7.16-beta"

View File

@ -1,4 +1,4 @@
{% extends "generic/form.html" %}
{% extends base_template|default:"generic/form.html" %}
{% load utils %}
{% load i18n %}

View File

@ -20,6 +20,7 @@
<link rel="stylesheet" href="{% static 'codemirror/lib/codemirror.css' %}">
<link rel="stylesheet" href="{% static 'codemirror/theme/monokai.css' %}">
<script src="{% static 'codemirror/mode/yaml/yaml.js' %}"></script>
<script src="{% static 'codemirror/mode/jinja2/jinja2.js' %}"></script>
{% endblock %}
{% block content %}
@ -29,21 +30,33 @@
<div class="">
<form action="" method="post" class="form-horizontal">
{% include 'partials/form.html' with form=form %}
{% block beneath_form %}
{% endblock %}
<a class="btn btn-default" href="{% back %}">{% trans "Cancel" %}</a>
<input type="submit" class="btn btn-primary" value="{% block action %}{% endblock %}" />
</form>
</div>
{% block beneath_form %}
{% endblock %}
<script>
let attributes = document.getElementsByName('attributes');
const attributes = document.getElementsByName('attributes');
if (attributes.length > 0) {
let myCodeMirror = CodeMirror.fromTextArea(attributes[0], {
// https://github.com/codemirror/CodeMirror/issues/5092
attributes[0].removeAttribute("required");
const attributesCM = CodeMirror.fromTextArea(attributes[0], {
mode: 'yaml',
theme: 'monokai',
lineNumbers: true,
});
}
const expressions = document.getElementsByName('expression');
if (expressions.length > 0) {
// https://github.com/codemirror/CodeMirror/issues/5092
expressions[0].removeAttribute("required");
const expressionCM = CodeMirror.fromTextArea(expressions[0], {
mode: 'jinja2',
theme: 'monokai',
lineNumbers: true,
});
}
</script>
</div>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends "generic/form.html" %}
{% extends base_template|default:"generic/form.html" %}
{% load utils %}
{% load i18n %}

View File

@ -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,13 @@ class PropertyMappingUpdateView(
success_url = reverse_lazy("passbook_admin:property-mappings")
success_message = _("Successfully updated Property Mapping")
def get_context_data(self, **kwargs):
kwargs = super().get_context_data(**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)

View File

@ -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,
),
]

View File

@ -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",
),
]

View File

@ -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}"

View File

@ -8,7 +8,6 @@ from urllib.parse import urlparse
import yaml
from django.conf import ImproperlyConfigured
from django.utils.autoreload import autoreload_started
from structlog import get_logger
SEARCH_PATHS = ["passbook/lib/default.yml", "/etc/passbook/config.yml", "",] + glob(
@ -142,12 +141,3 @@ class ConfigLoader:
CONFIG = ConfigLoader()
def signal_handler(sender, **_):
"""Add all loaded config files to autoreload watcher"""
for path in CONFIG.loaded_file:
sender.watch_file(path)
autoreload_started.connect(signal_handler)

View File

@ -1,5 +1,5 @@
"""passbook policy engine"""
from multiprocessing import Pipe
from multiprocessing import Pipe, set_start_method
from multiprocessing.connection import Connection
from typing import List, Optional, Tuple
@ -12,6 +12,9 @@ from passbook.policies.process import PolicyProcess, cache_key
from passbook.policies.struct import PolicyRequest, PolicyResult
LOGGER = get_logger()
# This is only really needed for macOS, because Python 3.8 changed the default to spawn
# spawn causes issues with objects that aren't picklable, and also the django setup
set_start_method("fork")
class PolicyProcessInfo:
@ -36,13 +39,15 @@ class PolicyEngine:
policies: List[Policy] = []
request: PolicyRequest
__processes: List[PolicyProcessInfo] = []
__cached_policies: List[PolicyResult]
__processes: List[PolicyProcessInfo]
def __init__(self, policies, user: User, request: HttpRequest = None):
self.policies = policies
self.request = PolicyRequest(user)
if request:
self.request.http_request = request
self.__cached_policies = []
self.__processes = []
def _select_subclasses(self) -> List[Policy]:
@ -55,13 +60,12 @@ class PolicyEngine:
def build(self) -> "PolicyEngine":
"""Build task group"""
cached_policies = []
for policy in self._select_subclasses():
cached_policy = cache.get(cache_key(policy, self.request.user), None)
if cached_policy and self.use_cache:
LOGGER.debug("Taking result from cache", policy=policy)
cached_policies.append(cached_policy)
else:
self.__cached_policies.append(cached_policy)
continue
LOGGER.debug("Evaluating policy", policy=policy)
our_end, task_end = Pipe(False)
task = PolicyProcess(policy, self.request, task_end)
@ -82,13 +86,14 @@ class PolicyEngine:
def result(self) -> Tuple[bool, List[str]]:
"""Get policy-checking result"""
messages: List[str] = []
for proc_info in self.__processes:
LOGGER.debug(
"Result", policy=proc_info.policy, passing=proc_info.result.passing
)
if proc_info.result.messages:
messages += proc_info.result.messages
if not proc_info.result.passing:
process_results: List[PolicyResult] = [
x.result for x in self.__processes if x.result
]
for result in process_results + self.__cached_policies:
LOGGER.debug("result", passing=result.passing)
if result.messages:
messages += result.messages
if not result.passing:
return False, messages
return True, messages

View File

@ -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):

View File

@ -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,
@ -40,14 +39,12 @@ class SAMLProviderForm(forms.ModelForm):
"assertion_valid_not_on_or_after",
"session_valid_not_on_or_after",
"property_mappings",
"digest_algorithm",
"signature_algorithm",
"signing",
"signing_cert",
"signing_key",
]
labels = {
"acs_url": "ACS URL",
"signing_cert": "Singing Certificate",
}
widgets = {
"name": forms.TextInput(),
"audience": forms.TextInput(),
@ -62,16 +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}
help_texts = {
"values": 'String substitution uses a syntax like "{variable} test}".'
}

View File

@ -0,0 +1,38 @@
# Generated by Django 2.2.9 on 2020-02-16 11:09
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_providers_saml", "0002_auto_20200214_1354"),
]
operations = [
migrations.AlterField(
model_name="samlpropertymapping",
name="saml_name",
field=models.TextField(verbose_name="SAML Name"),
),
migrations.AlterField(
model_name="samlpropertymapping",
name="values",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.TextField(),
help_text="This string can contain string substitutions delimited by {}. The following Variables are available: user, request",
size=None,
),
),
migrations.AlterField(
model_name="samlprovider",
name="acs_url",
field=models.URLField(verbose_name="ACS URL"),
),
migrations.AlterField(
model_name="samlprovider",
name="signing_cert",
field=models.TextField(verbose_name="Singing Certificate"),
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.0.3 on 2020-02-17 15:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_providers_saml", "0003_auto_20200216_1109"),
]
operations = [
migrations.AddField(
model_name="samlprovider",
name="digest_algorithm",
field=models.CharField(
choices=[("sha1", "SHA1"), ("sha256", "SHA256")],
default="sha256",
max_length=50,
),
),
migrations.AddField(
model_name="samlprovider",
name="signature_algorithm",
field=models.CharField(
choices=[
("rsa-sha1", "RSA-SHA1"),
("rsa-sha256", "RSA-SHA256"),
("ecdsa-sha256", "ECDSA-SHA256"),
("dsa-sha1", "DSA-SHA1"),
],
default="rsa-sha256",
max_length=50,
),
),
migrations.AlterField(
model_name="samlprovider",
name="processor_path",
field=models.CharField(choices=[], max_length=255),
),
]

View File

@ -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),
]

View File

@ -1,8 +1,7 @@
"""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 gettext as _
from django.utils.translation import ugettext_lazy as _
from structlog import get_logger
from passbook.core.models import PropertyMapping, Provider
@ -19,7 +18,7 @@ class SAMLProvider(Provider):
name = models.TextField()
processor_path = models.CharField(max_length=255, choices=[])
acs_url = models.URLField()
acs_url = models.URLField(verbose_name=_("ACS URL"))
audience = models.TextField(default="")
issuer = models.TextField()
@ -55,8 +54,24 @@ class SAMLProvider(Provider):
),
)
digest_algorithm = models.CharField(
max_length=50,
choices=(("sha1", _("SHA1")), ("sha256", _("SHA256")),),
default="sha256",
)
signature_algorithm = models.CharField(
max_length=50,
choices=(
("rsa-sha1", _("RSA-SHA1")),
("rsa-sha256", _("RSA-SHA256")),
("ecdsa-sha256", _("ECDSA-SHA256")),
("dsa-sha1", _("DSA-SHA1")),
),
default="rsa-sha256",
)
signing = models.BooleanField(default=True)
signing_cert = models.TextField()
signing_cert = models.TextField(verbose_name=_("Singing Certificate"))
signing_key = models.TextField()
form = "passbook.providers.saml.forms.SAMLProviderForm"
@ -100,9 +115,8 @@ class SAMLProvider(Provider):
class SAMLPropertyMapping(PropertyMapping):
"""SAML Property mapping, allowing Name/FriendlyName mapping to a list of strings"""
saml_name = models.TextField()
saml_name = models.TextField(verbose_name="SAML Name")
friendly_name = models.TextField(default=None, blank=True, null=True)
values = ArrayField(models.TextField())
form = "passbook.providers.saml.forms.SAMLPropertyMappingForm"

View File

@ -33,7 +33,6 @@ class Processor:
_assertion_params: Dict[str, Union[str, List[Dict[str, str]]]]
_request_params: Dict[str, str]
_system_params: Dict[str, str]
_response_params: Dict[str, str]
@property
@ -45,9 +44,6 @@ class Processor:
self.name = remote.name
self._remote = remote
self._logger = get_logger()
self._system_params = {
"ISSUER": self._remote.issuer,
}
def _build_assertion(self):
"""Builds _assertion_params."""
@ -70,8 +66,8 @@ class Processor:
"SP_NAME_QUALIFIER": self._remote.audience,
"SUBJECT": self._http_request.user.email,
"SUBJECT_FORMAT": self.subject_format,
"ISSUER": self._remote.issuer,
}
self._assertion_params.update(self._system_params)
self._assertion_params.update(self._request_params)
def _build_response(self):
@ -81,8 +77,8 @@ class Processor:
"ISSUE_INSTANT": get_time_string(),
"RESPONSE_ID": get_random_id(),
"RESPONSE_SIGNATURE": "", # initially unsigned
"ISSUER": self._remote.issuer,
}
self._response_params.update(self._system_params)
self._response_params.update(self._request_params)
def _encode_response(self):
@ -97,49 +93,26 @@ class Processor:
def _format_assertion(self):
"""Formats _assertion_params as _assertion_xml."""
# https://commons.lbl.gov/display/IDMgmt/Attribute+Definitions
self._assertion_params["ATTRIBUTES"] = [
{
"FriendlyName": "eduPersonPrincipalName",
"Name": "urn:oid:1.3.6.1.4.1.5923.1.1.1.6",
"Value": self._http_request.user.email,
},
{
"FriendlyName": "cn",
"Name": "urn:oid:2.5.4.3",
"Value": self._http_request.user.name,
},
{
"FriendlyName": "mail",
"Name": "urn:oid:0.9.2342.19200300.100.1.3",
"Value": self._http_request.user.email,
},
{
"FriendlyName": "displayName",
"Name": "urn:oid:2.16.840.1.113730.3.1.241",
"Value": self._http_request.user.username,
},
{
"FriendlyName": "uid",
"Name": "urn:oid:0.9.2342.19200300.100.1.1",
"Value": self._http_request.user.pk,
},
]
attributes = []
from passbook.providers.saml.models import SAMLPropertyMapping
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
)
)
self._assertion_params["ATTRIBUTES"].append(mapping_payload)
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(
"saml/xml/assertions/generic.xml", self._assertion_params, signed=True
)
@ -188,8 +161,9 @@ class Processor:
request_acs_url = self._request_params["ACS_URL"]
if self._remote.acs_url != request_acs_url:
msg = "couldn't find ACS url '{}' in SAML2IDP_REMOTES " "setting.".format(
request_acs_url
msg = (
f"ACS URL of {request_acs_url} doesn't match Provider "
f"ACS URL of {self._remote.acs_url}."
)
self._logger.info(msg)
raise CannotHandleAssertion(msg)

View File

@ -0,0 +1,20 @@
{% extends "generic/form.html" %}
{% load i18n %}
{% block beneath_form %}
<div class="form-group ">
<label class="col-sm-2 control-label" for="friendly_name-2">
</label>
<div class="col-sm-10">
<p>
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
<ul>
<li><code>user</code>: Passbook User Object (<a href="https://beryju.github.io/passbook/reference/property-mappings/user-object/">Reference</a>)</li>
<li><code>request</code>: Django HTTP Request Object (<a href="https://docs.djangoproject.com/en/3.0/ref/request-response/#httprequest-objects">Reference</a>) </li>
<li><code>provider</code>: Passbook SAML Provider Object (<a href="https://github.com/BeryJu/passbook/blob/master/passbook/providers/saml/models.py#L16">Reference</a>) </li>
</ul>
</p>
</div>
</div>
{% endblock %}

View File

@ -6,7 +6,10 @@ import zlib
def decode_base64_and_inflate(b64string):
"""Base64 decode and ZLib decompress b64string"""
decoded_data = base64.b64decode(b64string)
try:
return zlib.decompress(decoded_data, -15)
except zlib.error:
return decoded_data
def deflate_and_base64_encode(string_val):

View File

@ -88,10 +88,5 @@ def get_response_xml(parameters, saml_provider: SAMLProvider, assertion_id=""):
signature_xml = get_signature_xml()
params["RESPONSE_SIGNATURE"] = signature_xml
signed = sign_with_signxml(
saml_provider.signing_key,
raw_response,
saml_provider.signing_cert,
reference_uri=assertion_id,
)
signed = sign_with_signxml(raw_response, saml_provider, reference_uri=assertion_id,)
return signed

View File

@ -1,4 +1,6 @@
"""Signing code goes here."""
from typing import TYPE_CHECKING
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from lxml import etree # nosec
@ -7,25 +9,34 @@ from structlog import get_logger
from passbook.lib.utils.template import render_to_string
if TYPE_CHECKING:
from passbook.providers.saml.models import SAMLProvider
LOGGER = get_logger()
def sign_with_signxml(private_key, data, cert, reference_uri=None):
def sign_with_signxml(data: str, provider: "SAMLProvider", reference_uri=None) -> str:
"""Sign Data with signxml"""
key = serialization.load_pem_private_key(
str.encode("\n".join([x.strip() for x in private_key.split("\n")])),
str.encode("\n".join([x.strip() for x in provider.signing_key.split("\n")])),
password=None,
backend=default_backend(),
)
# defused XML is not used here because it messes up XML namespaces
# Data is trusted, so lxml is ok
root = etree.fromstring(data) # nosec
signer = XMLSigner(c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#")
signed = signer.sign(root, key=key, cert=[cert], reference_uri=reference_uri)
XMLVerifier().verify(signed, x509_cert=cert)
signer = XMLSigner(
c14n_algorithm="http://www.w3.org/2001/10/xml-exc-c14n#",
signature_algorithm=provider.signature_algorithm,
digest_algorithm=provider.digest_algorithm,
)
signed = signer.sign(
root, key=key, cert=[provider.signing_cert], reference_uri=reference_uri
)
XMLVerifier().verify(signed, x509_cert=provider.signing_cert)
return etree.tostring(signed).decode("utf-8") # nosec
def get_signature_xml():
def get_signature_xml() -> str:
"""Returns XML Signature for subject."""
return render_to_string("saml/xml/signature.xml", {})

View File

@ -5,7 +5,7 @@ from django.contrib.auth import logout
from django.contrib.auth.mixins import AccessMixin
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.http import HttpResponse, HttpResponseBadRequest, HttpRequest
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect, render, reverse
from django.utils.datastructures import MultiValueDictKeyError
from django.utils.decorators import method_decorator
@ -27,7 +27,7 @@ LOGGER = get_logger()
URL_VALIDATOR = URLValidator(schemes=("http", "https"))
def _generate_response(request: HttpRequest, provider: SAMLProvider):
def _generate_response(request: HttpRequest, provider: SAMLProvider) -> HttpResponse:
"""Generate a SAML response using processor_instance and return it in the proper Django
response."""
try:
@ -58,13 +58,16 @@ class AccessRequiredView(AccessMixin, View):
def _has_access(self) -> bool:
"""Check if user has access to application"""
LOGGER.debug(
"_has_access", user=self.request.user, app=self.provider.application
)
policy_engine = PolicyEngine(
self.provider.application.policies.all(), self.request.user, self.request
)
policy_engine.build()
return policy_engine.passing
def dispatch(self, request, *args, **kwargs):
def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
if not request.user.is_authenticated:
return self.handle_no_permission()
if not self._has_access():
@ -84,7 +87,7 @@ class LoginBeginView(AccessRequiredView):
stores it in the session prior to enforcing login."""
@method_decorator(csrf_exempt)
def dispatch(self, request, application):
def dispatch(self, request: HttpRequest, application: str) -> HttpResponse:
if request.method == "POST":
source = request.POST
else:
@ -108,7 +111,9 @@ class LoginBeginView(AccessRequiredView):
class RedirectToSPView(AccessRequiredView):
"""Return autosubmit form"""
def get(self, request, acs_url, saml_response, relay_state):
def get(
self, request: HttpRequest, acs_url: str, saml_response: str, relay_state: str
) -> HttpResponse:
"""Return autosubmit form"""
return render(
request,
@ -149,7 +154,7 @@ class LoginProcessView(AccessRequiredView):
return HttpResponseBadRequest()
# pylint: disable=unused-argument
def post(self, request, application: str) -> HttpResponse:
def post(self, request: HttpRequest, application: str) -> HttpResponse:
"""Handle post request, return back to ACS"""
# User access gets checked in dispatch
if request.POST.get("ACSUrl", None):
@ -178,7 +183,7 @@ class LogoutView(CSRFExemptMixin, AccessRequiredView):
though it's technically not SAML 2.0)."""
# pylint: disable=unused-argument
def get(self, request, application):
def get(self, request: HttpRequest, application: str) -> HttpResponse:
"""Perform logout"""
logout(request)
@ -199,7 +204,7 @@ class SLOLogout(CSRFExemptMixin, AccessRequiredView):
logs out the user and returns a standard logged-out page."""
# pylint: disable=unused-argument
def post(self, request, application):
def post(self, request: HttpRequest, application: str) -> HttpResponse:
"""Perform logout"""
request.session["SAMLRequest"] = request.POST["SAMLRequest"]
# TODO: Parse SAML LogoutRequest from POST data, similar to login_process().
@ -214,7 +219,7 @@ class SLOLogout(CSRFExemptMixin, AccessRequiredView):
class DescriptorDownloadView(AccessRequiredView):
"""Replies with the XML Metadata IDSSODescriptor."""
def get(self, request, application):
def get(self, request: HttpRequest, application: str) -> HttpResponse:
"""Replies with the XML Metadata IDSSODescriptor."""
entity_id = self.provider.issuer
slo_url = request.build_absolute_uri(
@ -250,7 +255,7 @@ class InitiateLoginView(AccessRequiredView):
"""IdP-initiated Login"""
# pylint: disable=unused-argument
def get(self, request, application):
def get(self, request: HttpRequest, application: str) -> HttpResponse:
"""Initiates an IdP-initiated link to a simple SP resource/target URL."""
self.provider.processor.init_deep_link(request, "")
self.provider.processor.is_idp_initiated = True

View File

@ -62,7 +62,8 @@ class WSGILogger:
if environ.get("QUERY_STRING") != "":
query_string = f"?{environ.get('QUERY_STRING')}"
self.logger.info(
f"{environ.get('PATH_INFO', '')}{query_string}",
"request",
path=f"{environ.get('PATH_INFO', '')}{query_string}",
host=host,
method=environ.get("REQUEST_METHOD", ""),
protocol=environ.get("SERVER_PROTOCOL", ""),

View File

@ -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):

View File

@ -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

View File

@ -45,23 +45,17 @@ class LDAPSourceForm(forms.ModelForm):
"policies": FilteredSelectMultiple(_("policies"), False),
"property_mappings": FilteredSelectMultiple(_("Property Mappings"), False),
}
labels = {
"server_uri": _("Server URI"),
"bind_cn": _("Bind CN"),
"start_tls": _("Enable Start TLS"),
"base_dn": _("Base DN"),
"additional_user_dn": _("Addition User DN"),
"additional_group_dn": _("Addition Group DN"),
}
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(),

View File

@ -13,8 +13,9 @@ def create_default_ad_property_mappings(apps: Apps, schema_editor):
"sAMAccountName": "username",
"mail": "email",
}
db_alias = schema_editor.connection.alias
for ldap_property, object_field in mapping.items():
LDAPPropertyMapping.objects.get_or_create(
LDAPPropertyMapping.objects.using(db_alias).get_or_create(
ldap_property=ldap_property,
object_field=object_field,
defaults={

View File

@ -0,0 +1,60 @@
# Generated by Django 2.2.9 on 2020-02-16 11:16
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_sources_ldap", "0005_auto_20191011_1059"),
]
operations = [
migrations.AlterField(
model_name="ldappropertymapping",
name="ldap_property",
field=models.TextField(verbose_name="LDAP Property"),
),
migrations.AlterField(
model_name="ldapsource",
name="additional_group_dn",
field=models.TextField(
help_text="Prepended to Base DN for Group-queries.",
verbose_name="Addition Group DN",
),
),
migrations.AlterField(
model_name="ldapsource",
name="additional_user_dn",
field=models.TextField(
help_text="Prepended to Base DN for User-queries.",
verbose_name="Addition User DN",
),
),
migrations.AlterField(
model_name="ldapsource",
name="base_dn",
field=models.TextField(verbose_name="Base DN"),
),
migrations.AlterField(
model_name="ldapsource",
name="bind_cn",
field=models.TextField(verbose_name="Bind CN"),
),
migrations.AlterField(
model_name="ldapsource",
name="server_uri",
field=models.TextField(
validators=[
django.core.validators.URLValidator(schemes=["ldap", "ldaps"])
],
verbose_name="Server URI",
),
),
migrations.AlterField(
model_name="ldapsource",
name="start_tls",
field=models.BooleanField(default=False, verbose_name="Enable Start TLS"),
),
]

View File

@ -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),
]

View File

@ -2,7 +2,7 @@
from django.core.validators import URLValidator
from django.db import models
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _
from passbook.core.models import Group, PropertyMapping, Source
@ -10,17 +10,22 @@ from passbook.core.models import Group, PropertyMapping, Source
class LDAPSource(Source):
"""LDAP Authentication source"""
server_uri = models.TextField(validators=[URLValidator(schemes=["ldap", "ldaps"])])
bind_cn = models.TextField()
server_uri = models.TextField(
validators=[URLValidator(schemes=["ldap", "ldaps"])],
verbose_name=_("Server URI"),
)
bind_cn = models.TextField(verbose_name=_("Bind CN"))
bind_password = models.TextField()
start_tls = models.BooleanField(default=False)
start_tls = models.BooleanField(default=False, verbose_name=_("Enable Start TLS"))
base_dn = models.TextField()
base_dn = models.TextField(verbose_name=_("Base DN"))
additional_user_dn = models.TextField(
help_text=_("Prepended to Base DN for User-queries.")
help_text=_("Prepended to Base DN for User-queries."),
verbose_name=_("Addition User DN"),
)
additional_group_dn = models.TextField(
help_text=_("Prepended to Base DN for Group-queries.")
help_text=_("Prepended to Base DN for Group-queries."),
verbose_name=_("Addition Group DN"),
)
user_object_filter = models.TextField(
@ -54,13 +59,12 @@ class LDAPSource(Source):
class LDAPPropertyMapping(PropertyMapping):
"""Map LDAP Property to User or Group object"""
ldap_property = models.TextField()
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:

View File

@ -0,0 +1,18 @@
{% extends "generic/form.html" %}
{% load i18n %}
{% block beneath_form %}
<div class="form-group ">
<label class="col-sm-2 control-label" for="friendly_name-2">
</label>
<div class="col-sm-10">
<p>
Expression using <a href="https://jinja.palletsprojects.com/en/2.11.x/templates/">Jinja</a>. Following variables are available:
<ul>
<li><code>ldap</code>: A Dictionary of all values retrieved from LDAP.</li>
</ul>
</p>
</div>
</div>
{% endblock %}

View File

@ -38,12 +38,6 @@ class OAuthSourceForm(forms.ModelForm):
"provider_type": forms.Select(choices=MANAGER.get_name_tuple()),
"policies": FilteredSelectMultiple(_("policies"), False),
}
labels = {
"request_token_url": _("Request Token URL"),
"authorization_url": _("Authorization URL"),
"access_token_url": _("Access Token URL"),
"profile_url": _("Profile URL"),
}
class GitHubOAuthSourceForm(OAuthSourceForm):

View File

@ -0,0 +1,35 @@
# Generated by Django 3.0.3 on 2020-02-17 15:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_sources_oauth", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="oauthsource",
name="access_token_url",
field=models.CharField(max_length=255, verbose_name="Access Token URL"),
),
migrations.AlterField(
model_name="oauthsource",
name="authorization_url",
field=models.CharField(max_length=255, verbose_name="Authorization URL"),
),
migrations.AlterField(
model_name="oauthsource",
name="profile_url",
field=models.CharField(max_length=255, verbose_name="Profile URL"),
),
migrations.AlterField(
model_name="oauthsource",
name="request_token_url",
field=models.CharField(
blank=True, max_length=255, verbose_name="Request Token URL"
),
),
]

View File

@ -2,7 +2,7 @@
from django.db import models
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _
from passbook.core.models import Source, UserSettings, UserSourceConnection
from passbook.sources.oauth.clients import get_client
@ -12,10 +12,16 @@ class OAuthSource(Source):
"""Configuration for OAuth provider."""
provider_type = models.CharField(max_length=255)
request_token_url = models.CharField(blank=True, max_length=255)
authorization_url = models.CharField(max_length=255)
access_token_url = models.CharField(max_length=255)
profile_url = models.CharField(max_length=255)
request_token_url = models.CharField(
blank=True, max_length=255, verbose_name=_("Request Token URL")
)
authorization_url = models.CharField(
max_length=255, verbose_name=_("Authorization URL")
)
access_token_url = models.CharField(
max_length=255, verbose_name=_("Access Token URL")
)
profile_url = models.CharField(max_length=255, verbose_name=_("Profile URL"))
consumer_key = models.TextField()
consumer_secret = models.TextField()

View File

@ -28,11 +28,6 @@ class SAMLSourceForm(forms.ModelForm):
"auto_logout",
"signing_cert",
]
labels = {
"entity_id": "Entity ID",
"idp_url": "IDP URL",
"idp_logout_url": "IDP Logout URL",
}
widgets = {
"name": forms.TextInput(),
"policies": FilteredSelectMultiple(_("policies"), False),

View File

@ -0,0 +1,30 @@
# Generated by Django 3.0.3 on 2020-02-17 15:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_sources_saml", "0003_auto_20191107_1550"),
]
operations = [
migrations.AlterField(
model_name="samlsource",
name="entity_id",
field=models.TextField(blank=True, default=None, verbose_name="Entity ID"),
),
migrations.AlterField(
model_name="samlsource",
name="idp_logout_url",
field=models.URLField(
blank=True, default=None, null=True, verbose_name="IDP Logout URL"
),
),
migrations.AlterField(
model_name="samlsource",
name="idp_url",
field=models.URLField(verbose_name="IDP URL"),
),
]

View File

@ -1,7 +1,7 @@
"""saml sp models"""
from django.db import models
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _
from passbook.core.models import Source
@ -9,9 +9,11 @@ from passbook.core.models import Source
class SAMLSource(Source):
"""SAML2 Source"""
entity_id = models.TextField(blank=True, default=None)
idp_url = models.URLField()
idp_logout_url = models.URLField(default=None, blank=True, null=True)
entity_id = models.TextField(blank=True, default=None, verbose_name=_("Entity ID"))
idp_url = models.URLField(verbose_name=_("IDP URL"))
idp_logout_url = models.URLField(
default=None, blank=True, null=True, verbose_name=_("IDP Logout URL")
)
auto_logout = models.BooleanField(default=False)
signing_cert = models.TextField()