diff --git a/.github/ISSUE_TEMPLATE/issue-default.yml b/.github/ISSUE_TEMPLATE/issue-default.yml index 27ec10415..47fb2c226 100644 --- a/.github/ISSUE_TEMPLATE/issue-default.yml +++ b/.github/ISSUE_TEMPLATE/issue-default.yml @@ -6,13 +6,13 @@ body: id: title-help attributes: value: | - > Titles should be short, descriptive, and compelling. + > Titles should be short, descriptive, and compelling. Use sentence case. - type: textarea id: issue-description attributes: - label: Issue description and context + label: Issue description description: | - Describe the issue so that someone who wasn't present for its discovery can understand the problem and why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). Share desired outcomes or potential next steps. Images or links to other content/context (like documents or Slack discussions) are welcome. + Describe the issue so that someone who wasn't present for its discovery can understand why it matters. Use full sentences, plain language, and [good formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). validations: required: true - type: textarea @@ -20,16 +20,22 @@ body: attributes: label: Acceptance criteria description: "If known, share 1-3 statements that would need to be true for this issue to be considered resolved. Use a [task list](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists#creating-task-lists) if appropriate." - placeholder: "- [ ] The button does the thing." + placeholder: "- [ ]" + - type: textarea + id: additional-context + attributes: + label: Additional context + description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome." - type: textarea id: links-to-other-issues attributes: label: Links to other issues description: | - Add the issue #number of other issues this relates to and how (e.g., 🚧 Blocks, ⛔️ Is blocked by, 🔄 Relates to). + "Add issue #numbers this relates to and how (e.g., 🚧 [construction] Blocks, ⛔️ [no_entry] Is blocked by, 🔄 [arrows_counterclockwise] Relates to)." placeholder: 🔄 Relates to... - type: markdown id: note attributes: value: | - > We may edit this issue's text to document our understanding and clarify the product work. + > We may edit the text in this issue to document our understanding and clarify the product work. + diff --git a/docs/architecture/decisions/0023-use-geventconnpool.md b/docs/architecture/decisions/0023-use-geventconnpool.md index c24318b4f..9288f3e86 100644 --- a/docs/architecture/decisions/0023-use-geventconnpool.md +++ b/docs/architecture/decisions/0023-use-geventconnpool.md @@ -4,7 +4,7 @@ Date: 2023-13-10 ## Status -In Review +Accepted ## Context diff --git a/ops/manifests/manifest-stable.yaml b/ops/manifests/manifest-stable.yaml index bc5e933f6..6295fa63b 100644 --- a/ops/manifests/manifest-stable.yaml +++ b/ops/manifests/manifest-stable.yaml @@ -18,7 +18,7 @@ applications: # Tell Django where to find its configuration DJANGO_SETTINGS_MODULE: registrar.config.settings # Tell Django where it is being hosted - DJANGO_BASE_URL: https://getgov-stable.app.cloud.gov + DJANGO_BASE_URL: https://manage.get.gov # Tell Django how much stuff to log DJANGO_LOG_LEVEL: INFO # default public site location diff --git a/src/Pipfile b/src/Pipfile index 6900b0bcf..33f2c0954 100644 --- a/src/Pipfile +++ b/src/Pipfile @@ -25,7 +25,10 @@ django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"} boto3 = "*" typing-extensions ='*' django-login-required-middleware = "*" +greenlet = "*" +gevent = "*" fred-epplib = {git = "https://github.com/cisagov/epplib.git", ref = "master"} +geventconnpool = {git = "https://github.com/rasky/geventconnpool.git", ref = "1bbb93a714a331a069adf27265fe582d9ba7ecd4"} [dev-packages] django-debug-toolbar = "*" diff --git a/src/Pipfile.lock b/src/Pipfile.lock index 3e7ae367d..9d7daf597 100644 --- a/src/Pipfile.lock +++ b/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "1242c67b31261243a35128410d4a928fca3729ddac13b8c8e25adf31445c6328" + "sha256": "423c746438717fb7d281dfac02d3950e6e5033c6190f4adf0c63ccdf9433fae5" }, "pipfile-spec": 6, "requires": {}, @@ -14,6 +14,14 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43", + "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d" + ], + "markers": "python_version >= '3.8'", + "version": "==0.6.0" + }, "asgiref": { "hashes": [ "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e", @@ -24,19 +32,20 @@ }, "boto3": { "hashes": [ - "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85", - "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0" + "sha256:38658585791f47cca3fc6aad03838de0136778b533e8c71c6a9590aedc60fbde", + "sha256:a8228522c7db33694c0746dec8b48c05473671626359dd62ab6829eb7871eddc" ], "index": "pypi", - "version": "==1.26.145" + "markers": "python_version >= '3.7'", + "version": "==1.28.66" }, "botocore": { "hashes": [ - "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a", - "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45" + "sha256:70e94a5f9bd46b26b63a41fb441ad35f2ae8862ad9d90765b6fa31ccc02c0a19", + "sha256:8d161a97a25eb381721b4b7251d5126ef4ec57e452114250b3e51ba5e4ff45a4" ], "markers": "python_version >= '3.7'", - "version": "==1.29.145" + "version": "==1.31.66" }, "cachetools": { "hashes": [ @@ -44,15 +53,16 @@ "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==5.3.1" }, "certifi": { "hashes": [ - "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7", - "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716" + "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082", + "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9" ], "markers": "python_version >= '3.6'", - "version": "==2023.5.7" + "version": "==2023.7.22" }, "cfenv": { "hashes": [ @@ -64,178 +74,186 @@ }, "cffi": { "hashes": [ - "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", - "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", - "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", - "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", - "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", - "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", - "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", - "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", - "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", - "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", - "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", - "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", - "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", - "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", - "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", - "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", - "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", - "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", - "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", - "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", - "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", - "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", - "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", - "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", - "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", - "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", - "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", - "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", - "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", - "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", - "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", - "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", - "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", - "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", - "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", - "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", - "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", - "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", - "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", - "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", - "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", - "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", - "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", - "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", - "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", - "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", - "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", - "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", - "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", - "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", - "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", - "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", - "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", - "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", - "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", - "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", - "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", - "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", - "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", - "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", - "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", - "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", - "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", - "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" + "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", + "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", + "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", + "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", + "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", + "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", + "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", + "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", + "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", + "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", + "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", + "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", + "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", + "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", + "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", + "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", + "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", + "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", + "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", + "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", + "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", + "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", + "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", + "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", + "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", + "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", + "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", + "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", + "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", + "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", + "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", + "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", + "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", + "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", + "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", + "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", + "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", + "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", + "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", + "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", + "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", + "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", + "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", + "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", + "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", + "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", + "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", + "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", + "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", + "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", + "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", + "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" ], - "version": "==1.15.1" + "markers": "python_version >= '3.8'", + "version": "==1.16.0" }, "charset-normalizer": { "hashes": [ - "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6", - "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1", - "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e", - "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373", - "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62", - "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230", - "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be", - "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c", - "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0", - "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448", - "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f", - "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649", - "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d", - "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0", - "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706", - "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a", - "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59", - "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23", - "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5", - "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb", - "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e", - "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e", - "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c", - "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28", - "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d", - "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41", - "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974", - "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce", - "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f", - "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1", - "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d", - "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8", - "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017", - "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31", - "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7", - "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8", - "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e", - "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14", - "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd", - "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d", - "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795", - "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b", - "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b", - "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b", - "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203", - "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f", - "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19", - "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1", - "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a", - "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac", - "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9", - "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0", - "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137", - "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f", - "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6", - "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5", - "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909", - "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f", - "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0", - "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324", - "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755", - "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb", - "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854", - "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c", - "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60", - "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84", - "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0", - "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b", - "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1", - "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531", - "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1", - "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11", - "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326", - "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df", - "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab" + "sha256:02673e456dc5ab13659f85196c534dc596d4ef260e4d86e856c3b2773ce09843", + "sha256:02af06682e3590ab952599fbadac535ede5d60d78848e555aa58d0c0abbde786", + "sha256:03680bb39035fbcffe828eae9c3f8afc0428c91d38e7d61aa992ef7a59fb120e", + "sha256:0570d21da019941634a531444364f2482e8db0b3425fcd5ac0c36565a64142c8", + "sha256:09c77f964f351a7369cc343911e0df63e762e42bac24cd7d18525961c81754f4", + "sha256:0d3d5b7db9ed8a2b11a774db2bbea7ba1884430a205dbd54a32d61d7c2a190fa", + "sha256:1063da2c85b95f2d1a430f1c33b55c9c17ffaf5e612e10aeaad641c55a9e2b9d", + "sha256:12ebea541c44fdc88ccb794a13fe861cc5e35d64ed689513a5c03d05b53b7c82", + "sha256:153e7b6e724761741e0974fc4dcd406d35ba70b92bfe3fedcb497226c93b9da7", + "sha256:15b26ddf78d57f1d143bdf32e820fd8935d36abe8a25eb9ec0b5a71c82eb3895", + "sha256:1872d01ac8c618a8da634e232f24793883d6e456a66593135aeafe3784b0848d", + "sha256:187d18082694a29005ba2944c882344b6748d5be69e3a89bf3cc9d878e548d5a", + "sha256:1b2919306936ac6efb3aed1fbf81039f7087ddadb3160882a57ee2ff74fd2382", + "sha256:232ac332403e37e4a03d209a3f92ed9071f7d3dbda70e2a5e9cff1c4ba9f0678", + "sha256:23e8565ab7ff33218530bc817922fae827420f143479b753104ab801145b1d5b", + "sha256:24817cb02cbef7cd499f7c9a2735286b4782bd47a5b3516a0e84c50eab44b98e", + "sha256:249c6470a2b60935bafd1d1d13cd613f8cd8388d53461c67397ee6a0f5dce741", + "sha256:24a91a981f185721542a0b7c92e9054b7ab4fea0508a795846bc5b0abf8118d4", + "sha256:2502dd2a736c879c0f0d3e2161e74d9907231e25d35794584b1ca5284e43f596", + "sha256:250c9eb0f4600361dd80d46112213dff2286231d92d3e52af1e5a6083d10cad9", + "sha256:278c296c6f96fa686d74eb449ea1697f3c03dc28b75f873b65b5201806346a69", + "sha256:2935ffc78db9645cb2086c2f8f4cfd23d9b73cc0dc80334bc30aac6f03f68f8c", + "sha256:2f4a0033ce9a76e391542c182f0d48d084855b5fcba5010f707c8e8c34663d77", + "sha256:30a85aed0b864ac88309b7d94be09f6046c834ef60762a8833b660139cfbad13", + "sha256:380c4bde80bce25c6e4f77b19386f5ec9db230df9f2f2ac1e5ad7af2caa70459", + "sha256:3ae38d325b512f63f8da31f826e6cb6c367336f95e418137286ba362925c877e", + "sha256:3b447982ad46348c02cb90d230b75ac34e9886273df3a93eec0539308a6296d7", + "sha256:3debd1150027933210c2fc321527c2299118aa929c2f5a0a80ab6953e3bd1908", + "sha256:4162918ef3098851fcd8a628bf9b6a98d10c380725df9e04caf5ca6dd48c847a", + "sha256:468d2a840567b13a590e67dd276c570f8de00ed767ecc611994c301d0f8c014f", + "sha256:4cc152c5dd831641e995764f9f0b6589519f6f5123258ccaca8c6d34572fefa8", + "sha256:542da1178c1c6af8873e143910e2269add130a299c9106eef2594e15dae5e482", + "sha256:557b21a44ceac6c6b9773bc65aa1b4cc3e248a5ad2f5b914b91579a32e22204d", + "sha256:5707a746c6083a3a74b46b3a631d78d129edab06195a92a8ece755aac25a3f3d", + "sha256:588245972aca710b5b68802c8cad9edaa98589b1b42ad2b53accd6910dad3545", + "sha256:5adf257bd58c1b8632046bbe43ee38c04e1038e9d37de9c57a94d6bd6ce5da34", + "sha256:619d1c96099be5823db34fe89e2582b336b5b074a7f47f819d6b3a57ff7bdb86", + "sha256:63563193aec44bce707e0c5ca64ff69fa72ed7cf34ce6e11d5127555756fd2f6", + "sha256:67b8cc9574bb518ec76dc8e705d4c39ae78bb96237cb533edac149352c1f39fe", + "sha256:6a685067d05e46641d5d1623d7c7fdf15a357546cbb2f71b0ebde91b175ffc3e", + "sha256:70f1d09c0d7748b73290b29219e854b3207aea922f839437870d8cc2168e31cc", + "sha256:750b446b2ffce1739e8578576092179160f6d26bd5e23eb1789c4d64d5af7dc7", + "sha256:7966951325782121e67c81299a031f4c115615e68046f79b85856b86ebffc4cd", + "sha256:7b8b8bf1189b3ba9b8de5c8db4d541b406611a71a955bbbd7385bbc45fcb786c", + "sha256:7f5d10bae5d78e4551b7be7a9b29643a95aded9d0f602aa2ba584f0388e7a557", + "sha256:805dfea4ca10411a5296bcc75638017215a93ffb584c9e344731eef0dcfb026a", + "sha256:81bf654678e575403736b85ba3a7867e31c2c30a69bc57fe88e3ace52fb17b89", + "sha256:82eb849f085624f6a607538ee7b83a6d8126df6d2f7d3b319cb837b289123078", + "sha256:85a32721ddde63c9df9ebb0d2045b9691d9750cb139c161c80e500d210f5e26e", + "sha256:86d1f65ac145e2c9ed71d8ffb1905e9bba3a91ae29ba55b4c46ae6fc31d7c0d4", + "sha256:86f63face3a527284f7bb8a9d4f78988e3c06823f7bea2bd6f0e0e9298ca0403", + "sha256:8eaf82f0eccd1505cf39a45a6bd0a8cf1c70dcfc30dba338207a969d91b965c0", + "sha256:93aa7eef6ee71c629b51ef873991d6911b906d7312c6e8e99790c0f33c576f89", + "sha256:96c2b49eb6a72c0e4991d62406e365d87067ca14c1a729a870d22354e6f68115", + "sha256:9cf3126b85822c4e53aa28c7ec9869b924d6fcfb76e77a45c44b83d91afd74f9", + "sha256:9fe359b2e3a7729010060fbca442ca225280c16e923b37db0e955ac2a2b72a05", + "sha256:a0ac5e7015a5920cfce654c06618ec40c33e12801711da6b4258af59a8eff00a", + "sha256:a3f93dab657839dfa61025056606600a11d0b696d79386f974e459a3fbc568ec", + "sha256:a4b71f4d1765639372a3b32d2638197f5cd5221b19531f9245fcc9ee62d38f56", + "sha256:aae32c93e0f64469f74ccc730a7cb21c7610af3a775157e50bbd38f816536b38", + "sha256:aaf7b34c5bc56b38c931a54f7952f1ff0ae77a2e82496583b247f7c969eb1479", + "sha256:abecce40dfebbfa6abf8e324e1860092eeca6f7375c8c4e655a8afb61af58f2c", + "sha256:abf0d9f45ea5fb95051c8bfe43cb40cda383772f7e5023a83cc481ca2604d74e", + "sha256:ac71b2977fb90c35d41c9453116e283fac47bb9096ad917b8819ca8b943abecd", + "sha256:ada214c6fa40f8d800e575de6b91a40d0548139e5dc457d2ebb61470abf50186", + "sha256:b09719a17a2301178fac4470d54b1680b18a5048b481cb8890e1ef820cb80455", + "sha256:b1121de0e9d6e6ca08289583d7491e7fcb18a439305b34a30b20d8215922d43c", + "sha256:b3b2316b25644b23b54a6f6401074cebcecd1244c0b8e80111c9a3f1c8e83d65", + "sha256:b3d9b48ee6e3967b7901c052b670c7dda6deb812c309439adaffdec55c6d7b78", + "sha256:b5bcf60a228acae568e9911f410f9d9e0d43197d030ae5799e20dca8df588287", + "sha256:b8f3307af845803fb0b060ab76cf6dd3a13adc15b6b451f54281d25911eb92df", + "sha256:c2af80fb58f0f24b3f3adcb9148e6203fa67dd3f61c4af146ecad033024dde43", + "sha256:c350354efb159b8767a6244c166f66e67506e06c8924ed74669b2c70bc8735b1", + "sha256:c5a74c359b2d47d26cdbbc7845e9662d6b08a1e915eb015d044729e92e7050b7", + "sha256:c71f16da1ed8949774ef79f4a0260d28b83b3a50c6576f8f4f0288d109777989", + "sha256:d47ecf253780c90ee181d4d871cd655a789da937454045b17b5798da9393901a", + "sha256:d7eff0f27edc5afa9e405f7165f85a6d782d308f3b6b9d96016c010597958e63", + "sha256:d97d85fa63f315a8bdaba2af9a6a686e0eceab77b3089af45133252618e70884", + "sha256:db756e48f9c5c607b5e33dd36b1d5872d0422e960145b08ab0ec7fd420e9d649", + "sha256:dc45229747b67ffc441b3de2f3ae5e62877a282ea828a5bdb67883c4ee4a8810", + "sha256:e0fc42822278451bc13a2e8626cf2218ba570f27856b536e00cfa53099724828", + "sha256:e39c7eb31e3f5b1f88caff88bcff1b7f8334975b46f6ac6e9fc725d829bc35d4", + "sha256:e46cd37076971c1040fc8c41273a8b3e2c624ce4f2be3f5dfcb7a430c1d3acc2", + "sha256:e5c1502d4ace69a179305abb3f0bb6141cbe4714bc9b31d427329a95acfc8bdd", + "sha256:edfe077ab09442d4ef3c52cb1f9dab89bff02f4524afc0acf2d46be17dc479f5", + "sha256:effe5406c9bd748a871dbcaf3ac69167c38d72db8c9baf3ff954c344f31c4cbe", + "sha256:f0d1e3732768fecb052d90d62b220af62ead5748ac51ef61e7b32c266cac9293", + "sha256:f5969baeaea61c97efa706b9b107dcba02784b1601c74ac84f2a532ea079403e", + "sha256:f8888e31e3a85943743f8fc15e71536bda1c81d5aa36d014a3c0c44481d7db6e", + "sha256:fc52b79d83a3fe3a360902d3f5d79073a993597d48114c29485e9431092905d8" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.1.0" + "version": "==3.3.0" }, "cryptography": { "hashes": [ - "sha256:059e348f9a3c1950937e1b5d7ba1f8e968508ab181e75fc32b879452f08356db", - "sha256:1a5472d40c8f8e91ff7a3d8ac6dfa363d8e3138b961529c996f3e2df0c7a411a", - "sha256:1a8e6c2de6fbbcc5e14fd27fb24414507cb3333198ea9ab1258d916f00bc3039", - "sha256:1fee5aacc7367487b4e22484d3c7e547992ed726d14864ee33c0176ae43b0d7c", - "sha256:5d092fdfedaec4cbbffbf98cddc915ba145313a6fdaab83c6e67f4e6c218e6f3", - "sha256:5f0ff6e18d13a3de56f609dd1fd11470918f770c6bd5d00d632076c727d35485", - "sha256:7bfc55a5eae8b86a287747053140ba221afc65eb06207bedf6e019b8934b477c", - "sha256:7fa01527046ca5facdf973eef2535a27fec4cb651e4daec4d043ef63f6ecd4ca", - "sha256:8dde71c4169ec5ccc1087bb7521d54251c016f126f922ab2dfe6649170a3b8c5", - "sha256:8f4ab7021127a9b4323537300a2acfb450124b2def3756f64dc3a3d2160ee4b5", - "sha256:948224d76c4b6457349d47c0c98657557f429b4e93057cf5a2f71d603e2fc3a3", - "sha256:9a6c7a3c87d595608a39980ebaa04d5a37f94024c9f24eb7d10262b92f739ddb", - "sha256:b46e37db3cc267b4dea1f56da7346c9727e1209aa98487179ee8ebed09d21e43", - "sha256:b4ceb5324b998ce2003bc17d519080b4ec8d5b7b70794cbd2836101406a9be31", - "sha256:cb33ccf15e89f7ed89b235cff9d49e2e62c6c981a6061c9c8bb47ed7951190bc", - "sha256:d198820aba55660b4d74f7b5fd1f17db3aa5eb3e6893b0a41b75e84e4f9e0e4b", - "sha256:d34579085401d3f49762d2f7d6634d6b6c2ae1242202e860f4d26b046e3a1006", - "sha256:eb8163f5e549a22888c18b0d53d6bb62a20510060a22fd5a995ec8a05268df8a", - "sha256:f73bff05db2a3e5974a6fd248af2566134d8981fd7ab012e5dd4ddb1d9a70699" + "sha256:004b6ccc95943f6a9ad3142cfabcc769d7ee38a3f60fb0dddbfb431f818c3a67", + "sha256:047c4603aeb4bbd8db2756e38f5b8bd7e94318c047cfe4efeb5d715e08b49311", + "sha256:0d9409894f495d465fe6fda92cb70e8323e9648af912d5b9141d616df40a87b8", + "sha256:23a25c09dfd0d9f28da2352503b23e086f8e78096b9fd585d1d14eca01613e13", + "sha256:2ed09183922d66c4ec5fdaa59b4d14e105c084dd0febd27452de8f6f74704143", + "sha256:35c00f637cd0b9d5b6c6bd11b6c3359194a8eba9c46d4e875a3660e3b400005f", + "sha256:37480760ae08065437e6573d14be973112c9e6dcaf5f11d00147ee74f37a3829", + "sha256:3b224890962a2d7b57cf5eeb16ccaafba6083f7b811829f00476309bce2fe0fd", + "sha256:5a0f09cefded00e648a127048119f77bc2b2ec61e736660b5789e638f43cc397", + "sha256:5b72205a360f3b6176485a333256b9bcd48700fc755fef51c8e7e67c4b63e3ac", + "sha256:7e53db173370dea832190870e975a1e09c86a879b613948f09eb49324218c14d", + "sha256:7febc3094125fc126a7f6fb1f420d0da639f3f32cb15c8ff0dc3997c4549f51a", + "sha256:80907d3faa55dc5434a16579952ac6da800935cd98d14dbd62f6f042c7f5e839", + "sha256:86defa8d248c3fa029da68ce61fe735432b047e32179883bdb1e79ed9bb8195e", + "sha256:8ac4f9ead4bbd0bc8ab2d318f97d85147167a488be0e08814a37eb2f439d5cf6", + "sha256:93530900d14c37a46ce3d6c9e6fd35dbe5f5601bf6b3a5c325c7bffc030344d9", + "sha256:9eeb77214afae972a00dee47382d2591abe77bdae166bda672fb1e24702a3860", + "sha256:b5f4dfe950ff0479f1f00eda09c18798d4f49b98f4e2006d644b3301682ebdca", + "sha256:c3391bd8e6de35f6f1140e50aaeb3e2b3d6a9012536ca23ab0d9c35ec18c8a91", + "sha256:c880eba5175f4307129784eca96f4e70b88e57aa3f680aeba3bab0e980b0f37d", + "sha256:cecfefa17042941f94ab54f769c8ce0fe14beff2694e9ac684176a2535bf9714", + "sha256:e40211b4923ba5a6dc9769eab704bdb3fbb58d56c5b336d30996c24fcf12aadb", + "sha256:efc8ad4e6fc4f1752ebfb58aefece8b4e3c4cae940b0994d43649bdfce8d0d4f" ], "markers": "python_version >= '3.7'", - "version": "==41.0.1" + "version": "==41.0.4" }, "defusedxml": { "hashes": [ @@ -247,10 +265,10 @@ }, "dj-database-url": { "hashes": [ - "sha256:9c9e5f7224f62635a787e9cc3c6762c9be2b19541a21e3c08fa573bd01609b4b", - "sha256:a35a9f0f43775ca6f90d819dc456233ef7bcc76b47377d5d908b75c7eb320624" + "sha256:04bc34b248d4c21aaa13e4ab419ae6575ef5f10f3df735ce7da97722caa356e0", + "sha256:f2042cefe1086e539c9da39fad5ad7f61173bf79665e69bf7e4de55fa88b135f" ], - "version": "==2.0.0" + "version": "==2.1.0" }, "dj-email-url": { "hashes": [ @@ -261,19 +279,20 @@ }, "django": { "hashes": [ - "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", - "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" + "sha256:08f41f468b63335aea0d904c5729e0250300f6a1907bf293a65499496cdbc68f", + "sha256:a64d2487cdb00ad7461434320ccc38e60af9c404773a2f95ab0093b4453a3215" ], "index": "pypi", - "version": "==4.2.1" + "markers": "python_version >= '3.8'", + "version": "==4.2.6" }, "django-allow-cidr": { "hashes": [ - "sha256:24b71f70257e97bab9fdb5ad8342c96eeea1d45bc06a36332978574252219401", - "sha256:6709f4581dfd2a00476a134741a738a7f67714ec4f8596c55b22cf3b2ac5a12e" + "sha256:11126c5bb9df3a61ff9d97304856ba7e5b26d46c6d456709a6d9e28483bff47f", + "sha256:382c5d7a9807279e3e96e4f4892b59163a2b30128c596902bf5f80e133e1ccbb" ], "index": "pypi", - "version": "==0.6.0" + "version": "==0.7.1" }, "django-auditlog": { "hashes": [ @@ -281,6 +300,7 @@ "sha256:b9d3acebb64f3f2785157efe3f2f802e0929aafc579d85bbfb9827db4adab532" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.3.0" }, "django-cache-url": { @@ -318,19 +338,20 @@ "phonenumberslite" ], "hashes": [ - "sha256:4eaab35fe9a163046dc3a47188771385c56a788e0e11b7bbcc662e1e6b7b9104", - "sha256:63721dbdc7424cd594a08d80f550e790cf6e7c903cbc0fb4dd9d86baac8b8c51" + "sha256:16778f2717ea2aecc6178beb0d6bc431c78c6a8b0474e1fa8face040efeb6e9e", + "sha256:20c7c5c449e33eed5fd45ef8d3dc668faabaeff3277eddd1892b262d686ba381" ], - "index": "pypi", - "version": "==7.1.0" + "markers": "python_version >= '3.8'", + "version": "==7.2.0" }, "django-widget-tweaks": { "hashes": [ - "sha256:9bfc5c705684754a83cc81da328b39ad1b80f32bd0f4340e2a810cbab4b0c00e", - "sha256:fe6b17d5d595c63331f300917980db2afcf71f240ab9341b954aea8f45d25b9a" + "sha256:1c2180681ebb994e922c754804c7ffebbe1245014777ac47897a81f57cc629c7", + "sha256:a41b7b2f05bd44d673d11ebd6c09a96f1d013ee98121cb98c384fe84e33b881e" ], "index": "pypi", - "version": "==1.4.12" + "markers": "python_version >= '3.8'", + "version": "==1.5.0" }, "environs": { "extras": [ @@ -340,16 +361,16 @@ "sha256:1e549569a3de49c05f856f40bce86979e7d5ffbbc4398e7f338574c220189124", "sha256:a76307b36fbe856bdca7ee9161e6c466fd7fcffc297109a118c59b54e27e30c9" ], - "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==9.5.0" }, "faker": { "hashes": [ - "sha256:a70de9ec7a14a02d278755a11134baa5a297bb82600f115022d0d07080a9e77a", - "sha256:dd15fa165ced55f668fbb0ad20ece98ab78ddacd58dc056950d66980ff61fa79" + "sha256:a62a3fd3bfa3122d4f57dfa26a1cc37d76751a76c8ddd63cf9d24078c57913a4", + "sha256:e28090068293c5a83e7f4d636417d45fae1031ca8a8136cc2415549ebc2111e2" ], - "index": "pypi", - "version": "==18.10.0" + "markers": "python_version >= '3.8'", + "version": "==19.11.0" }, "fred-epplib": { "git": "https://github.com/cisagov/epplib.git", @@ -369,13 +390,134 @@ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.18.3" }, - "gunicorn": { + "gevent": { "hashes": [ - "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", - "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" + "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a", + "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2", + "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535", + "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e", + "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653", + "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1", + "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c", + "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648", + "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599", + "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea", + "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6", + "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f", + "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9", + "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e", + "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34", + "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397", + "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507", + "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b", + "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd", + "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe", + "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a", + "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b", + "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771", + "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e", + "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69", + "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a", + "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011", + "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7", + "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71", + "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5", + "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae", + "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7", + "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39", + "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d", + "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599", + "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07", + "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904", + "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a", + "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543", + "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303" ], "index": "pypi", - "version": "==20.1.0" + "markers": "python_version >= '3.8'", + "version": "==23.9.1" + }, + "geventconnpool": { + "git": "https://github.com/rasky/geventconnpool.git", + "ref": "1bbb93a714a331a069adf27265fe582d9ba7ecd4" + }, + "greenlet": { + "hashes": [ + "sha256:02a807b2a58d5cdebb07050efe3d7deaf915468d112dfcf5e426d0564aa3aa4a", + "sha256:0b72b802496cccbd9b31acea72b6f87e7771ccfd7f7927437d592e5c92ed703c", + "sha256:0d3f83ffb18dc57243e0151331e3c383b05e5b6c5029ac29f754745c800f8ed9", + "sha256:10b5582744abd9858947d163843d323d0b67be9432db50f8bf83031032bc218d", + "sha256:123910c58234a8d40eaab595bc56a5ae49bdd90122dde5bdc012c20595a94c14", + "sha256:1482fba7fbed96ea7842b5a7fc11d61727e8be75a077e603e8ab49d24e234383", + "sha256:19834e3f91f485442adc1ee440171ec5d9a4840a1f7bd5ed97833544719ce10b", + "sha256:1d363666acc21d2c204dd8705c0e0457d7b2ee7a76cb16ffc099d6799744ac99", + "sha256:211ef8d174601b80e01436f4e6905aca341b15a566f35a10dd8d1e93f5dbb3b7", + "sha256:269d06fa0f9624455ce08ae0179430eea61085e3cf6457f05982b37fd2cefe17", + "sha256:2e7dcdfad252f2ca83c685b0fa9fba00e4d8f243b73839229d56ee3d9d219314", + "sha256:334ef6ed8337bd0b58bb0ae4f7f2dcc84c9f116e474bb4ec250a8bb9bd797a66", + "sha256:343675e0da2f3c69d3fb1e894ba0a1acf58f481f3b9372ce1eb465ef93cf6fed", + "sha256:37f60b3a42d8b5499be910d1267b24355c495064f271cfe74bf28b17b099133c", + "sha256:38ad562a104cd41e9d4644f46ea37167b93190c6d5e4048fcc4b80d34ecb278f", + "sha256:3c0d36f5adc6e6100aedbc976d7428a9f7194ea79911aa4bf471f44ee13a9464", + "sha256:3fd2b18432e7298fcbec3d39e1a0aa91ae9ea1c93356ec089421fabc3651572b", + "sha256:4a1a6244ff96343e9994e37e5b4839f09a0207d35ef6134dce5c20d260d0302c", + "sha256:4cd83fb8d8e17633ad534d9ac93719ef8937568d730ef07ac3a98cb520fd93e4", + "sha256:527cd90ba3d8d7ae7dceb06fda619895768a46a1b4e423bdb24c1969823b8362", + "sha256:56867a3b3cf26dc8a0beecdb4459c59f4c47cdd5424618c08515f682e1d46692", + "sha256:621fcb346141ae08cb95424ebfc5b014361621b8132c48e538e34c3c93ac7365", + "sha256:63acdc34c9cde42a6534518e32ce55c30f932b473c62c235a466469a710bfbf9", + "sha256:6512592cc49b2c6d9b19fbaa0312124cd4c4c8a90d28473f86f92685cc5fef8e", + "sha256:6672fdde0fd1a60b44fb1751a7779c6db487e42b0cc65e7caa6aa686874e79fb", + "sha256:6a5b2d4cdaf1c71057ff823a19d850ed5c6c2d3686cb71f73ae4d6382aaa7a06", + "sha256:6a68d670c8f89ff65c82b936275369e532772eebc027c3be68c6b87ad05ca695", + "sha256:6bb36985f606a7c49916eff74ab99399cdfd09241c375d5a820bb855dfb4af9f", + "sha256:73b2f1922a39d5d59cc0e597987300df3396b148a9bd10b76a058a2f2772fc04", + "sha256:7709fd7bb02b31908dc8fd35bfd0a29fc24681d5cc9ac1d64ad07f8d2b7db62f", + "sha256:8060b32d8586e912a7b7dac2d15b28dbbd63a174ab32f5bc6d107a1c4143f40b", + "sha256:80dcd3c938cbcac986c5c92779db8e8ce51a89a849c135172c88ecbdc8c056b7", + "sha256:813720bd57e193391dfe26f4871186cf460848b83df7e23e6bef698a7624b4c9", + "sha256:831d6f35037cf18ca5e80a737a27d822d87cd922521d18ed3dbc8a6967be50ce", + "sha256:871b0a8835f9e9d461b7fdaa1b57e3492dd45398e87324c047469ce2fc9f516c", + "sha256:952256c2bc5b4ee8df8dfc54fc4de330970bf5d79253c863fb5e6761f00dda35", + "sha256:96d9ea57292f636ec851a9bb961a5cc0f9976900e16e5d5647f19aa36ba6366b", + "sha256:9a812224a5fb17a538207e8cf8e86f517df2080c8ee0f8c1ed2bdaccd18f38f4", + "sha256:9adbd8ecf097e34ada8efde9b6fec4dd2a903b1e98037adf72d12993a1c80b51", + "sha256:9de687479faec7db5b198cc365bc34addd256b0028956501f4d4d5e9ca2e240a", + "sha256:a048293392d4e058298710a54dfaefcefdf49d287cd33fb1f7d63d55426e4355", + "sha256:aa15a2ec737cb609ed48902b45c5e4ff6044feb5dcdfcf6fa8482379190330d7", + "sha256:abe1ef3d780de56defd0c77c5ba95e152f4e4c4e12d7e11dd8447d338b85a625", + "sha256:ad6fb737e46b8bd63156b8f59ba6cdef46fe2b7db0c5804388a2d0519b8ddb99", + "sha256:b1660a15a446206c8545edc292ab5c48b91ff732f91b3d3b30d9a915d5ec4779", + "sha256:b505fcfc26f4148551826a96f7317e02c400665fa0883fe505d4fcaab1dabfdd", + "sha256:b822fab253ac0f330ee807e7485769e3ac85d5eef827ca224feaaefa462dc0d0", + "sha256:bdd696947cd695924aecb3870660b7545a19851f93b9d327ef8236bfc49be705", + "sha256:bdfaeecf8cc705d35d8e6de324bf58427d7eafb55f67050d8f28053a3d57118c", + "sha256:be557119bf467d37a8099d91fbf11b2de5eb1fd5fc5b91598407574848dc910f", + "sha256:c6b5ce7f40f0e2f8b88c28e6691ca6806814157ff05e794cdd161be928550f4c", + "sha256:c94e4e924d09b5a3e37b853fe5924a95eac058cb6f6fb437ebb588b7eda79870", + "sha256:cc3e2679ea13b4de79bdc44b25a0c4fcd5e94e21b8f290791744ac42d34a0353", + "sha256:d1e22c22f7826096ad503e9bb681b05b8c1f5a8138469b255eb91f26a76634f2", + "sha256:d5539f6da3418c3dc002739cb2bb8d169056aa66e0c83f6bacae0cd3ac26b423", + "sha256:d55db1db455c59b46f794346efce896e754b8942817f46a1bada2d29446e305a", + "sha256:e09dea87cc91aea5500262993cbd484b41edf8af74f976719dd83fe724644cd6", + "sha256:e52a712c38e5fb4fd68e00dc3caf00b60cb65634d50e32281a9d6431b33b4af1", + "sha256:e693e759e172fa1c2c90d35dea4acbdd1d609b6936115d3739148d5e4cd11947", + "sha256:ecf94aa539e97a8411b5ea52fc6ccd8371be9550c4041011a091eb8b3ca1d810", + "sha256:f351479a6914fd81a55c8e68963609f792d9b067fb8a60a042c585a621e0de4f", + "sha256:f47932c434a3c8d3c86d865443fadc1fbf574e9b11d6650b656e602b1797908a" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==3.0.0" + }, + "gunicorn": { + "hashes": [ + "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0", + "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033" + ], + "index": "pypi", + "markers": "python_version >= '3.5'", + "version": "==21.2.0" }, "idna": { "hashes": [ @@ -395,86 +537,101 @@ }, "lxml": { "hashes": [ - "sha256:01d36c05f4afb8f7c20fd9ed5badca32a2029b93b1750f571ccc0b142531caf7", - "sha256:04876580c050a8c5341d706dd464ff04fd597095cc8c023252566a8826505726", - "sha256:05ca3f6abf5cf78fe053da9b1166e062ade3fa5d4f92b4ed688127ea7d7b1d03", - "sha256:090c6543d3696cbe15b4ac6e175e576bcc3f1ccfbba970061b7300b0c15a2140", - "sha256:0dc313ef231edf866912e9d8f5a042ddab56c752619e92dfd3a2c277e6a7299a", - "sha256:0f2b1e0d79180f344ff9f321327b005ca043a50ece8713de61d1cb383fb8ac05", - "sha256:13598ecfbd2e86ea7ae45ec28a2a54fb87ee9b9fdb0f6d343297d8e548392c03", - "sha256:16efd54337136e8cd72fb9485c368d91d77a47ee2d42b057564aae201257d419", - "sha256:1ab8f1f932e8f82355e75dda5413a57612c6ea448069d4fb2e217e9a4bed13d4", - "sha256:223f4232855ade399bd409331e6ca70fb5578efef22cf4069a6090acc0f53c0e", - "sha256:2455cfaeb7ac70338b3257f41e21f0724f4b5b0c0e7702da67ee6c3640835b67", - "sha256:2899456259589aa38bfb018c364d6ae7b53c5c22d8e27d0ec7609c2a1ff78b50", - "sha256:2a29ba94d065945944016b6b74e538bdb1751a1db6ffb80c9d3c2e40d6fa9894", - "sha256:2a87fa548561d2f4643c99cd13131acb607ddabb70682dcf1dff5f71f781a4bf", - "sha256:2e430cd2824f05f2d4f687701144556646bae8f249fd60aa1e4c768ba7018947", - "sha256:36c3c175d34652a35475a73762b545f4527aec044910a651d2bf50de9c3352b1", - "sha256:3818b8e2c4b5148567e1b09ce739006acfaa44ce3156f8cbbc11062994b8e8dd", - "sha256:3ab9fa9d6dc2a7f29d7affdf3edebf6ece6fb28a6d80b14c3b2fb9d39b9322c3", - "sha256:3efea981d956a6f7173b4659849f55081867cf897e719f57383698af6f618a92", - "sha256:4c8f293f14abc8fd3e8e01c5bd86e6ed0b6ef71936ded5bf10fe7a5efefbaca3", - "sha256:5344a43228767f53a9df6e5b253f8cdca7dfc7b7aeae52551958192f56d98457", - "sha256:58bfa3aa19ca4c0f28c5dde0ff56c520fbac6f0daf4fac66ed4c8d2fb7f22e74", - "sha256:5b4545b8a40478183ac06c073e81a5ce4cf01bf1734962577cf2bb569a5b3bbf", - "sha256:5f50a1c177e2fa3ee0667a5ab79fdc6b23086bc8b589d90b93b4bd17eb0e64d1", - "sha256:63da2ccc0857c311d764e7d3d90f429c252e83b52d1f8f1d1fe55be26827d1f4", - "sha256:6749649eecd6a9871cae297bffa4ee76f90b4504a2a2ab528d9ebe912b101975", - "sha256:6804daeb7ef69e7b36f76caddb85cccd63d0c56dedb47555d2fc969e2af6a1a5", - "sha256:689bb688a1db722485e4610a503e3e9210dcc20c520b45ac8f7533c837be76fe", - "sha256:699a9af7dffaf67deeae27b2112aa06b41c370d5e7633e0ee0aea2e0b6c211f7", - "sha256:6b418afe5df18233fc6b6093deb82a32895b6bb0b1155c2cdb05203f583053f1", - "sha256:76cf573e5a365e790396a5cc2b909812633409306c6531a6877c59061e42c4f2", - "sha256:7b515674acfdcadb0eb5d00d8a709868173acece5cb0be3dd165950cbfdf5409", - "sha256:7b770ed79542ed52c519119473898198761d78beb24b107acf3ad65deae61f1f", - "sha256:7d2278d59425777cfcb19735018d897ca8303abe67cc735f9f97177ceff8027f", - "sha256:7e91ee82f4199af8c43d8158024cbdff3d931df350252288f0d4ce656df7f3b5", - "sha256:821b7f59b99551c69c85a6039c65b75f5683bdc63270fec660f75da67469ca24", - "sha256:822068f85e12a6e292803e112ab876bc03ed1f03dddb80154c395f891ca6b31e", - "sha256:8340225bd5e7a701c0fa98284c849c9b9fc9238abf53a0ebd90900f25d39a4e4", - "sha256:85cabf64adec449132e55616e7ca3e1000ab449d1d0f9d7f83146ed5bdcb6d8a", - "sha256:880bbbcbe2fca64e2f4d8e04db47bcdf504936fa2b33933efd945e1b429bea8c", - "sha256:8d0b4612b66ff5d62d03bcaa043bb018f74dfea51184e53f067e6fdcba4bd8de", - "sha256:8e20cb5a47247e383cf4ff523205060991021233ebd6f924bca927fcf25cf86f", - "sha256:925073b2fe14ab9b87e73f9a5fde6ce6392da430f3004d8b72cc86f746f5163b", - "sha256:998c7c41910666d2976928c38ea96a70d1aa43be6fe502f21a651e17483a43c5", - "sha256:9b22c5c66f67ae00c0199f6055705bc3eb3fcb08d03d2ec4059a2b1b25ed48d7", - "sha256:9f102706d0ca011de571de32c3247c6476b55bb6bc65a20f682f000b07a4852a", - "sha256:a08cff61517ee26cb56f1e949cca38caabe9ea9fbb4b1e10a805dc39844b7d5c", - "sha256:a0a336d6d3e8b234a3aae3c674873d8f0e720b76bc1d9416866c41cd9500ffb9", - "sha256:a35f8b7fa99f90dd2f5dc5a9fa12332642f087a7641289ca6c40d6e1a2637d8e", - "sha256:a38486985ca49cfa574a507e7a2215c0c780fd1778bb6290c21193b7211702ab", - "sha256:a5da296eb617d18e497bcf0a5c528f5d3b18dadb3619fbdadf4ed2356ef8d941", - "sha256:a6e441a86553c310258aca15d1c05903aaf4965b23f3bc2d55f200804e005ee5", - "sha256:a82d05da00a58b8e4c0008edbc8a4b6ec5a4bc1e2ee0fb6ed157cf634ed7fa45", - "sha256:ab323679b8b3030000f2be63e22cdeea5b47ee0abd2d6a1dc0c8103ddaa56cd7", - "sha256:b1f42b6921d0e81b1bcb5e395bc091a70f41c4d4e55ba99c6da2b31626c44892", - "sha256:b23e19989c355ca854276178a0463951a653309fb8e57ce674497f2d9f208746", - "sha256:b264171e3143d842ded311b7dccd46ff9ef34247129ff5bf5066123c55c2431c", - "sha256:b26a29f0b7fc6f0897f043ca366142d2b609dc60756ee6e4e90b5f762c6adc53", - "sha256:b64d891da92e232c36976c80ed7ebb383e3f148489796d8d31a5b6a677825efe", - "sha256:b9cc34af337a97d470040f99ba4282f6e6bac88407d021688a5d585e44a23184", - "sha256:bc718cd47b765e790eecb74d044cc8d37d58562f6c314ee9484df26276d36a38", - "sha256:be7292c55101e22f2a3d4d8913944cbea71eea90792bf914add27454a13905df", - "sha256:c83203addf554215463b59f6399835201999b5e48019dc17f182ed5ad87205c9", - "sha256:c9ec3eaf616d67db0764b3bb983962b4f385a1f08304fd30c7283954e6a7869b", - "sha256:ca34efc80a29351897e18888c71c6aca4a359247c87e0b1c7ada14f0ab0c0fb2", - "sha256:ca989b91cf3a3ba28930a9fc1e9aeafc2a395448641df1f387a2d394638943b0", - "sha256:d02a5399126a53492415d4906ab0ad0375a5456cc05c3fc0fc4ca11771745cda", - "sha256:d17bc7c2ccf49c478c5bdd447594e82692c74222698cfc9b5daae7ae7e90743b", - "sha256:d5bf6545cd27aaa8a13033ce56354ed9e25ab0e4ac3b5392b763d8d04b08e0c5", - "sha256:d6b430a9938a5a5d85fc107d852262ddcd48602c120e3dbb02137c83d212b380", - "sha256:da248f93f0418a9e9d94b0080d7ebc407a9a5e6d0b57bb30db9b5cc28de1ad33", - "sha256:da4dd7c9c50c059aba52b3524f84d7de956f7fef88f0bafcf4ad7dde94a064e8", - "sha256:df0623dcf9668ad0445e0558a21211d4e9a149ea8f5666917c8eeec515f0a6d1", - "sha256:e5168986b90a8d1f2f9dc1b841467c74221bd752537b99761a93d2d981e04889", - "sha256:efa29c2fe6b4fdd32e8ef81c1528506895eca86e1d8c4657fda04c9b3786ddf9", - "sha256:f1496ea22ca2c830cbcbd473de8f114a320da308438ae65abad6bab7867fe38f", - "sha256:f49e52d174375a7def9915c9f06ec4e569d235ad428f70751765f48d5926678c" + "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3", + "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d", + "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a", + "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120", + "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305", + "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287", + "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23", + "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52", + "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f", + "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4", + "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584", + "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f", + "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693", + "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef", + "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5", + "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02", + "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc", + "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7", + "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da", + "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a", + "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40", + "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8", + "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd", + "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601", + "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c", + "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be", + "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2", + "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c", + "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129", + "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc", + "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2", + "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1", + "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7", + "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d", + "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477", + "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d", + "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e", + "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7", + "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2", + "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574", + "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf", + "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b", + "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98", + "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12", + "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42", + "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35", + "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d", + "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce", + "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d", + "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f", + "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db", + "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4", + "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694", + "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac", + "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2", + "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7", + "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96", + "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d", + "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b", + "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a", + "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13", + "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340", + "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6", + "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458", + "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c", + "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c", + "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9", + "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432", + "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991", + "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69", + "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf", + "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb", + "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b", + "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833", + "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76", + "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85", + "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e", + "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50", + "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8", + "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4", + "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b", + "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5", + "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190", + "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7", + "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa", + "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0", + "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9", + "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0", + "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b", + "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5", + "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7", + "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==4.9.2" + "version": "==4.9.3" }, "mako": { "hashes": [ @@ -486,75 +643,86 @@ }, "markupsafe": { "hashes": [ - "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed", - "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc", - "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2", - "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460", - "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7", - "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0", - "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1", - "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa", - "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03", - "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323", - "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65", - "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013", - "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036", - "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f", - "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4", - "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419", - "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2", - "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619", - "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a", - "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a", - "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd", - "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7", - "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666", - "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65", - "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859", - "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625", - "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff", - "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156", - "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd", - "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba", - "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f", - "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1", - "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094", - "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a", - "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513", - "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed", - "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d", - "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3", - "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147", - "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c", - "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603", - "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601", - "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a", - "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1", - "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d", - "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3", - "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54", - "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2", - "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6", - "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58" + "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e", + "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e", + "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431", + "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686", + "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c", + "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559", + "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc", + "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb", + "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939", + "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c", + "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0", + "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4", + "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9", + "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575", + "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba", + "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d", + "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd", + "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3", + "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00", + "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155", + "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac", + "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52", + "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f", + "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8", + "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b", + "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007", + "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24", + "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea", + "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198", + "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0", + "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee", + "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be", + "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2", + "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1", + "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707", + "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6", + "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c", + "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58", + "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823", + "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779", + "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636", + "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c", + "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad", + "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee", + "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc", + "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2", + "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48", + "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7", + "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e", + "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b", + "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa", + "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5", + "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e", + "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb", + "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9", + "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57", + "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc", + "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc", + "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2", + "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11" ], "markers": "python_version >= '3.7'", - "version": "==2.1.2" + "version": "==2.1.3" }, "marshmallow": { "hashes": [ - "sha256:90032c0fd650ce94b6ec6dc8dfeb0e3ff50c144586462c389b81a07205bedb78", - "sha256:93f0958568da045b0021ec6aeb7ac37c81bfcccbb9a0e7ed8559885070b3a19b" + "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889", + "sha256:684939db93e80ad3561392f47be0230743131560a41c5110684c16e21ade0a5c" ], - "markers": "python_version >= '3.7'", - "version": "==3.19.0" + "markers": "python_version >= '3.8'", + "version": "==3.20.1" }, "oic": { "hashes": [ - "sha256:2de3b83f1299dda8ed0460baad8bb2d4c6ac8bfc08a220c768b7e6e754caf9e7", - "sha256:f82e087e0ffaba2194ebd24694721a25167d5d467fb06bf4f4da9f48d43e0cc6" + "sha256:385a1f64bb59519df1e23840530921bf416740240f505ea6d161e331d3d39fad", + "sha256:fcbf948a22e4d4df66f6bf57d327933f32a7b539640d9b42883457634360ba78" ], "index": "pypi", - "version": "==1.6.0" + "markers": "python_version ~= '3.7'", + "version": "==1.6.1" }, "orderedmultidict": { "hashes": [ @@ -565,86 +733,94 @@ }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==23.2" }, "phonenumberslite": { "hashes": [ - "sha256:670a3e1ea775a9fad89d843d2d18a0e2bb0c3719e1a6bb2b96fb12f7ddd579a0", - "sha256:d1d23707dbde2b8b6940f80b181638a3e10334ff2f122c7165ce43e91c6639fe" + "sha256:7c719e35ef551a895459382e9faf592f52647312dd90b543b06460aa0e1c49c4", + "sha256:cf6cf56c889c6787ec6b30b5791693f6dd678f633358f4aeea1fddf98d4cadcb" ], - "version": "==8.13.13" + "version": "==8.13.23" }, "psycopg2-binary": { "hashes": [ - "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514", - "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe", - "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be", - "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3", - "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d", - "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e", - "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081", - "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb", - "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c", - "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee", - "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b", - "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8", - "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5", - "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc", - "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0", - "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0", - "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963", - "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f", - "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503", - "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2", - "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b", - "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9", - "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f", - "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303", - "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b", - "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d", - "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70", - "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2", - "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0", - "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141", - "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896", - "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763", - "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1", - "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c", - "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b", - "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e", - "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e", - "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f", - "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19", - "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb", - "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6", - "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b", - "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8", - "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3", - "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848", - "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365", - "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3", - "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e", - "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1", - "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb", - "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827", - "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7", - "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd", - "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a", - "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820", - "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54", - "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df", - "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4", - "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1", - "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249", - "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232", - "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7" + "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", + "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77", + "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", + "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", + "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3", + "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", + "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67", + "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876", + "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152", + "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", + "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a", + "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6", + "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503", + "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f", + "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", + "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", + "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f", + "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e", + "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59", + "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94", + "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7", + "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", + "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420", + "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae", + "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291", + "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", + "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980", + "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692", + "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", + "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716", + "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472", + "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", + "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", + "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", + "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", + "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5", + "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984", + "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", + "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", + "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", + "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", + "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", + "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", + "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", + "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90", + "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", + "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", + "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", + "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", + "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", + "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", + "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", + "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", + "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", + "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", + "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", + "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1", + "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd", + "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", + "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", + "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55", + "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", + "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972", + "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f", + "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", + "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", + "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957", + "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", + "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52" ], "index": "pypi", - "version": "==2.9.6" + "markers": "python_version >= '3.7'", + "version": "==2.9.9" }, "pycparser": { "hashes": [ @@ -655,83 +831,170 @@ }, "pycryptodomex": { "hashes": [ - "sha256:160a39a708c36fa0b168ab79386dede588e62aec06eb505add870739329aecc6", - "sha256:192306cf881fe3467dda0e174a4f47bb3a8bb24b90c9cdfbdc248eec5fc0578c", - "sha256:1949e09ea49b09c36d11a951b16ff2a05a0ffe969dda1846e4686ee342fe8646", - "sha256:215be2980a6b70704c10796dd7003eb4390e7be138ac6fb8344bf47e71a8d470", - "sha256:27072a494ce621cc7a9096bbf60ed66826bb94db24b49b7359509e7951033e74", - "sha256:2dc4eab20f4f04a2d00220fdc9258717b82d31913552e766d5f00282c031b70a", - "sha256:302a8f37c224e7b5d72017d462a2be058e28f7be627bdd854066e16722d0fc0c", - "sha256:3d9314ac785a5b75d5aaf924c5f21d6ca7e8df442e5cf4f0fefad4f6e284d422", - "sha256:3e3ecb5fe979e7c1bb0027e518340acf7ee60415d79295e5251d13c68dde576e", - "sha256:4d9379c684efea80fdab02a3eb0169372bca7db13f9332cb67483b8dc8b67c37", - "sha256:50308fcdbf8345e5ec224a5502b4215178bdb5e95456ead8ab1a69ffd94779cb", - "sha256:5594a125dae30d60e94f37797fc67ce3c744522de7992c7c360d02fdb34918f8", - "sha256:58fc0aceb9c961b9897facec9da24c6a94c5db04597ec832060f53d4d6a07196", - "sha256:6421d23d6a648e83ba2670a352bcd978542dad86829209f59d17a3f087f4afef", - "sha256:6875eb8666f68ddbd39097867325bd22771f595b4e2b0149739b5623c8bf899b", - "sha256:6ed3606832987018615f68e8ed716a7065c09a0fe94afd7c9ca1b6777f0ac6eb", - "sha256:71687eed47df7e965f6e0bf3cadef98f368d5221f0fb89d2132effe1a3e6a194", - "sha256:73d64b32d84cf48d9ec62106aa277dbe99ab5fbfd38c5100bc7bddd3beb569f7", - "sha256:75672205148bdea34669173366df005dbd52be05115e919551ee97171083423d", - "sha256:76f0a46bee539dae4b3dfe37216f678769349576b0080fdbe431d19a02da42ff", - "sha256:8ff129a5a0eb5ff16e45ca4fa70a6051da7f3de303c33b259063c19be0c43d35", - "sha256:ac614363a86cc53d8ba44b6c469831d1555947e69ab3276ae8d6edc219f570f7", - "sha256:ba95abd563b0d1b88401658665a260852a8e6c647026ee6a0a65589287681df8", - "sha256:bbdcce0a226d9205560a5936b05208c709b01d493ed8307792075dedfaaffa5f", - "sha256:bec6c80994d4e7a38312072f89458903b65ec99bed2d65aa4de96d997a53ea7a", - "sha256:c2953afebf282a444c51bf4effe751706b4d0d63d7ca2cc51db21f902aa5b84e", - "sha256:d35a8ffdc8b05e4b353ba281217c8437f02c57d7233363824e9d794cf753c419", - "sha256:d56c9ec41258fd3734db9f5e4d2faeabe48644ba9ca23b18e1839b3bdf093222", - "sha256:d84e105787f5e5d36ec6a581ff37a1048d12e638688074b2a00bcf402f9aa1c2", - "sha256:e00a4bacb83a2627e8210cb353a2e31f04befc1155db2976e5e239dd66482278", - "sha256:f237278836dda412a325e9340ba2e6a84cb0f56b9244781e5b61f10b3905de88", - "sha256:f9ab5ef0718f6a8716695dea16d83b671b22c45e9c0c78fd807c32c0192e54b5" + "sha256:09c9401dc06fb3d94cb1ec23b4ea067a25d1f4c6b7b118ff5631d0b5daaab3cc", + "sha256:0b2f1982c5bc311f0aab8c293524b861b485d76f7c9ab2c3ac9a25b6f7655975", + "sha256:136b284e9246b4ccf4f752d435c80f2c44fc2321c198505de1d43a95a3453b3c", + "sha256:1789d89f61f70a4cd5483d4dfa8df7032efab1118f8b9894faae03c967707865", + "sha256:2126bc54beccbede6eade00e647106b4f4c21e5201d2b0a73e9e816a01c50905", + "sha256:258c4233a3fe5a6341780306a36c6fb072ef38ce676a6d41eec3e591347919e8", + "sha256:263de9a96d2fcbc9f5bd3a279f14ea0d5f072adb68ebd324987576ec25da084d", + "sha256:50cb18d4dd87571006fd2447ccec85e6cec0136632a550aa29226ba075c80644", + "sha256:5b883e1439ab63af976656446fb4839d566bb096f15fc3c06b5a99cde4927188", + "sha256:5d73e9fa3fe830e7b6b42afc49d8329b07a049a47d12e0ef9225f2fd220f19b2", + "sha256:61056a1fd3254f6f863de94c233b30dd33bc02f8c935b2000269705f1eeeffa4", + "sha256:67c8eb79ab33d0fbcb56842992298ddb56eb6505a72369c20f60bc1d2b6fb002", + "sha256:6e45bb4635b3c4e0a00ca9df75ef6295838c85c2ac44ad882410cb631ed1eeaa", + "sha256:7cb51096a6a8d400724104db8a7e4f2206041a1f23e58924aa3d8d96bcb48338", + "sha256:800a2b05cfb83654df80266692f7092eeefe2a314fa7901dcefab255934faeec", + "sha256:8df69e41f7e7015a90b94d1096ec3d8e0182e73449487306709ec27379fff761", + "sha256:917033016ecc23c8933205585a0ab73e20020fdf671b7cd1be788a5c4039840b", + "sha256:a12144d785518f6491ad334c75ccdc6ad52ea49230b4237f319dbb7cef26f464", + "sha256:a3866d68e2fc345162b1b9b83ef80686acfe5cec0d134337f3b03950a0a8bf56", + "sha256:a588a1cb7781da9d5e1c84affd98c32aff9c89771eac8eaa659d2760666f7139", + "sha256:a77b79852175064c822b047fee7cf5a1f434f06ad075cc9986aa1c19a0c53eb0", + "sha256:af83a554b3f077564229865c45af0791be008ac6469ef0098152139e6bd4b5b6", + "sha256:b801216c48c0886742abf286a9a6b117e248ca144d8ceec1f931ce2dd0c9cb40", + "sha256:bfb040b5dda1dff1e197d2ef71927bd6b8bfcb9793bc4dfe0bb6df1e691eaacb", + "sha256:c01678aee8ac0c1a461cbc38ad496f953f9efcb1fa19f5637cbeba7544792a53", + "sha256:c74eb1f73f788facece7979ce91594dc177e1a9b5d5e3e64697dd58299e5cb4d", + "sha256:c9a68a2f7bd091ccea54ad3be3e9d65eded813e6d79fdf4cc3604e26cdd6384f", + "sha256:d4dd3b381ff5a5907a3eb98f5f6d32c64d319a840278ceea1dcfcc65063856f3", + "sha256:e8e5ecbd4da4157889fce8ba49da74764dd86c891410bfd6b24969fa46edda51", + "sha256:eb2fc0ec241bf5e5ef56c8fbec4a2634d631e4c4f616a59b567947a0f35ad83c", + "sha256:edbe083c299835de7e02c8aa0885cb904a75087d35e7bab75ebe5ed336e8c3e2", + "sha256:ff64fd720def623bf64d8776f8d0deada1cc1bf1ec3c1f9d6f5bb5bd098d034f" ], "index": "pypi", - "version": "==3.18.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==3.19.0" }, "pydantic": { "hashes": [ - "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375", - "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277", - "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d", - "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4", - "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca", - "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c", - "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01", - "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18", - "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68", - "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887", - "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459", - "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4", - "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5", - "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e", - "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1", - "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33", - "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a", - "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56", - "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108", - "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2", - "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4", - "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878", - "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0", - "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e", - "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6", - "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f", - "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800", - "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea", - "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f", - "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b", - "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1", - "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd", - "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319", - "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab", - "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85", - "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f" + "sha256:94f336138093a5d7f426aac732dcfe7ab4eb4da243c88f891d65deb4a2556ee7", + "sha256:bc3ddf669d234f4220e6e1c4d96b061abe0998185a8d7855c0126782b7abc8c1" ], "markers": "python_version >= '3.7'", - "version": "==1.10.8" + "version": "==2.4.2" + }, + "pydantic-core": { + "hashes": [ + "sha256:042462d8d6ba707fd3ce9649e7bf268633a41018d6a998fb5fbacb7e928a183e", + "sha256:0523aeb76e03f753b58be33b26540880bac5aa54422e4462404c432230543f33", + "sha256:05560ab976012bf40f25d5225a58bfa649bb897b87192a36c6fef1ab132540d7", + "sha256:0675ba5d22de54d07bccde38997e780044dcfa9a71aac9fd7d4d7a1d2e3e65f7", + "sha256:073d4a470b195d2b2245d0343569aac7e979d3a0dcce6c7d2af6d8a920ad0bea", + "sha256:07ec6d7d929ae9c68f716195ce15e745b3e8fa122fc67698ac6498d802ed0fa4", + "sha256:0880e239827b4b5b3e2ce05e6b766a7414e5f5aedc4523be6b68cfbc7f61c5d0", + "sha256:0c27f38dc4fbf07b358b2bc90edf35e82d1703e22ff2efa4af4ad5de1b3833e7", + "sha256:0d8a8adef23d86d8eceed3e32e9cca8879c7481c183f84ed1a8edc7df073af94", + "sha256:0e2a35baa428181cb2270a15864ec6286822d3576f2ed0f4cd7f0c1708472aff", + "sha256:0f8682dbdd2f67f8e1edddcbffcc29f60a6182b4901c367fc8c1c40d30bb0a82", + "sha256:0fa467fd300a6f046bdb248d40cd015b21b7576c168a6bb20aa22e595c8ffcdd", + "sha256:128552af70a64660f21cb0eb4876cbdadf1a1f9d5de820fed6421fa8de07c893", + "sha256:1396e81b83516b9d5c9e26a924fa69164156c148c717131f54f586485ac3c15e", + "sha256:149b8a07712f45b332faee1a2258d8ef1fb4a36f88c0c17cb687f205c5dc6e7d", + "sha256:14ac492c686defc8e6133e3a2d9eaf5261b3df26b8ae97450c1647286750b901", + "sha256:14cfbb00959259e15d684505263d5a21732b31248a5dd4941f73a3be233865b9", + "sha256:14e09ff0b8fe6e46b93d36a878f6e4a3a98ba5303c76bb8e716f4878a3bee92c", + "sha256:154ea7c52e32dce13065dbb20a4a6f0cc012b4f667ac90d648d36b12007fa9f7", + "sha256:15d6bca84ffc966cc9976b09a18cf9543ed4d4ecbd97e7086f9ce9327ea48891", + "sha256:1d40f55222b233e98e3921df7811c27567f0e1a4411b93d4c5c0f4ce131bc42f", + "sha256:25bd966103890ccfa028841a8f30cebcf5875eeac8c4bde4fe221364c92f0c9a", + "sha256:2cf5bb4dd67f20f3bbc1209ef572a259027c49e5ff694fa56bed62959b41e1f9", + "sha256:2e0e2959ef5d5b8dc9ef21e1a305a21a36e254e6a34432d00c72a92fdc5ecda5", + "sha256:320f14bd4542a04ab23747ff2c8a778bde727158b606e2661349557f0770711e", + "sha256:3625578b6010c65964d177626fde80cf60d7f2e297d56b925cb5cdeda6e9925a", + "sha256:39215d809470f4c8d1881758575b2abfb80174a9e8daf8f33b1d4379357e417c", + "sha256:3f0ac9fb8608dbc6eaf17956bf623c9119b4db7dbb511650910a82e261e6600f", + "sha256:417243bf599ba1f1fef2bb8c543ceb918676954734e2dcb82bf162ae9d7bd514", + "sha256:420a692b547736a8d8703c39ea935ab5d8f0d2573f8f123b0a294e49a73f214b", + "sha256:443fed67d33aa85357464f297e3d26e570267d1af6fef1c21ca50921d2976302", + "sha256:48525933fea744a3e7464c19bfede85df4aba79ce90c60b94d8b6e1eddd67096", + "sha256:485a91abe3a07c3a8d1e082ba29254eea3e2bb13cbbd4351ea4e5a21912cc9b0", + "sha256:4a5be350f922430997f240d25f8219f93b0c81e15f7b30b868b2fddfc2d05f27", + "sha256:4d966c47f9dd73c2d32a809d2be529112d509321c5310ebf54076812e6ecd884", + "sha256:524ff0ca3baea164d6d93a32c58ac79eca9f6cf713586fdc0adb66a8cdeab96a", + "sha256:53df009d1e1ba40f696f8995683e067e3967101d4bb4ea6f667931b7d4a01357", + "sha256:5994985da903d0b8a08e4935c46ed8daf5be1cf217489e673910951dc533d430", + "sha256:5cabb9710f09d5d2e9e2748c3e3e20d991a4c5f96ed8f1132518f54ab2967221", + "sha256:5fdb39f67c779b183b0c853cd6b45f7db84b84e0571b3ef1c89cdb1dfc367325", + "sha256:600d04a7b342363058b9190d4e929a8e2e715c5682a70cc37d5ded1e0dd370b4", + "sha256:631cb7415225954fdcc2a024119101946793e5923f6c4d73a5914d27eb3d3a05", + "sha256:63974d168b6233b4ed6a0046296803cb13c56637a7b8106564ab575926572a55", + "sha256:64322bfa13e44c6c30c518729ef08fda6026b96d5c0be724b3c4ae4da939f875", + "sha256:655f8f4c8d6a5963c9a0687793da37b9b681d9ad06f29438a3b2326d4e6b7970", + "sha256:6835451b57c1b467b95ffb03a38bb75b52fb4dc2762bb1d9dbed8de31ea7d0fc", + "sha256:6db2eb9654a85ada248afa5a6db5ff1cf0f7b16043a6b070adc4a5be68c716d6", + "sha256:7c4d1894fe112b0864c1fa75dffa045720a194b227bed12f4be7f6045b25209f", + "sha256:7eb037106f5c6b3b0b864ad226b0b7ab58157124161d48e4b30c4a43fef8bc4b", + "sha256:8282bab177a9a3081fd3d0a0175a07a1e2bfb7fcbbd949519ea0980f8a07144d", + "sha256:82f55187a5bebae7d81d35b1e9aaea5e169d44819789837cdd4720d768c55d15", + "sha256:8572cadbf4cfa95fb4187775b5ade2eaa93511f07947b38f4cd67cf10783b118", + "sha256:8cdbbd92154db2fec4ec973d45c565e767ddc20aa6dbaf50142676484cbff8ee", + "sha256:8f6e6aed5818c264412ac0598b581a002a9f050cb2637a84979859e70197aa9e", + "sha256:92f675fefa977625105708492850bcbc1182bfc3e997f8eecb866d1927c98ae6", + "sha256:962ed72424bf1f72334e2f1e61b68f16c0e596f024ca7ac5daf229f7c26e4208", + "sha256:9badf8d45171d92387410b04639d73811b785b5161ecadabf056ea14d62d4ede", + "sha256:9c120c9ce3b163b985a3b966bb701114beb1da4b0468b9b236fc754783d85aa3", + "sha256:9f6f3e2598604956480f6c8aa24a3384dbf6509fe995d97f6ca6103bb8c2534e", + "sha256:a1254357f7e4c82e77c348dabf2d55f1d14d19d91ff025004775e70a6ef40ada", + "sha256:a1392e0638af203cee360495fd2cfdd6054711f2db5175b6e9c3c461b76f5175", + "sha256:a1c311fd06ab3b10805abb72109f01a134019739bd3286b8ae1bc2fc4e50c07a", + "sha256:a5cb87bdc2e5f620693148b5f8f842d293cae46c5f15a1b1bf7ceeed324a740c", + "sha256:a7a7902bf75779bc12ccfc508bfb7a4c47063f748ea3de87135d433a4cca7a2f", + "sha256:aad7bd686363d1ce4ee930ad39f14e1673248373f4a9d74d2b9554f06199fb58", + "sha256:aafdb89fdeb5fe165043896817eccd6434aee124d5ee9b354f92cd574ba5e78f", + "sha256:ae8a8843b11dc0b03b57b52793e391f0122e740de3df1474814c700d2622950a", + "sha256:b00bc4619f60c853556b35f83731bd817f989cba3e97dc792bb8c97941b8053a", + "sha256:b1f22a9ab44de5f082216270552aa54259db20189e68fc12484873d926426921", + "sha256:b3c01c2fb081fced3bbb3da78510693dc7121bb893a1f0f5f4b48013201f362e", + "sha256:b3dcd587b69bbf54fc04ca157c2323b8911033e827fffaecf0cafa5a892a0904", + "sha256:b4a6db486ac8e99ae696e09efc8b2b9fea67b63c8f88ba7a1a16c24a057a0776", + "sha256:bec7dd208a4182e99c5b6c501ce0b1f49de2802448d4056091f8e630b28e9a52", + "sha256:c0877239307b7e69d025b73774e88e86ce82f6ba6adf98f41069d5b0b78bd1bf", + "sha256:caa48fc31fc7243e50188197b5f0c4228956f97b954f76da157aae7f67269ae8", + "sha256:cfe1090245c078720d250d19cb05d67e21a9cd7c257698ef139bc41cf6c27b4f", + "sha256:d43002441932f9a9ea5d6f9efaa2e21458221a3a4b417a14027a1d530201ef1b", + "sha256:d64728ee14e667ba27c66314b7d880b8eeb050e58ffc5fec3b7a109f8cddbd63", + "sha256:d6495008733c7521a89422d7a68efa0a0122c99a5861f06020ef5b1f51f9ba7c", + "sha256:d8f1ebca515a03e5654f88411420fea6380fc841d1bea08effb28184e3d4899f", + "sha256:d99277877daf2efe074eae6338453a4ed54a2d93fb4678ddfe1209a0c93a2468", + "sha256:da01bec0a26befab4898ed83b362993c844b9a607a86add78604186297eb047e", + "sha256:db9a28c063c7c00844ae42a80203eb6d2d6bbb97070cfa00194dff40e6f545ab", + "sha256:dda81e5ec82485155a19d9624cfcca9be88a405e2857354e5b089c2a982144b2", + "sha256:e357571bb0efd65fd55f18db0a2fb0ed89d0bb1d41d906b138f088933ae618bb", + "sha256:e544246b859f17373bed915182ab841b80849ed9cf23f1f07b73b7c58baee5fb", + "sha256:e562617a45b5a9da5be4abe72b971d4f00bf8555eb29bb91ec2ef2be348cd132", + "sha256:e570ffeb2170e116a5b17e83f19911020ac79d19c96f320cbfa1fa96b470185b", + "sha256:e6f31a17acede6a8cd1ae2d123ce04d8cca74056c9d456075f4f6f85de055607", + "sha256:e9121b4009339b0f751955baf4543a0bfd6bc3f8188f8056b1a25a2d45099934", + "sha256:ebedb45b9feb7258fac0a268a3f6bec0a2ea4d9558f3d6f813f02ff3a6dc6698", + "sha256:ecaac27da855b8d73f92123e5f03612b04c5632fd0a476e469dfc47cd37d6b2e", + "sha256:ecdbde46235f3d560b18be0cb706c8e8ad1b965e5c13bbba7450c86064e96561", + "sha256:ed550ed05540c03f0e69e6d74ad58d026de61b9eaebebbaaf8873e585cbb18de", + "sha256:eeb3d3d6b399ffe55f9a04e09e635554012f1980696d6b0aca3e6cf42a17a03b", + "sha256:ef337945bbd76cce390d1b2496ccf9f90b1c1242a3a7bc242ca4a9fc5993427a", + "sha256:f1365e032a477c1430cfe0cf2856679529a2331426f8081172c4a74186f1d595", + "sha256:f23b55eb5464468f9e0e9a9935ce3ed2a870608d5f534025cd5536bca25b1402", + "sha256:f2e9072d71c1f6cfc79a36d4484c82823c560e6f5599c43c1ca6b5cdbd54f881", + "sha256:f323306d0556351735b54acbf82904fe30a27b6a7147153cbe6e19aaaa2aa429", + "sha256:f36a3489d9e28fe4b67be9992a23029c3cec0babc3bd9afb39f49844a8c721c5", + "sha256:f64f82cc3443149292b32387086d02a6c7fb39b8781563e0ca7b8d7d9cf72bd7", + "sha256:f6defd966ca3b187ec6c366604e9296f585021d922e666b99c47e78738b5666c", + "sha256:f7c2b8eb9fc872e68b46eeaf835e86bccc3a58ba57d0eedc109cbb14177be531", + "sha256:fa7db7558607afeccb33c0e4bf1c9a9a835e26599e76af6fe2fcea45904083a6", + "sha256:fcb83175cc4936a5425dde3356f079ae03c0802bbdf8ff82c035f8a54b333521" + ], + "markers": "python_version >= '3.7'", + "version": "==2.10.1" + }, + "pydantic-settings": { + "hashes": [ + "sha256:962dc3672495aad6ae96a4390fac7e593591e144625e5112d359f8f67fb75945", + "sha256:ddd907b066622bd67603b75e2ff791875540dc485b7307c4fffc015719da8625" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.3" }, "pyjwkest": { "hashes": [ @@ -762,23 +1025,24 @@ "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==2.31.0" }, "s3transfer": { "hashes": [ - "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", - "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" + "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", + "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" ], "markers": "python_version >= '3.7'", - "version": "==0.6.1" + "version": "==0.7.0" }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87", + "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a" ], - "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "markers": "python_version >= '3.8'", + "version": "==68.2.2" }, "six": { "hashes": [ @@ -798,27 +1062,79 @@ }, "typing-extensions": { "hashes": [ - "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", - "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], "index": "pypi", - "version": "==4.6.3" + "markers": "python_version >= '3.8'", + "version": "==4.8.0" }, "urllib3": { "hashes": [ - "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", - "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.16" + "markers": "python_version >= '3.7'", + "version": "==2.0.7" }, "whitenoise": { "hashes": [ - "sha256:599dc6ca57e48929dfeffb2e8e187879bfe2aed0d49ca419577005b7f2cc930b", - "sha256:a02d6660ad161ff17e3042653c8e3f5ecbb2a2481a006bde125b9efb9a30113a" + "sha256:8998f7370973447fac1e8ef6e8ded2c5209a7b1f67c1012866dbcd09681c3251", + "sha256:b1f9db9bf67dc183484d760b99f4080185633136a273a03f6436034a41064146" ], "index": "pypi", - "version": "==6.4.0" + "markers": "python_version >= '3.8'", + "version": "==6.6.0" + }, + "zope.event": { + "hashes": [ + "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26", + "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd" + ], + "markers": "python_version >= '3.7'", + "version": "==5.0" + }, + "zope.interface": { + "hashes": [ + "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff", + "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c", + "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac", + "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f", + "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d", + "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309", + "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736", + "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179", + "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb", + "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941", + "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d", + "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92", + "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b", + "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41", + "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f", + "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3", + "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d", + "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8", + "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3", + "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1", + "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1", + "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40", + "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d", + "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1", + "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605", + "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7", + "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd", + "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43", + "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0", + "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b", + "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379", + "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a", + "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83", + "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56", + "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9", + "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de" + ], + "markers": "python_version >= '3.7'", + "version": "==6.1" } }, "develop": { @@ -836,6 +1152,7 @@ "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e" ], "index": "pypi", + "markers": "python_version >= '3.7'", "version": "==1.7.5" }, "beautifulsoup4": { @@ -848,50 +1165,45 @@ }, "black": { "hashes": [ - "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5", - "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915", - "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326", - "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940", - "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b", - "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30", - "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c", - "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c", - "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab", - "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27", - "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2", - "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961", - "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9", - "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb", - "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70", - "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331", - "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2", - "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266", - "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d", - "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6", - "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b", - "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925", - "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8", - "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4", - "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3" + "sha256:0e232f24a337fed7a82c1185ae46c56c4a6167fb0fe37411b43e876892c76699", + "sha256:30b78ac9b54cf87bcb9910ee3d499d2bc893afd52495066c49d9ee6b21eee06e", + "sha256:31946ec6f9c54ed7ba431c38bc81d758970dd734b96b8e8c2b17a367d7908171", + "sha256:31b9f87b277a68d0e99d2905edae08807c007973eaa609da5f0c62def6b7c0bd", + "sha256:47c4510f70ec2e8f9135ba490811c071419c115e46f143e4dce2ac45afdcf4c9", + "sha256:481167c60cd3e6b1cb8ef2aac0f76165843a374346aeeaa9d86765fe0dd0318b", + "sha256:6901631b937acbee93c75537e74f69463adaf34379a04eef32425b88aca88a23", + "sha256:76baba9281e5e5b230c9b7f83a96daf67a95e919c2dfc240d9e6295eab7b9204", + "sha256:7fb5fc36bb65160df21498d5a3dd330af8b6401be3f25af60c6ebfe23753f747", + "sha256:960c21555be135c4b37b7018d63d6248bdae8514e5c55b71e994ad37407f45b8", + "sha256:a3c2ddb35f71976a4cfeca558848c2f2f89abc86b06e8dd89b5a65c1e6c0f22a", + "sha256:c870bee76ad5f7a5ea7bd01dc646028d05568d33b0b09b7ecfc8ec0da3f3f39c", + "sha256:d3d9129ce05b0829730323bdcb00f928a448a124af5acf90aa94d9aba6969604", + "sha256:db451a3363b1e765c172c3fd86213a4ce63fb8524c938ebd82919bf2a6e28c6a", + "sha256:e223b731a0e025f8ef427dd79d8cd69c167da807f5710add30cdf131f13dd62e", + "sha256:f20ff03f3fdd2fd4460b4f631663813e57dc277e37fb216463f3b907aa5a9bdd", + "sha256:f74892b4b836e5162aa0452393112a574dac85e13902c57dfbaaf388e4eda37c", + "sha256:f8dc7d50d94063cdfd13c82368afd8588bac4ce360e4224ac399e769d6704e98" ], "index": "pypi", - "version": "==23.3.0" + "markers": "python_version >= '3.8'", + "version": "==23.10.0" }, "blinker": { "hashes": [ - "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213", - "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0" + "sha256:152090d27c1c5c722ee7e48504b02d76502811ce02e1523553b4cf8c8b3d3a8d", + "sha256:296320d6c28b006eb5e32d4712202dbcdcbf5dc482da298c2f44881c43884aaa" ], "markers": "python_version >= '3.7'", - "version": "==1.6.2" + "version": "==1.6.3" }, "boto3": { "hashes": [ - "sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85", - "sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0" + "sha256:38658585791f47cca3fc6aad03838de0136778b533e8c71c6a9590aedc60fbde", + "sha256:a8228522c7db33694c0746dec8b48c05473671626359dd62ab6829eb7871eddc" ], "index": "pypi", - "version": "==1.26.145" + "markers": "python_version >= '3.7'", + "version": "==1.28.66" }, "boto3-mocking": { "hashes": [ @@ -899,55 +1211,59 @@ "sha256:d0273366d3cb86c5bf3d6f31d1c6e40c11b714d49b5f2d5125234d0d59aa9378" ], "index": "pypi", + "markers": "python_version >= '3.6'", "version": "==0.1.1" }, "boto3-stubs": { "hashes": [ - "sha256:9413cb395c803d5b85e9ec7b16fba855a613ecd78b2e0011e2f6b62cf0b4fc1e", - "sha256:be2007f92138781288c7a22eba30b7d60742466fc28edd04637b31fabee854a5" + "sha256:06e696ce8529f899a2ba388d6604ca8ed8ba367dd53898e27b4ce49e8b3fd2aa", + "sha256:b85689c50a6768bb0fcb85e06394d7898b330b82f34cec26c36d912e6a41280d" ], "index": "pypi", - "version": "==1.26.145" + "markers": "python_version >= '3.7'", + "version": "==1.28.66" }, "botocore": { "hashes": [ - "sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a", - "sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45" + "sha256:70e94a5f9bd46b26b63a41fb441ad35f2ae8862ad9d90765b6fa31ccc02c0a19", + "sha256:8d161a97a25eb381721b4b7251d5126ef4ec57e452114250b3e51ba5e4ff45a4" ], "markers": "python_version >= '3.7'", - "version": "==1.29.145" + "version": "==1.31.66" }, "botocore-stubs": { "hashes": [ - "sha256:80ffab72ad428d20cb1cf538ee55fcd94f7d81315b77d84fec99e218c3974e8b", - "sha256:928c58a434dd83bef956e3b5bb1e96278fff5eee9f8b8ab08d916cef1e9a2014" + "sha256:5ea5f4af18ee654cf510d69b3bc7c1ed3236b50fcd4e3eb98c11d28033ff05c3", + "sha256:b65fa3ff36e8a70518a143b5025559918a68d7a20b85c88f8a1f067f6620a205" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==1.29.145" + "version": "==1.31.66" }, "click": { "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" ], "markers": "python_version >= '3.7'", - "version": "==8.1.3" + "version": "==8.1.7" }, "django": { "hashes": [ - "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee", - "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c" + "sha256:08f41f468b63335aea0d904c5729e0250300f6a1907bf293a65499496cdbc68f", + "sha256:a64d2487cdb00ad7461434320ccc38e60af9c404773a2f95ab0093b4453a3215" ], "index": "pypi", - "version": "==4.2.1" + "markers": "python_version >= '3.8'", + "version": "==4.2.6" }, "django-debug-toolbar": { "hashes": [ - "sha256:a0b532ef5d52544fd745d1dcfc0557fa75f6f0d1962a8298bd568427ef2fa436", - "sha256:f57882e335593cb8e74c2bda9f1116bbb9ca8fc0d81b50a75ace0f83de5173c7" + "sha256:af99128c06e8e794479e65ab62cc6c7d1e74e1c19beb44dcbf9bad7a9c017327", + "sha256:bc7fdaafafcdedefcc67a4a5ad9dac96efd6e41db15bc74d402a54a2ba4854dc" ], "index": "pypi", - "version": "==4.1.0" + "markers": "python_version >= '3.8'", + "version": "==4.2.0" }, "django-model2puml": { "hashes": [ @@ -958,35 +1274,37 @@ }, "django-stubs": { "hashes": [ - "sha256:66477bdba25407623f4079205e58f3c7265a4f0d8f7c9f540a6edc16f8883a5b", - "sha256:8c15d5f7b05926805cfb25f2bfbf3509c37792fbd8aec5aedea358b85d8bccd5" + "sha256:5a23cf622f1426a0b0c48bd6e2ef709a66275d72073baf6fdf5ac36fc4cce736", + "sha256:706b2456bd0e56c468dfd8f27b0e7dde001c5c7cd3010d67fcbda9d95467e050" ], "index": "pypi", - "version": "==4.2.1" + "markers": "python_version >= '3.8'", + "version": "==4.2.5" }, "django-stubs-ext": { "hashes": [ - "sha256:2696d6f7d8538341b060cffa9565c72ea797e866687e040b86d29cad8799e5fe", - "sha256:4b6b63e49f4ba30d93ec46f87507648c99c9de6911e651ad69db7084fd5b2f4e" + "sha256:8c4d1fb5f68419b3b2474c659681a189803e27d6a5e5abf5aa0da57601b58633", + "sha256:921cd7ae4614e74c234bc0fe86ee75537d163addfe1fc6f134bf03e29d86c01e" ], "markers": "python_version >= '3.8'", - "version": "==4.2.1" + "version": "==4.2.5" }, "django-webtest": { "hashes": [ - "sha256:c8c32041791cdae468e443097c432c67cf17cad339e1ab88b01a6c4841ee4c74", - "sha256:ef075e98b38fe3836dc533c2924d3e37c6bb3483008c40567115518a0303b1af" + "sha256:9597d26ced599bc5d4d9366bb451469fc9707b4779f79543cdf401ae6c5aeb35", + "sha256:e29baf8337e7fe7db41ce63ca6661f7b5c77fe56f506f48b305e09313f5475b4" ], "index": "pypi", - "version": "==1.9.10" + "version": "==1.9.11" }, "flake8": { "hashes": [ - "sha256:3833794e27ff64ea4e9cf5d410082a8b97ff1a06c16aa3d2027339cd0f1195c7", - "sha256:c61007e76655af75e6785a931f452915b371dc48f56efd765247c8fe68f2b181" + "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23", + "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5" ], "index": "pypi", - "version": "==6.0.0" + "markers": "python_full_version >= '3.8.1'", + "version": "==6.1.0" }, "gitdb": { "hashes": [ @@ -998,11 +1316,11 @@ }, "gitpython": { "hashes": [ - "sha256:8ce3bcf69adfdf7c7d503e78fd3b1c492af782d58893b650adb2ac8912ddd573", - "sha256:f04893614f6aa713a60cbbe1e6a97403ef633103cdd0ef5eb6efe0deb98dbe8d" + "sha256:22b126e9ffb671fdd0c129796343a02bf67bf2994b35449ffc9321aa755e18a4", + "sha256:cf14627d5a8049ffbf49915732e5eddbe8134c3bdb9d476e6182b676fc573f8a" ], "markers": "python_version >= '3.7'", - "version": "==3.1.31" + "version": "==3.1.40" }, "jmespath": { "hashes": [ @@ -1014,11 +1332,11 @@ }, "markdown-it-py": { "hashes": [ - "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30", - "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1" + "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", + "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" ], - "markers": "python_version >= '3.7'", - "version": "==2.2.0" + "markers": "python_version >= '3.8'", + "version": "==3.0.0" }, "mccabe": { "hashes": [ @@ -1038,35 +1356,37 @@ }, "mypy": { "hashes": [ - "sha256:1c4c42c60a8103ead4c1c060ac3cdd3ff01e18fddce6f1016e08939647a0e703", - "sha256:44797d031a41516fcf5cbfa652265bb994e53e51994c1bd649ffcd0c3a7eccbf", - "sha256:473117e310febe632ddf10e745a355714e771ffe534f06db40702775056614c4", - "sha256:4c99c3ecf223cf2952638da9cd82793d8f3c0c5fa8b6ae2b2d9ed1e1ff51ba85", - "sha256:550a8b3a19bb6589679a7c3c31f64312e7ff482a816c96e0cecec9ad3a7564dd", - "sha256:658fe7b674769a0770d4b26cb4d6f005e88a442fe82446f020be8e5f5efb2fae", - "sha256:6e33bb8b2613614a33dff70565f4c803f889ebd2f859466e42b46e1df76018dd", - "sha256:6e42d29e324cdda61daaec2336c42512e59c7c375340bd202efa1fe0f7b8f8ca", - "sha256:74bc9b6e0e79808bf8678d7678b2ae3736ea72d56eede3820bd3849823e7f305", - "sha256:76ec771e2342f1b558c36d49900dfe81d140361dd0d2df6cd71b3db1be155409", - "sha256:7d23370d2a6b7a71dc65d1266f9a34e4cde9e8e21511322415db4b26f46f6b8c", - "sha256:87df44954c31d86df96c8bd6e80dfcd773473e877ac6176a8e29898bfb3501cb", - "sha256:8c5979d0deb27e0f4479bee18ea0f83732a893e81b78e62e2dda3e7e518c92ee", - "sha256:95d8d31a7713510685b05fbb18d6ac287a56c8f6554d88c19e73f724a445448a", - "sha256:a22435632710a4fcf8acf86cbd0d69f68ac389a3892cb23fbad176d1cddaf228", - "sha256:a8763e72d5d9574d45ce5881962bc8e9046bf7b375b0abf031f3e6811732a897", - "sha256:c1eb485cea53f4f5284e5baf92902cd0088b24984f4209e25981cc359d64448d", - "sha256:c5d2cc54175bab47011b09688b418db71403aefad07cbcd62d44010543fc143f", - "sha256:cbc07246253b9e3d7d74c9ff948cd0fd7a71afcc2b77c7f0a59c26e9395cb152", - "sha256:d0b6c62206e04061e27009481cb0ec966f7d6172b5b936f3ead3d74f29fe3dcf", - "sha256:ddae0f39ca146972ff6bb4399f3b2943884a774b8771ea0a8f50e971f5ea5ba8", - "sha256:e1f4d16e296f5135624b34e8fb741eb0eadedca90862405b1f1fde2040b9bd11", - "sha256:e86c2c6852f62f8f2b24cb7a613ebe8e0c7dc1402c61d36a609174f63e0ff017", - "sha256:ebc95f8386314272bbc817026f8ce8f4f0d2ef7ae44f947c4664efac9adec929", - "sha256:f9dca1e257d4cc129517779226753dbefb4f2266c4eaad610fc15c6a7e14283e", - "sha256:faff86aa10c1aa4a10e1a301de160f3d8fc8703b88c7e98de46b531ff1276a9a" + "sha256:19f905bcfd9e167159b3d63ecd8cb5e696151c3e59a1742e79bc3bcb540c42c7", + "sha256:21a1ad938fee7d2d96ca666c77b7c494c3c5bd88dff792220e1afbebb2925b5e", + "sha256:40b1844d2e8b232ed92e50a4bd11c48d2daa351f9deee6c194b83bf03e418b0c", + "sha256:41697773aa0bf53ff917aa077e2cde7aa50254f28750f9b88884acea38a16169", + "sha256:49ae115da099dcc0922a7a895c1eec82c1518109ea5c162ed50e3b3594c71208", + "sha256:4c46b51de523817a0045b150ed11b56f9fff55f12b9edd0f3ed35b15a2809de0", + "sha256:4cbe68ef919c28ea561165206a2dcb68591c50f3bcf777932323bc208d949cf1", + "sha256:4d01c00d09a0be62a4ca3f933e315455bde83f37f892ba4b08ce92f3cf44bcc1", + "sha256:59a0d7d24dfb26729e0a068639a6ce3500e31d6655df8557156c51c1cb874ce7", + "sha256:68351911e85145f582b5aa6cd9ad666c8958bcae897a1bfda8f4940472463c45", + "sha256:7274b0c57737bd3476d2229c6389b2ec9eefeb090bbaf77777e9d6b1b5a9d143", + "sha256:81af8adaa5e3099469e7623436881eff6b3b06db5ef75e6f5b6d4871263547e5", + "sha256:82e469518d3e9a321912955cc702d418773a2fd1e91c651280a1bda10622f02f", + "sha256:8b27958f8c76bed8edaa63da0739d76e4e9ad4ed325c814f9b3851425582a3cd", + "sha256:8c223fa57cb154c7eab5156856c231c3f5eace1e0bed9b32a24696b7ba3c3245", + "sha256:8f57e6b6927a49550da3d122f0cb983d400f843a8a82e65b3b380d3d7259468f", + "sha256:925cd6a3b7b55dfba252b7c4561892311c5358c6b5a601847015a1ad4eb7d332", + "sha256:a43ef1c8ddfdb9575691720b6352761f3f53d85f1b57d7745701041053deff30", + "sha256:a8032e00ce71c3ceb93eeba63963b864bf635a18f6c0c12da6c13c450eedb183", + "sha256:b96ae2c1279d1065413965c607712006205a9ac541895004a1e0d4f281f2ff9f", + "sha256:bb8ccb4724f7d8601938571bf3f24da0da791fe2db7be3d9e79849cb64e0ae85", + "sha256:bbaf4662e498c8c2e352da5f5bca5ab29d378895fa2d980630656178bd607c46", + "sha256:cfd13d47b29ed3bbaafaff7d8b21e90d827631afda134836962011acb5904b71", + "sha256:d4473c22cc296425bbbce7e9429588e76e05bc7342da359d6520b6427bf76660", + "sha256:d8fbb68711905f8912e5af474ca8b78d077447d8f3918997fecbf26943ff3cbb", + "sha256:e5012e5cc2ac628177eaac0e83d622b2dd499e28253d4107a08ecc59ede3fc2c", + "sha256:eb4f18589d196a4cbe5290b435d135dee96567e07c2b2d43b5c4621b6501531a" ], "index": "pypi", - "version": "==1.3.0" + "markers": "python_version >= '3.8'", + "version": "==1.6.1" }, "mypy-extensions": { "hashes": [ @@ -1086,19 +1406,19 @@ }, "packaging": { "hashes": [ - "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", - "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f" + "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5", + "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7" ], "markers": "python_version >= '3.7'", - "version": "==23.1" + "version": "==23.2" }, "pathspec": { "hashes": [ - "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687", - "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293" + "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20", + "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3" ], "markers": "python_version >= '3.7'", - "version": "==0.11.1" + "version": "==0.11.2" }, "pbr": { "hashes": [ @@ -1110,35 +1430,35 @@ }, "platformdirs": { "hashes": [ - "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f", - "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5" + "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3", + "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e" ], "markers": "python_version >= '3.7'", - "version": "==3.5.1" + "version": "==3.11.0" }, "pycodestyle": { "hashes": [ - "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053", - "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610" + "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", + "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" ], - "markers": "python_version >= '3.6'", - "version": "==2.10.0" + "markers": "python_version >= '3.8'", + "version": "==2.11.1" }, "pyflakes": { "hashes": [ - "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf", - "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd" + "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774", + "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc" ], - "markers": "python_version >= '3.6'", - "version": "==3.0.1" + "markers": "python_version >= '3.8'", + "version": "==3.1.0" }, "pygments": { "hashes": [ - "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c", - "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1" + "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", + "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" ], "markers": "python_version >= '3.7'", - "version": "==2.15.1" + "version": "==2.16.1" }, "python-dateutil": { "hashes": [ @@ -1150,65 +1470,75 @@ }, "pyyaml": { "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", + "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", + "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", + "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", + "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", + "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", + "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", + "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", + "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", + "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", + "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", + "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", + "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", + "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", + "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", + "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", + "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", + "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", + "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", + "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", + "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", + "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", + "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", + "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", + "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", + "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", + "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", + "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", + "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", + "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", + "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", + "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", + "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", + "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", + "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", + "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", + "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", + "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", + "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", + "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", + "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", + "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", + "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", + "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", + "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", + "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", + "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", + "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", + "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", + "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" ], "markers": "python_version >= '3.6'", - "version": "==6.0" + "version": "==6.0.1" }, "rich": { "hashes": [ - "sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1", - "sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75" + "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245", + "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef" ], "markers": "python_full_version >= '3.7.0'", - "version": "==13.4.1" + "version": "==13.6.0" }, "s3transfer": { "hashes": [ - "sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346", - "sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9" + "sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a", + "sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e" ], "markers": "python_version >= '3.7'", - "version": "==0.6.1" + "version": "==0.7.0" }, "six": { "hashes": [ @@ -1220,19 +1550,19 @@ }, "smmap": { "hashes": [ - "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94", - "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936" + "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62", + "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da" ], - "markers": "python_version >= '3.6'", - "version": "==5.0.0" + "markers": "python_version >= '3.7'", + "version": "==5.0.1" }, "soupsieve": { "hashes": [ - "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8", - "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea" + "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690", + "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7" ], - "markers": "python_version >= '3.7'", - "version": "==2.4.1" + "markers": "python_version >= '3.8'", + "version": "==2.5" }, "sqlparse": { "hashes": [ @@ -1260,72 +1590,67 @@ }, "types-awscrt": { "hashes": [ - "sha256:50fe7610aa40550a23d79d6167b2b8536281f038f42846eda0e520d6e3e01787", - "sha256:763d8d543f145d51cd16ea407d079608b126941d24525e16cb1de31d949ff563" + "sha256:7b55f5a12ccd4407bc8f1e35c69bb40c931f8513ce1ad81a4527fce3989003fd", + "sha256:9a21caac4287c113dd52665707785c45bb1d3242b7a2b8aeb57c49e9e749a330" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.16.19" + "version": "==0.19.3" }, "types-cachetools": { "hashes": [ - "sha256:67fa46d51a650896770aee0ba80f0e61dc4a7d1373198eec1bc0622263eaa256", - "sha256:c0c5fa00199017d974c935bf043c467d5204e4f835141e489b48765b5ac1d960" + "sha256:595f0342d246c8ba534f5a762cf4c2f60ecb61e8002b8b2277fd5cf791d4e851", + "sha256:f7f8a25bfe306f2e6bc2ad0a2f949d9e72f2d91036d509c36d3810bf728bc6e1" ], "index": "pypi", - "version": "==5.3.0.5" + "version": "==5.3.0.6" }, "types-pytz": { "hashes": [ - "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3", - "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac" + "sha256:1999a123a3dc0e39a2ef6d19f3f8584211de9e6a77fe7a0259f04a524e90a5cf", + "sha256:cc23d0192cd49c8f6bba44ee0c81e4586a8f30204970fc0894d209a6b08dab9a" ], - "version": "==2023.3.0.0" + "version": "==2023.3.1.1" }, "types-pyyaml": { "hashes": [ - "sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f", - "sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97" + "sha256:334373d392fde0fdf95af5c3f1661885fa10c52167b14593eb856289e1855062", + "sha256:c05bc6c158facb0676674b7f11fe3960db4f389718e19e62bd2b84d6205cfd24" ], - "version": "==6.0.12.10" + "version": "==6.0.12.12" }, "types-requests": { "hashes": [ - "sha256:3de667cffa123ce698591de0ad7db034a5317457a596eb0b4944e5a9d9e8d1ac", - "sha256:afb06ef8f25ba83d59a1d424bd7a5a939082f94b94e90ab5e6116bd2559deaa3" + "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc", + "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92" ], "index": "pypi", - "version": "==2.31.0.1" + "markers": "python_version >= '3.7'", + "version": "==2.31.0.10" }, "types-s3transfer": { "hashes": [ - "sha256:6d1ac1dedac750d570428362acdf60fdd4f277b0788855c3894d3226756b2bfb", - "sha256:75ac1d7143d58c1e6af467cfd4a96c67ee058a3adf7c249d9309999e1f5f41e4" + "sha256:aca0f2486d0a3a5037cd5b8f3e20a4522a29579a8dd183281ff0aa1c4e2c8aa7", + "sha256:ae9ed9273465d9f43da8b96307383da410c6b59c3b2464c88d20b578768e97c6" ], "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==0.6.1" - }, - "types-urllib3": { - "hashes": [ - "sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5", - "sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c" - ], - "version": "==1.26.25.13" + "version": "==0.7.0" }, "typing-extensions": { "hashes": [ - "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", - "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" + "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0", + "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef" ], "index": "pypi", - "version": "==4.6.3" + "markers": "python_version >= '3.8'", + "version": "==4.8.0" }, "urllib3": { "hashes": [ - "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f", - "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14" + "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84", + "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.16" + "markers": "python_version >= '3.7'", + "version": "==2.0.7" }, "waitress": { "hashes": [ diff --git a/src/epplibwrapper/client.py b/src/epplibwrapper/client.py index 0234ef6c6..e4b7a5d53 100644 --- a/src/epplibwrapper/client.py +++ b/src/epplibwrapper/client.py @@ -1,7 +1,10 @@ """Provide a wrapper around epplib to handle authentication and errors.""" import logging + from time import sleep +from gevent import Timeout +from epplibwrapper.utility.pool_status import PoolStatus try: from epplib.client import Client @@ -16,6 +19,7 @@ from django.conf import settings from .cert import Cert, Key from .errors import LoginError, RegistryError from .socket import Socket +from .utility.pool import EPPConnectionPool logger = logging.getLogger(__name__) @@ -39,9 +43,8 @@ class EPPLibWrapper: ATTN: This should not be used directly. Use `Domain` from domain.py. """ - def __init__(self) -> None: + def __init__(self, start_connection_pool=True) -> None: """Initialize settings which will be used for all connections.""" - # prepare (but do not send) a Login command self._login = commands.Login( cl_id=settings.SECRET_REGISTRY_CL_ID, @@ -51,6 +54,7 @@ class EPPLibWrapper: "urn:ietf:params:xml:ns:contact-1.0", ], ) + # establish a client object with a TCP socket transport self._client = Client( SocketTransport( @@ -60,37 +64,77 @@ class EPPLibWrapper: password=settings.SECRET_REGISTRY_KEY_PASSPHRASE, ) ) - # prepare a context manager which will connect and login when invoked - # (it will also logout and disconnect when the context manager exits) - self._connect = Socket(self._client, self._login) + + self.pool_options = { + # Pool size + "size": settings.EPP_CONNECTION_POOL_SIZE, + # Which errors the pool should look out for. + # Avoid changing this unless necessary, + # it can and will break things. + "exc_classes": (TransportError,), + # Occasionally pings the registry to keep the connection alive. + # Value in seconds => (keepalive / size) + "keepalive": settings.POOL_KEEP_ALIVE, + } + + self._pool = None + + # Tracks the status of the pool + self.pool_status = PoolStatus() + + if start_connection_pool: + self.start_connection_pool() def _send(self, command): """Helper function used by `send`.""" + cmd_type = command.__class__.__name__ + + # Start a timeout to check if the pool is hanging + timeout = Timeout(settings.POOL_TIMEOUT) + timeout.start() + try: - cmd_type = command.__class__.__name__ - with self._connect as wire: - response = wire.send(command) + if not self.pool_status.connection_success: + raise LoginError( + "Couldn't connect to the registry after three attempts" + ) + with self._pool.get() as connection: + response = connection.send(command) + except Timeout as t: + # If more than one pool exists, + # multiple timeouts can be floating around. + # We need to be specific as to which we are targeting. + if t is timeout: + # Flag that the pool is frozen, + # then restart the pool. + self.pool_status.pool_hanging = True + self.start_connection_pool() except (ValueError, ParsingError) as err: - message = "%s failed to execute due to some syntax error." - logger.warning(message, cmd_type, exc_info=True) + message = f"{cmd_type} failed to execute due to some syntax error." + logger.error(f"{message} Error: {err}", exc_info=True) raise RegistryError(message) from err except TransportError as err: - message = "%s failed to execute due to a connection error." - logger.warning(message, cmd_type, exc_info=True) + message = f"{cmd_type} failed to execute due to a connection error." + logger.error(f"{message} Error: {err}", exc_info=True) raise RegistryError(message) from err except LoginError as err: - message = "%s failed to execute due to a registry login error." - logger.warning(message, cmd_type, exc_info=True) + # For linter due to it not liking this line length + text = "failed to execute due to a registry login error." + message = f"{cmd_type} {text}" + logger.error(f"{message} Error: {err}", exc_info=True) raise RegistryError(message) from err except Exception as err: - message = "%s failed to execute due to an unknown error." % err - logger.warning(message, cmd_type, exc_info=True) + message = f"{cmd_type} failed to execute due to an unknown error." + logger.error(f"{message} Error: {err}", exc_info=True) raise RegistryError(message) from err else: if response.code >= 2000: raise RegistryError(response.msg, code=response.code) else: return response + finally: + # Close the timeout no matter what happens + timeout.close() def send(self, command, *, cleaned=False): """Login, send the command, then close the connection. Tries 3 times.""" @@ -98,6 +142,23 @@ class EPPLibWrapper: if not cleaned: raise ValueError("Please sanitize user input before sending it.") + # Reopen the pool if its closed + # Only occurs when a login error is raised, after connection is successful + if not self.pool_status.pool_running: + # We want to reopen the connection pool, + # but we don't want the end user to wait while it opens. + # Raise syntax doesn't allow this, so we use a try/catch + # block. + try: + logger.error("Can't contact the Registry. Pool was not running.") + raise RegistryError("Can't contact the Registry. Pool was not running.") + except RegistryError as err: + raise err + finally: + # Code execution will halt after here. + # The end user will need to recall .send. + self.start_connection_pool() + counter = 0 # we'll try 3 times while True: try: @@ -109,11 +170,73 @@ class EPPLibWrapper: else: # don't try again raise err + def get_pool(self): + """Get the current pool instance""" + return self._pool + + def _create_pool(self, client, login, options): + """Creates and returns new pool instance""" + return EPPConnectionPool(client, login, options) + + def start_connection_pool(self, restart_pool_if_exists=True): + """Starts a connection pool for the registry. + + restart_pool_if_exists -> bool: + If an instance of the pool already exists, + then then that instance will be killed first. + It is generally recommended to keep this enabled. + """ + # Since we reuse the same creds for each pool, we can test on + # one socket, and if successful, then we know we can connect. + if not self._test_registry_connection_success(): + logger.warning("Cannot contact the Registry") + self.pool_status.connection_success = False + else: + self.pool_status.connection_success = True + + # If this function is reinvoked, then ensure + # that we don't have duplicate data sitting around. + if self._pool is not None and restart_pool_if_exists: + logger.info("Connection pool restarting...") + self.kill_pool() + + self._pool = self._create_pool(self._client, self._login, self.pool_options) + + self.pool_status.pool_running = True + self.pool_status.pool_hanging = False + + logger.info("Connection pool started") + + def kill_pool(self): + """Kills the existing pool. Use this instead + of self._pool = None, as that doesn't clear + gevent instances.""" + if self._pool is not None: + self._pool.kill_all_connections() + self._pool = None + self.pool_status.pool_running = False + return None + logger.info("kill_pool() was invoked but there was no pool to delete") + + def _test_registry_connection_success(self): + """Check that determines if our login + credentials are valid, and/or if the Registrar + can be contacted + """ + socket = Socket(self._client, self._login) + can_login = False + + # Something went wrong if this doesn't exist + if hasattr(socket, "test_connection_success"): + can_login = socket.test_connection_success() + + return can_login + try: # Initialize epplib CLIENT = EPPLibWrapper() - logger.debug("registry client initialized") + logger.info("registry client initialized") except Exception: CLIENT = None # type: ignore logger.warning( diff --git a/src/epplibwrapper/socket.py b/src/epplibwrapper/socket.py index 5c9acce79..6040f6682 100644 --- a/src/epplibwrapper/socket.py +++ b/src/epplibwrapper/socket.py @@ -1,7 +1,9 @@ import logging +from time import sleep try: from epplib import commands + from epplib.client import Client except ImportError: pass @@ -14,24 +16,84 @@ logger = logging.getLogger(__name__) class Socket: """Context manager which establishes a TCP connection with registry.""" - def __init__(self, client, login) -> None: + def __init__(self, client: Client, login: commands.Login) -> None: """Save the epplib client and login details.""" self.client = client self.login = login def __enter__(self): + """Runs connect(), which opens a connection with EPPLib.""" + self.connect() + + def __exit__(self, *args, **kwargs): + """Runs disconnect(), which closes a connection with EPPLib.""" + self.disconnect() + + def connect(self): """Use epplib to connect.""" self.client.connect() response = self.client.send(self.login) - if response.code >= 2000: + if self.is_login_error(response.code): self.client.close() raise LoginError(response.msg) return self.client - def __exit__(self, *args, **kwargs): + def disconnect(self): """Close the connection.""" try: self.client.send(commands.Logout()) self.client.close() except Exception: logger.warning("Connection to registry was not cleanly closed.") + + def send(self, command): + """Sends a command to the registry. + If the RegistryError code is >= 2000, + then this function raises a LoginError. + The calling function should handle this.""" + response = self.client.send(command) + if self.is_login_error(response.code): + self.client.close() + raise LoginError(response.msg) + + return response + + def is_login_error(self, code): + """Returns the result of code >= 2000 for RegistryError. + This indicates that something weird happened on the Registry, + and that we should return a LoginError.""" + return code >= 2000 + + def test_connection_success(self): + """Tests if a successful connection can be made with the registry. + Tries 3 times.""" + # Something went wrong if this doesn't exist + if not hasattr(self.client, "connect"): + logger.warning("self.client does not have a connect attribute") + return False + + counter = 0 # we'll try 3 times + while True: + try: + self.client.connect() + response = self.client.send(self.login) + except LoginError as err: + if err.should_retry() and counter < 3: + counter += 1 + sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms + else: # don't try again + return False + # Occurs when an invalid creds are passed in - such as on localhost + except OSError as err: + logger.error(err) + return False + else: + self.disconnect() + + # If we encounter a login error, fail + if self.is_login_error(response.code): + logger.warning("A login error was found in test_connection_success") + return False + + # Otherwise, just return true + return True diff --git a/src/epplibwrapper/tests/__init__.py b/src/epplibwrapper/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/epplibwrapper/tests/test_pool.py b/src/epplibwrapper/tests/test_pool.py new file mode 100644 index 000000000..4e919ba76 --- /dev/null +++ b/src/epplibwrapper/tests/test_pool.py @@ -0,0 +1,258 @@ +import datetime +from pathlib import Path +from unittest.mock import MagicMock, patch +from dateutil.tz import tzlocal # type: ignore +from django.test import TestCase +from epplibwrapper.client import EPPLibWrapper +from epplibwrapper.errors import RegistryError +from epplibwrapper.socket import Socket +from epplibwrapper.utility.pool import EPPConnectionPool +from registrar.models.domain import registry +from contextlib import ExitStack + +import logging + +try: + from epplib import commands + from epplib.client import Client + from epplib.exceptions import TransportError + from epplib.transport import SocketTransport + from epplib.models import common, info +except ImportError: + pass + +logger = logging.getLogger(__name__) + + +class TestConnectionPool(TestCase): + """Tests for our connection pooling behaviour""" + + def setUp(self): + # Mimic the settings added to settings.py + self.pool_options = { + # Current pool size + "size": 1, + # Which errors the pool should look out for + "exc_classes": (TransportError,), + # Occasionally pings the registry to keep the connection alive. + # Value in seconds => (keepalive / size) + "keepalive": 60, + } + + def fake_socket(self, login, client): + # Linter reasons + pw = "none" + # Create a fake client object + fake_client = Client( + SocketTransport( + "none", + cert_file="path/to/cert_file", + key_file="path/to/key_file", + password=pw, + ) + ) + + return Socket(fake_client, MagicMock()) + + def patch_success(self): + return True + + def fake_send(self, command, cleaned=None): + mock = MagicMock( + code=1000, + msg="Command completed successfully", + res_data=None, + cl_tr_id="xkw1uo#2023-10-17T15:29:09.559376", + sv_tr_id="5CcH4gxISuGkq8eqvr1UyQ==-35a", + extensions=[], + msg_q=None, + ) + return mock + + def fake_client(mock_client): + pw = "none" + client = Client( + SocketTransport( + "none", + cert_file="path/to/cert_file", + key_file="path/to/key_file", + password=pw, + ) + ) + return client + + @patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success) + def test_pool_sends_data(self): + """A .send is invoked on the pool successfully""" + expected_result = { + "cl_tr_id": None, + "code": 1000, + "extensions": [], + "msg": "Command completed successfully", + "msg_q": None, + "res_data": [ + info.InfoDomainResultData( + roid="DF1340360-GOV", + statuses=[ + common.Status( + state="serverTransferProhibited", + description=None, + lang="en", + ), + common.Status(state="inactive", description=None, lang="en"), + ], + cl_id="gov2023-ote", + cr_id="gov2023-ote", + cr_date=datetime.datetime( + 2023, 8, 15, 23, 56, 36, tzinfo=tzlocal() + ), + up_id="gov2023-ote", + up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()), + tr_date=None, + name="test3.gov", + registrant="TuaWnx9hnm84GCSU", + admins=[], + nsset=None, + keyset=None, + ex_date=datetime.date(2024, 8, 15), + auth_info=info.DomainAuthInfo(pw="2fooBAR123fooBaz"), + ) + ], + "sv_tr_id": "wRRNVhKhQW2m6wsUHbo/lA==-29a", + } + + # Mock a response from EPP + def fake_receive(command, cleaned=None): + location = Path(__file__).parent / "utility" / "infoDomain.xml" + xml = (location).read_bytes() + return xml + + # Mock what happens inside the "with" + with ExitStack() as stack: + stack.enter_context( + patch.object(EPPConnectionPool, "_create_socket", self.fake_socket) + ) + stack.enter_context(patch.object(Socket, "connect", self.fake_client)) + stack.enter_context(patch.object(SocketTransport, "send", self.fake_send)) + stack.enter_context(patch.object(SocketTransport, "receive", fake_receive)) + # Restart the connection pool + registry.start_connection_pool() + # Pool should be running, and be the right size + self.assertEqual(registry.pool_status.connection_success, True) + self.assertEqual(registry.pool_status.pool_running, True) + + # Send a command + result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True) + + # Should this ever fail, it either means that the schema has changed, + # or the pool is broken. + # If the schema has changed: Update the associated infoDomain.xml file + self.assertEqual(result.__dict__, expected_result) + + # The number of open pools should match the number of requested ones. + # If it is 0, then they failed to open + self.assertEqual(len(registry._pool.conn), self.pool_options["size"]) + + @patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success) + def test_pool_restarts_on_send(self): + """A .send is invoked, but the pool isn't running. + The pool should restart.""" + expected_result = { + "cl_tr_id": None, + "code": 1000, + "extensions": [], + "msg": "Command completed successfully", + "msg_q": None, + "res_data": [ + info.InfoDomainResultData( + roid="DF1340360-GOV", + statuses=[ + common.Status( + state="serverTransferProhibited", + description=None, + lang="en", + ), + common.Status(state="inactive", description=None, lang="en"), + ], + cl_id="gov2023-ote", + cr_id="gov2023-ote", + cr_date=datetime.datetime( + 2023, 8, 15, 23, 56, 36, tzinfo=tzlocal() + ), + up_id="gov2023-ote", + up_date=datetime.datetime(2023, 8, 17, 2, 3, 19, tzinfo=tzlocal()), + tr_date=None, + name="test3.gov", + registrant="TuaWnx9hnm84GCSU", + admins=[], + nsset=None, + keyset=None, + ex_date=datetime.date(2024, 8, 15), + auth_info=info.DomainAuthInfo(pw="2fooBAR123fooBaz"), + ) + ], + "sv_tr_id": "wRRNVhKhQW2m6wsUHbo/lA==-29a", + } + + # Mock a response from EPP + def fake_receive(command, cleaned=None): + location = Path(__file__).parent / "utility" / "infoDomain.xml" + xml = (location).read_bytes() + return xml + + # Mock what happens inside the "with" + with ExitStack() as stack: + stack.enter_context( + patch.object(EPPConnectionPool, "_create_socket", self.fake_socket) + ) + stack.enter_context(patch.object(Socket, "connect", self.fake_client)) + stack.enter_context(patch.object(SocketTransport, "send", self.fake_send)) + stack.enter_context(patch.object(SocketTransport, "receive", fake_receive)) + # Kill the connection pool + registry.kill_pool() + + self.assertEqual(registry.pool_status.connection_success, False) + self.assertEqual(registry.pool_status.pool_running, False) + + # An exception should be raised as end user will be informed + # that they cannot connect to EPP + with self.assertRaises(RegistryError): + expected = "InfoDomain failed to execute due to a connection error." + result = registry.send( + commands.InfoDomain(name="test.gov"), cleaned=True + ) + self.assertEqual(result, expected) + + # A subsequent command should be successful, as the pool restarts + result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True) + # Should this ever fail, it either means that the schema has changed, + # or the pool is broken. + # If the schema has changed: Update the associated infoDomain.xml file + self.assertEqual(result.__dict__, expected_result) + + # The number of open pools should match the number of requested ones. + # If it is 0, then they failed to open + self.assertEqual(len(registry._pool.conn), self.pool_options["size"]) + + @patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success) + def test_raises_connection_error(self): + """A .send is invoked on the pool, but registry connection is lost + right as we send a command.""" + + with ExitStack() as stack: + stack.enter_context( + patch.object(EPPConnectionPool, "_create_socket", self.fake_socket) + ) + stack.enter_context(patch.object(Socket, "connect", self.fake_client)) + + # Pool should be running + self.assertEqual(registry.pool_status.connection_success, True) + self.assertEqual(registry.pool_status.pool_running, True) + + # Try to send a command out - should fail + with self.assertRaises(RegistryError): + expected = "InfoDomain failed to execute due to a connection error." + result = registry.send( + commands.InfoDomain(name="test.gov"), cleaned=True + ) + self.assertEqual(result, expected) diff --git a/src/epplibwrapper/tests/utility/infoDomain.xml b/src/epplibwrapper/tests/utility/infoDomain.xml new file mode 100644 index 000000000..541812577 --- /dev/null +++ b/src/epplibwrapper/tests/utility/infoDomain.xml @@ -0,0 +1,33 @@ + + + + + + Command completed successfully + + + + test3.gov + DF1340360-GOV + + + TuaWnx9hnm84GCSU + CONT2 + CONT3 + gov2023-ote + gov2023-ote + 2023-08-15T23:56:36Z + gov2023-ote + 2023-08-17T02:03:19Z + 2024-08-15T23:56:36Z + + 2fooBAR123fooBaz + + + + + wRRNVhKhQW2m6wsUHbo/lA==-29a + + + + diff --git a/src/epplibwrapper/utility/pool.py b/src/epplibwrapper/utility/pool.py new file mode 100644 index 000000000..36771252b --- /dev/null +++ b/src/epplibwrapper/utility/pool.py @@ -0,0 +1,134 @@ +import logging +from typing import List +import gevent +from geventconnpool import ConnectionPool +from epplibwrapper.socket import Socket +from epplibwrapper.utility.pool_error import PoolError, PoolErrorCodes + +try: + from epplib.commands import Hello + from epplib.exceptions import TransportError +except ImportError: + pass + +from gevent.lock import BoundedSemaphore +from collections import deque + +logger = logging.getLogger(__name__) + + +class EPPConnectionPool(ConnectionPool): + """A connection pool for EPPLib. + + Args: + client (Client): The client + login (commands.Login): Login creds + options (dict): Options for the ConnectionPool + base class + """ + + def __init__(self, client, login, options: dict): + # For storing shared credentials + self._client = client + self._login = login + + # Keep track of each greenlet + self.greenlets: List[gevent.Greenlet] = [] + + # Define optional pool settings. + # Kept in a dict so that the parent class, + # client.py, can maintain seperation/expandability + self.size = 1 + if "size" in options: + self.size = options["size"] + + self.exc_classes = tuple((TransportError,)) + if "exc_classes" in options: + self.exc_classes = options["exc_classes"] + + self.keepalive = None + if "keepalive" in options: + self.keepalive = options["keepalive"] + + # Determines the period in which new + # gevent threads are spun up. + # This time period is in seconds. So for instance, .1 would be .1 seconds. + self.spawn_frequency = 0.1 + if "spawn_frequency" in options: + self.spawn_frequency = options["spawn_frequency"] + + self.conn: deque = deque() + self.lock = BoundedSemaphore(self.size) + + self.populate_all_connections() + + def _new_connection(self): + socket = self._create_socket(self._client, self._login) + try: + connection = socket.connect() + return connection + except Exception as err: + message = f"Failed to execute due to a registry error: {err}" + logger.error(message, exc_info=True) + # We want to raise a pool error rather than a LoginError here + # because if this occurs internally, we should handle this + # differently than we otherwise would for LoginError. + raise PoolError(code=PoolErrorCodes.NEW_CONNECTION_FAILED) from err + + def _keepalive(self, c): + """Sends a command to the server to keep the connection alive.""" + try: + # Sends a ping to the registry via EPPLib + c.send(Hello()) + except Exception as err: + message = "Failed to keep the connection alive." + logger.error(message, exc_info=True) + raise PoolError(code=PoolErrorCodes.KEEP_ALIVE_FAILED) from err + + def _create_socket(self, client, login) -> Socket: + """Creates and returns a socket instance""" + socket = Socket(client, login) + return socket + + def get_connections(self): + """Returns the connection queue""" + return self.conn + + def kill_all_connections(self): + """Kills all active connections in the pool.""" + try: + if len(self.conn) > 0: + gevent.killall(self.greenlets) + + self.greenlets.clear() + self.conn.clear() + + # Clear the semaphore + self.lock = BoundedSemaphore(self.size) + else: + logger.info("No connections to kill.") + except Exception as err: + logger.error("Could not kill all connections.") + raise PoolError(code=PoolErrorCodes.KILL_ALL_FAILED) from err + + def populate_all_connections(self): + """Generates the connection pool. + If any connections exist, kill them first. + Based off of the __init__ definition for geventconnpool. + """ + if len(self.conn) > 0: + self.kill_all_connections() + + # Setup the lock + for i in range(self.size): + self.lock.acquire() + + # Open multiple connections + for i in range(self.size): + self.greenlets.append( + gevent.spawn_later(self.spawn_frequency * i, self._addOne) + ) + + # Open a "keepalive" thread if we want to ping open connections + if self.keepalive: + self.greenlets.append(gevent.spawn(self._keepalive_periodic)) diff --git a/src/epplibwrapper/utility/pool_error.py b/src/epplibwrapper/utility/pool_error.py new file mode 100644 index 000000000..821962774 --- /dev/null +++ b/src/epplibwrapper/utility/pool_error.py @@ -0,0 +1,52 @@ +from enum import IntEnum + + +class PoolErrorCodes(IntEnum): + """Used in the PoolError class for + error mapping. + + Overview of contact error codes: + - 2000 KILL_ALL_FAILED + - 2001 NEW_CONNECTION_FAILED + - 2002 KEEP_ALIVE_FAILED + """ + + KILL_ALL_FAILED = 2000 + NEW_CONNECTION_FAILED = 2001 + KEEP_ALIVE_FAILED = 2002 + + +class PoolError(Exception): + """ + Overview of contact error codes: + - 2000 KILL_ALL_FAILED + - 2001 NEW_CONNECTION_FAILED + - 2002 KEEP_ALIVE_FAILED + + Note: These are separate from the error codes returned from EppLib + """ + + # Used variables due to linter requirements + kill_failed = "Could not kill all connections. Are multiple pools running?" + conn_failed = ( + "Failed to execute due to a registry error." + " See previous logs to determine the cause of the error." + ) + alive_failed = ( + "Failed to keep the connection alive. " + "It is likely that the registry returned a LoginError." + ) + _error_mapping = { + PoolErrorCodes.KILL_ALL_FAILED: kill_failed, + PoolErrorCodes.NEW_CONNECTION_FAILED: conn_failed, + PoolErrorCodes.KEEP_ALIVE_FAILED: alive_failed, + } + + def __init__(self, *args, code=None, **kwargs): + super().__init__(*args, **kwargs) + self.code = code + if self.code in self._error_mapping: + self.message = self._error_mapping.get(self.code) + + def __str__(self): + return f"{self.message}" diff --git a/src/epplibwrapper/utility/pool_status.py b/src/epplibwrapper/utility/pool_status.py new file mode 100644 index 000000000..3a0ae750f --- /dev/null +++ b/src/epplibwrapper/utility/pool_status.py @@ -0,0 +1,12 @@ +class PoolStatus: + """A list of Booleans to keep track of Pool Status. + + pool_running -> bool: Tracks if the pool itself is active or not. + connection_success -> bool: Tracks if connection is possible with the registry. + pool_hanging -> pool: Tracks if the pool has exceeded its timeout period. + """ + + def __init__(self): + self.pool_running = False + self.connection_success = False + self.pool_hanging = False diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 8d0ed8c2e..904ce66a4 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -219,9 +219,9 @@ class MyUserAdmin(BaseUserAdmin): # (which should in theory be the ONLY group) def group(self, obj): if obj.groups.filter(name="full_access_group").exists(): - return "Full access" + return "full_access_group" elif obj.groups.filter(name="cisa_analysts_group").exists(): - return "Analyst" + return "cisa_analysts_group" return "" def get_list_display(self, request): @@ -294,6 +294,26 @@ class ContactAdmin(ListHeaderAdmin): contact.admin_order_field = "first_name" # type: ignore + # Read only that we'll leverage for CISA Analysts + analyst_readonly_fields = [ + "user", + ] + + def get_readonly_fields(self, request, obj=None): + """Set the read-only state on form elements. + We have 1 conditions that determine which fields are read-only: + admin user permissions. + """ + + readonly_fields = list(self.readonly_fields) + + if request.user.has_perm("registrar.full_access_permission"): + return readonly_fields + # Return restrictive Read-only fields for analysts and + # users who might not belong to groups + readonly_fields.extend([field for field in self.analyst_readonly_fields]) + return readonly_fields # Read-only fields for analysts + class WebsiteAdmin(ListHeaderAdmin): """Custom website admin class.""" @@ -420,9 +440,6 @@ class DomainInformationAdmin(ListHeaderAdmin): "creator", "type_of_work", "more_organization_information", - "address_line1", - "address_line2", - "zipcode", "domain", "submitter", "no_other_contacts_rationale", @@ -557,9 +574,6 @@ class DomainApplicationAdmin(ListHeaderAdmin): analyst_readonly_fields = [ "creator", "about_your_organization", - "address_line1", - "address_line2", - "zipcode", "requested_domain", "alternative_domains", "purpose", @@ -721,7 +735,7 @@ class DomainAdmin(ListHeaderAdmin): ] def organization_type(self, obj): - return obj.domain_info.organization_type + return obj.domain_info.get_organization_type_display() organization_type.admin_order_field = ( # type: ignore "domain_info__organization_type" diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index c21060382..851de8fcf 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -383,3 +383,30 @@ function prepareDeleteButtons() { } })(); + +/** + * An IIFE that triggers a modal on the DS Data Form under certain conditions + * + */ +(function triggerModalOnDsDataForm() { + let saveButon = document.querySelector("#save-ds-data"); + + // The view context will cause a hitherto hidden modal trigger to + // show up. On save, we'll test for that modal trigger appearing. We'll + // run that test once every 100 ms for 5 secs, which should balance performance + // while accounting for network or lag issues. + if (saveButon) { + let i = 0; + var tryToTriggerModal = setInterval(function() { + i++; + if (i > 100) { + clearInterval(tryToTriggerModal); + } + let modalTrigger = document.querySelector("#ds-toggle-dnssec-alert"); + if (modalTrigger) { + modalTrigger.click() + clearInterval(tryToTriggerModal); + } + }, 50); + } +})(); diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss index 0857ec603..cb2117fb9 100644 --- a/src/registrar/assets/sass/_theme/_buttons.scss +++ b/src/registrar/assets/sass/_theme/_buttons.scss @@ -22,8 +22,11 @@ a.breadcrumb__back { } } -a.usa-button:not(.usa-button--unstyled, .usa-button--outline) { +a.usa-button { text-decoration: none; +} + +a.usa-button:not(.usa-button--unstyled, .usa-button--outline) { color: color('white'); } @@ -111,15 +114,3 @@ a.usa-button--unstyled:visited { margin-left: units(2); } } - - -// WARNING: crazy hack ahead: -// Cancel button(s) on the DNSSEC form pages -// We want to position the cancel button on the -// dnssec forms next to the submit button -// This button's markup is in its own form -.btn-cancel { - position: relative; - top: -39.2px; - left: 88px; -} diff --git a/src/registrar/assets/sass/_theme/_forms.scss b/src/registrar/assets/sass/_theme/_forms.scss index ed118bb94..38b42c3d0 100644 --- a/src/registrar/assets/sass/_theme/_forms.scss +++ b/src/registrar/assets/sass/_theme/_forms.scss @@ -4,6 +4,10 @@ margin-top: units(3); } +.usa-form .usa-button.margin-top-1 { + margin-top: units(1); +} + .usa-form--extra-large { max-width: none; } diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 7b96af5ee..3e5734dcc 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -534,6 +534,20 @@ SECRET_REGISTRY_KEY = secret_registry_key SECRET_REGISTRY_KEY_PASSPHRASE = secret_registry_key_passphrase SECRET_REGISTRY_HOSTNAME = secret_registry_hostname +# Use this variable to set the size of our connection pool in client.py +# WARNING: Setting this value too high could cause frequent app crashes! +# Having too many connections open could cause the sandbox to timeout, +# as the spinup time could exceed the timeout time. +EPP_CONNECTION_POOL_SIZE = 1 + +# Determines the interval in which we ping open connections in seconds +# Calculated as POOL_KEEP_ALIVE / EPP_CONNECTION_POOL_SIZE +POOL_KEEP_ALIVE = 60 + +# Determines how long we try to keep a pool alive for, +# before restarting it. +POOL_TIMEOUT = 60 + # endregion # region: Security and Privacy----------------------------------------------### @@ -581,7 +595,7 @@ ALLOWED_HOSTS = [ "getgov-bl.app.cloud.gov", "getgov-rjm.app.cloud.gov", "getgov-dk.app.cloud.gov", - "get.gov", + "manage.get.gov", ] # Extend ALLOWED_HOSTS. diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index bd2215620..c00d1c589 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -100,11 +100,6 @@ urlpatterns = [ views.DomainDsDataView.as_view(), name="domain-dns-dnssec-dsdata", ), - path( - "domain//dns/dnssec/keydata", - views.DomainKeyDataView.as_view(), - name="domain-dns-dnssec-keydata", - ), path( "domain//your-contact-information", views.DomainYourContactInformationView.as_view(), diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index dfe51785b..f03fb025d 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -86,6 +86,12 @@ class UserFixture: "first_name": "Kristina", "last_name": "Yin", }, + { + "username": "ac49d7c1-368a-4e6b-8f1d-60250e20a16f", + "first_name": "Vicky", + "last_name": "Chin", + "email": "szu.chin@associates.cisa.dhs.gov", + }, ] STAFF = [ @@ -150,6 +156,12 @@ class UserFixture: "last_name": "Yin-Analyst", "email": "kristina.yin+1@gsa.gov", }, + { + "username": "8f42302e-b83a-4c9e-8764-fc19e2cea576", + "first_name": "Vickster-Analyst", + "last_name": "Chin-Analyst", + "email": "szu.chin@ecstech.com", + }, ] def load_users(cls, users, group_name): diff --git a/src/registrar/forms/__init__.py b/src/registrar/forms/__init__.py index 7d2baf646..c3aa89fed 100644 --- a/src/registrar/forms/__init__.py +++ b/src/registrar/forms/__init__.py @@ -8,6 +8,4 @@ from .domain import ( DomainDnssecForm, DomainDsdataFormset, DomainDsdataForm, - DomainKeydataFormset, - DomainKeydataForm, ) diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index 516683247..2fd78cdd8 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -153,7 +153,8 @@ class RegistrarFormSet(forms.BaseFormSet): class OrganizationTypeForm(RegistrarForm): organization_type = forms.ChoiceField( - choices=DomainApplication.OrganizationChoices.choices, + # use the long names in the application form + choices=DomainApplication.OrganizationChoicesVerbose.choices, widget=forms.RadioSelect, error_messages={"required": "Select the type of organization you represent."}, ) diff --git a/src/registrar/forms/common.py b/src/registrar/forms/common.py index 159113488..a27545a84 100644 --- a/src/registrar/forms/common.py +++ b/src/registrar/forms/common.py @@ -1,6 +1,6 @@ # common.py # -# ALGORITHM_CHOICES are options for alg attribute in DS Data and Key Data +# ALGORITHM_CHOICES are options for alg attribute in DS Data # reference: # https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml ALGORITHM_CHOICES = [ @@ -24,15 +24,3 @@ DIGEST_TYPE_CHOICES = [ (0, "(0) Reserved"), (1, "(1) SHA-256"), ] -# PROTOCOL_CHOICES are options for protocol attribute in Key Data -# reference: https://datatracker.ietf.org/doc/html/rfc4034#section-2.1.2 -PROTOCOL_CHOICES = [ - (3, "(3) DNSSEC"), -] -# FLAG_CHOICES are options for flags attribute in Key Data -# reference: https://datatracker.ietf.org/doc/html/rfc4034#section-2.1.1 -FLAG_CHOICES = [ - (0, "(0)"), - (256, "(256) ZSK"), - (257, "(257) KSK"), -] diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index 8abc7e14a..6bbade5ef 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -10,8 +10,6 @@ from ..models import Contact, DomainInformation from .common import ( ALGORITHM_CHOICES, DIGEST_TYPE_CHOICES, - FLAG_CHOICES, - PROTOCOL_CHOICES, ) @@ -188,44 +186,3 @@ DomainDsdataFormset = formset_factory( extra=0, can_delete=True, ) - - -class DomainKeydataForm(forms.Form): - """Form for adding or editing DNSSEC Key Data to a domain.""" - - flag = forms.TypedChoiceField( - required=True, - label="Flag", - coerce=int, - choices=FLAG_CHOICES, - error_messages={"required": ("Flag is required.")}, - ) - - protocol = forms.TypedChoiceField( - required=True, - label="Protocol", - coerce=int, - choices=PROTOCOL_CHOICES, - error_messages={"required": ("Protocol is required.")}, - ) - - algorithm = forms.TypedChoiceField( - required=True, - label="Algorithm", - coerce=int, - choices=[(None, "--Select--")] + ALGORITHM_CHOICES, # type: ignore - error_messages={"required": ("Algorithm is required.")}, - ) - - pub_key = forms.CharField( - required=True, - label="Pub key", - error_messages={"required": ("Pub key is required.")}, - ) - - -DomainKeydataFormset = formset_factory( - DomainKeydataForm, - extra=0, - can_delete=True, -) diff --git a/src/registrar/migrations/0041_alter_domainapplication_organization_type_and_more.py b/src/registrar/migrations/0041_alter_domainapplication_organization_type_and_more.py new file mode 100644 index 000000000..07cfe0e77 --- /dev/null +++ b/src/registrar/migrations/0041_alter_domainapplication_organization_type_and_more.py @@ -0,0 +1,52 @@ +# Generated by Django 4.2.1 on 2023-10-20 21:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0040_alter_userdomainrole_role"), + ] + + operations = [ + migrations.AlterField( + model_name="domainapplication", + name="organization_type", + field=models.CharField( + blank=True, + choices=[ + ("federal", "Federal"), + ("interstate", "Interstate"), + ("state_or_territory", "State or territory"), + ("tribal", "Tribal"), + ("county", "County"), + ("city", "City"), + ("special_district", "Special district"), + ("school_district", "School district"), + ], + help_text="Type of organization", + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="organization_type", + field=models.CharField( + blank=True, + choices=[ + ("federal", "Federal"), + ("interstate", "Interstate"), + ("state_or_territory", "State or territory"), + ("tribal", "Tribal"), + ("county", "County"), + ("city", "City"), + ("special_district", "Special district"), + ("school_district", "School district"), + ], + help_text="Type of Organization", + max_length=255, + null=True, + ), + ), + ] diff --git a/src/registrar/migrations/0042_create_groups_v03.py b/src/registrar/migrations/0042_create_groups_v03.py new file mode 100644 index 000000000..01b7985bf --- /dev/null +++ b/src/registrar/migrations/0042_create_groups_v03.py @@ -0,0 +1,37 @@ +# This migration creates the create_full_access_group and create_cisa_analyst_group groups +# It is dependent on 0035 (which populates ContentType and Permissions) +# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS +# in the user_group model then: +# [NOT RECOMMENDED] +# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions +# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups +# step 3: fake run the latest migration in the migrations list +# [RECOMMENDED] +# Alternatively: +# step 1: duplicate the migration that loads data +# step 2: docker-compose exec app ./manage.py migrate + +from django.db import migrations +from registrar.models import UserGroup +from typing import Any + + +# For linting: RunPython expects a function reference, +# so let's give it one +def create_groups(apps, schema_editor) -> Any: + UserGroup.create_cisa_analyst_group(apps, schema_editor) + UserGroup.create_full_access_group(apps, schema_editor) + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0041_alter_domainapplication_organization_type_and_more"), + ] + + operations = [ + migrations.RunPython( + create_groups, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), + ] diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index f287c401c..1d28c9e89 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -1,5 +1,4 @@ from auditlog.registry import auditlog # type: ignore - from .contact import Contact from .domain_application import DomainApplication from .domain_information import DomainInformation diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 9060ef90d..4ba2ae3d4 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -9,6 +9,14 @@ from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignor from django.db import models from typing import Any + + +from registrar.utility.errors import ( + ActionNotAllowed, + NameserverError, + NameserverErrorCodes as nsErrorCodes, +) + from epplibwrapper import ( CLIENT as registry, commands, @@ -19,15 +27,8 @@ from epplibwrapper import ( ErrorCode, ) -from registrar.utility.errors import ( - ActionNotAllowed, - NameserverError, - NameserverErrorCodes as nsErrorCodes, -) - from registrar.models.utility.contact_error import ContactError, ContactErrorCodes - from .utility.domain_field import DomainField from .utility.domain_helper import DomainHelper from .utility.time_stamped_model import TimeStampedModel @@ -522,12 +523,11 @@ class Domain(TimeStampedModel, DomainHelper): addExtension: dict remExtension: dict - addExtension includes all dsData or keyData to be added - remExtension includes all dsData or keyData to be removed + addExtension includes all dsData to be added + remExtension includes all dsData to be removed - method operates on dsData OR keyData, never a mix of the two; - operates based on which is present in _dnssecdata; - if neither is present, addExtension will be empty dict, and + method operates on dsData; + if dsData is not present, addExtension will be empty dict, and remExtension will be all existing dnssecdata to be deleted """ @@ -560,32 +560,9 @@ class Domain(TimeStampedModel, DomainHelper): else: addDnssecdata["dsData"] = None - elif _dnssecdata and _dnssecdata.keyData is not None: - # initialize addDnssecdata and remDnssecdata for keyData - addDnssecdata["keyData"] = _dnssecdata.keyData - - if oldDnssecdata and len(oldDnssecdata.keyData) > 0: - # if existing keyData not in new keyData, mark for removal - keyDataForRemoval = [ - keyData - for keyData in oldDnssecdata.keyData - if keyData not in _dnssecdata.keyData - ] - if len(keyDataForRemoval) > 0: - remDnssecdata["keyData"] = keyDataForRemoval - - # if new keyData not in existing keyData, mark for add - keyDataForAdd = [ - keyData - for keyData in _dnssecdata.keyData - if keyData not in oldDnssecdata.keyData - ] - if len(keyDataForAdd) > 0: - addDnssecdata["keyData"] = keyDataForAdd else: - # there are no new dsData or keyData, remove all + # there are no new dsData, remove all dsData from existing remDnssecdata["dsData"] = getattr(oldDnssecdata, "dsData", None) - remDnssecdata["keyData"] = getattr(oldDnssecdata, "keyData", None) return addDnssecdata, remDnssecdata @@ -595,12 +572,10 @@ class Domain(TimeStampedModel, DomainHelper): addParams = { "maxSigLife": _addDnssecdata.get("maxSigLife", None), "dsData": _addDnssecdata.get("dsData", None), - "keyData": _addDnssecdata.get("keyData", None), } remParams = { "maxSigLife": _remDnssecdata.get("maxSigLife", None), "remDsData": _remDnssecdata.get("dsData", None), - "remKeyData": _remDnssecdata.get("keyData", None), } addRequest = commands.UpdateDomain(name=self.name) addExtension = commands.UpdateDomainDNSSECExtension(**addParams) @@ -609,19 +584,9 @@ class Domain(TimeStampedModel, DomainHelper): remExtension = commands.UpdateDomainDNSSECExtension(**remParams) remRequest.add_extension(remExtension) try: - if ( - "dsData" in _addDnssecdata - and _addDnssecdata["dsData"] is not None - or "keyData" in _addDnssecdata - and _addDnssecdata["keyData"] is not None - ): + if "dsData" in _addDnssecdata and _addDnssecdata["dsData"] is not None: registry.send(addRequest, cleaned=True) - if ( - "dsData" in _remDnssecdata - and _remDnssecdata["dsData"] is not None - or "keyData" in _remDnssecdata - and _remDnssecdata["keyData"] is not None - ): + if "dsData" in _remDnssecdata and _remDnssecdata["dsData"] is not None: registry.send(remRequest, cleaned=True) except RegistryError as e: logger.error( diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index f15474117..68fbfab0d 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -105,28 +105,57 @@ class DomainApplication(TimeStampedModel): ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)" class OrganizationChoices(models.TextChoices): + + """ + Primary organization choices: + For use in django admin + Keys need to match OrganizationChoicesVerbose + """ + + FEDERAL = "federal", "Federal" + INTERSTATE = "interstate", "Interstate" + STATE_OR_TERRITORY = "state_or_territory", "State or territory" + TRIBAL = "tribal", "Tribal" + COUNTY = "county", "County" + CITY = "city", "City" + SPECIAL_DISTRICT = "special_district", "Special district" + SCHOOL_DISTRICT = "school_district", "School district" + + class OrganizationChoicesVerbose(models.TextChoices): + + """ + Secondary organization choices + For use in the application form and on the templates + Keys need to match OrganizationChoices + """ + FEDERAL = ( "federal", - "Federal: an agency of the U.S. government's executive, legislative, " - "or judicial branches", + "Federal: an agency of the U.S. government's executive, " + "legislative, or judicial branches", ) INTERSTATE = "interstate", "Interstate: an organization of two or more states" - STATE_OR_TERRITORY = "state_or_territory", ( - "State or territory: one of the 50 U.S. states, the District of " - "Columbia, American Samoa, Guam, Northern Mariana Islands, " - "Puerto Rico, or the U.S. Virgin Islands" + STATE_OR_TERRITORY = ( + "state_or_territory", + "State or territory: one of the 50 U.S. states, the District of Columbia, " + "American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. " + "Virgin Islands", ) - TRIBAL = "tribal", ( - "Tribal: a tribal government recognized by the federal or " - "a state government" + TRIBAL = ( + "tribal", + "Tribal: a tribal government recognized by the federal or a state " + "government", ) COUNTY = "county", "County: a county, parish, or borough" CITY = "city", "City: a city, town, township, village, etc." - SPECIAL_DISTRICT = "special_district", ( - "Special district: an independent organization within a single state" + SPECIAL_DISTRICT = ( + "special_district", + "Special district: an independent organization within a single state", ) - SCHOOL_DISTRICT = "school_district", ( - "School district: a school district that is not part of a local government" + SCHOOL_DISTRICT = ( + "school_district", + "School district: a school district that is not part of a local " + "government", ) class BranchChoices(models.TextChoices): @@ -297,6 +326,7 @@ class DomainApplication(TimeStampedModel): # ##### data fields from the initial form ##### organization_type = models.CharField( max_length=255, + # use the short names in Django admin choices=OrganizationChoices.choices, null=True, blank=True, diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 3b93aff48..d2bc5c53d 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -21,6 +21,7 @@ class DomainInformation(TimeStampedModel): StateTerritoryChoices = DomainApplication.StateTerritoryChoices + # use the short names in Django admin OrganizationChoices = DomainApplication.OrganizationChoices BranchChoices = DomainApplication.BranchChoices diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index acf59cb68..7207a5a61 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -4,6 +4,9 @@ from django.contrib.auth.models import AbstractUser from django.db import models from .domain_invitation import DomainInvitation +from .transition_domain import TransitionDomain +from .domain_information import DomainInformation +from .domain import Domain from phonenumber_field.modelfields import PhoneNumberField # type: ignore @@ -62,12 +65,9 @@ class User(AbstractUser): def is_restricted(self): return self.status == self.RESTRICTED - def first_login(self): - """Callback when the user is authenticated for the very first time. - - When a user first arrives on the site, we need to retrieve any domain - invitations that match their email address. - """ + def check_domain_invitations_on_login(self): + """When a user first arrives on the site, we need to retrieve any domain + invitations that match their email address.""" for invitation in DomainInvitation.objects.filter( email=self.email, status=DomainInvitation.INVITED ): @@ -82,6 +82,101 @@ class User(AbstractUser): "Failed to retrieve invitation %s", invitation, exc_info=True ) + def create_domain_and_invite(self, transition_domain: TransitionDomain): + transition_domain_name = transition_domain.domain_name + transition_domain_status = transition_domain.status + transition_domain_email = transition_domain.username + + # type safety check. name should never be none + if transition_domain_name is not None: + new_domain = Domain( + name=transition_domain_name, state=transition_domain_status + ) + new_domain.save() + # check that a domain invitation doesn't already + # exist for this e-mail / Domain pair + domain_email_already_in_domain_invites = DomainInvitation.objects.filter( + email=transition_domain_email.lower(), domain=new_domain + ).exists() + if not domain_email_already_in_domain_invites: + # Create new domain invitation + new_domain_invitation = DomainInvitation( + email=transition_domain_email.lower(), domain=new_domain + ) + new_domain_invitation.save() + + def check_transition_domains_on_login(self): + """When a user first arrives on the site, we need to check + if they are logging in with the same e-mail as a + transition domain and update our database accordingly.""" + + for transition_domain in TransitionDomain.objects.filter(username=self.email): + # Looks like the user logged in with the same e-mail as + # one or more corresponding transition domains. + # Create corresponding DomainInformation objects. + + # NOTE: adding an ADMIN user role for this user + # for each domain should already be done + # in the invitation.retrieve() method. + # However, if the migration scripts for transition + # domain objects were not executed correctly, + # there could be transition domains without + # any corresponding Domain & DomainInvitation objects, + # which means the invitation.retrieve() method might + # not execute. + # Check that there is a corresponding domain object + # for this transition domain. If not, we have an error + # with our data and migrations need to be run again. + + # Get the domain that corresponds with this transition domain + domain_exists = Domain.objects.filter( + name=transition_domain.domain_name + ).exists() + if not domain_exists: + logger.warn( + """There are transition domains without + corresponding domain objects! + Please run migration scripts for transition domains + (See data_migration.md)""" + ) + # No need to throw an exception...just create a domain + # and domain invite, then proceed as normal + self.create_domain_and_invite(transition_domain) + + domain = Domain.objects.get(name=transition_domain.domain_name) + + # Create a domain information object, if one doesn't + # already exist + domain_info_exists = DomainInformation.objects.filter( + domain=domain + ).exists() + if not domain_info_exists: + new_domain_info = DomainInformation(creator=self, domain=domain) + new_domain_info.save() + + def first_login(self): + """Callback when the user is authenticated for the very first time. + + When a user first arrives on the site, we need to retrieve any domain + invitations that match their email address. + + We also need to check if they are logging in with the same e-mail + as a transition domain and update our domainInfo objects accordingly. + """ + + # PART 1: TRANSITION DOMAINS + # + # NOTE: THIS MUST RUN FIRST + # (If we have an issue where transition domains were + # not fully converted into Domain and DomainInvitation + # objects, this method will fill in the gaps. + # This will ensure the Domain Invitations method + # runs correctly (no missing invites)) + self.check_transition_domains_on_login() + + # PART 2: DOMAIN INVITATIONS + self.check_domain_invitations_on_login() + class Meta: permissions = [ ("analyst_access_permission", "Analyst Access Permission"), diff --git a/src/registrar/models/user_group.py b/src/registrar/models/user_group.py index 5cdb1f2ec..568741786 100644 --- a/src/registrar/models/user_group.py +++ b/src/registrar/models/user_group.py @@ -24,7 +24,7 @@ class UserGroup(Group): { "app_label": "registrar", "model": "contact", - "permissions": ["view_contact"], + "permissions": ["change_contact"], }, { "app_label": "registrar", @@ -56,6 +56,11 @@ class UserGroup(Group): "model": "domaininvitation", "permissions": ["add_domaininvitation", "view_domaininvitation"], }, + { + "app_label": "registrar", + "model": "website", + "permissions": ["change_website"], + }, ] # Avoid error: You can't execute queries until the end diff --git a/src/registrar/templates/application_review.html b/src/registrar/templates/application_review.html index be81303b8..6a4dcbffd 100644 --- a/src/registrar/templates/application_review.html +++ b/src/registrar/templates/application_review.html @@ -1,5 +1,6 @@ {% extends 'application_form.html' %} {% load static url_helpers %} +{% load custom_filters %} {% block form_required_fields_help_text %} {# there are no required fields on this page so don't show this #} @@ -26,7 +27,13 @@
{{ form_titles|get_item:step }}
{% if step == Step.ORGANIZATION_TYPE %} - {{ application.get_organization_type_display|default:"Incomplete" }} + {% if application.organization_type is not None %} + {% with long_org_type=application.organization_type|get_organization_long_name %} + {{ long_org_type }} + {% endwith %} + {% else %} + Incomplete + {% endif %} {% endif %} {% if step == Step.TRIBAL_GOVERNMENT %} {{ application.tribe_name|default:"Incomplete" }} diff --git a/src/registrar/templates/application_status.html b/src/registrar/templates/application_status.html index a68c07c8a..79d0f7ff9 100644 --- a/src/registrar/templates/application_status.html +++ b/src/registrar/templates/application_status.html @@ -1,5 +1,7 @@ {% extends 'base.html' %} +{% load custom_filters %} + {% block title %}Domain request status | {{ domainapplication.requested_domain.name }} | {% endblock %} {% load static url_helpers %} @@ -50,7 +52,9 @@

Summary of your domain request

{% with heading_level='h3' %} - {% include "includes/summary_item.html" with title='Type of organization' value=domainapplication.get_organization_type_display heading_level=heading_level %} + {% with long_org_type=domainapplication.organization_type|get_organization_long_name %} + {% include "includes/summary_item.html" with title='Type of organization' value=long_org_type heading_level=heading_level %} + {% endwith %} {% if domainapplication.tribe_name %} {% include "includes/summary_item.html" with title='Tribal government' value=domainapplication.tribe_name heading_level=heading_level %} diff --git a/src/registrar/templates/domain_detail.html b/src/registrar/templates/domain_detail.html index c5394e433..dfd583b1e 100644 --- a/src/registrar/templates/domain_detail.html +++ b/src/registrar/templates/domain_detail.html @@ -52,7 +52,7 @@ {% include "includes/summary_item.html" with title='Security email' value='None provided' edit_link=url %} {% endif %} {% url 'domain-users' pk=domain.id as url %} - {% include "includes/summary_item.html" with title='User management' users='true' list=True value=domain.permissions.all edit_link=url %} + {% include "includes/summary_item.html" with title='Domain managers' users='true' list=True value=domain.permissions.all edit_link=url %}
{% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_dns.html b/src/registrar/templates/domain_dns.html index b16c1cb8b..45529a19e 100644 --- a/src/registrar/templates/domain_dns.html +++ b/src/registrar/templates/domain_dns.html @@ -14,7 +14,7 @@ {% url 'domain-dns-nameservers' pk=domain.id as url %}

DNS name servers

- {% url 'domain-dnssec' pk=domain.id as url %} + {% url 'domain-dns-dnssec' pk=domain.id as url %}

DNSSEC

{% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_dnssec.html b/src/registrar/templates/domain_dnssec.html index 5eedb2184..691ba79b2 100644 --- a/src/registrar/templates/domain_dnssec.html +++ b/src/registrar/templates/domain_dnssec.html @@ -7,51 +7,45 @@

DNSSEC

-

DNSSEC, or DNS Security Extensions, is additional security layer to protect your domain. Enabling DNSSEC ensures that when someone visits your domain, they can be certain that it's connecting to the correct server, preventing potential hijacking or tampering with your domain's records.

+

DNSSEC, or DNS Security Extensions, is additional security layer to protect your website. Enabling DNSSEC ensures that when someone visits your website, they can be certain that it’s connecting to the correct server, preventing potential hijacking or tampering with your domain's records.

{% csrf_token %} {% if has_dnssec_records %} -
-
- In order to fully disable DNSSEC on your domain, you will need to work with your DNS provider to remove your DNSSEC-related records from your zone. +
+
+

+

To fully disable DNSSEC

+
    +
  • Click “Disable DNSSEC” below.
  • +
  • Wait until the Time to Live (TTL) expires on your DNSSEC records managed by your DNS hosting provider. This is often less than 24 hours, but confirm with your provider.
  • +
  • After the TTL expiration, disable DNSSEC at your DNS hosting provider.
  • +
+

Warning: If you disable DNSSEC at your DNS hosting provider before TTL expiration, this may cause your domain to appear offline.

+

DNSSEC is enabled on your domain

Disable DNSSEC - {% elif dnssec_enabled %} -
-

Add DS Records

-

In order to enable DNSSEC and add Delegation Signer (DS) records, you must first configure it with your DNS hosting service. Your configuration will determine whether you need to add DS Data or Key Data. Contact your DNS hosting provider if you are unsure which record type to add.

-

- Add DS Data - Add Key Data - -

-
{% else %}
- It is strongly recommended that you only enable DNSSEC if you know how to set it up properly at your hosting service. If you make a mistake, it could cause your domain name to stop working. + It is strongly recommended that you only enable DNSSEC if you know how to set it up properly at your hosting service. If you make a mistake, it could cause your domain name to stop working.
- + Enable DNSSEC
{% endif %} @@ -62,7 +56,7 @@ aria-labelledby="Are you sure you want to continue?" aria-describedby="Your DNSSEC records will be deleted from the registry." > - {% include 'includes/modal.html' with modal_heading="Are you sure you want to continue?" modal_description="Your DNSSEC records will be deleted from the registry." modal_button=modal_button|safe %} + {% include 'includes/modal.html' with modal_heading="Are you sure you want to disable DNSSEC?" modal_button=modal_button|safe %}
{% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_dsdata.html b/src/registrar/templates/domain_dsdata.html index ca4dce783..bdf4deb46 100644 --- a/src/registrar/templates/domain_dsdata.html +++ b/src/registrar/templates/domain_dsdata.html @@ -4,47 +4,24 @@ {% block title %}DS Data | {{ domain.name }} | {% endblock %} {% block domain_content %} - {% for form in formset %} - {% include "includes/form_errors.html" with form=form %} - {% endfor %} - - {% if domain.dnssecdata is None and not dnssec_ds_confirmed %} + {% if domain.dnssecdata is None %}
- You have no DS Data added. Enable DNSSEC by adding DS Data or return to the DNSSEC page and click 'enable.' + You have no DS Data added. Enable DNSSEC by adding DS Data.
{% endif %} + {% for form in formset %} + {% include "includes/form_errors.html" with form=form %} + {% endfor %} +

DS Data

- {% if domain.dnssecdata is not None and domain.dnssecdata.keyData is not None %} -
-
-

Warning, you cannot add DS Data

-

- You cannot add DS Data because you have already added Key Data. Delete your Key Data records in order to add DS Data. -

-
-
- {% elif not dnssec_ds_confirmed %} -

In order to enable DNSSEC, you must first configure it with your DNS hosting service.

-

Enter the values given by your DNS provider for DS Data.

-

Required fields are marked with an asterisk (*).

-
- {% csrf_token %} - -
- {% else %} +

In order to enable DNSSEC, you must first configure it with your DNS hosting service.

Enter the values given by your DNS provider for DS Data.

+ {% include "includes/required_fields.html" %}
@@ -104,20 +81,40 @@ -
-
+ + {% if trigger_modal %} + {% endif %} + {# Use data-force-action to take esc out of the equation and pass cancel_button_resets_ds_form to effectuate a reset in the view #} +
+ {% include 'includes/modal.html' with cancel_button_resets_ds_form=True modal_heading="Warning: You are about to delete all DS records on your domain" modal_description="To fully disable DNSSEC: In addition to deleting your DS records here you’ll also need to delete the DS records at your DNS host. To avoid causing your domain to appear offline you should wait to delete your DS records at your DNS host until the Time to Live (TTL) expires. This is often less than 24 hours, but confirm with your provider." modal_button=modal_button|safe %} +
+ {% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_keydata.html b/src/registrar/templates/domain_keydata.html deleted file mode 100644 index 167d86370..000000000 --- a/src/registrar/templates/domain_keydata.html +++ /dev/null @@ -1,110 +0,0 @@ -{% extends "domain_base.html" %} -{% load static field_helpers url_helpers %} - -{% block title %}Key Data | {{ domain.name }} | {% endblock %} - -{% block domain_content %} - {% for form in formset %} - {% include "includes/form_errors.html" with form=form %} - {% endfor %} - -

Key Data

- - {% if domain.dnssecdata is not None and domain.dnssecdata.dsData is not None %} -
-
-

Warning, you cannot add Key Data

-

- You cannot add Key Data because you have already added DS Data. Delete your DS Data records in order to add Key Data. -

-
-
- {% elif not dnssec_key_confirmed %} -

In order to enable DNSSEC and add DS records, you must first configure it with your DNS hosting service. Your configuration will determine whether you need to add DS Data or Key Data. Contact your DNS hosting provider if you are unsure which record type to add.

-
- {% csrf_token %} - -
- {% else %} - -

Enter the values given by your DNS provider for DS Key Data.

- {% include "includes/required_fields.html" %} - -
- {% csrf_token %} - {{ formset.management_form }} - - {% for form in formset %} -
- - DS Data record {{forloop.counter}} - -

DS Data record {{forloop.counter}}

- -
-
- {% with attr_required=True add_group_class="usa-form-group--unstyled-error" %} - {% input_with_errors form.flag %} - {% endwith %} -
-
- {% with attr_required=True add_group_class="usa-form-group--unstyled-error" %} - {% input_with_errors form.protocol %} - {% endwith %} -
-
- {% with attr_required=True add_group_class="usa-form-group--unstyled-error" %} - {% input_with_errors form.algorithm %} - {% endwith %} -
-
- -
-
- {% with attr_required=True add_group_class="usa-form-group--unstyled-error" %} - {% input_with_errors form.pub_key %} - {% endwith %} -
-
- -
-
- -
-
- -
- {% endfor %} - - - - -
- -
- -
- {% endif %} -{% endblock %} {# domain_content #} diff --git a/src/registrar/templates/domain_sidebar.html b/src/registrar/templates/domain_sidebar.html index 1acd87eeb..65c5254e9 100644 --- a/src/registrar/templates/domain_sidebar.html +++ b/src/registrar/templates/domain_sidebar.html @@ -34,7 +34,7 @@ > DNSSEC - {% if domain.dnssecdata is not None or request.path|startswith:url and request.path|endswith:'data' %} + {% if domain.dnssecdata is not None or request.path|startswith:url and request.path|endswith:'dsdata' %}
  • {% url 'domain-dns-dnssec-dsdata' pk=domain.id as url %} @@ -44,15 +44,6 @@ DS Data
  • - -
  • - {% url 'domain-dns-dnssec-keydata' pk=domain.id as url %} - - DS Key Data - -
{% endif %} @@ -100,7 +91,7 @@ - User management + Domain managers diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index 22b9d18d1..f66eef5a6 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -1,10 +1,23 @@ {% extends "domain_base.html" %} -{% load static %} +{% load static url_helpers %} -{% block title %}User management | {{ domain.name }} | {% endblock %} +{% block title %}Domain managers | {{ domain.name }} | {% endblock %} {% block domain_content %} -

User management

+

Domain managers

+ +

+ Domain managers can update all information related to a domain within the + .gov registrar, including contact details, authorizing official, security + email, and DNS name servers. +

+ +
    +
  • There is no limit to the number of domain managers you can add.
  • +
  • After adding a domain manager, an email invitation will be sent to that user with + instructions on how to set up an account.
  • +
  • To remove a domain manager, contact us for assistance. +
{% if domain.permissions %}
diff --git a/src/registrar/templates/includes/modal.html b/src/registrar/templates/includes/modal.html index 996759576..1f9fbcfd4 100644 --- a/src/registrar/templates/includes/modal.html +++ b/src/registrar/templates/includes/modal.html @@ -1,3 +1,5 @@ +{% load static form_helpers url_helpers %} +
- + {% comment %} The cancel button the DS form actually triggers a context change in the view, + in addition to being a close modal hook {% endcomment %} + {% if cancel_button_resets_ds_form %} +
+ {% csrf_token %} + +
+ {% else %} + + {% endif %}
diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py index 3614db18e..14e2c9e3e 100644 --- a/src/registrar/templatetags/custom_filters.py +++ b/src/registrar/templatetags/custom_filters.py @@ -1,7 +1,10 @@ +import logging from django import template import re +from registrar.models.domain_application import DomainApplication register = template.Library() +logger = logging.getLogger(__name__) @register.filter(name="extract_value") @@ -48,3 +51,16 @@ def contains_checkbox(html_list): if re.search(r']*type="checkbox"', html_string): return True return False + + +@register.filter +def get_organization_long_name(organization_type): + organization_choices_dict = dict( + DomainApplication.OrganizationChoicesVerbose.choices + ) + long_form_type = organization_choices_dict[organization_type] + if long_form_type is None: + logger.error("Organization type error, triggered by a template's custom filter") + return "Error" + + return long_form_type diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 463530e85..2f1430388 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -774,12 +774,6 @@ class MockEppLib(TestCase): "digestType": 1, "digest": "ec0bdd990b39feead889f0ba613db4adecb4adec", } - keyDataDict = { - "flags": 257, - "protocol": 3, - "alg": 1, - "pubKey": "AQPJ////4Q==", - } dnssecExtensionWithDsData = extensions.DNSSECExtension( **{ "dsData": [ @@ -795,11 +789,6 @@ class MockEppLib(TestCase): ], # type: ignore } ) - dnssecExtensionWithKeyData = extensions.DNSSECExtension( - **{ - "keyData": [common.DNSSECKeyData(**keyDataDict)], # type: ignore - } - ) dnssecExtensionRemovingDsData = extensions.DNSSECExtension() infoDomainHasIP = fakedEppObject( @@ -951,10 +940,6 @@ class MockEppLib(TestCase): self.mockDataInfoDomain, self.dnssecExtensionWithMultDsData, ), - "dnssec-keydata.gov": ( - self.mockDataInfoDomain, - self.dnssecExtensionWithKeyData, - ), "dnssec-none.gov": (self.mockDataInfoDomain, None), "my-nameserver.gov": ( self.infoDomainTwoHosts diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 51ace34f7..f2db9d5ee 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -11,6 +11,7 @@ from registrar.admin import ( ListHeaderAdmin, MyUserAdmin, AuditedAdmin, + ContactAdmin, ) from registrar.models import ( Domain, @@ -52,6 +53,26 @@ class TestDomainAdmin(MockEppLib): self.factory = RequestFactory() super().setUp() + def test_short_org_name_in_domains_list(self): + """ + Make sure the short name is displaying in admin on the list page + """ + self.client.force_login(self.superuser) + application = completed_application(status=DomainApplication.IN_REVIEW) + application.approve() + + response = self.client.get("/admin/registrar/domain/") + + # There are 3 template references to Federal (3) plus one reference in the table + # for our actual application + self.assertContains(response, "Federal", count=4) + # This may be a bit more robust + self.assertContains( + response, 'Federal', count=1 + ) + # Now let's make sure the long description does not exist + self.assertNotContains(response, "Federal: an agency of the U.S. government") + @skip("Why did this test stop working, and is is a good test") def test_place_and_remove_hold(self): domain = create_ready_domain() @@ -243,8 +264,11 @@ class TestDomainAdmin(MockEppLib): raise def tearDown(self): - User.objects.all().delete() super().tearDown() + Domain.objects.all().delete() + DomainInformation.objects.all().delete() + DomainApplication.objects.all().delete() + User.objects.all().delete() class TestDomainApplicationAdminForm(TestCase): @@ -300,6 +324,23 @@ class TestDomainApplicationAdmin(MockEppLib): self.superuser = create_superuser() self.staffuser = create_user() + def test_short_org_name_in_applications_list(self): + """ + Make sure the short name is displaying in admin on the list page + """ + self.client.force_login(self.superuser) + completed_application() + response = self.client.get("/admin/registrar/domainapplication/") + # There are 3 template references to Federal (3) plus one reference in the table + # for our actual application + self.assertContains(response, "Federal", count=4) + # This may be a bit more robust + self.assertContains( + response, 'Federal', count=1 + ) + # Now let's make sure the long description does not exist + self.assertNotContains(response, "Federal: an agency of the U.S. government") + @boto3_mocking.patching def test_save_model_sends_submitted_email(self): # make sure there is no user with this email @@ -620,9 +661,6 @@ class TestDomainApplicationAdmin(MockEppLib): expected_fields = [ "creator", "about_your_organization", - "address_line1", - "address_line2", - "zipcode", "requested_domain", "alternative_domains", "purpose", @@ -1313,3 +1351,38 @@ class DomainSessionVariableTest(TestCase): {"_edit_domain": "true"}, follow=True, ) + + +class ContactAdminTest(TestCase): + def setUp(self): + self.site = AdminSite() + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.admin = ContactAdmin(model=get_user_model(), admin_site=None) + self.superuser = create_superuser() + self.staffuser = create_user() + + def test_readonly_when_restricted_staffuser(self): + request = self.factory.get("/") + request.user = self.staffuser + + readonly_fields = self.admin.get_readonly_fields(request) + + expected_fields = [ + "user", + ] + + self.assertEqual(readonly_fields, expected_fields) + + def test_readonly_when_restricted_superuser(self): + request = self.factory.get("/") + request.user = self.superuser + + readonly_fields = self.admin.get_readonly_fields(request) + + expected_fields = [] + + self.assertEqual(readonly_fields, expected_fields) + + def tearDown(self): + User.objects.all().delete() diff --git a/src/registrar/tests/test_migrations.py b/src/registrar/tests/test_migrations.py index 95e5853ff..165ef6f71 100644 --- a/src/registrar/tests/test_migrations.py +++ b/src/registrar/tests/test_migrations.py @@ -36,7 +36,7 @@ class TestGroups(TestCase): # Define the expected permission codenames expected_permissions = [ "view_logentry", - "view_contact", + "change_contact", "view_domain", "change_domainapplication", "change_domaininformation", @@ -45,6 +45,7 @@ class TestGroups(TestCase): "change_draftdomain", "analyst_access_permission", "change_user", + "change_website", ] # Get the codenames of actual permissions associated with the group diff --git a/src/registrar/tests/test_models.py b/src/registrar/tests/test_models.py index e76dea035..8f8442b6e 100644 --- a/src/registrar/tests/test_models.py +++ b/src/registrar/tests/test_models.py @@ -14,7 +14,8 @@ from registrar.models import ( UserDomainRole, ) -import boto3_mocking # type: ignore +import boto3_mocking +from registrar.models.transition_domain import TransitionDomain # type: ignore from .common import MockSESClient, less_console_noise, completed_application from django_fsm import TransitionNotAllowed @@ -612,3 +613,48 @@ class TestInvitations(TestCase): """A new user's first_login callback retrieves their invitations.""" self.user.first_login() self.assertTrue(UserDomainRole.objects.get(user=self.user, domain=self.domain)) + + +class TestUser(TestCase): + """For now, just test actions that + occur on user login.""" + + def setUp(self): + self.email = "mayor@igorville.gov" + self.domain_name = "igorvilleInTransition.gov" + self.user, _ = User.objects.get_or_create(email=self.email) + + # clean out the roles each time + UserDomainRole.objects.all().delete() + + TransitionDomain.objects.get_or_create( + username="mayor@igorville.gov", domain_name=self.domain_name + ) + + def tearDown(self): + super().tearDown() + Domain.objects.all().delete() + DomainInvitation.objects.all().delete() + DomainInformation.objects.all().delete() + TransitionDomain.objects.all().delete() + User.objects.all().delete() + + def test_check_transition_domains_on_login(self): + """A new user's first_login callback checks transition domains. + Makes DomainInformation object.""" + self.domain, _ = Domain.objects.get_or_create(name=self.domain_name) + + self.user.first_login() + self.assertTrue(DomainInformation.objects.get(domain=self.domain)) + + def test_check_transition_domains_without_domains_on_login(self): + """A new user's first_login callback checks transition domains. + This test makes sure that in the event a domain does not exist + for a given transition domain, both a domain and domain invitation + are created.""" + self.user.first_login() + self.assertTrue(Domain.objects.get(name=self.domain_name)) + + domain = Domain.objects.get(name=self.domain_name) + self.assertTrue(DomainInvitation.objects.get(email=self.email, domain=domain)) + self.assertTrue(DomainInformation.objects.get(domain=domain)) diff --git a/src/registrar/tests/test_models_domain.py b/src/registrar/tests/test_models_domain.py index 5759df1be..1d87c6b1c 100644 --- a/src/registrar/tests/test_models_domain.py +++ b/src/registrar/tests/test_models_domain.py @@ -1991,79 +1991,6 @@ class TestRegistrantDNSSEC(MockEppLib): patcher.stop() - def test_user_adds_dnssec_keydata(self): - """ - Scenario: Registrant adds DNSSEC key data. - Verify that both the setter and getter are functioning properly - - This test verifies: - 1 - setter calls UpdateDomain command - 2 - setter adds the UpdateDNSSECExtension extension to the command - 3 - setter causes the getter to call info domain on next get from cache - 4 - getter properly parses dnssecdata from InfoDomain response and sets to cache - - """ - - # need to use a separate patcher and side_effect for this test, as - # response from InfoDomain must be different for different iterations - # of the same command - def side_effect(_request, cleaned): - if isinstance(_request, commands.InfoDomain): - if mocked_send.call_count == 1: - return MagicMock(res_data=[self.mockDataInfoDomain]) - else: - return MagicMock( - res_data=[self.mockDataInfoDomain], - extensions=[self.dnssecExtensionWithKeyData], - ) - else: - return MagicMock(res_data=[self.mockDataInfoHosts]) - - patcher = patch("registrar.models.domain.registry.send") - mocked_send = patcher.start() - mocked_send.side_effect = side_effect - - domain, _ = Domain.objects.get_or_create(name="dnssec-keydata.gov") - - domain.dnssecdata = self.dnssecExtensionWithKeyData - # get the DNS SEC extension added to the UpdateDomain command - # and verify that it is properly sent - # args[0] is the _request sent to registry - args, _ = mocked_send.call_args - # assert that the extension matches - self.assertEquals( - args[0].extensions[0], - self.createUpdateExtension(self.dnssecExtensionWithKeyData), - ) - # test that the dnssecdata getter is functioning properly - dnssecdata_get = domain.dnssecdata - mocked_send.assert_has_calls( - [ - call( - commands.UpdateDomain( - name="dnssec-keydata.gov", - nsset=None, - keyset=None, - registrant=None, - auth_info=None, - ), - cleaned=True, - ), - call( - commands.InfoDomain( - name="dnssec-keydata.gov", - ), - cleaned=True, - ), - ] - ) - - self.assertEquals( - dnssecdata_get.keyData, self.dnssecExtensionWithKeyData.keyData - ) - - patcher.stop() - def test_update_is_unsuccessful(self): """ Scenario: An update to the dns data is unsuccessful diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 4b9ac1dcb..10b2adee2 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -142,9 +142,12 @@ class DomainApplicationTests(TestWithUser, WebTest): @boto3_mocking.patching def test_application_form_submission(self): - """Can fill out the entire form and submit. + """ + Can fill out the entire form and submit. As we add additional form pages, we need to include them here to make this test work. + + This test also looks for the long organization name on the summary page. """ num_pages_tested = 0 # elections, type_of_work, tribal_government, no_other_contacts @@ -428,7 +431,8 @@ class DomainApplicationTests(TestWithUser, WebTest): review_form = review_page.form # Review page contains all the previously entered data - self.assertContains(review_page, "Federal") + # Let's make sure the long org name is displayed + self.assertContains(review_page, "Federal: an agency of the U.S. government") self.assertContains(review_page, "Executive") self.assertContains(review_page, "Testorg") self.assertContains(review_page, "address 1") @@ -1066,6 +1070,26 @@ class DomainApplicationTests(TestWithUser, WebTest): # page = self.app.get(url) # self.assertNotContains(page, "VALUE") + def test_long_org_name_in_application(self): + """ + Make sure the long name is displaying in the application form, + org step + """ + request = self.app.get(reverse("application:")).follow() + self.assertContains(request, "Federal: an agency of the U.S. government") + + def test_long_org_name_in_application_manage(self): + """ + Make sure the long name is displaying in the application summary + page (manage your application) + """ + completed_application(status=DomainApplication.SUBMITTED, user=self.user) + home_page = self.app.get("/") + self.assertContains(home_page, "city.gov") + # click the "Edit" link + detail_page = home_page.click("Manage") + self.assertContains(detail_page, "Federal: an agency of the U.S. government") + class TestWithDomainPermissions(TestWithUser): def setUp(self): @@ -1082,7 +1106,6 @@ class TestWithDomainPermissions(TestWithUser): self.domain_multdsdata, _ = Domain.objects.get_or_create( name="dnssec-multdsdata.gov" ) - self.domain_keydata, _ = Domain.objects.get_or_create(name="dnssec-keydata.gov") # We could simply use domain (igorville) but this will be more readable in tests # that inherit this setUp self.domain_dnssec_none, _ = Domain.objects.get_or_create( @@ -1099,9 +1122,6 @@ class TestWithDomainPermissions(TestWithUser): DomainInformation.objects.get_or_create( creator=self.user, domain=self.domain_multdsdata ) - DomainInformation.objects.get_or_create( - creator=self.user, domain=self.domain_keydata - ) DomainInformation.objects.get_or_create( creator=self.user, domain=self.domain_dnssec_none ) @@ -1124,11 +1144,6 @@ class TestWithDomainPermissions(TestWithUser): domain=self.domain_multdsdata, role=UserDomainRole.Roles.MANAGER, ) - UserDomainRole.objects.get_or_create( - user=self.user, - domain=self.domain_keydata, - role=UserDomainRole.Roles.MANAGER, - ) UserDomainRole.objects.get_or_create( user=self.user, domain=self.domain_dnssec_none, @@ -1257,14 +1272,14 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest): self.assertContains(detail_page, "(1.2.3.4, 2.3.4.5)") -class TestDomainUserManagement(TestDomainOverview): - def test_domain_user_management(self): +class TestDomainManagers(TestDomainOverview): + def test_domain_managers(self): response = self.client.get( reverse("domain-users", kwargs={"pk": self.domain.id}) ) - self.assertContains(response, "User management") + self.assertContains(response, "Domain managers") - def test_domain_user_management_add_link(self): + def test_domain_managers_add_link(self): """Button to get to user add page works.""" management_page = self.app.get( reverse("domain-users", kwargs={"pk": self.domain.id}) @@ -1691,38 +1706,13 @@ class TestDomainDNSSEC(TestDomainOverview): def test_dnssec_page_refreshes_enable_button(self): """DNSSEC overview page loads when domain has no DNSSEC data - and shows a 'Enable DNSSEC' button. When button is clicked the template - updates. When user navigates away then comes back to the page, the - 'Enable DNSSEC' button is shown again.""" - # home_page = self.app.get("/") + and shows a 'Enable DNSSEC' button.""" page = self.client.get( reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}) ) self.assertContains(page, "Enable DNSSEC") - # Prepare the data for the POST request - post_data = { - "enable_dnssec": "Enable DNSSEC", - } - updated_page = self.client.post( - reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}), - post_data, - follow=True, - ) - - self.assertEqual(updated_page.status_code, 200) - - self.assertContains(updated_page, "Add DS Data") - self.assertContains(updated_page, "Add Key Data") - - self.app.get("/") - - back_to_page = self.client.get( - reverse("domain-dns-dnssec", kwargs={"pk": self.domain.id}) - ) - self.assertContains(back_to_page, "Enable DNSSEC") - def test_dnssec_page_loads_with_data_in_domain(self): """DNSSEC overview page loads when domain has DNSSEC data and the template contains a button to disable DNSSEC.""" @@ -1767,43 +1757,25 @@ class TestDomainDNSSEC(TestDomainOverview): ) self.assertContains(page, "DS Data record 1") - def test_ds_form_loads_with_key_data(self): - """DNSSEC Add DS Data page loads when there is - domain DNSSEC KEY data and shows an alert""" - - page = self.client.get( - reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_keydata.id}) + def test_ds_data_form_modal(self): + """When user clicks on save, a modal pops up.""" + add_data_page = self.app.get( + reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}) ) - self.assertContains(page, "Warning, you cannot add DS Data") - - def test_key_form_loads_with_no_domain_data(self): - """DNSSEC Add Key Data page loads when there is no - domain DNSSEC data and shows a button to Add DS Key record""" - - page = self.client.get( - reverse( - "domain-dns-dnssec-keydata", kwargs={"pk": self.domain_dnssec_none.id} - ) + # Assert that a hidden trigger for the modal does not exist. + # This hidden trigger will pop on the page when certain condition are met: + # 1) Initial form contained DS data, 2) All data is deleted and form is + # submitted. + self.assertNotContains(add_data_page, "Trigger Disable DNSSEC Modal") + # Simulate a delete all data + form_data = {} + response = self.client.post( + reverse("domain-dns-dnssec-dsdata", kwargs={"pk": self.domain_dsdata.id}), + data=form_data, ) - self.assertContains(page, "Add DS Key record") - - def test_key_form_loads_with_key_data(self): - """DNSSEC Add Key Data page loads when there is - domain DNSSEC Key data and shows the data""" - - page = self.client.get( - reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.domain_keydata.id}) - ) - self.assertContains(page, "DS Data record 1") - - def test_key_form_loads_with_ds_data(self): - """DNSSEC Add Key Data page loads when there is - domain DNSSEC DS data and shows an alert""" - - page = self.client.get( - reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.domain_dsdata.id}) - ) - self.assertContains(page, "Warning, you cannot add Key Data") + self.assertEqual(response.status_code, 200) # Adjust status code as needed + # Now check to see whether the JS trigger for the modal is present on the page + self.assertContains(response, "Trigger Disable DNSSEC Modal") def test_ds_data_form_submits(self): """DS Data form submits successfully @@ -1849,50 +1821,6 @@ class TestDomainDNSSEC(TestDomainOverview): # the field. self.assertContains(result, "Key tag is required", count=2, status_code=200) - def test_key_data_form_submits(self): - """Key Data form submits successfully - - Uses self.app WebTest because we need to interact with forms. - """ - add_data_page = self.app.get( - reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.domain_keydata.id}) - ) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - with less_console_noise(): # swallow log warning message - result = add_data_page.forms[0].submit() - # form submission was a post, response should be a redirect - self.assertEqual(result.status_code, 302) - self.assertEqual( - result["Location"], - reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.domain_keydata.id}), - ) - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - page = result.follow() - self.assertContains( - page, "The Key Data records for this domain have been updated." - ) - - def test_key_data_form_invalid(self): - """Key Data form errors with invalid data - - Uses self.app WebTest because we need to interact with forms. - """ - add_data_page = self.app.get( - reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.domain_keydata.id}) - ) - session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - # first two nameservers are required, so if we empty one out we should - # get a form error - add_data_page.forms[0]["form-0-pub_key"] = "" - with less_console_noise(): # swallow logged warning message - result = add_data_page.forms[0].submit() - # form submission was a post with an error, response should be a 200 - # error text appears twice, once at the top of the page, once around - # the field. - self.assertContains(result, "Pub key is required", count=2, status_code=200) - class TestApplicationStatus(TestWithUser, WebTest): def setUp(self): diff --git a/src/registrar/views/__init__.py b/src/registrar/views/__init__.py index 5fd81df8c..c1400d7c0 100644 --- a/src/registrar/views/__init__.py +++ b/src/registrar/views/__init__.py @@ -7,7 +7,6 @@ from .domain import ( DomainNameserversView, DomainDNSSECView, DomainDsDataView, - DomainKeyDataView, DomainYourContactInformationView, DomainSecurityEmailView, DomainUsersView, diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index fce94f175..a2f2c1198 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -10,8 +10,8 @@ import logging from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin from django.db import IntegrityError +from django.http import HttpResponseRedirect from django.shortcuts import redirect -from django.template import RequestContext from django.urls import reverse from django.views.generic.edit import FormMixin @@ -34,8 +34,6 @@ from ..forms import ( DomainDnssecForm, DomainDsdataFormset, DomainDsdataForm, - DomainKeydataFormset, - DomainKeydataForm, ) from epplibwrapper import ( @@ -293,8 +291,8 @@ class DomainDNSSECView(DomainFormBaseView): # Create HTML for the modal button modal_button = ( '' + 'class="usa-button usa-button--secondary" ' + 'name="disable_dnssec">Confirm' ) context["modal_button"] = modal_button @@ -319,12 +317,6 @@ class DomainDNSSECView(DomainFormBaseView): errmsg = "Error removing existing DNSSEC record(s)." logger.error(errmsg + ": " + err) messages.error(self.request, errmsg) - request.session["dnssec_ds_confirmed"] = False - request.session["dnssec_key_confirmed"] = False - elif "enable_dnssec" in request.POST: - request.session["dnssec_enabled"] = True - request.session["dnssec_ds_confirmed"] = False - request.session["dnssec_key_confirmed"] = False return self.form_valid(form) @@ -341,24 +333,17 @@ class DomainDsDataView(DomainFormBaseView): dnssecdata: extensions.DNSSECExtension = self.object.dnssecdata initial_data = [] - if dnssecdata is not None: - if dnssecdata.keyData is not None: - # TODO: Throw an error - # Note: This is moot if we're - # removing key data - pass - - if dnssecdata.dsData is not None: - # Add existing nameservers as initial data - initial_data.extend( - { - "key_tag": record.keyTag, - "algorithm": record.alg, - "digest_type": record.digestType, - "digest": record.digest, - } - for record in dnssecdata.dsData - ) + if dnssecdata is not None and dnssecdata.dsData is not None: + # Add existing nameservers as initial data + initial_data.extend( + { + "key_tag": record.keyTag, + "algorithm": record.alg, + "digest_type": record.digestType, + "digest": record.digest, + } + for record in dnssecdata.dsData + ) # Ensure at least 1 record, filled or empty while len(initial_data) == 0: @@ -376,38 +361,49 @@ class DomainDsDataView(DomainFormBaseView): # use "formset" instead of "form" for the key context["formset"] = context.pop("form") - # set the dnssec_ds_confirmed flag in the context for this view - # based either on the existence of DS Data in the domain, - # or on the flag stored in the session - dnssecdata: extensions.DNSSECExtension = self.object.dnssecdata - - if dnssecdata is not None and dnssecdata.dsData is not None: - self.request.session["dnssec_ds_confirmed"] = True - - context["dnssec_ds_confirmed"] = self.request.session.get( - "dnssec_ds_confirmed", False - ) return context def post(self, request, *args, **kwargs): """Formset submission posts to this view.""" self._get_domain(request) formset = self.get_form() + override = False - if "confirm-ds" in request.POST: - request.session["dnssec_ds_confirmed"] = True - request.session["dnssec_key_confirmed"] = False - return super().form_valid(formset) - + # This is called by the form cancel button, + # and also by the modal's X and cancel buttons if "btn-cancel-click" in request.POST: - return redirect("/", {"formset": formset}, RequestContext(request)) + url = self.get_success_url() + return HttpResponseRedirect(url) - if formset.is_valid(): + # This is called by the Disable DNSSEC modal to override + if "disable-override-click" in request.POST: + override = True + + # This is called when all DNSSEC data has been deleted and the + # Save button is pressed + if len(formset) == 0 and formset.initial != [{}] and override is False: + # trigger the modal + # get context data from super() rather than self + # to preserve the context["form"] + context = super().get_context_data(form=formset) + context["trigger_modal"] = True + # Create HTML for the modal button + modal_button = ( + '' + ) + + # context to back out of a broken form on all fields delete + context["modal_button"] = modal_button + return self.render_to_response(context) + + if formset.is_valid() or override: return self.form_valid(formset) else: return self.form_invalid(formset) - def form_valid(self, formset): + def form_valid(self, formset, **kwargs): """The formset is valid, perform something with it.""" # Set the dnssecdata from the formset @@ -434,10 +430,12 @@ class DomainDsDataView(DomainFormBaseView): try: self.object.dnssecdata = dnssecdata except RegistryError as err: - errmsg = "Error updating DNSSEC data in the registry." - logger.error(errmsg) - logger.error(err) - messages.error(self.request, errmsg) + if err.is_connection_error(): + messages.error(self.request, CANNOT_CONTACT_REGISTRY) + logger.error(f"Registry connection error: {err}") + else: + messages.error(self.request, GENERIC_ERROR) + logger.error(f"Registry error: {err}") return self.form_invalid(formset) else: messages.success( @@ -447,123 +445,6 @@ class DomainDsDataView(DomainFormBaseView): return super().form_valid(formset) -class DomainKeyDataView(DomainFormBaseView): - """Domain DNSSEC key data editing view.""" - - template_name = "domain_keydata.html" - form_class = DomainKeydataFormset - form = DomainKeydataForm - - def get_initial(self): - """The initial value for the form (which is a formset here).""" - dnssecdata: extensions.DNSSECExtension = self.object.dnssecdata - initial_data = [] - - if dnssecdata is not None: - if dnssecdata.dsData is not None: - # TODO: Throw an error? - # Note: this is moot if we're - # removing Key data - pass - - if dnssecdata.keyData is not None: - # Add existing keydata as initial data - initial_data.extend( - { - "flag": record.flags, - "protocol": record.protocol, - "algorithm": record.alg, - "pub_key": record.pubKey, - } - for record in dnssecdata.keyData - ) - - # Ensure at least 1 record, filled or empty - while len(initial_data) == 0: - initial_data.append({}) - - return initial_data - - def get_success_url(self): - """Redirect to the Key Data page for the domain.""" - return reverse("domain-dns-dnssec-keydata", kwargs={"pk": self.object.pk}) - - def get_context_data(self, **kwargs): - """Adjust context from FormMixin for formsets.""" - context = super().get_context_data(**kwargs) - # use "formset" instead of "form" for the key - context["formset"] = context.pop("form") - - # set the dnssec_key_confirmed flag in the context for this view - # based either on the existence of Key Data in the domain, - # or on the flag stored in the session - dnssecdata: extensions.DNSSECExtension = self.object.dnssecdata - - if dnssecdata is not None and dnssecdata.keyData is not None: - self.request.session["dnssec_key_confirmed"] = True - - context["dnssec_key_confirmed"] = self.request.session.get( - "dnssec_key_confirmed", False - ) - return context - - def post(self, request, *args, **kwargs): - """Formset submission posts to this view.""" - self._get_domain(request) - formset = self.get_form() - - if "confirm-key" in request.POST: - request.session["dnssec_key_confirmed"] = True - request.session["dnssec_ds_confirmed"] = False - self.object.save() - return super().form_valid(formset) - - if "btn-cancel-click" in request.POST: - return redirect("/", {"formset": formset}, RequestContext(request)) - - if formset.is_valid(): - return self.form_valid(formset) - else: - return self.form_invalid(formset) - - def form_valid(self, formset): - """The formset is valid, perform something with it.""" - - # Set the nameservers from the formset - dnssecdata = extensions.DNSSECExtension() - - for form in formset: - try: - # if 'delete' not in form.cleaned_data - # or form.cleaned_data['delete'] == False: - keyrecord = { - "flags": int(form.cleaned_data["flag"]), - "protocol": int(form.cleaned_data["protocol"]), - "alg": int(form.cleaned_data["algorithm"]), - "pubKey": form.cleaned_data["pub_key"], - } - if dnssecdata.keyData is None: - dnssecdata.keyData = [] - dnssecdata.keyData.append(common.DNSSECKeyData(**keyrecord)) - except KeyError: - # no server information in this field, skip it - pass - try: - self.object.dnssecdata = dnssecdata - except RegistryError as err: - errmsg = "Error updating DNSSEC data in the registry." - logger.error(errmsg) - logger.error(err) - messages.error(self.request, errmsg) - return self.form_invalid(formset) - else: - messages.success( - self.request, "The Key Data records for this domain have been updated." - ) - # superclass has the redirect - return super().form_valid(formset) - - class DomainYourContactInformationView(DomainFormBaseView): """Domain your contact information editing view.""" @@ -656,7 +537,7 @@ class DomainSecurityEmailView(DomainFormBaseView): class DomainUsersView(DomainBaseView): - """User management page in the domain details.""" + """Domain managers page in the domain details.""" template_name = "domain_users.html" diff --git a/src/requirements.txt b/src/requirements.txt index ae6ed90df..ff289ea63 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,53 +1,61 @@ -i https://pypi.python.org/simple -asgiref==3.7.2 ; python_version >= '3.7' -boto3==1.26.145 -botocore==1.29.145 ; python_version >= '3.7' -cachetools==5.3.1 -certifi==2023.7.22 ; python_version >= '3.6' +annotated-types==0.6.0; python_version >= '3.8' +asgiref==3.7.2; python_version >= '3.7' +boto3==1.28.66; python_version >= '3.7' +botocore==1.31.66; python_version >= '3.7' +cachetools==5.3.1; python_version >= '3.7' +certifi==2023.7.22; python_version >= '3.6' cfenv==0.5.3 -cffi==1.15.1 -charset-normalizer==3.1.0 ; python_full_version >= '3.7.0' -cryptography==41.0.4 ; python_version >= '3.7' -defusedxml==0.7.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -dj-database-url==2.0.0 +cffi==1.16.0; python_version >= '3.8' +charset-normalizer==3.3.0; python_full_version >= '3.7.0' +cryptography==41.0.4; python_version >= '3.7' +defusedxml==0.7.1; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +dj-database-url==2.1.0 dj-email-url==1.0.6 -django==4.2.3 -django-allow-cidr==0.6.0 -django-auditlog==2.3.0 +django==4.2.6; python_version >= '3.8' +django-allow-cidr==0.7.1 +django-auditlog==2.3.0; python_version >= '3.7' django-cache-url==3.4.4 django-csp==3.7 django-fsm==2.8.1 django-login-required-middleware==0.9.0 -django-phonenumber-field[phonenumberslite]==7.1.0 -django-widget-tweaks==1.4.12 -environs[django]==9.5.0 -faker==18.10.0 -git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c#egg=fred-epplib +django-phonenumber-field[phonenumberslite]==7.2.0; python_version >= '3.8' +django-widget-tweaks==1.5.0; python_version >= '3.8' +environs[django]==9.5.0; python_version >= '3.6' +faker==19.11.0; python_version >= '3.8' +fred-epplib@ git+https://github.com/cisagov/epplib.git@d56d183f1664f34c40ca9716a3a9a345f0ef561c furl==2.1.3 -future==0.18.3 ; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' -gunicorn==20.1.0 -idna==3.4 ; python_version >= '3.5' -jmespath==1.0.1 ; python_version >= '3.7' -lxml==4.9.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -mako==1.2.4 ; python_version >= '3.7' -markupsafe==2.1.2 ; python_version >= '3.7' -marshmallow==3.19.0 ; python_version >= '3.7' -oic==1.6.0 +future==0.18.3; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' +gevent==23.9.1; python_version >= '3.8' +geventconnpool@ git+https://github.com/rasky/geventconnpool.git@1bbb93a714a331a069adf27265fe582d9ba7ecd4 +greenlet==3.0.0; python_version >= '3.7' +gunicorn==21.2.0; python_version >= '3.5' +idna==3.4; python_version >= '3.5' +jmespath==1.0.1; python_version >= '3.7' +lxml==4.9.3; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +mako==1.2.4; python_version >= '3.7' +markupsafe==2.1.3; python_version >= '3.7' +marshmallow==3.20.1; python_version >= '3.8' +oic==1.6.1; python_version ~= '3.7' orderedmultidict==1.0.1 -packaging==23.1 ; python_version >= '3.7' -phonenumberslite==8.13.13 -psycopg2-binary==2.9.6 +packaging==23.2; python_version >= '3.7' +phonenumberslite==8.13.23 +psycopg2-binary==2.9.9; python_version >= '3.7' pycparser==2.21 -pycryptodomex==3.18.0 -pydantic==1.10.8 ; python_version >= '3.7' +pycryptodomex==3.19.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' +pydantic==2.4.2; python_version >= '3.7' +pydantic-core==2.10.1; python_version >= '3.7' +pydantic-settings==2.0.3; python_version >= '3.7' pyjwkest==1.4.2 -python-dateutil==2.8.2 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -python-dotenv==1.0.0 ; python_version >= '3.8' -requests==2.31.0 -s3transfer==0.6.1 ; python_version >= '3.7' -setuptools==67.8.0 ; python_version >= '3.7' -six==1.16.0 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' -sqlparse==0.4.4 ; python_version >= '3.5' -typing-extensions==4.6.3 -urllib3==1.26.17 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' -whitenoise==6.4.0 +python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +python-dotenv==1.0.0; python_version >= '3.8' +requests==2.31.0; python_version >= '3.7' +s3transfer==0.7.0; python_version >= '3.7' +setuptools==68.2.2; python_version >= '3.8' +six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +sqlparse==0.4.4; python_version >= '3.5' +typing-extensions==4.8.0; python_version >= '3.8' +urllib3==2.0.7; python_version >= '3.7' +whitenoise==6.6.0; python_version >= '3.8' +zope.event==5.0; python_version >= '3.7' +zope.interface==6.1; python_version >= '3.7'