mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-05 17:28:31 +02:00
Merge branch 'main' into sspj/domain-cache
This commit is contained in:
commit
3884536869
367 changed files with 1733 additions and 19554 deletions
|
@ -23,6 +23,9 @@ requests = "*"
|
|||
django-fsm = "*"
|
||||
django-phonenumber-field = {extras = ["phonenumberslite"], version = "*"}
|
||||
boto3 = "*"
|
||||
typing-extensions ='*'
|
||||
fred-epplib = {git = "https://github.com/cisagov/epplib.git", ref = "master"}
|
||||
|
||||
|
||||
[dev-packages]
|
||||
django-debug-toolbar = "*"
|
||||
|
@ -37,3 +40,4 @@ django-webtest = "*"
|
|||
types-cachetools = "*"
|
||||
boto3-mocking = "*"
|
||||
boto3-stubs = "*"
|
||||
django-model2puml = "*"
|
||||
|
|
503
src/Pipfile.lock
generated
503
src/Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "ebec8b958bcfde525ad74aa1e777b55855e86b2d63264612bc2855bf167070b1"
|
||||
"sha256": "fd7d0efa9a87dfe4b2bb228ee0e7978fba16c7cfdd3c443870900cfe899e2cfd"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
|
@ -16,43 +16,43 @@
|
|||
"default": {
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac",
|
||||
"sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"
|
||||
"sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e",
|
||||
"sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.6.0"
|
||||
"version": "==3.7.2"
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:38ca632be379963f2a2749b5f63a81fe1679913b954914f470ad282c77674bbc",
|
||||
"sha256:4d575c180312bec6108852bae12e6396b9d1bb404154d652c57ee849c62fbb83"
|
||||
"sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85",
|
||||
"sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.122"
|
||||
"version": "==1.26.145"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:9e4984a9e9777c6b949aa1e98323fa35480d9f99d447af7e179ae611f7ed5af9",
|
||||
"sha256:c3b41078d235761b9c5dc22f534a76952622ef96787b96bbd10242ec4d73f2a5"
|
||||
"sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a",
|
||||
"sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.29.122"
|
||||
"version": "==1.29.145"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
"sha256:13dfddc7b8df938c21a940dfa6557ce6e94a2f1cdfa58eb90c805721d58f2c14",
|
||||
"sha256:429e1a1e845c008ea6c85aa35d4b98b65d6a9763eeef3e37e92728a12d1de9d4"
|
||||
"sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590",
|
||||
"sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3.0"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3",
|
||||
"sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"
|
||||
"sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
|
||||
"sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2022.12.7"
|
||||
"version": "==2023.5.7"
|
||||
},
|
||||
"cfenv": {
|
||||
"hashes": [
|
||||
|
@ -214,28 +214,28 @@
|
|||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440",
|
||||
"sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288",
|
||||
"sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b",
|
||||
"sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958",
|
||||
"sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b",
|
||||
"sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d",
|
||||
"sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a",
|
||||
"sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404",
|
||||
"sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b",
|
||||
"sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e",
|
||||
"sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2",
|
||||
"sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c",
|
||||
"sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b",
|
||||
"sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9",
|
||||
"sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b",
|
||||
"sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636",
|
||||
"sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99",
|
||||
"sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e",
|
||||
"sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"
|
||||
"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"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==40.0.2"
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==41.0.1"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
|
@ -261,11 +261,11 @@
|
|||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78",
|
||||
"sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997"
|
||||
"sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee",
|
||||
"sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.2"
|
||||
"version": "==4.2.1"
|
||||
},
|
||||
"django-allow-cidr": {
|
||||
"hashes": [
|
||||
|
@ -277,11 +277,11 @@
|
|||
},
|
||||
"django-auditlog": {
|
||||
"hashes": [
|
||||
"sha256:189bcc486e5c5720fac3070ac9bf1c3911f45618ed85a30dfa7d272324ef5e9a",
|
||||
"sha256:39008937a7161ff0fce75f5e3c028240049c64fb1b20f4caa325c5c835e28802"
|
||||
"sha256:7bc2c87e4aff62dec9785d1b2359a2b27148f8c286f8a52b9114fc7876c5a9f7",
|
||||
"sha256:b9d3acebb64f3f2785157efe3f2f802e0929aafc579d85bbfb9827db4adab532"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.2.2"
|
||||
"version": "==2.3.0"
|
||||
},
|
||||
"django-cache-url": {
|
||||
"hashes": [
|
||||
|
@ -338,11 +338,15 @@
|
|||
},
|
||||
"faker": {
|
||||
"hashes": [
|
||||
"sha256:49060d40e6659e116f53353c5771ad2f2cbcd12b15771f49e3000a3a451f13ec",
|
||||
"sha256:ac903ba8cb5adbce2cdd15e5536118d484bbe01126f3c774dd9f6df77b61232d"
|
||||
"sha256:a70de9ec7a14a02d278755a11134baa5a297bb82600f115022d0d07080a9e77a",
|
||||
"sha256:dd15fa165ced55f668fbb0ad20ece98ab78ddacd58dc056950d66980ff61fa79"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==18.6.0"
|
||||
"version": "==18.10.0"
|
||||
},
|
||||
"fred-epplib": {
|
||||
"git": "https://github.com/cisagov/epplib.git",
|
||||
"ref": "f818cbf0b069a12f03e1d72e4b9f4900924b832d"
|
||||
},
|
||||
"furl": {
|
||||
"hashes": [
|
||||
|
@ -382,6 +386,89 @@
|
|||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.0.1"
|
||||
},
|
||||
"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"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==4.9.2"
|
||||
},
|
||||
"mako": {
|
||||
"hashes": [
|
||||
"sha256:c97c79c018b9165ac9922ae4f32da095ffd3c4e6872b45eded42926deea46818",
|
||||
|
@ -456,11 +543,11 @@
|
|||
},
|
||||
"oic": {
|
||||
"hashes": [
|
||||
"sha256:1bb80d7717faed750f1d8d482c06616a45f1fa5b2295d0620049040b4b48b97e",
|
||||
"sha256:6cd66c5203baa8cd90dd2b73d0cc83785e96c91fab67fa7b587549fcadb3c092"
|
||||
"sha256:2de3b83f1299dda8ed0460baad8bb2d4c6ac8bfc08a220c768b7e6e754caf9e7",
|
||||
"sha256:f82e087e0ffaba2194ebd24694721a25167d5d467fb06bf4f4da9f48d43e0cc6"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.5.0"
|
||||
"version": "==1.6.0"
|
||||
},
|
||||
"orderedmultidict": {
|
||||
"hashes": [
|
||||
|
@ -479,10 +566,10 @@
|
|||
},
|
||||
"phonenumberslite": {
|
||||
"hashes": [
|
||||
"sha256:90e7ad011dc571c9ba76a0816d7fc92a7de8944dcb273f074edfb48f20f18f75",
|
||||
"sha256:9d8162427baa4a0fdcb4902c5ca5936d2c165374c0dc6693227c68e5852d5c88"
|
||||
"sha256:670a3e1ea775a9fad89d843d2d18a0e2bb0c3719e1a6bb2b96fb12f7ddd579a0",
|
||||
"sha256:d1d23707dbde2b8b6940f80b181638a3e10334ff2f122c7165ce43e91c6639fe"
|
||||
],
|
||||
"version": "==8.13.11"
|
||||
"version": "==8.13.13"
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
|
@ -561,42 +648,83 @@
|
|||
},
|
||||
"pycryptodomex": {
|
||||
"hashes": [
|
||||
"sha256:0af93aad8d62e810247beedef0261c148790c52f3cd33643791cc6396dd217c1",
|
||||
"sha256:12056c38e49d972f9c553a3d598425f8a1c1d35b2e4330f89d5ff1ffb70de041",
|
||||
"sha256:23d83b610bd97704f0cd3acc48d99b76a15c8c1540d8665c94d514a49905bad7",
|
||||
"sha256:2d4d395f109faba34067a08de36304e846c791808524614c731431ee048fe70a",
|
||||
"sha256:32e764322e902bbfac49ca1446604d2839381bbbdd5a57920c9daaf2e0b778df",
|
||||
"sha256:3c2516b42437ae6c7a29ef3ddc73c8d4714e7b6df995b76be4695bbe4b3b5cd2",
|
||||
"sha256:40e8a11f578bd0851b02719c862d55d3ee18d906c8b68a9c09f8c564d6bb5b92",
|
||||
"sha256:4b51e826f0a04d832eda0790bbd0665d9bfe73e5a4d8ea93b6a9b38beeebe935",
|
||||
"sha256:4c4674f4b040321055c596aac926d12f7f6859dfe98cd12f4d9453b43ab6adc8",
|
||||
"sha256:55eed98b4150a744920597c81b3965b632038781bab8a08a12ea1d004213c600",
|
||||
"sha256:599bb4ae4bbd614ca05f49bd4e672b7a250b80b13ae1238f05fd0f09d87ed80a",
|
||||
"sha256:5c23482860302d0d9883404eaaa54b0615eefa5274f70529703e2c43cc571827",
|
||||
"sha256:64b876d57cb894b31056ad8dd6a6ae1099b117ae07a3d39707221133490e5715",
|
||||
"sha256:67a3648025e4ddb72d43addab764336ba2e670c8377dba5dd752e42285440d31",
|
||||
"sha256:6feedf4b0e36b395329b4186a805f60f900129cdf0170e120ecabbfcb763995d",
|
||||
"sha256:78f0ddd4adc64baa39b416f3637aaf99f45acb0bcdc16706f0cc7ebfc6f10109",
|
||||
"sha256:7a6651a07f67c28b6e978d63aa3a3fccea0feefed9a8453af3f7421a758461b7",
|
||||
"sha256:7a8dc3ee7a99aae202a4db52de5a08aa4d01831eb403c4d21da04ec2f79810db",
|
||||
"sha256:7cc28dd33f1f3662d6da28ead4f9891035f63f49d30267d3b41194c8778997c8",
|
||||
"sha256:7fa0b52df90343fafe319257b31d909be1d2e8852277fb0376ba89d26d2921db",
|
||||
"sha256:88b0d5bb87eaf2a31e8a759302b89cf30c97f2f8ca7d83b8c9208abe8acb447a",
|
||||
"sha256:a4fa037078e92c7cc49f6789a8bac3de06856740bb2038d05f2d9a2e4b165d59",
|
||||
"sha256:a57e3257bacd719769110f1f70dd901c5b6955e9596ad403af11a3e6e7e3311c",
|
||||
"sha256:ab33c2d9f275e05e235dbca1063753b5346af4a5cac34a51fa0da0d4edfb21d7",
|
||||
"sha256:c84689c73358dfc23f9fdcff2cb9e7856e65e2ce3b5ed8ff630d4c9bdeb1867b",
|
||||
"sha256:c92537b596bd5bffb82f8964cabb9fef1bca8a28a9e0a69ffd3ec92a4a7ad41b",
|
||||
"sha256:caa937ff29d07a665dfcfd7a84f0d4207b2ebf483362fa9054041d67fdfacc20",
|
||||
"sha256:d38ab9e53b1c09608ba2d9b8b888f1e75d6f66e2787e437adb1fecbffec6b112",
|
||||
"sha256:d4cf0128da167562c49b0e034f09e9cedd733997354f2314837c2fa461c87bb1",
|
||||
"sha256:db23d7341e21b273d2440ec6faf6c8b1ca95c8894da612e165be0b89a8688340",
|
||||
"sha256:ee8bf4fdcad7d66beb744957db8717afc12d176e3fd9c5d106835133881a049b",
|
||||
"sha256:f854c8476512cebe6a8681cc4789e4fcff6019c17baa0fd72b459155dc605ab4",
|
||||
"sha256:fd29d35ac80755e5c0a99d96b44fb9abbd7e871849581ea6a4cb826d24267537"
|
||||
"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"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.17"
|
||||
"version": "==3.18.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"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.10.8"
|
||||
},
|
||||
"pyjwkest": {
|
||||
"hashes": [
|
||||
|
@ -623,27 +751,27 @@
|
|||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b",
|
||||
"sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059"
|
||||
"sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f",
|
||||
"sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.29.0"
|
||||
"version": "==2.31.0"
|
||||
},
|
||||
"s3transfer": {
|
||||
"hashes": [
|
||||
"sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
|
||||
"sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
|
||||
"sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346",
|
||||
"sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.6.0"
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"setuptools": {
|
||||
"hashes": [
|
||||
"sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b",
|
||||
"sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"
|
||||
"sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f",
|
||||
"sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==67.7.2"
|
||||
"version": "==67.8.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -663,19 +791,19 @@
|
|||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
|
||||
"sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
|
||||
"sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c",
|
||||
"sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.5.0"
|
||||
"index": "pypi",
|
||||
"version": "==4.6.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
|
||||
"sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
|
||||
"sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f",
|
||||
"sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"
|
||||
],
|
||||
"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.15"
|
||||
"version": "==1.26.16"
|
||||
},
|
||||
"whitenoise": {
|
||||
"hashes": [
|
||||
|
@ -689,11 +817,11 @@
|
|||
"develop": {
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac",
|
||||
"sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"
|
||||
"sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e",
|
||||
"sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.6.0"
|
||||
"version": "==3.7.2"
|
||||
},
|
||||
"bandit": {
|
||||
"hashes": [
|
||||
|
@ -752,11 +880,11 @@
|
|||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:38ca632be379963f2a2749b5f63a81fe1679913b954914f470ad282c77674bbc",
|
||||
"sha256:4d575c180312bec6108852bae12e6396b9d1bb404154d652c57ee849c62fbb83"
|
||||
"sha256:30f8ab1cf89d5864a80ba2d5eb5316dbd2a63c9469877e0cffb522630438aa85",
|
||||
"sha256:77e8fa7c257f9ed8bfe0c3ffc2ccc47b1cfa27058f99415b6003699d1202e0c0"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.122"
|
||||
"version": "==1.26.145"
|
||||
},
|
||||
"boto3-mocking": {
|
||||
"hashes": [
|
||||
|
@ -768,27 +896,27 @@
|
|||
},
|
||||
"boto3-stubs": {
|
||||
"hashes": [
|
||||
"sha256:401e7fe51d88a51b527d883d195ed20c7f57aeb2c0aea24bbb3e911b6d2ad3aa",
|
||||
"sha256:743a37bfd7d1eed4d67cdf825283abc1d93b7900b81d7426aab7e691e075c897"
|
||||
"sha256:9413cb395c803d5b85e9ec7b16fba855a613ecd78b2e0011e2f6b62cf0b4fc1e",
|
||||
"sha256:be2007f92138781288c7a22eba30b7d60742466fc28edd04637b31fabee854a5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.26.122"
|
||||
"version": "==1.26.145"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:9e4984a9e9777c6b949aa1e98323fa35480d9f99d447af7e179ae611f7ed5af9",
|
||||
"sha256:c3b41078d235761b9c5dc22f534a76952622ef96787b96bbd10242ec4d73f2a5"
|
||||
"sha256:264a3f19ed280d80711b7e278be09acff7ed379a96432fdf179b4e6e3a687e6a",
|
||||
"sha256:65e2a2b1cc70583225f87d6d63736215f93c6234721967bdab872270ba7a1f45"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==1.29.122"
|
||||
"version": "==1.29.145"
|
||||
},
|
||||
"botocore-stubs": {
|
||||
"hashes": [
|
||||
"sha256:59873a3b535ec3ff0b6bf5f41c9f8a0f8c48032a871bea4d6e4faebbbfc68e8b",
|
||||
"sha256:e6e6c527a6cac0ec69dd1b755d530c9b2dab01d423ce47bdc636dd01ebb01b1b"
|
||||
"sha256:80ffab72ad428d20cb1cf538ee55fcd94f7d81315b77d84fec99e218c3974e8b",
|
||||
"sha256:928c58a434dd83bef956e3b5bb1e96278fff5eee9f8b8ab08d916cef1e9a2014"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||
"version": "==1.29.122"
|
||||
"version": "==1.29.145"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
|
@ -800,19 +928,26 @@
|
|||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:ad33ed68db9398f5dfb33282704925bce044bef4261cd4fb59e4e7f9ae505a78",
|
||||
"sha256:c36e2ab12824e2ac36afa8b2515a70c53c7742f0d6eaefa7311ec379558db997"
|
||||
"sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee",
|
||||
"sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.2"
|
||||
"version": "==4.2.1"
|
||||
},
|
||||
"django-debug-toolbar": {
|
||||
"hashes": [
|
||||
"sha256:89619f6e0ea1057dca47bfc429ed99b237ef70074dabc065a7faa5f00e1459cf",
|
||||
"sha256:bad339d68520652ddc1580c76f136fcbc3e020fd5ed96510a89a02ec81bb3fb1"
|
||||
"sha256:a0b532ef5d52544fd745d1dcfc0557fa75f6f0d1962a8298bd568427ef2fa436",
|
||||
"sha256:f57882e335593cb8e74c2bda9f1116bbb9ca8fc0d81b50a75ace0f83de5173c7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.0"
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"django-model2puml": {
|
||||
"hashes": [
|
||||
"sha256:6e773d742e556020a04d3216ce5dee5d3551da162e2d42a997f85b4ed1854771"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.4.1"
|
||||
},
|
||||
"django-stubs": {
|
||||
"hashes": [
|
||||
|
@ -896,35 +1031,35 @@
|
|||
},
|
||||
"mypy": {
|
||||
"hashes": [
|
||||
"sha256:023fe9e618182ca6317ae89833ba422c411469156b690fde6a315ad10695a521",
|
||||
"sha256:031fc69c9a7e12bcc5660b74122ed84b3f1c505e762cc4296884096c6d8ee140",
|
||||
"sha256:2de7babe398cb7a85ac7f1fd5c42f396c215ab3eff731b4d761d68d0f6a80f48",
|
||||
"sha256:2e93a8a553e0394b26c4ca683923b85a69f7ccdc0139e6acd1354cc884fe0128",
|
||||
"sha256:390bc685ec209ada4e9d35068ac6988c60160b2b703072d2850457b62499e336",
|
||||
"sha256:3a2d219775a120581a0ae8ca392b31f238d452729adbcb6892fa89688cb8306a",
|
||||
"sha256:3efde4af6f2d3ccf58ae825495dbb8d74abd6d176ee686ce2ab19bd025273f41",
|
||||
"sha256:4a99fe1768925e4a139aace8f3fb66db3576ee1c30b9c0f70f744ead7e329c9f",
|
||||
"sha256:4b41412df69ec06ab141808d12e0bf2823717b1c363bd77b4c0820feaa37249e",
|
||||
"sha256:4c8d8c6b80aa4a1689f2a179d31d86ae1367ea4a12855cc13aa3ba24bb36b2d8",
|
||||
"sha256:4d19f1a239d59f10fdc31263d48b7937c585810288376671eaf75380b074f238",
|
||||
"sha256:4e4a682b3f2489d218751981639cffc4e281d548f9d517addfd5a2917ac78119",
|
||||
"sha256:695c45cea7e8abb6f088a34a6034b1d273122e5530aeebb9c09626cea6dca4cb",
|
||||
"sha256:701189408b460a2ff42b984e6bd45c3f41f0ac9f5f58b8873bbedc511900086d",
|
||||
"sha256:70894c5345bea98321a2fe84df35f43ee7bb0feec117a71420c60459fc3e1eed",
|
||||
"sha256:8293a216e902ac12779eb7a08f2bc39ec6c878d7c6025aa59464e0c4c16f7eb9",
|
||||
"sha256:8d26b513225ffd3eacece727f4387bdce6469192ef029ca9dd469940158bc89e",
|
||||
"sha256:a197ad3a774f8e74f21e428f0de7f60ad26a8d23437b69638aac2764d1e06a6a",
|
||||
"sha256:bea55fc25b96c53affab852ad94bf111a3083bc1d8b0c76a61dd101d8a388cf5",
|
||||
"sha256:c9a084bce1061e55cdc0493a2ad890375af359c766b8ac311ac8120d3a472950",
|
||||
"sha256:d0e9464a0af6715852267bf29c9553e4555b61f5904a4fc538547a4d67617937",
|
||||
"sha256:d8e9187bfcd5ffedbe87403195e1fc340189a68463903c39e2b63307c9fa0394",
|
||||
"sha256:eaeaa0888b7f3ccb7bcd40b50497ca30923dba14f385bde4af78fac713d6d6f6",
|
||||
"sha256:f46af8d162f3d470d8ffc997aaf7a269996d205f9d746124a179d3abe05ac602",
|
||||
"sha256:f70a40410d774ae23fcb4afbbeca652905a04de7948eaf0b1789c8d1426b72d1",
|
||||
"sha256:fe91be1c51c90e2afe6827601ca14353bbf3953f343c2129fa1e247d55fd95ba"
|
||||
"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"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.2.0"
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
|
@ -968,11 +1103,11 @@
|
|||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4",
|
||||
"sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"
|
||||
"sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f",
|
||||
"sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.5.0"
|
||||
"version": "==3.5.1"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
|
@ -1054,19 +1189,19 @@
|
|||
},
|
||||
"rich": {
|
||||
"hashes": [
|
||||
"sha256:2d11b9b8dd03868f09b4fffadc84a6a8cda574e40dc90821bd845720ebb8e89c",
|
||||
"sha256:69cdf53799e63f38b95b9bf9c875f8c90e78dd62b2f00c13a911c7a3b9fa4704"
|
||||
"sha256:76f6b65ea7e5c5d924ba80e322231d7cb5b5981aa60bfc1e694f1bc097fe6fe1",
|
||||
"sha256:d204aadb50b936bf6b1a695385429d192bc1fdaf3e8b907e8e26f4c4e4b5bf75"
|
||||
],
|
||||
"markers": "python_full_version >= '3.7.0'",
|
||||
"version": "==13.3.5"
|
||||
"version": "==13.4.1"
|
||||
},
|
||||
"s3transfer": {
|
||||
"hashes": [
|
||||
"sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd",
|
||||
"sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947"
|
||||
"sha256:3c0da2d074bf35d6870ef157158641178a4204a6e689e82546083e31e0311346",
|
||||
"sha256:640bb492711f4c0c0905e1f62b6aaeb771881935ad27884852411f8e9cacbca9"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==0.6.0"
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
|
@ -1102,11 +1237,11 @@
|
|||
},
|
||||
"stevedore": {
|
||||
"hashes": [
|
||||
"sha256:2c428d2338976279e8eb2196f7a94910960d9f7ba2f41f3988511e95ca447021",
|
||||
"sha256:bd5a71ff5e5e5f5ea983880e4a1dd1bb47f8feebbb3d95b592398e2f02194771"
|
||||
"sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d",
|
||||
"sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"
|
||||
],
|
||||
"markers": "python_version >= '3.8'",
|
||||
"version": "==5.0.0"
|
||||
"version": "==5.1.0"
|
||||
},
|
||||
"tomli": {
|
||||
"hashes": [
|
||||
|
@ -1118,11 +1253,11 @@
|
|||
},
|
||||
"types-awscrt": {
|
||||
"hashes": [
|
||||
"sha256:40854d9d7ce055620d5d41e5adc84df11b879aedbd2cf20de84e73f084aa5797",
|
||||
"sha256:fe38c6fd71199a9f739b69a7c2f3a574585457c4f63730a62830628a7bffc5b0"
|
||||
"sha256:50fe7610aa40550a23d79d6167b2b8536281f038f42846eda0e520d6e3e01787",
|
||||
"sha256:763d8d543f145d51cd16ea407d079608b126941d24525e16cb1de31d949ff563"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||
"version": "==0.16.16"
|
||||
"version": "==0.16.19"
|
||||
},
|
||||
"types-cachetools": {
|
||||
"hashes": [
|
||||
|
@ -1141,49 +1276,49 @@
|
|||
},
|
||||
"types-pyyaml": {
|
||||
"hashes": [
|
||||
"sha256:5aed5aa66bd2d2e158f75dda22b059570ede988559f030cf294871d3b647e3e8",
|
||||
"sha256:c51b1bd6d99ddf0aa2884a7a328810ebf70a4262c292195d3f4f9a0005f9eeb6"
|
||||
"sha256:662fa444963eff9b68120d70cda1af5a5f2aa57900003c2006d7626450eaae5f",
|
||||
"sha256:ebab3d0700b946553724ae6ca636ea932c1b0868701d4af121630e78d695fc97"
|
||||
],
|
||||
"version": "==6.0.12.9"
|
||||
"version": "==6.0.12.10"
|
||||
},
|
||||
"types-requests": {
|
||||
"hashes": [
|
||||
"sha256:0d580652ce903f643f8c3b494dd01d29367ea57cea0c7ad7f65cf3169092edb0",
|
||||
"sha256:cc1aba862575019306b2ed134eb1ea994cab1c887a22e18d3383e6dd42e9789b"
|
||||
"sha256:3de667cffa123ce698591de0ad7db034a5317457a596eb0b4944e5a9d9e8d1ac",
|
||||
"sha256:afb06ef8f25ba83d59a1d424bd7a5a939082f94b94e90ab5e6116bd2559deaa3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.28.11.17"
|
||||
"version": "==2.31.0.1"
|
||||
},
|
||||
"types-s3transfer": {
|
||||
"hashes": [
|
||||
"sha256:40e665643f0647832d51c4a26d8a8275cda9134b02bf22caf28198b79bcad382",
|
||||
"sha256:d9c669b30fdd61347720434aacb8ecc4645d900712a70b10f495104f9039c07b"
|
||||
"sha256:6d1ac1dedac750d570428362acdf60fdd4f277b0788855c3894d3226756b2bfb",
|
||||
"sha256:75ac1d7143d58c1e6af467cfd4a96c67ee058a3adf7c249d9309999e1f5f41e4"
|
||||
],
|
||||
"markers": "python_version >= '3.7' and python_version < '4.0'",
|
||||
"version": "==0.6.0.post7"
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"types-urllib3": {
|
||||
"hashes": [
|
||||
"sha256:04235e792139cf3624b25d38faab593456738fbdb7439634046172e3b1339400",
|
||||
"sha256:697102ddf4f781eed6f692353f40cee1098643526f5a8b99f49d2ede90fd3754"
|
||||
"sha256:3300538c9dc11dad32eae4827ac313f5d986b8b21494801f1bf97a1ac6c03ae5",
|
||||
"sha256:5dbd1d2bef14efee43f5318b5d36d805a489f6600252bb53626d4bfafd95e27c"
|
||||
],
|
||||
"version": "==1.26.25.11"
|
||||
"version": "==1.26.25.13"
|
||||
},
|
||||
"typing-extensions": {
|
||||
"hashes": [
|
||||
"sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb",
|
||||
"sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"
|
||||
"sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c",
|
||||
"sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==4.5.0"
|
||||
"index": "pypi",
|
||||
"version": "==4.6.2"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305",
|
||||
"sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"
|
||||
"sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f",
|
||||
"sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"
|
||||
],
|
||||
"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.15"
|
||||
"version": "==1.26.16"
|
||||
},
|
||||
"waitress": {
|
||||
"hashes": [
|
||||
|
|
|
@ -27,6 +27,8 @@ services:
|
|||
- DJANGO_DEBUG=True
|
||||
# Tell Django where it is being hosted
|
||||
- DJANGO_BASE_URL=http://localhost:8080
|
||||
# Public site URL link
|
||||
- GETGOV_PUBLIC_SITE_URL=https://beta.get.gov
|
||||
# Set a username for accessing the registry
|
||||
- REGISTRY_CL_ID=nothing
|
||||
# Set a password for accessing the registry
|
||||
|
|
|
@ -43,10 +43,15 @@ except NameError:
|
|||
# Attn: these imports should NOT be at the top of the file
|
||||
try:
|
||||
from .client import CLIENT, commands
|
||||
from .errors import RegistryError, ErrorCode
|
||||
from epplib.models import common
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
__all__ = [
|
||||
"CLIENT",
|
||||
"commands",
|
||||
"common",
|
||||
"ErrorCode",
|
||||
"RegistryError",
|
||||
]
|
||||
|
|
|
@ -67,54 +67,47 @@ class EPPLibWrapper:
|
|||
def _send(self, command):
|
||||
"""Helper function used by `send`."""
|
||||
try:
|
||||
cmd_type = command.__class__.__name__
|
||||
with self._connect as wire:
|
||||
response = wire.send(command)
|
||||
except (ValueError, ParsingError) as err:
|
||||
logger.warning(
|
||||
"%s failed to execute due to some syntax error."
|
||||
% command.__class__.__name__,
|
||||
exc_info=True,
|
||||
)
|
||||
raise RegistryError() from err
|
||||
message = "%s failed to execute due to some syntax error."
|
||||
logger.warning(message, cmd_type, exc_info=True)
|
||||
raise RegistryError(message) from err
|
||||
except TransportError as err:
|
||||
logger.warning(
|
||||
"%s failed to execute due to a connection error."
|
||||
% command.__class__.__name__,
|
||||
exc_info=True,
|
||||
)
|
||||
raise RegistryError() from err
|
||||
message = "%s failed to execute due to a connection error."
|
||||
logger.warning(message, cmd_type, exc_info=True)
|
||||
raise RegistryError(message) from err
|
||||
except LoginError as err:
|
||||
logger.warning(
|
||||
"%s failed to execute due to a registry login error."
|
||||
% command.__class__.__name__,
|
||||
exc_info=True,
|
||||
)
|
||||
raise RegistryError() from err
|
||||
message = "%s failed to execute due to a registry login error."
|
||||
logger.warning(message, cmd_type, exc_info=True)
|
||||
raise RegistryError(message) from err
|
||||
except Exception as err:
|
||||
logger.warning(
|
||||
"%s failed to execute due to an unknown error."
|
||||
% command.__class__.__name__,
|
||||
exc_info=True,
|
||||
)
|
||||
raise RegistryError() from err
|
||||
message = "%s failed to execute due to an unknown error."
|
||||
logger.warning(message, cmd_type, exc_info=True)
|
||||
raise RegistryError(message) from err
|
||||
else:
|
||||
if response.code >= 2000:
|
||||
raise RegistryError(response.msg)
|
||||
raise RegistryError(response.msg, code=response.code)
|
||||
else:
|
||||
return response
|
||||
|
||||
def send(self, command):
|
||||
def send(self, command, *, cleaned=False):
|
||||
"""Login, send the command, then close the connection. Tries 3 times."""
|
||||
# try to prevent use of this method without appropriate safeguards
|
||||
if not cleaned:
|
||||
raise ValueError("Please sanitize user input before sending it.")
|
||||
|
||||
counter = 0 # we'll try 3 times
|
||||
while True:
|
||||
try:
|
||||
return self._send(command)
|
||||
except RegistryError as err:
|
||||
if counter == 3: # don't try again
|
||||
raise err
|
||||
else:
|
||||
if err.should_retry() and counter < 3:
|
||||
counter += 1
|
||||
sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms
|
||||
else: # don't try again
|
||||
raise err
|
||||
|
||||
|
||||
try:
|
||||
|
|
|
@ -1,5 +1,77 @@
|
|||
from enum import IntEnum
|
||||
|
||||
|
||||
class ErrorCode(IntEnum):
|
||||
"""
|
||||
Overview of registry response codes from RFC 5730. See RFC 5730 for full text.
|
||||
|
||||
- 1000 - 1500 Success
|
||||
- 2000 - 2308 Registrar did something silly
|
||||
- 2400 - 2500 Registry did something silly
|
||||
- 2501 - 2502 Something malicious or abusive may have occurred
|
||||
"""
|
||||
|
||||
COMMAND_COMPLETED_SUCCESSFULLY = 1000
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_ACTION_PENDING = 1001
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_NO_MESSAGES = 1300
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_ACK_TO_DEQUEUE = 1301
|
||||
COMMAND_COMPLETED_SUCCESSFULLY_ENDING_SESSION = 1500
|
||||
|
||||
UNKNOWN_COMMAND = 2000
|
||||
COMMAND_SYNTAX_ERROR = 2001
|
||||
COMMAND_USE_ERROR = 2002
|
||||
REQUIRED_PARAMETER_MISSING = 2003
|
||||
PARAMETER_VALUE_RANGE_ERROR = 2004
|
||||
PARAMETER_VALUE_SYNTAX_ERROR = 2005
|
||||
UNIMPLEMENTED_PROTOCOL_VERSION = 2100
|
||||
UNIMPLEMENTED_COMMAND = 2101
|
||||
UNIMPLEMENTED_OPTION = 2102
|
||||
UNIMPLEMENTED_EXTENSION = 2103
|
||||
BILLING_FAILURE = 2104
|
||||
OBJECT_IS_NOT_ELIGIBLE_FOR_RENEWAL = 2105
|
||||
OBJECT_IS_NOT_ELIGIBLE_FOR_TRANSFER = 2106
|
||||
AUTHENTICATION_ERROR = 2200
|
||||
AUTHORIZATION_ERROR = 2201
|
||||
INVALID_AUTHORIZATION_INFORMATION = 2202
|
||||
OBJECT_PENDING_TRANSFER = 2300
|
||||
OBJECT_NOT_PENDING_TRANSFER = 2301
|
||||
OBJECT_EXISTS = 2302
|
||||
OBJECT_DOES_NOT_EXIST = 2303
|
||||
OBJECT_STATUS_PROHIBITS_OPERATION = 2304
|
||||
OBJECT_ASSOCIATION_PROHIBITS_OPERATION = 2305
|
||||
PARAMETER_VALUE_POLICY_ERROR = 2306
|
||||
UNIMPLEMENTED_OBJECT_SERVICE = 2307
|
||||
DATA_MANAGEMENT_POLICY_VIOLATION = 2308
|
||||
|
||||
COMMAND_FAILED = 2400
|
||||
COMMAND_FAILED_SERVER_CLOSING_CONNECTION = 2500
|
||||
|
||||
AUTHENTICATION_ERROR_SERVER_CLOSING_CONNECTION = 2501
|
||||
SESSION_LIMIT_EXCEEDED_SERVER_CLOSING_CONNECTION = 2502
|
||||
|
||||
|
||||
class RegistryError(Exception):
|
||||
pass
|
||||
"""
|
||||
Overview of registry response codes from RFC 5730. See RFC 5730 for full text.
|
||||
|
||||
- 1000 - 1500 Success
|
||||
- 2000 - 2308 Registrar did something silly
|
||||
- 2400 - 2500 Registry did something silly
|
||||
- 2501 - 2502 Something malicious or abusive may have occurred
|
||||
"""
|
||||
|
||||
def __init__(self, *args, code=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.code = code
|
||||
|
||||
def should_retry(self):
|
||||
return self.code == ErrorCode.COMMAND_FAILED
|
||||
|
||||
def is_server_error(self):
|
||||
return self.code is not None and (self.code >= 2400 and self.code <= 2500)
|
||||
|
||||
def is_client_error(self):
|
||||
return self.code is not None and (self.code >= 2000 and self.code <= 2308)
|
||||
|
||||
|
||||
class LoginError(RegistryError):
|
||||
|
|
|
@ -77,6 +77,7 @@ h2 {
|
|||
font-weight: font-weight('semibold');
|
||||
line-height: line-height('heading', 3);
|
||||
margin: units(4) 0 units(1);
|
||||
color: color('primary-darker');
|
||||
}
|
||||
|
||||
.register-form-step > h1 {
|
||||
|
@ -431,4 +432,4 @@ abbr[title] {
|
|||
@include at-media('tablet') {
|
||||
height: units('mobile');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,8 @@ secret_registry_key = b64decode(secret("REGISTRY_KEY", ""))
|
|||
secret_registry_key_passphrase = secret("REGISTRY_KEY_PASSPHRASE", "")
|
||||
secret_registry_hostname = secret("REGISTRY_HOSTNAME")
|
||||
|
||||
secret_getgov_public_site_url = secret("GETGOV_PUBLIC_SITE_URL", "")
|
||||
|
||||
# region: Basic Django Config-----------------------------------------------###
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / "subdir".
|
||||
|
@ -109,6 +111,8 @@ INSTALLED_APPS = [
|
|||
"registrar",
|
||||
# Our internal API application
|
||||
"api",
|
||||
# Only for generating documentation, uncomment to run manage.py generate_puml
|
||||
# "puml_generator",
|
||||
]
|
||||
|
||||
# Middleware are routines for processing web requests.
|
||||
|
@ -503,6 +507,10 @@ ROOT_URLCONF = "registrar.config.urls"
|
|||
# Must be relative and end with "/"
|
||||
STATIC_URL = "public/"
|
||||
|
||||
# Base URL of our separate static public website. Used by the
|
||||
# {% public_site_url subdir/path %} template tag
|
||||
GETGOV_PUBLIC_SITE_URL = secret_getgov_public_site_url
|
||||
|
||||
# endregion
|
||||
# region: Registry----------------------------------------------------------###
|
||||
|
||||
|
|
|
@ -59,12 +59,12 @@ urlpatterns = [
|
|||
),
|
||||
path(
|
||||
"application/<int:pk>/withdraw",
|
||||
views.ApplicationWithdraw.as_view(),
|
||||
views.ApplicationWithdrawConfirmation.as_view(),
|
||||
name="application-withdraw-confirmation",
|
||||
),
|
||||
path(
|
||||
"application/<int:pk>/withdrawconfirmed",
|
||||
views.ApplicationWithdraw.updatestatus,
|
||||
views.ApplicationWithdrawn.as_view(),
|
||||
name="application-withdrawn",
|
||||
),
|
||||
path("health/", views.health),
|
||||
|
@ -83,6 +83,16 @@ urlpatterns = [
|
|||
views.DomainNameserversView.as_view(),
|
||||
name="domain-nameservers",
|
||||
),
|
||||
path(
|
||||
"domain/<int:pk>/your-contact-information",
|
||||
views.DomainYourContactInformationView.as_view(),
|
||||
name="domain-your-contact-information",
|
||||
),
|
||||
path(
|
||||
"domain/<int:pk>/authorizing-official",
|
||||
views.DomainAuthorizingOfficialView.as_view(),
|
||||
name="domain-authorizing-official",
|
||||
),
|
||||
path(
|
||||
"domain/<int:pk>/security-email",
|
||||
views.DomainSecurityEmailView.as_view(),
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
from .application_wizard import *
|
||||
from .domain import DomainAddUserForm, NameserverFormset, DomainSecurityEmailForm
|
||||
from .domain import (
|
||||
DomainAddUserForm,
|
||||
NameserverFormset,
|
||||
DomainSecurityEmailForm,
|
||||
ContactForm,
|
||||
)
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
from django import forms
|
||||
from django.forms import formset_factory
|
||||
|
||||
from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
||||
|
||||
from ..models import Contact
|
||||
|
||||
|
||||
class DomainAddUserForm(forms.Form):
|
||||
|
||||
|
@ -24,6 +28,37 @@ NameserverFormset = formset_factory(
|
|||
)
|
||||
|
||||
|
||||
class ContactForm(forms.ModelForm):
|
||||
|
||||
"""Form for updating contacts."""
|
||||
|
||||
class Meta:
|
||||
model = Contact
|
||||
fields = ["first_name", "middle_name", "last_name", "title", "email", "phone"]
|
||||
widgets = {
|
||||
"first_name": forms.TextInput,
|
||||
"middle_name": forms.TextInput,
|
||||
"last_name": forms.TextInput,
|
||||
"title": forms.TextInput,
|
||||
"email": forms.EmailInput,
|
||||
"phone": RegionalPhoneNumberWidget,
|
||||
}
|
||||
|
||||
# the database fields have blank=True so ModelForm doesn't create
|
||||
# required fields by default. Use this list in __init__ to mark each
|
||||
# of these fields as required
|
||||
required = ["first_name", "last_name", "title", "email", "phone"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# take off maxlength attribute for the phone number field
|
||||
# which interferes with out input_with_errors template tag
|
||||
self.fields["phone"].widget.attrs.pop("maxlength", None)
|
||||
|
||||
for field_name in self.required:
|
||||
self.fields[field_name].required = True
|
||||
|
||||
|
||||
class DomainSecurityEmailForm(forms.Form):
|
||||
|
||||
"""Form for adding or editing a security email to a domain."""
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
# Generated by Django 4.2.1 on 2023-05-31 23:09
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0022_draftdomain_domainapplication_approved_domain_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="contact",
|
||||
name="first_name",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="First name",
|
||||
null=True,
|
||||
verbose_name="first name / given name",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="contact",
|
||||
name="last_name",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="Last name",
|
||||
null=True,
|
||||
verbose_name="last name / family name",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="contact",
|
||||
name="title",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Title",
|
||||
null=True,
|
||||
verbose_name="title or role in your organization",
|
||||
),
|
||||
),
|
||||
]
|
19
src/registrar/migrations/0024_alter_contact_email.py
Normal file
19
src/registrar/migrations/0024_alter_contact_email.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 4.2.1 on 2023-06-01 19:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0023_alter_contact_first_name_alter_contact_last_name_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="contact",
|
||||
name="email",
|
||||
field=models.EmailField(
|
||||
blank=True, db_index=True, help_text="Email", max_length=254, null=True
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,12 +1,13 @@
|
|||
# Generated by Django 4.2.1 on 2023-05-26 19:21
|
||||
# Generated by Django 4.2.1 on 2023-06-01 21:47
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db import migrations
|
||||
import django_fsm # type: ignore
|
||||
import registrar.models.utility.domain_field
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0022_draftdomain_domainapplication_approved_domain_and_more"),
|
||||
("registrar", "0024_alter_contact_email"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
@ -21,7 +22,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name="domain",
|
||||
name="state",
|
||||
field=models.CharField(
|
||||
field=django_fsm.FSMField(
|
||||
choices=[
|
||||
("created", "Created"),
|
||||
("deleted", "Deleted"),
|
||||
|
@ -30,6 +31,7 @@ class Migration(migrations.Migration):
|
|||
default="unknown",
|
||||
help_text="Very basic info about the lifecycle of this domain object",
|
||||
max_length=21,
|
||||
protected=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
|
@ -20,6 +20,7 @@ class Contact(TimeStampedModel):
|
|||
null=True,
|
||||
blank=True,
|
||||
help_text="First name",
|
||||
verbose_name="first name / given name",
|
||||
db_index=True,
|
||||
)
|
||||
middle_name = models.TextField(
|
||||
|
@ -31,14 +32,16 @@ class Contact(TimeStampedModel):
|
|||
null=True,
|
||||
blank=True,
|
||||
help_text="Last name",
|
||||
verbose_name="last name / family name",
|
||||
db_index=True,
|
||||
)
|
||||
title = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Title",
|
||||
verbose_name="title or role in your organization",
|
||||
)
|
||||
email = models.TextField(
|
||||
email = models.EmailField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Email",
|
||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
|||
|
||||
from datetime import date
|
||||
from string import digits
|
||||
from django_fsm import FSMField # type: ignore
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
@ -169,7 +170,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
|
||||
IDs are provided as strings, e.g.
|
||||
|
||||
{"registrant": "jd1234", "admin": "sh8013",...}
|
||||
{ PublicContact.ContactTypeChoices.REGISTRANT: "jd1234",
|
||||
PublicContact.ContactTypeChoices.ADMINISTRATIVE: "sh8013",...}
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
@ -199,7 +201,14 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
|
||||
@Cache
|
||||
def password(self) -> str:
|
||||
"""Get the `auth_info.pw` element from the registry. Not a real password."""
|
||||
"""
|
||||
Get the `auth_info.pw` element from the registry. Not a real password.
|
||||
|
||||
This `auth_info` element is required by the EPP protocol, but the registry is
|
||||
using a different mechanism to ensure unauthorized clients cannot perform
|
||||
actions on domains they do not own. This field provides no security features.
|
||||
It is not a secret.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@Cache
|
||||
|
@ -319,10 +328,11 @@ class Domain(TimeStampedModel, DomainHelper):
|
|||
help_text="Fully qualified domain name",
|
||||
)
|
||||
|
||||
state = models.CharField(
|
||||
state = FSMField(
|
||||
max_length=21,
|
||||
choices=State.choices,
|
||||
default=State.UNKNOWN,
|
||||
protected=True, # cannot change state directly, particularly in Django admin
|
||||
help_text="Very basic info about the lifecycle of this domain object",
|
||||
)
|
||||
|
||||
|
|
|
@ -562,11 +562,13 @@ class DomainApplication(TimeStampedModel):
|
|||
"""Show this step if the answer to the first question implies it.
|
||||
|
||||
This shows for answers that aren't "Federal" or "Interstate".
|
||||
This also doesnt show if user selected "School District" as well (#524)
|
||||
"""
|
||||
user_choice = self.organization_type
|
||||
excluded = [
|
||||
DomainApplication.OrganizationChoices.FEDERAL,
|
||||
DomainApplication.OrganizationChoices.INTERSTATE,
|
||||
DomainApplication.OrganizationChoices.SCHOOL_DISTRICT,
|
||||
]
|
||||
return bool(user_choice and user_choice not in excluded)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% extends 'application_form.html' %}
|
||||
{% load field_helpers %}
|
||||
{% load field_helpers url_helpers %}
|
||||
|
||||
{% block form_instructions %}
|
||||
<p>.Gov domain names are for use on the internet. Don’t register a .gov to simply reserve a
|
||||
|
@ -8,7 +8,7 @@ domain name or for mainly internal use.</p>
|
|||
<p>Describe the reason for your domain request. Explain how you plan to use this domain.
|
||||
Who is your intended audience? Will you use it for a website and/or email? Are you moving
|
||||
your website from another top-level domain (like .com or .org)?
|
||||
Read about <a href="{% url 'todo' %}">activities that are prohibited on .gov domains.</a></p>
|
||||
Read about <a href="{% public_site_url 'domains/requirements/' %}">activities that are prohibited on .gov domains.</a></p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<p> <b class="review__step__name">Last updated:</b> {{domainapplication.updated_at|date:"F j, Y"}}<br>
|
||||
<b class="review__step__name">Request #:</b> {{domainapplication.id}}</p>
|
||||
<p>{% include "includes/domain_application.html" %}</p>
|
||||
<p><a href="{% url 'application-withdraw-confirmation' domainapplication.id %}" class="usa-button usa-button--outline withdraw_outline">
|
||||
<p><a href="{% url 'application-withdraw-confirmation' pk=domainapplication.id %}" class="usa-button usa-button--outline withdraw_outline">
|
||||
Withdraw request</a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
43
src/registrar/templates/domain_authorizing_official.html
Normal file
43
src/registrar/templates/domain_authorizing_official.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
{% extends "domain_base.html" %}
|
||||
{% load static field_helpers%}
|
||||
|
||||
{% block title %}Domain authorizing official | {{ domain.name }} | {% endblock %}
|
||||
|
||||
{% block domain_content %}
|
||||
{# this is right after the messages block in the parent template #}
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
|
||||
<h1>Authorizing official</h1>
|
||||
|
||||
<p>Your authorizing official is the person within your organization who can
|
||||
authorize domain requests. This is generally the highest-ranking or
|
||||
highest-elected official in your organization. <a class="usa-link"
|
||||
href="https://federalist-877ab29f-16f6-4f12-961c-96cf064cf070.sites.pages.cloud.gov/site/cisagov/getgov-home/domains/eligibility/#you-must-have-approval-from-an-authorizing-official-within-your-organization">Read more about who can serve
|
||||
as an authorizing official.</a></p>
|
||||
|
||||
{% include "includes/required_fields.html" %}
|
||||
|
||||
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
||||
{% csrf_token %}
|
||||
|
||||
{% input_with_errors form.first_name %}
|
||||
|
||||
{% input_with_errors form.middle_name %}
|
||||
|
||||
{% input_with_errors form.last_name %}
|
||||
|
||||
{% input_with_errors form.title %}
|
||||
|
||||
{% input_with_errors form.email %}
|
||||
|
||||
{% input_with_errors form.phone %}
|
||||
|
||||
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="usa-button"
|
||||
>Save</button>
|
||||
</form>
|
||||
|
||||
{% endblock %} {# domain_content #}
|
|
@ -1,6 +1,33 @@
|
|||
{% extends "domain_base.html" %}
|
||||
{% load static url_helpers %}
|
||||
|
||||
{% block domain_content %}
|
||||
{{ block.super }}
|
||||
<p>Active: {% if domain.is_active %}Yes{% else %}No{% endif %}</p>
|
||||
<div class="margin-top-4 tablet:grid-col-10">
|
||||
|
||||
{% url 'domain-nameservers' pk=domain.id as url %}
|
||||
{% if domain.nameservers %}
|
||||
{% include "includes/summary_item.html" with title='DNS name servers' value=domain.nameservers list='true' edit_link=url %}
|
||||
{% else %}
|
||||
<h2 class="margin-top-neg-1"> DNS name servers </h2>
|
||||
<p> No DNS name servers have been added yet. Before your domain can be used we’ll need information about your domain name servers.</p>
|
||||
<a class="usa-button margin-bottom-1" href="{{url}}"> Add DNS name servers </a>
|
||||
{% endif %}
|
||||
|
||||
{% url 'todo' as url %}
|
||||
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url %}
|
||||
|
||||
{% url 'domain-authorizing-official' pk=domain.id as url %}
|
||||
{% include "includes/summary_item.html" with title='Authorizing official' value=domain.domain_info.authorizing_official contact='true' edit_link=url %}
|
||||
|
||||
{% url 'domain-your-contact-information' pk=domain.id as url %}
|
||||
{% include "includes/summary_item.html" with title='Your contact information' value=request.user.contact contact='true' edit_link=url %}
|
||||
|
||||
{% url 'domain-security-email' pk=domain.id as url %}
|
||||
{% include "includes/summary_item.html" with title='Security email' value=domain.security_email edit_link=url %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
</div>
|
||||
{% endblock %} {# domain_content #}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<a href="{{ url }}"
|
||||
{% if request.path == url %}class="usa-current"{% endif %}
|
||||
>
|
||||
Domain Overview
|
||||
Domain overview
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
|||
</li>
|
||||
|
||||
<li class="usa-sidenav__item">
|
||||
{% url 'todo' as url %}
|
||||
{% url 'domain-authorizing-official' pk=domain.id as url %}
|
||||
<a href="{{ url }}"
|
||||
{% if request.path == url %}class="usa-current"{% endif %}
|
||||
>
|
||||
|
@ -40,7 +40,7 @@
|
|||
</li>
|
||||
|
||||
<li class="usa-sidenav__item">
|
||||
{% url 'todo' as url %}
|
||||
{% url 'domain-your-contact-information' pk=domain.id as url %}
|
||||
<a href="{{ url }}"
|
||||
{% if request.path == url %}class="usa-current"{% endif %}
|
||||
>
|
||||
|
|
35
src/registrar/templates/domain_your_contact_information.html
Normal file
35
src/registrar/templates/domain_your_contact_information.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends "domain_base.html" %}
|
||||
{% load static field_helpers %}
|
||||
|
||||
{% block title %}Domain contact information | {{ domain.name }} | {% endblock %}
|
||||
|
||||
{% block domain_content %}
|
||||
|
||||
<h1>Domain contact information</h1>
|
||||
|
||||
<p>If you’d like us to use a different name, email, or phone number you can make those changes below. Changing your contact information here won’t affect your Login.gov account information.</p>
|
||||
|
||||
{% include "includes/required_fields.html" %}
|
||||
|
||||
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
||||
{% csrf_token %}
|
||||
|
||||
{% input_with_errors form.first_name %}
|
||||
|
||||
{% input_with_errors form.middle_name %}
|
||||
|
||||
{% input_with_errors form.last_name %}
|
||||
|
||||
{% input_with_errors form.title %}
|
||||
|
||||
{% input_with_errors form.email %}
|
||||
|
||||
{% input_with_errors form.phone %}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="usa-button"
|
||||
>Save</button>
|
||||
</form>
|
||||
|
||||
{% endblock %} {# domain_content #}
|
|
@ -1,11 +1,13 @@
|
|||
{% load static url_helpers %}
|
||||
|
||||
<section class="summary-item margin-top-3">
|
||||
<hr class="" />
|
||||
<p class="summary-item__title
|
||||
<hr class="" aria-hidden="true" />
|
||||
<h2 class="summary-item__title
|
||||
text-primary-dark text-semibold
|
||||
margin-top-0 margin-bottom-05"
|
||||
>
|
||||
{{ title }}
|
||||
</p>
|
||||
>
|
||||
{{ title }}
|
||||
</h2>
|
||||
{% if address %}
|
||||
{% include "includes/organization_address.html" with organization=value %}
|
||||
{% elif contact %}
|
||||
|
@ -30,11 +32,19 @@
|
|||
{% endif %}
|
||||
{% elif list %}
|
||||
{% if value|length == 1 %}
|
||||
<p class="margin-top-0">{{ value | first }} </p>
|
||||
{% if users %}
|
||||
<p class="margin-top-0">{{ value.0.user.email }} </p>
|
||||
{% else %}
|
||||
<p class="margin-top-0">{{ value | first }} </p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<ul class="usa-list margin-top-0">
|
||||
{% for item in value %}
|
||||
<li>{{ item }}</li>
|
||||
{% if users %}
|
||||
<li>{{ item.user.email }}</li>
|
||||
{% else %}
|
||||
<li>{{ item }}</li>
|
||||
{% endif %}
|
||||
{% empty %}
|
||||
<li>None</li>
|
||||
{% endfor %}</ul></p>
|
||||
|
@ -45,5 +55,13 @@
|
|||
{{ value }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if edit_link %}
|
||||
<a
|
||||
href="{{ edit_link }}"
|
||||
>
|
||||
Edit<span class="sr-only"> {{ title }}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</section>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django import template
|
||||
from django.urls import reverse
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
|
@ -15,3 +17,16 @@ def startswith(text, starts):
|
|||
if isinstance(text, str):
|
||||
return text.startswith(starts)
|
||||
return False
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def public_site_url(url_path):
|
||||
"""Make a full URL for this path at our public site.
|
||||
|
||||
The public site base url is set by a GETGOV_PUBLIC_SITE_URL environment
|
||||
variable.
|
||||
"""
|
||||
base_url = settings.GETGOV_PUBLIC_SITE_URL
|
||||
# join the two halves with a single slash
|
||||
public_url = "/".join([base_url.rstrip("/"), url_path.lstrip("/")])
|
||||
return public_url
|
||||
|
|
|
@ -15,6 +15,7 @@ from registrar.forms.application_wizard import (
|
|||
AnythingElseForm,
|
||||
TypeOfWorkForm,
|
||||
)
|
||||
from registrar.forms.domain import ContactForm
|
||||
|
||||
|
||||
class TestFormValidation(TestCase):
|
||||
|
@ -277,3 +278,13 @@ class TestFormValidation(TestCase):
|
|||
for error in form.non_field_errors()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class TestContactForm(TestCase):
|
||||
def test_contact_form_email_invalid(self):
|
||||
form = ContactForm(data={"email": "example.net"})
|
||||
self.assertEqual(form.errors["email"], ["Enter a valid email address."])
|
||||
|
||||
def test_contact_form_email_invalid2(self):
|
||||
form = ContactForm(data={"email": "@"})
|
||||
self.assertEqual(form.errors["email"], ["Enter a valid email address."])
|
||||
|
|
31
src/registrar/tests/test_templatetags.py
Normal file
31
src/registrar/tests/test_templatetags.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
"""Test template tags."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.template import Context, Template
|
||||
|
||||
|
||||
class TestTemplateTags(TestCase):
|
||||
def _render_template(self, string, context=None):
|
||||
"""Helper method to render a template given as a string.
|
||||
|
||||
Originally from https://stackoverflow.com/a/1690879
|
||||
"""
|
||||
context = context or {}
|
||||
context = Context(context)
|
||||
return Template(string).render(context)
|
||||
|
||||
def test_public_site_url(self):
|
||||
result = self._render_template(
|
||||
"{% load url_helpers %}{% public_site_url 'directory/page' %}"
|
||||
)
|
||||
self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL))
|
||||
self.assertTrue(result.endswith("/directory/page"))
|
||||
|
||||
def test_public_site_url_leading_slash(self):
|
||||
result = self._render_template(
|
||||
"{% load url_helpers %}{% public_site_url '/directory/page' %}"
|
||||
)
|
||||
self.assertTrue(result.startswith(settings.GETGOV_PUBLIC_SITE_URL))
|
||||
# slash-slash host slash directory slash page
|
||||
self.assertEqual(result.count("/"), 4)
|
|
@ -13,6 +13,7 @@ import boto3_mocking # type: ignore
|
|||
from registrar.models import (
|
||||
DomainApplication,
|
||||
Domain,
|
||||
DomainInformation,
|
||||
DraftDomain,
|
||||
DomainInvitation,
|
||||
Contact,
|
||||
|
@ -1030,12 +1031,16 @@ class TestWithDomainPermissions(TestWithUser):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
|
||||
self.domain_information, _ = DomainInformation.objects.get_or_create(
|
||||
creator=self.user, domain=self.domain
|
||||
)
|
||||
self.role, _ = UserDomainRole.objects.get_or_create(
|
||||
user=self.user, domain=self.domain, role=UserDomainRole.Roles.ADMIN
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
try:
|
||||
self.domain_information.delete()
|
||||
if hasattr(self.domain, "contacts"):
|
||||
self.domain.contacts.all().delete()
|
||||
self.domain.delete()
|
||||
|
@ -1048,61 +1053,41 @@ class TestWithDomainPermissions(TestWithUser):
|
|||
class TestDomainPermissions(TestWithDomainPermissions):
|
||||
def test_not_logged_in(self):
|
||||
"""Not logged in gets a redirect to Login."""
|
||||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("domain-users", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("domain-users-add", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("domain-nameservers", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("domain-security-email", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
for view_name in [
|
||||
"domain",
|
||||
"domain-users",
|
||||
"domain-users-add",
|
||||
"domain-nameservers",
|
||||
"domain-authorizing-official",
|
||||
"domain-your-contact-information",
|
||||
"domain-security-email",
|
||||
]:
|
||||
with self.subTest(view_name=view_name):
|
||||
response = self.client.get(
|
||||
reverse(view_name, kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
def test_no_domain_role(self):
|
||||
"""Logged in but no role gets 403 Forbidden."""
|
||||
self.client.force_login(self.user)
|
||||
self.role.delete() # user no longer has a role on this domain
|
||||
|
||||
with less_console_noise():
|
||||
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
with less_console_noise():
|
||||
response = self.client.get(
|
||||
reverse("domain-users", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
with less_console_noise():
|
||||
response = self.client.get(
|
||||
reverse("domain-users-add", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
with less_console_noise():
|
||||
response = self.client.get(
|
||||
reverse("domain-nameservers", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
with less_console_noise():
|
||||
response = self.client.get(
|
||||
reverse("domain-security-email", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
for view_name in [
|
||||
"domain",
|
||||
"domain-users",
|
||||
"domain-users-add",
|
||||
"domain-nameservers",
|
||||
"domain-authorizing-official",
|
||||
"domain-your-contact-information",
|
||||
"domain-security-email",
|
||||
]:
|
||||
with self.subTest(view_name=view_name):
|
||||
with less_console_noise():
|
||||
response = self.client.get(
|
||||
reverse(view_name, kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
|
||||
class TestDomainDetail(TestWithDomainPermissions, WebTest):
|
||||
|
@ -1139,7 +1124,7 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
|
|||
self.assertContains(response, "Add another user")
|
||||
|
||||
def test_domain_user_add_form(self):
|
||||
"""Adding a user works."""
|
||||
"""Adding an existing user works."""
|
||||
other_user, _ = get_user_model().objects.get_or_create(
|
||||
email="mayor@igorville.gov"
|
||||
)
|
||||
|
@ -1222,6 +1207,22 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
|
|||
with self.assertRaises(DomainInvitation.DoesNotExist):
|
||||
DomainInvitation.objects.get(id=invitation.id)
|
||||
|
||||
def test_domain_invitation_cancel_no_permissions(self):
|
||||
"""Posting to the delete view as a different user should fail."""
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
invitation, _ = DomainInvitation.objects.get_or_create(
|
||||
domain=self.domain, email=EMAIL
|
||||
)
|
||||
|
||||
other_user = User()
|
||||
other_user.save()
|
||||
self.client.force_login(other_user)
|
||||
with less_console_noise(): # permission denied makes console errors
|
||||
result = self.client.post(
|
||||
reverse("invitation-delete", kwargs={"pk": invitation.id})
|
||||
)
|
||||
self.assertEqual(result.status_code, 403)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_domain_invitation_flow(self):
|
||||
"""Send an invitation to a new user, log in and load the dashboard."""
|
||||
|
@ -1296,6 +1297,40 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
|
|||
# the field.
|
||||
self.assertContains(result, "This field is required", count=2, status_code=200)
|
||||
|
||||
def test_domain_authorizing_official(self):
|
||||
"""Can load domain's authorizing official page."""
|
||||
page = self.client.get(
|
||||
reverse("domain-authorizing-official", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
# once on the sidebar, once in the title
|
||||
self.assertContains(page, "Authorizing official", count=2)
|
||||
|
||||
def test_domain_authorizing_official_content(self):
|
||||
"""Authorizing official information appears on the page."""
|
||||
self.domain_information.authorizing_official = Contact(first_name="Testy")
|
||||
self.domain_information.authorizing_official.save()
|
||||
self.domain_information.save()
|
||||
page = self.app.get(
|
||||
reverse("domain-authorizing-official", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertContains(page, "Testy")
|
||||
|
||||
def test_domain_your_contact_information(self):
|
||||
"""Can load domain's your contact information page."""
|
||||
page = self.client.get(
|
||||
reverse("domain-your-contact-information", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertContains(page, "Domain contact information")
|
||||
|
||||
def test_domain_your_contact_information_content(self):
|
||||
"""Logged-in user's contact information appears on the page."""
|
||||
self.user.contact.first_name = "Testy"
|
||||
self.user.contact.save()
|
||||
page = self.app.get(
|
||||
reverse("domain-your-contact-information", kwargs={"pk": self.domain.id})
|
||||
)
|
||||
self.assertContains(page, "Testy")
|
||||
|
||||
def test_domain_security_email(self):
|
||||
"""Can load domain's security email page."""
|
||||
page = self.client.get(
|
||||
|
@ -1333,6 +1368,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
def setUp(self):
|
||||
super().setUp()
|
||||
self.app.set_user(self.user.username)
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def _completed_application(
|
||||
self,
|
||||
|
@ -1446,3 +1482,24 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
)
|
||||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "Withdrawn")
|
||||
|
||||
def test_application_status_no_permissions(self):
|
||||
"""Can't access applications without being the creator."""
|
||||
application = self._completed_application()
|
||||
other_user = User()
|
||||
other_user.save()
|
||||
application.creator = other_user
|
||||
application.save()
|
||||
|
||||
# PermissionDeniedErrors make lots of noise in test output
|
||||
with less_console_noise():
|
||||
for url_name in [
|
||||
"application-status",
|
||||
"application-withdraw-confirmation",
|
||||
"application-withdrawn",
|
||||
]:
|
||||
with self.subTest(url_name=url_name):
|
||||
page = self.client.get(
|
||||
reverse(url_name, kwargs={"pk": application.pk})
|
||||
)
|
||||
self.assertEqual(page.status_code, 403)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from .application import *
|
||||
from .domain import (
|
||||
DomainView,
|
||||
DomainAuthorizingOfficialView,
|
||||
DomainNameserversView,
|
||||
DomainYourContactInformationView,
|
||||
DomainSecurityEmailView,
|
||||
DomainUsersView,
|
||||
DomainAddUserView,
|
||||
|
|
|
@ -6,7 +6,6 @@ from django.shortcuts import redirect, render
|
|||
from django.urls import resolve, reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
from django.views import generic
|
||||
from django.contrib import messages
|
||||
|
||||
from registrar.forms import application_wizard as forms
|
||||
|
@ -14,7 +13,7 @@ from registrar.models import DomainApplication
|
|||
from registrar.utility import StrEnum
|
||||
from registrar.views.utility import StepsHelper
|
||||
|
||||
from .utility import DomainPermission
|
||||
from .utility import DomainApplicationPermissionView
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -478,29 +477,31 @@ class Finished(ApplicationWizard):
|
|||
return render(self.request, self.template_name, context)
|
||||
|
||||
|
||||
class ApplicationStatus(generic.DetailView):
|
||||
model = DomainApplication
|
||||
class ApplicationStatus(DomainApplicationPermissionView):
|
||||
template_name = "application_status.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
"""Get context details to process information from application"""
|
||||
context = super(ApplicationStatus, self).get_context_data(**kwargs)
|
||||
return context
|
||||
|
||||
class ApplicationWithdrawConfirmation(DomainApplicationPermissionView):
|
||||
"""This page will ask user to confirm if they want to withdraw
|
||||
|
||||
class ApplicationWithdraw(LoginRequiredMixin, generic.DetailView, DomainPermission):
|
||||
model = DomainApplication
|
||||
template_name = "application_withdraw_confirmation.html"
|
||||
""" The page above will display asking user to confirm if they want to withdraw;
|
||||
|
||||
Note it uses "DomainPermission" from Domain to ensure that the person who
|
||||
applied only have access to withdraw the request
|
||||
The DomainApplicationPermissionView restricts access so that only the
|
||||
`creator` of the application may withdraw it.
|
||||
"""
|
||||
|
||||
def updatestatus(request, pk):
|
||||
"""If user click on withdraw confirm button, it will be updated to withdraw
|
||||
and send back to homepage"""
|
||||
application = DomainApplication.objects.get(id=pk)
|
||||
template_name = "application_withdraw_confirmation.html"
|
||||
|
||||
|
||||
class ApplicationWithdrawn(DomainApplicationPermissionView):
|
||||
# this view renders no template
|
||||
template_name = ""
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""View class that does the actual withdrawing.
|
||||
|
||||
If user click on withdraw confirm button, this view updates the status
|
||||
to withdraw and send back to homepage.
|
||||
"""
|
||||
application = DomainApplication.objects.get(id=self.kwargs["pk"])
|
||||
application.status = "withdrawn"
|
||||
application.save()
|
||||
return HttpResponseRedirect(reverse("home"))
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
"""View for a single Domain."""
|
||||
"""Views for a single Domain.
|
||||
|
||||
Authorization is handled by the `DomainPermissionView`. To ensure that only
|
||||
authorized users can see information on a domain, every view here should
|
||||
inherit from `DomainPermissionView` (or DomainInvitationPermissionDeleteView).
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -7,35 +12,82 @@ from django.contrib.messages.views import SuccessMessageMixin
|
|||
from django.db import IntegrityError
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
from django.views.generic import DetailView
|
||||
from django.views.generic.edit import DeleteView, FormMixin
|
||||
from django.views.generic.edit import FormMixin
|
||||
|
||||
from registrar.models import Domain, DomainInvitation, User, UserDomainRole
|
||||
from registrar.models import (
|
||||
Domain,
|
||||
DomainInvitation,
|
||||
User,
|
||||
UserDomainRole,
|
||||
)
|
||||
|
||||
from ..forms import DomainAddUserForm, NameserverFormset, DomainSecurityEmailForm
|
||||
from ..forms import (
|
||||
DomainAddUserForm,
|
||||
NameserverFormset,
|
||||
DomainSecurityEmailForm,
|
||||
ContactForm,
|
||||
)
|
||||
from ..utility.email import send_templated_email, EmailSendingError
|
||||
from .utility import DomainPermission
|
||||
from .utility import DomainPermissionView, DomainInvitationPermissionDeleteView
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DomainView(DomainPermission, DetailView):
|
||||
class DomainView(DomainPermissionView):
|
||||
|
||||
"""Domain detail overview page."""
|
||||
|
||||
model = Domain
|
||||
template_name = "domain_detail.html"
|
||||
|
||||
|
||||
class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin):
|
||||
|
||||
"""Domain authorizing official editing view."""
|
||||
|
||||
model = Domain
|
||||
template_name = "domain_authorizing_official.html"
|
||||
context_object_name = "domain"
|
||||
form_class = ContactForm
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
"""Add domain_info.authorizing_official instance to make a bound form."""
|
||||
form_kwargs = super().get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs["instance"] = self.get_object().domain_info.authorizing_official
|
||||
return form_kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""Redirect to the overview page for the domain."""
|
||||
return reverse("domain-authorizing-official", kwargs={"pk": self.object.pk})
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Form submission posts to this view.
|
||||
|
||||
This post method harmonizes using DetailView and FormMixin together.
|
||||
"""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
"""The form is valid, save the authorizing official."""
|
||||
form.save()
|
||||
|
||||
messages.success(
|
||||
self.request, "The authorizing official for this domain has been updated."
|
||||
)
|
||||
# superclass has the redirect
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class DomainNameserversView(DomainPermission, FormMixin, DetailView):
|
||||
class DomainNameserversView(DomainPermissionView, FormMixin):
|
||||
|
||||
"""Domain nameserver editing view."""
|
||||
|
||||
model = Domain
|
||||
template_name = "domain_nameservers.html"
|
||||
context_object_name = "domain"
|
||||
form_class = NameserverFormset
|
||||
|
||||
def get_initial(self):
|
||||
|
@ -45,7 +97,7 @@ class DomainNameserversView(DomainPermission, FormMixin, DetailView):
|
|||
|
||||
|
||||
def get_success_url(self):
|
||||
"""Redirect to the overview page for the domain."""
|
||||
"""Redirect to the nameservers page for the domain."""
|
||||
return reverse("domain-nameservers", kwargs={"pk": self.object.pk})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -98,13 +150,51 @@ class DomainNameserversView(DomainPermission, FormMixin, DetailView):
|
|||
return super().form_valid(formset)
|
||||
|
||||
|
||||
class DomainSecurityEmailView(DomainPermission, FormMixin, DetailView):
|
||||
class DomainYourContactInformationView(DomainPermissionView, FormMixin):
|
||||
|
||||
"""Domain your contact information editing view."""
|
||||
|
||||
template_name = "domain_your_contact_information.html"
|
||||
form_class = ContactForm
|
||||
|
||||
def get_form_kwargs(self, *args, **kwargs):
|
||||
"""Add domain_info.submitter instance to make a bound form."""
|
||||
form_kwargs = super().get_form_kwargs(*args, **kwargs)
|
||||
form_kwargs["instance"] = self.request.user.contact
|
||||
return form_kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
"""Redirect to the your contact information for the domain."""
|
||||
return reverse("domain-your-contact-information", kwargs={"pk": self.object.pk})
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Form submission posts to this view."""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
# there is a valid email address in the form
|
||||
return self.form_valid(form)
|
||||
else:
|
||||
return self.form_invalid(form)
|
||||
|
||||
def form_valid(self, form):
|
||||
"""The form is valid, call setter in model."""
|
||||
|
||||
# Post to DB using values from the form
|
||||
form.save()
|
||||
|
||||
messages.success(
|
||||
self.request, "Your contact information for this domain has been updated."
|
||||
)
|
||||
# superclass has the redirect
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class DomainSecurityEmailView(DomainPermissionView, FormMixin):
|
||||
|
||||
"""Domain security email editing view."""
|
||||
|
||||
model = Domain
|
||||
template_name = "domain_security_email.html"
|
||||
context_object_name = "domain"
|
||||
form_class = DomainSecurityEmailForm
|
||||
|
||||
def get_initial(self):
|
||||
|
@ -115,11 +205,11 @@ class DomainSecurityEmailView(DomainPermission, FormMixin, DetailView):
|
|||
return initial
|
||||
|
||||
def get_success_url(self):
|
||||
"""Redirect to the overview page for the domain."""
|
||||
"""Redirect to the security email page for the domain."""
|
||||
return reverse("domain-security-email", kwargs={"pk": self.object.pk})
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
"""Formset submission posts to this view."""
|
||||
"""Form submission posts to this view."""
|
||||
self.object = self.get_object()
|
||||
form = self.get_form()
|
||||
if form.is_valid():
|
||||
|
@ -145,16 +235,14 @@ class DomainSecurityEmailView(DomainPermission, FormMixin, DetailView):
|
|||
return redirect(self.get_success_url())
|
||||
|
||||
|
||||
class DomainUsersView(DomainPermission, DetailView):
|
||||
class DomainUsersView(DomainPermissionView):
|
||||
|
||||
"""User management page in the domain details."""
|
||||
|
||||
model = Domain
|
||||
template_name = "domain_users.html"
|
||||
context_object_name = "domain"
|
||||
|
||||
|
||||
class DomainAddUserView(DomainPermission, FormMixin, DetailView):
|
||||
class DomainAddUserView(DomainPermissionView, FormMixin):
|
||||
|
||||
"""Inside of a domain's user management, a form for adding users.
|
||||
|
||||
|
@ -163,7 +251,6 @@ class DomainAddUserView(DomainPermission, FormMixin, DetailView):
|
|||
"""
|
||||
|
||||
template_name = "domain_add_user.html"
|
||||
model = Domain
|
||||
form_class = DomainAddUserForm
|
||||
|
||||
def get_success_url(self):
|
||||
|
@ -243,8 +330,9 @@ class DomainAddUserView(DomainPermission, FormMixin, DetailView):
|
|||
return redirect(self.get_success_url())
|
||||
|
||||
|
||||
class DomainInvitationDeleteView(SuccessMessageMixin, DeleteView):
|
||||
model = DomainInvitation
|
||||
class DomainInvitationDeleteView(
|
||||
DomainInvitationPermissionDeleteView, SuccessMessageMixin
|
||||
):
|
||||
object: DomainInvitation # workaround for type mismatch in DeleteView
|
||||
|
||||
def get_success_url(self):
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
from .steps_helper import StepsHelper
|
||||
from .always_404 import always_404
|
||||
from .mixins import DomainPermission
|
||||
|
||||
from .permission_views import (
|
||||
DomainPermissionView,
|
||||
DomainApplicationPermissionView,
|
||||
DomainInvitationPermissionDeleteView,
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
|
||||
from registrar.models import UserDomainRole
|
||||
from registrar.models import UserDomainRole, DomainApplication, DomainInvitation
|
||||
|
||||
|
||||
class PermissionsLoginMixin(PermissionRequiredMixin):
|
||||
|
@ -35,3 +35,48 @@ class DomainPermission(PermissionsLoginMixin):
|
|||
|
||||
# if we need to check more about the nature of role, do it here.
|
||||
return True
|
||||
|
||||
|
||||
class DomainApplicationPermission(PermissionsLoginMixin):
|
||||
|
||||
"""Does the logged-in user have access to this domain application?"""
|
||||
|
||||
def has_permission(self):
|
||||
"""Check if this user has access to this domain application.
|
||||
|
||||
The user is in self.request.user and the domain needs to be looked
|
||||
up from the domain's primary key in self.kwargs["pk"]
|
||||
"""
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
|
||||
# user needs to be the creator of the application
|
||||
# this query is empty if there isn't a domain application with this
|
||||
# id and this user as creator
|
||||
if not DomainApplication.objects.filter(
|
||||
creator=self.request.user, id=self.kwargs["pk"]
|
||||
).exists():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class DomainInvitationPermission(PermissionsLoginMixin):
|
||||
|
||||
"""Does the logged-in user have access to this domain invitation?
|
||||
|
||||
A user has access to a domain invitation if they have a role on the
|
||||
associated domain.
|
||||
"""
|
||||
|
||||
def has_permission(self):
|
||||
"""Check if this user has a role on the domain of this invitation."""
|
||||
if not self.request.user.is_authenticated:
|
||||
return False
|
||||
|
||||
if not DomainInvitation.objects.filter(
|
||||
id=self.kwargs["pk"], domain__permissions__user=self.request.user
|
||||
).exists():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
69
src/registrar/views/utility/permission_views.py
Normal file
69
src/registrar/views/utility/permission_views.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
"""View classes that enforce authorization."""
|
||||
|
||||
import abc # abstract base class
|
||||
|
||||
from django.views.generic import DetailView, DeleteView
|
||||
|
||||
from registrar.models import Domain, DomainApplication, DomainInvitation
|
||||
|
||||
from .mixins import (
|
||||
DomainPermission,
|
||||
DomainApplicationPermission,
|
||||
DomainInvitationPermission,
|
||||
)
|
||||
|
||||
|
||||
class DomainPermissionView(DomainPermission, DetailView, abc.ABC):
|
||||
|
||||
"""Abstract base view for domains that enforces permissions.
|
||||
|
||||
This abstract view cannot be instantiated. Actual views must specify
|
||||
`template_name`.
|
||||
"""
|
||||
|
||||
# DetailView property for what model this is viewing
|
||||
model = Domain
|
||||
# variable name in template context for the model object
|
||||
context_object_name = "domain"
|
||||
|
||||
# Abstract property enforces NotImplementedError on an attribute.
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def template_name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DomainApplicationPermissionView(DomainApplicationPermission, DetailView, abc.ABC):
|
||||
|
||||
"""Abstract base view for domain applications that enforces permissions
|
||||
|
||||
This abstract view cannot be instantiated. Actual views must specify
|
||||
`template_name`.
|
||||
"""
|
||||
|
||||
# DetailView property for what model this is viewing
|
||||
model = DomainApplication
|
||||
# variable name in template context for the model object
|
||||
context_object_name = "domainapplication"
|
||||
|
||||
# Abstract property enforces NotImplementedError on an attribute.
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def template_name(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DomainInvitationPermissionDeleteView(
|
||||
DomainInvitationPermission, DeleteView, abc.ABC
|
||||
):
|
||||
|
||||
"""Abstract view for deleting a domain invitation.
|
||||
|
||||
This one is fairly specialized, but this is the only thing that we do
|
||||
right now with domain invitations. We still have the full
|
||||
`DomainInvitationPermission` class, but here we just pair it with a
|
||||
DeleteView.
|
||||
"""
|
||||
|
||||
model = DomainInvitation
|
||||
object: DomainInvitation # workaround for type mismatch in DeleteView
|
|
@ -1,49 +1,52 @@
|
|||
-i https://pypi.python.org/simple
|
||||
asgiref==3.6.0 ; python_version >= '3.7'
|
||||
boto3==1.26.69
|
||||
botocore==1.29.69 ; python_version >= '3.7'
|
||||
cachetools==5.3.0
|
||||
certifi==2022.12.7 ; python_version >= '3.6'
|
||||
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.5.7 ; python_version >= '3.6'
|
||||
cfenv==0.5.3
|
||||
cffi==1.15.1
|
||||
charset-normalizer==3.0.1 ; python_version >= '3.6'
|
||||
cryptography==39.0.1 ; python_version >= '3.6'
|
||||
charset-normalizer==3.1.0 ; python_full_version >= '3.7.0'
|
||||
cryptography==41.0.1 ; 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==1.2.0
|
||||
dj-database-url==2.0.0
|
||||
dj-email-url==1.0.6
|
||||
django==4.1.6
|
||||
django==4.2.1
|
||||
django-allow-cidr==0.6.0
|
||||
django-auditlog==2.2.2
|
||||
django-auditlog==2.3.0
|
||||
django-cache-url==3.4.4
|
||||
django-csp==3.7
|
||||
django-fsm==2.8.1
|
||||
django-phonenumber-field[phonenumberslite]==7.0.2
|
||||
django-phonenumber-field[phonenumberslite]==7.1.0
|
||||
django-widget-tweaks==1.4.12
|
||||
environs[django]==9.5.0
|
||||
faker==17.0.0
|
||||
faker==18.10.0
|
||||
git+https://github.com/cisagov/epplib.git@f818cbf0b069a12f03e1d72e4b9f4900924b832d#egg=fred-epplib
|
||||
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.5.0
|
||||
oic==1.6.0
|
||||
orderedmultidict==1.0.1
|
||||
packaging==23.0 ; python_version >= '3.7'
|
||||
phonenumberslite==8.13.6
|
||||
psycopg2-binary==2.9.5
|
||||
packaging==23.1 ; python_version >= '3.7'
|
||||
phonenumberslite==8.13.13
|
||||
psycopg2-binary==2.9.6
|
||||
pycparser==2.21
|
||||
pycryptodomex==3.17
|
||||
pycryptodomex==3.18.0
|
||||
pydantic==1.10.8 ; 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==0.21.1 ; python_version >= '3.7'
|
||||
requests==2.28.2
|
||||
s3transfer==0.6.0 ; python_version >= '3.7'
|
||||
setuptools==67.2.0 ; python_version >= '3.7'
|
||||
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.3 ; python_version >= '3.5'
|
||||
typing-extensions==4.4.0 ; python_version >= '3.7'
|
||||
urllib3==1.26.14 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
|
||||
whitenoise==6.3.0
|
||||
sqlparse==0.4.4 ; python_version >= '3.5'
|
||||
typing-extensions==4.6.2
|
||||
urllib3==1.26.16 ; 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
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
10038 OUTOFSCOPE http://app:8080/users
|
||||
10038 OUTOFSCOPE http://app:8080/users/add
|
||||
10038 OUTOFSCOPE http://app:8080/nameservers
|
||||
10038 OUTOFSCOPE http://app:8080/your-contact-information
|
||||
10038 OUTOFSCOPE http://app:8080/security-email
|
||||
10038 OUTOFSCOPE http://app:8080/delete
|
||||
10038 OUTOFSCOPE http://app:8080/withdraw
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue