mirror of
https://github.com/tjsga/scavenger-hunt-2022.git
synced 2025-04-03 20:00:18 -04:00
initial commit
This commit is contained in:
parent
c557977d8e
commit
a867f38153
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
db.sqlite3
|
||||
secret.py
|
||||
__pycache__/
|
||||
*.pyc
|
9
Pipfile
Normal file
9
Pipfile
Normal file
|
@ -0,0 +1,9 @@
|
|||
[[source]]
|
||||
url = "https://pypi.python.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
Django = "~=3.2"
|
||||
psycopg2-binary = "~=2.8.6"
|
||||
social-auth-app-django = "~=4.0.0"
|
272
Pipfile.lock
generated
Normal file
272
Pipfile.lock
generated
Normal file
|
@ -0,0 +1,272 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "b4008672fb38a3e89baf1655aa6b39685d638b0b7f6012ef5fb8da7b48ed33c2"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.python.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"asgiref": {
|
||||
"hashes": [
|
||||
"sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9",
|
||||
"sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.4.1"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee",
|
||||
"sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"
|
||||
],
|
||||
"version": "==2021.5.30"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d",
|
||||
"sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771",
|
||||
"sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872",
|
||||
"sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c",
|
||||
"sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc",
|
||||
"sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762",
|
||||
"sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202",
|
||||
"sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5",
|
||||
"sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548",
|
||||
"sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a",
|
||||
"sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f",
|
||||
"sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20",
|
||||
"sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218",
|
||||
"sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c",
|
||||
"sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e",
|
||||
"sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56",
|
||||
"sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224",
|
||||
"sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a",
|
||||
"sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2",
|
||||
"sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a",
|
||||
"sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819",
|
||||
"sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346",
|
||||
"sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b",
|
||||
"sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e",
|
||||
"sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534",
|
||||
"sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb",
|
||||
"sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0",
|
||||
"sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156",
|
||||
"sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd",
|
||||
"sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87",
|
||||
"sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc",
|
||||
"sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195",
|
||||
"sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33",
|
||||
"sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f",
|
||||
"sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d",
|
||||
"sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd",
|
||||
"sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728",
|
||||
"sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7",
|
||||
"sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca",
|
||||
"sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99",
|
||||
"sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf",
|
||||
"sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e",
|
||||
"sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c",
|
||||
"sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5",
|
||||
"sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"
|
||||
],
|
||||
"version": "==1.14.6"
|
||||
},
|
||||
"charset-normalizer": {
|
||||
"hashes": [
|
||||
"sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b",
|
||||
"sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==2.0.4"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d",
|
||||
"sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959",
|
||||
"sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6",
|
||||
"sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873",
|
||||
"sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2",
|
||||
"sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713",
|
||||
"sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1",
|
||||
"sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177",
|
||||
"sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250",
|
||||
"sha256:b01fd6f2737816cb1e08ed4807ae194404790eac7ad030b34f2ce72b332f5586",
|
||||
"sha256:bf40af59ca2465b24e54f671b2de2c59257ddc4f7e5706dbd6930e26823668d3",
|
||||
"sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca",
|
||||
"sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d",
|
||||
"sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.4.7"
|
||||
},
|
||||
"defusedxml": {
|
||||
"hashes": [
|
||||
"sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69",
|
||||
"sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==0.7.1"
|
||||
},
|
||||
"django": {
|
||||
"hashes": [
|
||||
"sha256:7f92413529aa0e291f3be78ab19be31aefb1e1c9a52cd59e130f505f27a51f13",
|
||||
"sha256:f27f8544c9d4c383bbe007c57e3235918e258364577373d4920e9162837be022"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.2.6"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a",
|
||||
"sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"
|
||||
],
|
||||
"markers": "python_version >= '3'",
|
||||
"version": "==3.2"
|
||||
},
|
||||
"oauthlib": {
|
||||
"hashes": [
|
||||
"sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc",
|
||||
"sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==3.1.1"
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
"sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
|
||||
"sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67",
|
||||
"sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0",
|
||||
"sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6",
|
||||
"sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db",
|
||||
"sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94",
|
||||
"sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52",
|
||||
"sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056",
|
||||
"sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b",
|
||||
"sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd",
|
||||
"sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550",
|
||||
"sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679",
|
||||
"sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83",
|
||||
"sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77",
|
||||
"sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2",
|
||||
"sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77",
|
||||
"sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2",
|
||||
"sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd",
|
||||
"sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859",
|
||||
"sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1",
|
||||
"sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25",
|
||||
"sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152",
|
||||
"sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf",
|
||||
"sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f",
|
||||
"sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729",
|
||||
"sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71",
|
||||
"sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66",
|
||||
"sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4",
|
||||
"sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449",
|
||||
"sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
|
||||
"sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a",
|
||||
"sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
|
||||
"sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
|
||||
"sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
|
||||
"sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.8.6"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
|
||||
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.20"
|
||||
},
|
||||
"pyjwt": {
|
||||
"hashes": [
|
||||
"sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1",
|
||||
"sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==2.1.0"
|
||||
},
|
||||
"python3-openid": {
|
||||
"hashes": [
|
||||
"sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf",
|
||||
"sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"
|
||||
],
|
||||
"version": "==3.2.0"
|
||||
},
|
||||
"pytz": {
|
||||
"hashes": [
|
||||
"sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
|
||||
"sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
|
||||
],
|
||||
"version": "==2021.1"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
|
||||
"sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
|
||||
"version": "==2.26.0"
|
||||
},
|
||||
"requests-oauthlib": {
|
||||
"hashes": [
|
||||
"sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d",
|
||||
"sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a",
|
||||
"sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"
|
||||
],
|
||||
"version": "==1.3.0"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
|
||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"social-auth-app-django": {
|
||||
"hashes": [
|
||||
"sha256:2c69e57df0b30c9c1823519c5f1992cbe4f3f98fdc7d95c840e091a752708840",
|
||||
"sha256:567ad0e028311541d7dfed51d3bf2c60440a6fd236d5d4d06c5a618b3d6c57c5",
|
||||
"sha256:df5212370bd250108987c4748419a1a1d0cec750878856c2644c36aaa0fd3e58"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.0"
|
||||
},
|
||||
"social-auth-core": {
|
||||
"hashes": [
|
||||
"sha256:5ab43b3b15dce5f059db69cc3082c216574739f0edbc98629c8c6e8769c67eb4",
|
||||
"sha256:983b53167ac56e7ba4909db555602a6e7a98c97ca47183bb222eb85ba627bf2b"
|
||||
],
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==4.1.0"
|
||||
},
|
||||
"sqlparse": {
|
||||
"hashes": [
|
||||
"sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
|
||||
"sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==0.4.1"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4",
|
||||
"sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.6"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
0
hunt/__init__.py
Normal file
0
hunt/__init__.py
Normal file
0
hunt/apps/__init__.py
Normal file
0
hunt/apps/__init__.py
Normal file
0
hunt/apps/auth/__init__.py
Normal file
0
hunt/apps/auth/__init__.py
Normal file
6
hunt/apps/auth/apps.py
Normal file
6
hunt/apps/auth/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AuthConfig(AppConfig):
|
||||
name = "hunt.apps.auth"
|
||||
label = "authentication"
|
0
hunt/apps/auth/migrations/__init__.py
Normal file
0
hunt/apps/auth/migrations/__init__.py
Normal file
36
hunt/apps/auth/oauth.py
Normal file
36
hunt/apps/auth/oauth.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
from typing import Any, Dict, List
|
||||
|
||||
from social_core.backends.oauth import BaseOAuth2
|
||||
from social_core.pipeline.user import get_username as social_get_username
|
||||
|
||||
|
||||
def get_username(strategy, details, *args, user=None, **kwargs):
|
||||
result = social_get_username(strategy, details, user=user, *args, **kwargs)
|
||||
return result
|
||||
|
||||
|
||||
class IonOauth2(BaseOAuth2):
|
||||
name = "ion"
|
||||
AUTHORIZATION_URL = "https://ion.tjhsst.edu/oauth/authorize"
|
||||
ACCESS_TOKEN_URL = "https://ion.tjhsst.edu/oauth/token"
|
||||
ACCESS_TOKEN_METHOD = "POST"
|
||||
EXTRA_DATA = [("refresh_token", "refresh_token", True), ("expires_in", "expires")]
|
||||
|
||||
def get_scope(self) -> List[str]:
|
||||
return ["read"]
|
||||
|
||||
def get_user_details(self, response: Dict[str, Any]) -> Dict[str, Any]:
|
||||
profile = self.get_json(
|
||||
"https://ion.tjhsst.edu/api/profile", params={"access_token": response["access_token"]},
|
||||
)
|
||||
# fields used to populate/update User model
|
||||
data = {
|
||||
key: profile[key]
|
||||
for key in ("id", "first_name", "last_name", "is_student", "graduation_year")
|
||||
}
|
||||
data["username"] = profile["ion_username"]
|
||||
data["email"] = profile["tj_email"]
|
||||
return data
|
||||
|
||||
def get_user_id(self, details: Dict[str, Any], response: Any) -> int:
|
||||
return details["id"]
|
11
hunt/apps/auth/urls.py
Normal file
11
hunt/apps/auth/urls.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = "auth"
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index_view, name="index"),
|
||||
path("login/", views.login_view, name="login"),
|
||||
path("logout/", views.logout_view, name="logout"),
|
||||
]
|
18
hunt/apps/auth/views.py
Normal file
18
hunt/apps/auth/views.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from django.contrib.auth import logout
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
|
||||
def index_view(request):
|
||||
if request.user.is_authenticated:
|
||||
return redirect("main:index")
|
||||
else:
|
||||
return redirect("auth:login")
|
||||
|
||||
|
||||
def login_view(request):
|
||||
return render(request, "auth/login.html")
|
||||
|
||||
|
||||
def logout_view(request):
|
||||
logout(request)
|
||||
return redirect("auth:index")
|
0
hunt/apps/main/__init__.py
Normal file
0
hunt/apps/main/__init__.py
Normal file
6
hunt/apps/main/admin.py
Normal file
6
hunt/apps/main/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Challenge, Class
|
||||
|
||||
admin.site.register(Challenge)
|
||||
admin.site.register(Class)
|
6
hunt/apps/main/apps.py
Normal file
6
hunt/apps/main/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MainConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'hunt.apps.main'
|
35
hunt/apps/main/migrations/0001_initial.py
Normal file
35
hunt/apps/main/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-14 22:22
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Challenge',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('description', models.CharField(max_length=500)),
|
||||
('flag', models.CharField(max_length=50)),
|
||||
('points', models.IntegerField()),
|
||||
('exclusive', models.BooleanField(default=False)),
|
||||
('locked', models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Class',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('year', models.CharField(choices=[('2022', 'Seniors'), ('2023', 'Juniors'), ('2024', 'Sophomores'), ('2025', 'Freshmen')], max_length=20, unique=True)),
|
||||
('last_updated', models.DateTimeField(auto_now=True)),
|
||||
('challenges_completed', models.ManyToManyField(related_name='classes_completed', to='main.Challenge')),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-14 23:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='class',
|
||||
name='challenges_completed',
|
||||
field=models.ManyToManyField(blank=True, related_name='classes_completed', to='main.Challenge'),
|
||||
),
|
||||
]
|
0
hunt/apps/main/migrations/__init__.py
Normal file
0
hunt/apps/main/migrations/__init__.py
Normal file
34
hunt/apps/main/models.py
Normal file
34
hunt/apps/main/models.py
Normal file
|
@ -0,0 +1,34 @@
|
|||
from django.db import models
|
||||
from django.db.models.fields.related import ManyToManyField
|
||||
|
||||
|
||||
class Challenge(models.Model):
|
||||
id = models.AutoField(primary_key=True, null=False, blank=False)
|
||||
name = models.CharField(max_length=100, null=False, blank=False)
|
||||
description = models.CharField(max_length=500, null=False, blank=False)
|
||||
flag = models.CharField(max_length=50, null=False, blank=False)
|
||||
points = models.IntegerField(null=False, blank=False)
|
||||
exclusive = models.BooleanField(default=False)
|
||||
locked = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return "{} ({})".format(self.name, self.id)
|
||||
|
||||
class Class(models.Model):
|
||||
YEAR_CHOICES = (('2022', 'Seniors'), ('2023', 'Juniors'), ('2024', 'Sophomores'), ('2025', 'Freshmen'))
|
||||
|
||||
id = models.AutoField(primary_key=True, null=False, blank=False)
|
||||
year = models.CharField(max_length=20, choices=YEAR_CHOICES, null=False, blank=False, unique=True)
|
||||
challenges_completed = models.ManyToManyField(Challenge, related_name="classes_completed", blank=True)
|
||||
|
||||
last_updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.year
|
||||
|
||||
def get_points(self):
|
||||
sum = 0
|
||||
for c in self.challenges_completed.all():
|
||||
sum += c.points
|
||||
return sum
|
||||
|
11
hunt/apps/main/urls.py
Normal file
11
hunt/apps/main/urls.py
Normal file
|
@ -0,0 +1,11 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'main'
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("overview/", views.overview, name="overview"),
|
||||
path("validate/", views.validate_flag, name="validate_flag"),
|
||||
]
|
56
hunt/apps/main/views.py
Normal file
56
hunt/apps/main/views.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http.response import JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
|
||||
from .models import Challenge, Class
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
if request.user.is_participant():
|
||||
"""
|
||||
Challenges fall into one of three statuses with respect to the user:
|
||||
- available (user can complete)
|
||||
- completed (completed by users's class)
|
||||
- locked (can only be completed by one class and has been completed)
|
||||
"""
|
||||
challenges_completed_by_class = set(Class.objects.get(year=str(request.user.graduation_year)).challenges_completed.all())
|
||||
challenges_dict = dict()
|
||||
for c in Challenge.objects.all():
|
||||
if c in challenges_completed_by_class:
|
||||
challenges_dict[c.id] = [c, "completed"]
|
||||
elif c.locked:
|
||||
challenges_dict[c.id] = [c, "locked"]
|
||||
else:
|
||||
challenges_dict[c.id] = [c, "available"]
|
||||
return render(request, 'main/index.html', context={"challenges_dict": challenges_dict})
|
||||
else:
|
||||
return redirect(reverse("main:overview"))
|
||||
|
||||
@login_required
|
||||
def overview(request):
|
||||
data = sorted([(c.year, c.get_points()) for c in Class.objects.all()])
|
||||
return render(request, 'main/overview.html', context={'data': data})
|
||||
|
||||
@login_required
|
||||
def validate_flag(request):
|
||||
if request.is_ajax() and request.user.is_participant():
|
||||
challenge = get_object_or_404(Challenge, id=int(request.POST.get("challenge_id")))
|
||||
flag = request.POST.get("flag")
|
||||
if flag == challenge.flag:
|
||||
if not challenge.locked:
|
||||
request.user.challenges_done.add(challenge)
|
||||
if challenge.exclusive:
|
||||
challenge.locked = True
|
||||
challenge.save()
|
||||
hoco_class = Class.objects.get(year=str(request.user.graduation_year))
|
||||
hoco_class.challenges_completed.add(challenge)
|
||||
hoco_class.save()
|
||||
response = {"result": "success"}
|
||||
else:
|
||||
response = {"result": "failure"}
|
||||
return JsonResponse(response)
|
||||
else:
|
||||
return PermissionDenied
|
0
hunt/apps/users/__init__.py
Normal file
0
hunt/apps/users/__init__.py
Normal file
8
hunt/apps/users/admin.py
Normal file
8
hunt/apps/users/admin.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from .models import Group, User
|
||||
|
||||
# Register your models here.
|
||||
|
||||
admin.site.register(User)
|
||||
admin.site.register(Group)
|
6
hunt/apps/users/apps.py
Normal file
6
hunt/apps/users/apps.py
Normal file
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UserConfig(AppConfig):
|
||||
name = "hunt.apps.users"
|
||||
label = "users"
|
51
hunt/apps/users/migrations/0001_initial.py
Normal file
51
hunt/apps/users/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-14 22:22
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import hunt.apps.users.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('main', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='User',
|
||||
fields=[
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('username', models.CharField(max_length=32, unique=True)),
|
||||
('first_name', models.CharField(max_length=35)),
|
||||
('last_name', models.CharField(max_length=70)),
|
||||
('is_student', models.BooleanField(default=False)),
|
||||
('graduation_year', models.IntegerField(null=True)),
|
||||
('email', models.EmailField(max_length=50)),
|
||||
('is_superuser', models.BooleanField(default=False)),
|
||||
('date_joined', models.DateTimeField(auto_now_add=True)),
|
||||
('challenges_done', models.ManyToManyField(related_name='users_that_completed', to='main.Challenge')),
|
||||
('hoco_class', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user_set', to='main.class')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', hunt.apps.users.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Group',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('is_service', models.BooleanField(default=False)),
|
||||
('name', models.CharField(max_length=32)),
|
||||
('users', models.ManyToManyField(related_name='unix_groups', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
18
hunt/apps/users/migrations/0002_user_is_staff.py
Normal file
18
hunt/apps/users/migrations/0002_user_is_staff.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-14 22:26
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='is_staff',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
22
hunt/apps/users/migrations/0003_auto_20210814_2354.py
Normal file
22
hunt/apps/users/migrations/0003_auto_20210814_2354.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 3.2.6 on 2021-08-14 23:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_user_is_staff'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='user',
|
||||
old_name='is_staff',
|
||||
new_name='_is_staff',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='user',
|
||||
name='hoco_class',
|
||||
),
|
||||
]
|
0
hunt/apps/users/migrations/__init__.py
Normal file
0
hunt/apps/users/migrations/__init__.py
Normal file
86
hunt/apps/users/models.py
Normal file
86
hunt/apps/users/models.py
Normal file
|
@ -0,0 +1,86 @@
|
|||
import logging
|
||||
|
||||
from django.contrib.auth.models import AbstractBaseUser
|
||||
from django.contrib.auth.models import UserManager as DjangoUserManager
|
||||
from django.db import models
|
||||
|
||||
from ..main.models import Challenge, Class
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserManager(DjangoUserManager):
|
||||
pass
|
||||
|
||||
|
||||
class User(AbstractBaseUser):
|
||||
objects = UserManager()
|
||||
|
||||
USERNAME_FIELD = "username"
|
||||
EMAIL_FIELD = "email"
|
||||
REQUIRED_FIELDS = ["first_name", "last_name", "is_student", "graduation_year"]
|
||||
|
||||
id = models.AutoField(primary_key=True)
|
||||
|
||||
username = models.CharField(unique=True, max_length=32, null=False, blank=False)
|
||||
first_name = models.CharField(max_length=35, null=False, blank=False)
|
||||
last_name = models.CharField(max_length=70, null=False, blank=False)
|
||||
is_student = models.BooleanField(default=False, null=False)
|
||||
graduation_year = models.IntegerField(null=True)
|
||||
email = models.EmailField(max_length=50, null=False, blank=False)
|
||||
is_superuser = models.BooleanField(default=False, null=False)
|
||||
_is_staff = models.BooleanField(default=False, null=False)
|
||||
|
||||
challenges_done = models.ManyToManyField(Challenge, related_name="users_that_completed")
|
||||
|
||||
date_joined = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def has_perm(self, perm, obj=None) -> bool: # pylint: disable=unused-argument
|
||||
return self.is_superuser
|
||||
|
||||
def has_module_perms(self, app_label) -> bool: # pylint: disable=unused-argument
|
||||
return self.is_superuser
|
||||
|
||||
@property
|
||||
def is_staff(self) -> bool:
|
||||
return self._is_staff or self.is_superuser
|
||||
|
||||
@is_staff.setter
|
||||
def is_staff(self, staff: bool) -> None:
|
||||
self._is_staff = staff
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
return self.first_name + " " + self.last_name
|
||||
|
||||
@property
|
||||
def short_name(self) -> str:
|
||||
return self.first_name
|
||||
|
||||
def get_full_name(self) -> str:
|
||||
return self.full_name
|
||||
|
||||
def get_short_name(self) -> str:
|
||||
return self.short_name
|
||||
|
||||
def get_social_auth(self):
|
||||
return self.social_auth.get(provider="ion")
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
def __repr__(self):
|
||||
return "<User: {} ({})>".format(self.username, self.id)
|
||||
|
||||
def is_participant(self):
|
||||
return self.is_student and not self.is_superuser
|
||||
|
||||
|
||||
class Group(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
is_service = models.BooleanField(default=False)
|
||||
name = models.CharField(max_length=32)
|
||||
users = models.ManyToManyField(User, related_name="unix_groups")
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
177
hunt/settings/__init__.py
Normal file
177
hunt/settings/__init__.py
Normal file
|
@ -0,0 +1,177 @@
|
|||
"""
|
||||
Django settings for hunt project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.2.6.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.2/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-=6^bbhfem^#pl*@w29%mo$z#r5_#2d5-m@9q0&9egz@qtcfbah'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'hunt.sites.tjhsst.edu', 'hunt.tjhsst.edu']
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'social_django',
|
||||
'hunt.apps.main',
|
||||
'hunt.apps.auth',
|
||||
'hunt.apps.users',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'hunt.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'hunt.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = ('hunt.apps.auth.oauth.IonOauth2',)
|
||||
|
||||
SOCIAL_AUTH_USER_FIELDS = [
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'id',
|
||||
'is_student',
|
||||
'graduation_year',
|
||||
]
|
||||
|
||||
SOCIAL_AUTH_URL_NAMESPACE = "social"
|
||||
|
||||
SOCIAL_AUTH_PIPELINE = (
|
||||
"social_core.pipeline.social_auth.social_details",
|
||||
"social_core.pipeline.social_auth.social_uid",
|
||||
"social_core.pipeline.social_auth.auth_allowed",
|
||||
"social_core.pipeline.social_auth.social_user",
|
||||
"hunt.apps.auth.oauth.get_username",
|
||||
"social_core.pipeline.social_auth.associate_by_email",
|
||||
"social_core.pipeline.user.create_user",
|
||||
"social_core.pipeline.social_auth.associate_user",
|
||||
"social_core.pipeline.social_auth.load_extra_data",
|
||||
)
|
||||
|
||||
AUTH_USER_MODEL = "users.User"
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Login
|
||||
LOGIN_URL = "auth:login"
|
||||
LOGIN_REDIRECT_URL = "auth:index"
|
||||
LOGOUT_REDIRECT_URL = "auth:index"
|
||||
|
||||
SESSION_SAVE_EVERY_REQUEST = True
|
||||
|
||||
SOCIAL_AUTH_ALWAYS_ASSOCIATE = True
|
||||
SOCIAL_AUTH_LOGIN_ERROR_URL = "/"
|
||||
SOCIAL_AUTH_RAISE_EXCEPTIONS = False
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = BASE_DIR / "serve"
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / "static",
|
||||
]
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
|
||||
try:
|
||||
from .secret import * # noqa
|
||||
except ImportError:
|
||||
pass
|
5
hunt/settings/secret.sample
Normal file
5
hunt/settings/secret.sample
Normal file
|
@ -0,0 +1,5 @@
|
|||
DEBUG = False
|
||||
SECRET_KEY = 'supersecret'
|
||||
|
||||
SOCIAL_AUTH_ION_KEY = ''
|
||||
SOCIAL_AUTH_ION_SECRET = ''
|
83
hunt/static/css/base.css
Normal file
83
hunt/static/css/base.css
Normal file
|
@ -0,0 +1,83 @@
|
|||
body {
|
||||
margin: 0px;
|
||||
font-family: "Open Sans", "Helvetica Neue", sans-serif;
|
||||
font-size: 15px;
|
||||
background-color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header {
|
||||
background-color: rgb(24, 82, 103);
|
||||
text-align: left;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.header_img {
|
||||
width: 50px;
|
||||
margin-left: 10px;
|
||||
margin-top: 5px;
|
||||
padding: 6px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header_title {
|
||||
color:#ffffff;
|
||||
padding-top: 10px;
|
||||
font-size: 25px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header_nav {
|
||||
padding: 10px;
|
||||
color: #ffffff;
|
||||
font-size: 19px;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header_nav:hover {
|
||||
text-decoration: none;
|
||||
color: #a2a2a3;
|
||||
}
|
||||
|
||||
.dropdown-btn:hover {
|
||||
text-decoration: none;
|
||||
color: #a2a2a3;
|
||||
}
|
||||
|
||||
.drop-item:hover {
|
||||
text-decoration: none;
|
||||
color: #a2a2a3;
|
||||
}
|
||||
|
||||
.drop-item {
|
||||
font-size: 16px;
|
||||
padding-top: 5px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dropdown-btn {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
padding: 10px;
|
||||
margin-top: 9px;
|
||||
background-color:rgba(24, 82, 103, .9);
|
||||
}
|
||||
|
||||
.nav_section {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 48em) {
|
||||
.header {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav_section {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
24
hunt/static/css/index.css
Normal file
24
hunt/static/css/index.css
Normal file
|
@ -0,0 +1,24 @@
|
|||
.box {
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.available {
|
||||
background-color: lightgray;
|
||||
}
|
||||
|
||||
.completed {
|
||||
background-color: lightgreen;
|
||||
}
|
||||
|
||||
.locked {
|
||||
background-color: lightsalmon;
|
||||
}
|
||||
|
||||
.centered-div {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.left-div {
|
||||
text-align: left;
|
||||
}
|
43
hunt/static/css/login.css
Normal file
43
hunt/static/css/login.css
Normal file
|
@ -0,0 +1,43 @@
|
|||
.btn.btn-ion {
|
||||
text-decoration: none;
|
||||
color: #484848;
|
||||
display: inline-block;
|
||||
line-height: 18px;
|
||||
padding: 7px 10px;
|
||||
margin: 2px 0;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.9);
|
||||
white-space: nowrap;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background: #f7f7f4;
|
||||
background: -moz-linear-gradient(top,#f7f7f4 0%,#eaeaea 100%);
|
||||
background: -webkit-linear-gradient(top,#f7f7f4 0%,#eaeaea 100%);
|
||||
background: linear-gradient(to bottom,#f7f7f4 0%,#eaeaea 100%);
|
||||
border: 1px solid #ddd;
|
||||
border-bottom-color: #c5c5c5;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
-pie-box-shadow: none;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,.05);
|
||||
vertical-align: middle;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
-o-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
border: 1px solid rgb(24, 82, 103);
|
||||
margin: 15px auto;
|
||||
max-width: 438px;
|
||||
}
|
73
hunt/static/css/overview.css
Normal file
73
hunt/static/css/overview.css
Normal file
|
@ -0,0 +1,73 @@
|
|||
#hoco-scores {
|
||||
display: none;
|
||||
margin: 15px auto;
|
||||
width: 580px;
|
||||
text-align: center;
|
||||
|
||||
position: absolute;
|
||||
left: calc(50% - 290px);
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background-color: #545454;
|
||||
color: white;
|
||||
padding: 15px 15px 20px;
|
||||
margin: 5px;
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.class {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px) {
|
||||
.center-wrapper {
|
||||
margin-top: 40px;
|
||||
}
|
||||
#hoco-scores {
|
||||
width: 284px;
|
||||
zoom: 0.7;
|
||||
left: calc(50% - 142px);
|
||||
}
|
||||
}
|
||||
|
||||
.corner-ribbon {
|
||||
width: 95px;
|
||||
background: #e43;
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: -30px;
|
||||
text-align: center;
|
||||
line-height: 25px;
|
||||
letter-spacing: 1px;
|
||||
color: #f0f0f0;
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.gold {
|
||||
background-color: #C98910;
|
||||
}
|
||||
|
||||
.silver {
|
||||
background-color: #A8A8A8;
|
||||
}
|
||||
|
||||
.bronze {
|
||||
background-color: #965A38;
|
||||
}
|
||||
|
||||
@media screen and (max-height: 900px) {
|
||||
.center {
|
||||
top: 440px;
|
||||
}
|
||||
}
|
BIN
hunt/static/img/favicon.png
Normal file
BIN
hunt/static/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 165 KiB |
17
hunt/templates/auth/login.html
Normal file
17
hunt/templates/auth/login.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link rel='stylesheet' href="{% static 'css/login.css' %}">
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class='header' style='text-align:left;'>
|
||||
<img class='header_img' src="{% static 'img/favicon.png' %}"/>
|
||||
<h1 class='header_title'>Hoco Hunt 2021</h1>
|
||||
</div>
|
||||
<div class='login-box'>
|
||||
<p><b>Login in with your Ion account</b> to access the Hoco Hunt 2021.</p>
|
||||
<a href="{% url 'social:begin' 'ion' %}{% if request.GET.next %}?next={{ request.GET.next|urlencode }}{% endif %}" class='btn btn-ion'>Log in with Ion</a>
|
||||
</div>
|
||||
{% endblock %}
|
27
hunt/templates/base.html
Normal file
27
hunt/templates/base.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% load static %}
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<script src='https://kit.fontawesome.com/74f5f903f7.js' crossorigin='anonymous'></script>
|
||||
<script
|
||||
src="https://code.jquery.com/jquery-3.4.1.min.js"
|
||||
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
|
||||
crossorigin="anonymous">
|
||||
</script>
|
||||
<link rel='stylesheet' href='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
|
||||
<title>Hoco Hunt 2021</title>
|
||||
<link rel='icon' href="{% static 'img/favicon.png' %}" type='image/png'>
|
||||
|
||||
<link rel='stylesheet' href="{% static 'css/base.css' %}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% block root %}
|
||||
{% block main %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
40
hunt/templates/base_with_nav.html
Normal file
40
hunt/templates/base_with_nav.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block root %}
|
||||
<div id='root'>
|
||||
<div class='row header'>
|
||||
<div class='col-lg-5 col-md-6 col-sm-12 col-xs-12'>
|
||||
<a href="{% url 'main:index' %}">
|
||||
<img class='header_img' src="{% static 'img/favicon.png' %}"/>
|
||||
<h1 class='header_title'>Hoco Hunt 2021</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div class='col-lg-2 col-md-1'></div>
|
||||
<div class='col-lg-5 col-md-5 col-sm-12 col-xs-12 nav_section'>
|
||||
<div class='dropdown header_nav'>
|
||||
<button class='btn dropdown-toggle dropdown-btn' data-toggle='dropdown'>
|
||||
<i class='fas fa-user'></i>
|
||||
<span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu dropdown-menu-right'>
|
||||
{% if user.is_authenticated %}
|
||||
<li><h3 class='drop-item'>{{ user.username }}</h3></li>
|
||||
<li><a class='drop-item' href="{% url 'main:overview' %}"><h3 class='drop-item'>Overview</h3></a></li>
|
||||
{% if user.is_participant %}
|
||||
<li><a class='drop-item' href="{% url 'main:index' %}"><h3 class='drop-item'>Challenges</h3></a></li>
|
||||
{% endif %}
|
||||
<li><a class='drop-item' href="{% url 'auth:logout' %}"><h3 class='drop-item'>Logout</h3></a></li>
|
||||
{% else %}
|
||||
<li><a class='drop-item' href="{% url 'auth:login' %}"><h3 class='drop-item'>Login</h3></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js' integrity='sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1' crossorigin='anonymous'></script>
|
||||
<script src='https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js' integrity='sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM' crossorigin='anonymous'></script>
|
||||
{% block main %}{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
59
hunt/templates/main/index.html
Normal file
59
hunt/templates/main/index.html
Normal file
|
@ -0,0 +1,59 @@
|
|||
{% extends 'base_with_nav.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link rel='stylesheet' href="{% static 'css/index.css' %}">
|
||||
<script>
|
||||
function checkFlag(challenge_id) {
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url: "{% url 'main:validate_flag' %}",
|
||||
data: {
|
||||
csrfmiddlewaretoken: '{{ csrf_token }}',
|
||||
dataType: 'json',
|
||||
challenge_id: challenge_id,
|
||||
flag: $("#" + challenge_id).val(),
|
||||
},
|
||||
success: function(data) {
|
||||
if (data.result == "success") {
|
||||
$("#box" + challenge_id).removeClass("available").addClass("completed");
|
||||
} else {
|
||||
$("#" + challenge_id).css("background-color", "red");
|
||||
}
|
||||
},
|
||||
failure: function() {
|
||||
$("#" + challenge_id).css("background-color", "red");
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div class='row'>
|
||||
{% for challenge, status in challenges_dict.items %}
|
||||
<div class='col-lg-3 col-md-4 col-sm-6 col-xs-12'>
|
||||
<div id="box{{ status.0.id }}" class="box {% if status.1 == 'available' %}available{% elif status.1 == 'completed' %}completed{% else %}locked{% endif %}">
|
||||
<div class="centered-div">
|
||||
<h4>{{ status.0.name }}</h4>
|
||||
<p>Points: {{ status.0.points }}</p>
|
||||
</div>
|
||||
<div class="left-div">
|
||||
{% if status.0.exclusive %}
|
||||
<p>This challenge's points are only available to the first class to complete it.</p>
|
||||
{% elif status.1 == 'locked' %}
|
||||
<p>This challenge was exclusive and has already been completed by another class.</p>
|
||||
{% endif %}
|
||||
<p>{{ status.0.description }}</p>
|
||||
</div>
|
||||
{% if status.1 == 'available' %}
|
||||
<hr>
|
||||
<input type="text" id="{{ status.0.id }}" placeholder="Enter the flag here"/>
|
||||
<input type="submit" value="Submit" onclick="checkFlag({{ status.0.id }})" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
67
hunt/templates/main/overview.html
Normal file
67
hunt/templates/main/overview.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
{% extends 'base_with_nav.html' %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block head %}
|
||||
<link rel='stylesheet' href="{% static 'css/overview.css' %}">
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
giveRibbons();
|
||||
$("#hoco-scores").fadeIn();
|
||||
});
|
||||
function giveRibbons() {
|
||||
var arr = [];
|
||||
$("#hoco-scores .box").each(function() {
|
||||
var score = parseFloat($(this).find(".score").text());
|
||||
arr.push([$(this), score]);
|
||||
});
|
||||
arr.sort(function(a, b) {
|
||||
return b[1] - a[1];
|
||||
});
|
||||
var place = 0;
|
||||
$.each(arr, function(k, v) {
|
||||
if (k > 0 && arr[k - 1][1] != v[1]) {
|
||||
place += 1;
|
||||
}
|
||||
if (place == 0) {
|
||||
v[0].find(".corner-ribbon").addClass("gold").text("1st");
|
||||
}
|
||||
else if (place == 1) {
|
||||
v[0].find(".corner-ribbon").addClass("silver").text("2nd");
|
||||
}
|
||||
else if (place == 2) {
|
||||
v[0].find(".corner-ribbon").addClass("bronze").text("3rd");
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<div id="hoco-scores">
|
||||
<div class="box">
|
||||
<div class="class">{{ data.0.0 }}</div>
|
||||
<div class="corner-ribbon"></div>
|
||||
<div class="score" id="score-senior">{{ data.0.1 }}</div>
|
||||
points
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="class">{{ data.1.0 }}</div>
|
||||
<div class="corner-ribbon"></div>
|
||||
<div class="score" id="score-junior">{{ data.1.1 }}</div>
|
||||
points
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="class">{{ data.2.0 }}</div>
|
||||
<div class="corner-ribbon"></div>
|
||||
<div class="score" id="score-sophomore">{{ data.2.1 }}</div>
|
||||
points
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="class">{{ data.3.0 }}</div>
|
||||
<div class="corner-ribbon"></div>
|
||||
<div class="score" id="score-freshman">{{ data.3.1 }}</div>
|
||||
points
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
9
hunt/urls.py
Normal file
9
hunt/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path("", include("social_django.urls", namespace="social")),
|
||||
path("", include("hunt.apps.auth.urls", namespace="auth")),
|
||||
path("main/", include("hunt.apps.main.urls", namespace="main")),
|
||||
]
|
16
hunt/wsgi.py
Normal file
16
hunt/wsgi.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for hunt project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hunt.settings')
|
||||
|
||||
application = get_wsgi_application()
|
22
manage.py
Executable file
22
manage.py
Executable file
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'hunt.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user