From de799d498330d43467b2bf87c4c81db994cfa4bb Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 22 Aug 2020 19:47:41 +0000 Subject: [PATCH 01/93] Display if a student is Waiting for payment --- webclient/src/InstructorClasses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index fa23ff8..3603dba 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -90,7 +90,7 @@ function AttendanceRow(props) { return (
-

{student.student_name}:

+

{student.student_name}{student.attendance_status === 'Waiting for payment' && ' (Waiting for payment)'}:

:
- + {bypass_code ? + + Outside Registration +

This page allows you to sign up from outside of Protospace.

+
+ : + <> + - Or + Or + + }
diff --git a/webclient/src/LoginSignup.js b/webclient/src/LoginSignup.js index 36e2c70..ec8634d 100644 --- a/webclient/src/LoginSignup.js +++ b/webclient/src/LoginSignup.js @@ -110,7 +110,7 @@ export function SignupForm(props) { return (
-
Sign Up from Protospace
+
Sign Up to Spaceport
Date: Tue, 1 Sep 2020 19:13:45 +0000 Subject: [PATCH 04/93] Clarify chart data --- webclient/src/Charts.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/webclient/src/Charts.js b/webclient/src/Charts.js index 6b39705..7f3c76b 100644 --- a/webclient/src/Charts.js +++ b/webclient/src/Charts.js @@ -87,9 +87,9 @@ export function Charts(props) { }

-

The Member Count is the amount of Prepaid, Current, Due, and Overdue members on Spaceport.

+

Member Count: number of Prepaid, Current, Due, and Overdue members on Spaceport.

-

The Green Count is the amount of Prepaid and Current members.

+

Green Count: number of Prepaid and Current members.

Space Activity
@@ -118,7 +118,7 @@ export function Charts(props) { }

-

Cards Scans is the number of individual members who have scanned to enter the space.

+

Cards Scans: number of individual members who have scanned to enter the space.

Signup Count
@@ -146,14 +146,14 @@ export function Charts(props) { type='monotone' dataKey='vetted_count' fill='#80b3d3' - name='Vetted Count' + name='Later Vetted Count' maxBarSize={20} animationDuration={1200} /> -

The Signup Count is the number of brand new account registrations that month.

+

Signup Count: number of brand new account registrations that month.

-

The Vetted Count is the number of those signups who eventually got vetted (at a later date).

+

Later Vetted Count: number of those signups who eventually got vetted (at a later date).

-

The Retain Count is the number of those signups who are still a member currently.

+

Retained Count: number of those signups who are still a member currently.

); From f795cd6d407e061e2f038d3151740bbe4091f81c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 1 Sep 2020 19:17:27 +0000 Subject: [PATCH 05/93] Add link to Minecraft map to stats --- webclient/src/Home.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index c6c41fd..8ce5954 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -229,6 +229,7 @@ export function Home(props) {

} trigger={[more]} /> + {' '}[map]

From bd5387f6d4557d5a8df28648eb179af715c8e65b Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 1 Sep 2020 19:22:33 +0000 Subject: [PATCH 06/93] Update serialize-javascript package --- webclient/package.json | 3 ++- webclient/yarn.lock | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/webclient/package.json b/webclient/package.json index b7a9703..f36ce83 100644 --- a/webclient/package.json +++ b/webclient/package.json @@ -20,7 +20,8 @@ "react-scripts": "3.4.1", "react-to-print": "~2.5.1", "recharts": "~1.8.5", - "semantic-ui-react": "~0.88.2" + "semantic-ui-react": "~0.88.2", + "serialize-javascript": "^3.1.0" }, "scripts": { "start": "react-scripts start", diff --git a/webclient/yarn.lock b/webclient/yarn.lock index 3df4f32..c89939e 100644 --- a/webclient/yarn.lock +++ b/webclient/yarn.lock @@ -8842,7 +8842,7 @@ raf@^3.4.0, raf@^3.4.1: dependencies: performance-now "^2.1.0" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== @@ -9754,6 +9754,13 @@ serialize-javascript@^2.1.2: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" integrity sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ== +serialize-javascript@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-3.1.0.tgz#8bf3a9170712664ef2561b44b691eafe399214ea" + integrity sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg== + dependencies: + randombytes "^2.1.0" + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" From 9bc0f5ca32c6bf0edcafc66335309b490108c120 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 6 Sep 2020 22:18:48 +0000 Subject: [PATCH 07/93] Add coords to Minecraft map link --- webclient/src/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 8ce5954..8bd3fda 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -229,7 +229,7 @@ export function Home(props) {

} trigger={[more]} /> - {' '}[map] + {' '}[map]

From 38921b64bb6208d12fbfa4403534d1e6b569a02e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Sep 2020 22:19:54 +0000 Subject: [PATCH 08/93] Bump http-proxy from 1.18.0 to 1.18.1 in /webclient Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.18.0 to 1.18.1. - [Release notes](https://github.com/http-party/node-http-proxy/releases) - [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md) - [Commits](https://github.com/http-party/node-http-proxy/compare/1.18.0...1.18.1) Signed-off-by: dependabot[bot] --- webclient/yarn.lock | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/webclient/yarn.lock b/webclient/yarn.lock index c89939e..6262a25 100644 --- a/webclient/yarn.lock +++ b/webclient/yarn.lock @@ -3664,7 +3664,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: +debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -4376,9 +4376,9 @@ eventemitter3@^2.0.3: integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= eventemitter3@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" - integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== events@^3.0.0: version "3.1.0" @@ -4767,11 +4767,9 @@ flush-write-stream@^1.0.0: readable-stream "^2.3.6" follow-redirects@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" - integrity sha512-4eyLK6s6lH32nOvLLwlIOnr9zrL8Sm+OvW4pVTJNoXeGzYIkHVf+pADQi+OJ0E67hiuSLezPVPyBcIZO50TmmQ== - dependencies: - debug "^3.0.0" + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== for-in@^0.1.3: version "0.1.8" @@ -5335,9 +5333,9 @@ http-proxy-middleware@0.19.1: micromatch "^3.1.10" http-proxy@^1.17.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" - integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ== + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== dependencies: eventemitter3 "^4.0.0" follow-redirects "^1.0.0" From 0b09246cc87aa54c05a49a8fa3e1dc8053a1ef00 Mon Sep 17 00:00:00 2001 From: Patrick Spencer <30306028+pspencer53@users.noreply.github.com> Date: Tue, 8 Sep 2020 00:46:34 -0600 Subject: [PATCH 09/93] Update ldap_functions.py --- ldapserver/ldap_functions.py | 87 +++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 0ae5017..b31bcfe 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -100,7 +100,7 @@ def set_password(username, password): def find_group(groupname): ''' - Search for a group by name or sAMAccountname. Retrun the DN + Search for a group by name or sAMAccountname ''' ldap_conn = init_ldap() try: @@ -112,7 +112,6 @@ def find_group(groupname): abort(HTTP_NOTFOUND) return results[0][0] - finally: ldap_conn.unbind() @@ -134,6 +133,26 @@ def create_group(groupname,description): ] rcode = ldap_conn.add_s(dn, ldif) + return rcode + + finally: + ldap_conn.unbind() + +def add_to_group(groupname,username): + ''' + Add a user to a Group; required data is GroupName, Username + ''' + ldap_conn = init_ldap() + try: + ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) + group_dn = find_group(groupname) + user_dn = find_user(username) + + # -- TODO: Check to see if user is already a member, skip if not needed (Done) + if not is_member(groupname,username): + mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] + ldap_conn.modify_s(group_dn, mod_acct) + return(True) finally: ldap_conn.unbind() @@ -153,57 +172,51 @@ def list_group(groupname): members_tmp = results[0][1]['member'] for m in members_tmp: members.append(m) -# print("m = {}".format(m)) #Debug return(members) finally: ldap_conn.unbind() -def add_to_group(groupname,username): +def is_member(groupname,username): + ''' + Checks to see if a user is a member of a group ''' - Add a user to a Group; required data is GroupName, Username - ''' - print("== Enter add_to_group ==") ldap_conn = init_ldap() try: - print(' --- Enter add_to_group with {0}, {1}---'.format(groupname,username)) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - # get DN of the groupname - group_dn = find_group(groupname) - - #get DN of the username + group_dn = find_group(groupname) user_dn = find_user(username) + memflag = False + criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) + results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) + members_tmp = results[0][1]['member'] - # -- TODO: Check to see if user is already a member, skip if not needed + for m in members_tmp: + if m == user_dn: + memflag = True + + return memflag - mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] - result = ldap_conn.modify_s(group_dn, mod_acct) - finally: ldap_conn.unbind() +def abort(message): + print(message) + exit() + - +# =========================================================================== if __name__ == '__main__': #print(find_user('tanner.collin')) - #print(set_password('dsaftanner.collin', 'Supersecret@@')) - - # create a new group - create_group("testgroup") - print(find_group("testgroup") - - # List Group members - print("-- Members of {}".format("Laser Trainers")) - group_members = list_group("Laser Trainers") - for member in group_members: - print('{}'.format(member)) - - # add users to test group - add_to_group("testgroup","pat.spencer") - add_to_group("testgroup","Tanner.Collin") - # List Group members - print("-- Members of {}".format("testgroup")) - group_members = list_group("testgroup") - for member in group_members: - print('{}'.format(member)) + #print(set_password('tanner.collin', 'Supersecret@@')) + print("============================================================") + print(create_group("newgroup","new group")) + print(" ============== ") + print(list_group("newgroup")) + print(" ============== ") + print(is_member('newgroup','tanner.collin')) + print(" ============== ") + print(add_to_group('newgroup','tanner.collin')) + print(" ============== ") + print(list_group("newgroup")) From 707e89595ee62355182d4c89caa9796d7e29ed11 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 10 Sep 2020 19:15:42 -0600 Subject: [PATCH 10/93] Fix whitespace --- ldapserver/ldap_functions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index b31bcfe..1f35bbd 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -147,7 +147,7 @@ def add_to_group(groupname,username): ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) group_dn = find_group(groupname) user_dn = find_user(username) - + # -- TODO: Check to see if user is already a member, skip if not needed (Done) if not is_member(groupname,username): mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] @@ -179,7 +179,7 @@ def list_group(groupname): ldap_conn.unbind() def is_member(groupname,username): - ''' + ''' Checks to see if a user is a member of a group ''' ldap_conn = init_ldap() @@ -195,7 +195,7 @@ def is_member(groupname,username): for m in members_tmp: if m == user_dn: memflag = True - + return memflag finally: @@ -204,7 +204,7 @@ def is_member(groupname,username): def abort(message): print(message) exit() - + # =========================================================================== if __name__ == '__main__': From 6cd0ea7bd0ae2c49bcff1310cfe52b90df16fb63 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 10 Sep 2020 20:29:29 -0600 Subject: [PATCH 11/93] Fix LDAP bugs and and remove_from_group --- ldapserver/ldap_functions.py | 84 ++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 1f35bbd..d20e216 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -115,7 +115,7 @@ def find_group(groupname): finally: ldap_conn.unbind() -def create_group(groupname,description): +def create_group(groupname, description): ''' Create a Group; required data is sAMAccountName, Description ''' @@ -138,7 +138,7 @@ def create_group(groupname,description): finally: ldap_conn.unbind() -def add_to_group(groupname,username): +def add_to_group(groupname, username): ''' Add a user to a Group; required data is GroupName, Username ''' @@ -148,11 +148,32 @@ def add_to_group(groupname,username): group_dn = find_group(groupname) user_dn = find_user(username) - # -- TODO: Check to see if user is already a member, skip if not needed (Done) - if not is_member(groupname,username): + if not is_member(groupname, username): mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] ldap_conn.modify_s(group_dn, mod_acct) - return(True) + return True + else: + return False + + finally: + ldap_conn.unbind() + +def remove_from_group(groupname, username): + ''' + Remove a user from a Group; required data is GroupName, Username + ''' + ldap_conn = init_ldap() + try: + ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) + group_dn = find_group(groupname) + user_dn = find_user(username) + + if is_member(groupname, username): + mod_acct = [(ldap.MOD_DELETE, 'member', user_dn.encode())] + ldap_conn.modify_s(group_dn, mod_acct) + return True + else: + return False finally: ldap_conn.unbind() @@ -161,7 +182,6 @@ def list_group(groupname): ''' List users in a Group; required data is GroupName ''' - members = [] ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) @@ -169,16 +189,12 @@ def list_group(groupname): criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) - members_tmp = results[0][1]['member'] - for m in members_tmp: - members.append(m) - - return(members) - + members_tmp = results[0][1] + return members_tmp.get('member', []) finally: ldap_conn.unbind() -def is_member(groupname,username): +def is_member(groupname, username): ''' Checks to see if a user is a member of a group ''' @@ -186,37 +202,33 @@ def is_member(groupname,username): try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) group_dn = find_group(groupname) - user_dn = find_user(username) + user_dn = find_user(username).encode() memflag = False criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) - members_tmp = results[0][1]['member'] - - for m in members_tmp: - if m == user_dn: - memflag = True - - return memflag - + members_tmp = results[0][1] + members = members_tmp.get('member', []) + return user_dn in members finally: ldap_conn.unbind() -def abort(message): - print(message) - exit() - # =========================================================================== if __name__ == '__main__': + pass #print(find_user('tanner.collin')) #print(set_password('tanner.collin', 'Supersecret@@')) - print("============================================================") - print(create_group("newgroup","new group")) - print(" ============== ") - print(list_group("newgroup")) - print(" ============== ") - print(is_member('newgroup','tanner.collin')) - print(" ============== ") - print(add_to_group('newgroup','tanner.collin')) - print(" ============== ") - print(list_group("newgroup")) + #print("============================================================") + #print(create_group("newgroup", "new group")) + #print(" ============== ") + #print(list_group("newgroup")) + #print(" ============== ") + #print(is_member('newgroup','tanner.collin')) + #print(" ============== ") + #print(add_to_group('newgroup','tanner.collin')) + #print(" ============== ") + #print(list_group("newgroup")) + #print(" ============== ") + #print(remove_from_group('newgroup','tanner.collin')) + #print(" ============== ") + #print(list_group("newgroup")) From 3133114f93ed8853ce16c152d92f82528ceea8ff Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 10 Sep 2020 20:41:29 -0600 Subject: [PATCH 12/93] Add LDAP group API routes --- ldapserver/server.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ldapserver/server.py b/ldapserver/server.py index ee696c0..e2f7009 100644 --- a/ldapserver/server.py +++ b/ldapserver/server.py @@ -46,5 +46,25 @@ def set_password(): ldap_functions.set_password(username, password) return '' +@app.route('/add-to-group', methods=['POST']) +def add_to_group(): + check_auth() + + groupname = request.form['groupname'] + username = request.form['username'] + + ldap_functions.add_to_group(groupname, username) + return '' + +@app.route('/remove-from-group', methods=['POST']) +def remove_from_group(): + check_auth() + + groupname = request.form['groupname'] + username = request.form['username'] + + ldap_functions.remove_from_group(groupname, username) + return '' + if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') From 2cc21d360d14450b02247b4ba73989f3e78a9788 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 11 Sep 2020 00:04:38 -0600 Subject: [PATCH 13/93] Add find_dn and dump_users LDAP functions --- ldapserver/ldap_functions.py | 68 +++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 4 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index d20e216..e869cf6 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -23,6 +23,19 @@ def init_ldap(): return ldap_conn +def convert(data): + if isinstance(data, dict): + return {convert(key): convert(value) for key, value in data.items()} + elif isinstance(data, (list, tuple)): + if len(data) == 1: + return convert(data[0]) + else: + return [convert(element) for element in data] + elif isinstance(data, (bytes, bytearray)): + return data.decode() + else: + return data + def find_user(username): ''' Search for a user by sAMAccountname @@ -31,7 +44,7 @@ def find_user(username): try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(sAMAccountName={})(!(objectClass=computer)))'.format(username) - results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email'] ) + results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) if len(results) != 1: abort(HTTP_NOTFOUND) @@ -40,6 +53,20 @@ def find_user(username): finally: ldap_conn.unbind() +def find_dn(dn): + ''' + Search for a user by dn + ''' + ldap_conn = init_ldap() + try: + ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) + criteria = '(&(objectClass=user)(!(objectClass=computer)))' + results = ldap_conn.search_s(dn, ldap.SCOPE_SUBTREE, criteria, ['sAMAccountName']) + + return results[0][1]['sAMAccountName'][0].decode() + finally: + ldap_conn.unbind() + def create_user(first, last, username, email, password): ''' Create a User; required data is first, last, email, username, password @@ -188,9 +215,11 @@ def list_group(groupname): group_dn = find_group(groupname) criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) - results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) + results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member']) members_tmp = results[0][1] - return members_tmp.get('member', []) + members = members_tmp.get('member', []) + return [find_dn(dn.decode()) for dn in members] + finally: ldap_conn.unbind() @@ -212,16 +241,46 @@ def is_member(groupname, username): finally: ldap_conn.unbind() +def dump_users(): + ''' + Dump all AD users + ''' + ldap_conn = init_ldap() + try: + ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) + criteria = '(&(objectClass=user)(sAMAccountName=*))' + attributes = ['cn', 'sAMAccountName', 'mail', 'displayName', 'givenName', 'name', 'sn', 'logonCount'] + results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, attributes) + results = convert(results) + + output = {} + for r in results: + tmp = r[1] + tmp['dn'] = r[0] + output[r[1]['sAMAccountName']] = tmp + + import json + return json.dumps(output, indent=4) + + finally: + ldap_conn.unbind() + # =========================================================================== + #guid = '\\b4\\51\\1adce6709c449bd21a812c423e82' + #guid = ''.join(['\\%s' % guid[i:i+2] for i in range(0, len(guid), 2)]) + #print(guid) + #criteria = '(&(objectClass=user)(objectGUID={}))'.format(guid) + if __name__ == '__main__': pass #print(find_user('tanner.collin')) #print(set_password('tanner.collin', 'Supersecret@@')) + #print(find_dn('CN=Tanner Collin,OU=MembersOU,DC=ps,DC=protospace,DC=ca')) #print("============================================================") #print(create_group("newgroup", "new group")) #print(" ============== ") - #print(list_group("newgroup")) + #print(list_group("Laser Users")) #print(" ============== ") #print(is_member('newgroup','tanner.collin')) #print(" ============== ") @@ -232,3 +291,4 @@ if __name__ == '__main__': #print(remove_from_group('newgroup','tanner.collin')) #print(" ============== ") #print(list_group("newgroup")) + #print(dump_users()) From bfd90768c2d72b91d06abbb53ddf9a80718cab31 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 11 Sep 2020 01:36:00 -0600 Subject: [PATCH 14/93] Allow searching LDAP users by email --- ldapserver/ldap_functions.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index e869cf6..4e14e6f 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -36,14 +36,14 @@ def convert(data): else: return data -def find_user(username): +def find_user(query): ''' - Search for a user by sAMAccountname + Search for a user by sAMAccountname or email ''' ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - criteria = '(&(objectClass=user)(sAMAccountName={})(!(objectClass=computer)))'.format(username) + criteria = '(&(objectClass=user)(|(mail={})(sAMAccountName={}))(!(objectClass=computer)))'.format(query, query) results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) if len(results) != 1: @@ -267,6 +267,7 @@ def dump_users(): # =========================================================================== + #guid = '\\b4\\51\\1adce6709c449bd21a812c423e82' #guid = ''.join(['\\%s' % guid[i:i+2] for i in range(0, len(guid), 2)]) #print(guid) @@ -275,6 +276,7 @@ def dump_users(): if __name__ == '__main__': pass #print(find_user('tanner.collin')) + #print(find_user('mail@tannercollin.com')) #print(set_password('tanner.collin', 'Supersecret@@')) #print(find_dn('CN=Tanner Collin,OU=MembersOU,DC=ps,DC=protospace,DC=ca')) #print("============================================================") @@ -292,3 +294,7 @@ if __name__ == '__main__': #print(" ============== ") #print(list_group("newgroup")) #print(dump_users()) + + #users = list_group('Laser Users') + #import json + #print(json.dumps(users, indent=4)) From 2d7c67a2075f50c77e09e20d9892010122181752 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 14 Sep 2020 00:13:00 +0000 Subject: [PATCH 15/93] Improve LDAP logging and group functions --- ldapserver/ldap_functions.py | 45 +++++++++++++++++++++++++----------- ldapserver/server.py | 26 +++++++++++++++++---- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 4e14e6f..1763bf0 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -1,3 +1,8 @@ +import logging +logger = logging.getLogger(__name__) + +logging.info('Logging enabled.') + import time import ldap import ldap.modlist as modlist @@ -7,14 +12,14 @@ import base64 from flask import abort HTTP_NOTFOUND = 404 -BASE_MEMBERS = 'OU=MembersOU,DC=ps,DC=protospace,DC=ca' # prod -BASE_GROUPS = 'OU=GroupsOU,DC=ps,DC=protospace,DC=ca' # prod +BASE_MEMBERS = 'OU=MembersOU,DC=lab39,DC=lab' # prod +BASE_GROUPS = 'OU=MembersOU,DC=lab39,DC=lab' # prod ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) -ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, './ProtospaceAD.cer') +ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, './lab39-dc1.cer') def init_ldap(): - ldap_conn = ldap.initialize('ldaps://ldap.ps.protospace.ca:636') + ldap_conn = ldap.initialize('ldaps://ldap.lab39.lab:636') ldap_conn.set_option(ldap.OPT_REFERRALS, 0) ldap_conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3) ldap_conn.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND) @@ -32,7 +37,10 @@ def convert(data): else: return [convert(element) for element in data] elif isinstance(data, (bytes, bytearray)): - return data.decode() + try: + return data.decode() + except UnicodeDecodeError: + return data.hex() else: return data @@ -42,10 +50,13 @@ def find_user(query): ''' ldap_conn = init_ldap() try: + logger.info('Looking up user', query) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(|(mail={})(sAMAccountName={}))(!(objectClass=computer)))'.format(query, query) results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) + logger.info(results) + if len(results) != 1: abort(HTTP_NOTFOUND) @@ -91,7 +102,9 @@ def create_user(first, last, username, email, password): ('company', [b'Spaceport']), ] - ldap_conn.add_s(dn, ldif) + result = ldap_conn.add_s(dn, ldif) + + logger.info(result) # set password pass_quotes = '"{}"'.format(password) @@ -99,9 +112,13 @@ def create_user(first, last, username, email, password): change_des = [(ldap.MOD_REPLACE, 'unicodePwd', [pass_uni])] result = ldap_conn.modify_s(dn, change_des) + logger.info(result) + # 512 will set user account to enabled mod_acct = [(ldap.MOD_REPLACE, 'userAccountControl', b'512')] result = ldap_conn.modify_s(dn, mod_acct) + + logger.info(result) finally: ldap_conn.unbind() @@ -131,10 +148,13 @@ def find_group(groupname): ''' ldap_conn = init_ldap() try: + logger.info('Looking up group', groupname) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['name','groupType'] ) + logger.info(results) + if len(results) != 1: abort(HTTP_NOTFOUND) @@ -172,8 +192,8 @@ def add_to_group(groupname, username): ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - group_dn = find_group(groupname) user_dn = find_user(username) + group_dn = find_group(groupname) if not is_member(groupname, username): mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] @@ -192,8 +212,8 @@ def remove_from_group(groupname, username): ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - group_dn = find_group(groupname) user_dn = find_user(username) + group_dn = find_group(groupname) if is_member(groupname, username): mod_acct = [(ldap.MOD_DELETE, 'member', user_dn.encode())] @@ -248,8 +268,8 @@ def dump_users(): ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - criteria = '(&(objectClass=user)(sAMAccountName=*))' - attributes = ['cn', 'sAMAccountName', 'mail', 'displayName', 'givenName', 'name', 'sn', 'logonCount'] + criteria = '(&(objectClass=user)(objectGUID=*))' + attributes = ['cn', 'sAMAccountName', 'mail', 'displayName', 'givenName', 'name', 'sn', 'logonCount', 'objectGUID'] results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, attributes) results = convert(results) @@ -261,7 +281,6 @@ def dump_users(): import json return json.dumps(output, indent=4) - finally: ldap_conn.unbind() @@ -275,8 +294,8 @@ def dump_users(): if __name__ == '__main__': pass - #print(find_user('tanner.collin')) - #print(find_user('mail@tannercollin.com')) + #print(create_user('Elon', 'Tusk', 'elon.tusk', 'elont@example.com', 'protospace*&^g87g6')) + print(find_user('test.testerb')) #print(set_password('tanner.collin', 'Supersecret@@')) #print(find_dn('CN=Tanner Collin,OU=MembersOU,DC=ps,DC=protospace,DC=ca')) #print("============================================================") diff --git a/ldapserver/server.py b/ldapserver/server.py index e2f7009..39174e6 100644 --- a/ldapserver/server.py +++ b/ldapserver/server.py @@ -1,6 +1,24 @@ from flask import Flask, abort, request app = Flask(__name__) +from logging.config import dictConfig + +dictConfig({ + 'version': 1, + 'formatters': {'default': { + 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', + }}, + 'handlers': {'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }}, + 'root': { + 'level': 'INFO', + 'handlers': ['wsgi'] + } +}) + import ldap_functions import secrets @@ -50,8 +68,8 @@ def set_password(): def add_to_group(): check_auth() - groupname = request.form['groupname'] - username = request.form['username'] + groupname = request.form['group'] + username = request.form.get('username', None) or request.form.get('email', None) ldap_functions.add_to_group(groupname, username) return '' @@ -60,8 +78,8 @@ def add_to_group(): def remove_from_group(): check_auth() - groupname = request.form['groupname'] - username = request.form['username'] + groupname = request.form['group'] + username = request.form.get('username', None) or request.form.get('email', None) ldap_functions.remove_from_group(groupname, username) return '' From 7e2a4ba6733f72b3ad3980e42742bca13bf2f670 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 00:18:38 +0000 Subject: [PATCH 16/93] Improve LDAP logging and secrets management --- ldapserver/ldap_functions.py | 40 +++++++++++++++-------------------- ldapserver/log.py | 22 +++++++++++++++++++ ldapserver/secrets.py.example | 6 ++++++ ldapserver/server.py | 18 ---------------- 4 files changed, 45 insertions(+), 41 deletions(-) create mode 100644 ldapserver/log.py diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 1763bf0..91dd0c1 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -1,8 +1,4 @@ -import logging -logger = logging.getLogger(__name__) - -logging.info('Logging enabled.') - +from log import logger import time import ldap import ldap.modlist as modlist @@ -12,14 +8,12 @@ import base64 from flask import abort HTTP_NOTFOUND = 404 -BASE_MEMBERS = 'OU=MembersOU,DC=lab39,DC=lab' # prod -BASE_GROUPS = 'OU=MembersOU,DC=lab39,DC=lab' # prod ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) -ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, './lab39-dc1.cer') +ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, secrets.LDAP_CERTFILE) def init_ldap(): - ldap_conn = ldap.initialize('ldaps://ldap.lab39.lab:636') + ldap_conn = ldap.initialize(secrets.LDAP_URL) ldap_conn.set_option(ldap.OPT_REFERRALS, 0) ldap_conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3) ldap_conn.set_option(ldap.OPT_X_TLS,ldap.OPT_X_TLS_DEMAND) @@ -50,10 +44,10 @@ def find_user(query): ''' ldap_conn = init_ldap() try: - logger.info('Looking up user', query) + logger.info('Looking up user ' + query) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(|(mail={})(sAMAccountName={}))(!(objectClass=computer)))'.format(query, query) - results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) + results = ldap_conn.search_s(secrets.BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) logger.info(results) @@ -86,7 +80,7 @@ def create_user(first, last, username, email, password): ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - dn = 'CN={} {},{}'.format(first, last, BASE_MEMBERS) + dn = 'CN={} {},{}'.format(first, last, secrets.BASE_MEMBERS) full_name = '{} {}'.format(first, last) ldif = [ @@ -127,7 +121,7 @@ def set_password(username, password): try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(sAMAccountName={})(!(objectClass=computer)))'.format(username) - results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email'] ) + results = ldap_conn.search_s(secrets.BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email'] ) if len(results) != 1: abort(HTTP_NOTFOUND) @@ -148,10 +142,10 @@ def find_group(groupname): ''' ldap_conn = init_ldap() try: - logger.info('Looking up group', groupname) + logger.info('Looking up group ' + groupname) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) - results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['name','groupType'] ) + results = ldap_conn.search_s(secrets.BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['name','groupType'] ) logger.info(results) @@ -169,7 +163,7 @@ def create_group(groupname, description): ldap_conn = init_ldap() try: ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) - dn = 'CN={},{}'.format(groupname, BASE_GROUPS) + dn = 'CN={},{}'.format(groupname, secrets.BASE_GROUPS) ldif = [ ('objectClass', [b'top', b'group']), @@ -235,7 +229,7 @@ def list_group(groupname): group_dn = find_group(groupname) criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) - results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member']) + results = ldap_conn.search_s(secrets.BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member']) members_tmp = results[0][1] members = members_tmp.get('member', []) return [find_dn(dn.decode()) for dn in members] @@ -254,7 +248,7 @@ def is_member(groupname, username): user_dn = find_user(username).encode() memflag = False criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) - results = ldap_conn.search_s(BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) + results = ldap_conn.search_s(secrets.BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) members_tmp = results[0][1] members = members_tmp.get('member', []) return user_dn in members @@ -270,7 +264,7 @@ def dump_users(): ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(objectGUID=*))' attributes = ['cn', 'sAMAccountName', 'mail', 'displayName', 'givenName', 'name', 'sn', 'logonCount', 'objectGUID'] - results = ldap_conn.search_s(BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, attributes) + results = ldap_conn.search_s(secrets.BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, attributes) results = convert(results) output = {} @@ -295,7 +289,7 @@ def dump_users(): if __name__ == '__main__': pass #print(create_user('Elon', 'Tusk', 'elon.tusk', 'elont@example.com', 'protospace*&^g87g6')) - print(find_user('test.testerb')) + #print(find_user('test.testerb')) #print(set_password('tanner.collin', 'Supersecret@@')) #print(find_dn('CN=Tanner Collin,OU=MembersOU,DC=ps,DC=protospace,DC=ca')) #print("============================================================") @@ -314,6 +308,6 @@ if __name__ == '__main__': #print(list_group("newgroup")) #print(dump_users()) - #users = list_group('Laser Users') - #import json - #print(json.dumps(users, indent=4)) + users = list_group('Laser Users') + import json + print(json.dumps(users, indent=4)) diff --git a/ldapserver/log.py b/ldapserver/log.py new file mode 100644 index 0000000..23cd69e --- /dev/null +++ b/ldapserver/log.py @@ -0,0 +1,22 @@ +import logging +import logging.config + +logging.config.dictConfig({ + 'version': 1, + 'formatters': {'default': { + 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', + }}, + 'handlers': {'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }}, + 'root': { + 'level': 'INFO', + 'handlers': ['wsgi'] + } +}) + +logger = logging.getLogger(__name__) + +logger.info('Logging enabled.') diff --git a/ldapserver/secrets.py.example b/ldapserver/secrets.py.example index d73a4e4..3078416 100644 --- a/ldapserver/secrets.py.example +++ b/ldapserver/secrets.py.example @@ -8,3 +8,9 @@ AUTH_TOKEN = '' LDAP_USERNAME = '' LDAP_PASSWORD = '' + +LDAP_CERTFILE = '' +LDAP_URL = '' + +BASE_MEMBERS = '' +BASE_GROUPS = '' diff --git a/ldapserver/server.py b/ldapserver/server.py index 39174e6..f7f43d2 100644 --- a/ldapserver/server.py +++ b/ldapserver/server.py @@ -1,24 +1,6 @@ from flask import Flask, abort, request app = Flask(__name__) -from logging.config import dictConfig - -dictConfig({ - 'version': 1, - 'formatters': {'default': { - 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', - }}, - 'handlers': {'wsgi': { - 'class': 'logging.StreamHandler', - 'stream': 'ext://flask.logging.wsgi_errors_stream', - 'formatter': 'default' - }}, - 'root': { - 'level': 'INFO', - 'handlers': ['wsgi'] - } -}) - import ldap_functions import secrets From 5f08bd5e01857284c3ded367a06126e180f23a21 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 11 Sep 2020 22:52:23 +0000 Subject: [PATCH 17/93] Add missing LDAP server requirements --- apiserver/docs/source/ldap.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/docs/source/ldap.rst b/apiserver/docs/source/ldap.rst index b69f8fa..ff685cd 100644 --- a/apiserver/docs/source/ldap.rst +++ b/apiserver/docs/source/ldap.rst @@ -10,7 +10,7 @@ Install dependencies: .. sourcecode:: bash $ sudo apt update - $ sudo apt install build-essential python3 python3-dev python3-pip python-virtualenv python3-virtualenv supervisor + $ sudo apt install build-essential python3 python3-dev python3-pip python-virtualenv python3-virtualenv supervisor libsasl2-dev libldap2-dev libssl-dev Clone the repo: From 164aa3c9c7e0202ad0c5d58f01bdc18f884c023c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 13 Sep 2020 20:09:59 +0000 Subject: [PATCH 18/93] Stop checking old_models.py on registration --- apiserver/apiserver/api/utils.py | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index 9382f89..c82b22a 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -19,11 +19,6 @@ from django.core.cache import cache from django.utils.timezone import now, pytz from . import models, serializers, utils_ldap -try: - from . import old_models -except ImportError: - logger.info('Running without old portal data...') - old_models = None STATIC_FOLDER = 'data/static/' @@ -292,10 +287,6 @@ def link_old_member(data, user): Since this runs AFTER registration, we need to delete the user on any failures or else the username will be taken when they try again ''' - if not old_models: - msg = 'Unable to link, old DB wasn\'t imported.' - logger.info(msg) - raise ValidationError(dict(email=msg)) try: member = models.Member.objects.get(old_email__iexact=data['email']) @@ -342,12 +333,11 @@ def link_old_member(data, user): models.Training.objects.filter(member_id=member.id).update(user=user) def create_new_member(data, user): - if old_models: - old_members = old_models.Members.objects.using('old_portal') - if old_members.filter(email__iexact=data['email']).exists(): - msg = 'Account was found in old portal.' - logger.info(msg) - raise ValidationError(dict(email=msg)) + members = models.Member.objects + if members.filter(old_email__iexact=data['email']).exists(): + msg = 'Account was found in old portal.' + logger.info(msg) + raise ValidationError(dict(email=msg)) if utils_ldap.is_configured(): result = utils_ldap.find_user(user.username) From 28b8de41a8796d0d192582dce7b06751f9467ec6 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 13 Sep 2020 20:14:42 +0000 Subject: [PATCH 19/93] Alert Tanner about LDAP server errors --- apiserver/apiserver/api/serializers.py | 5 ++++- apiserver/apiserver/api/utils.py | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 5e29659..b6a2e54 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -447,7 +447,10 @@ class MyPasswordChangeSerializer(PasswordChangeSerializer): if utils_ldap.is_configured(): if utils_ldap.set_password(data) != 200: - raise ValidationError(dict(non_field_errors='Problem connecting to LDAP server: set.')) + msg = 'Problem connecting to LDAP server: set.' + utils.alert_tanner(msg) + logger.info(msg) + raise ValidationError(dict(non_field_errors=msg)) super().save() diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index c82b22a..afbcce6 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -309,15 +309,18 @@ def link_old_member(data, user): if result == 200: if utils_ldap.set_password(data) != 200: msg = 'Problem connecting to LDAP server: set.' + alert_tanner(msg) logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) elif result == 404: if utils_ldap.create_user(data) != 200: msg = 'Problem connecting to LDAP server: create.' + alert_tanner(msg) logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) else: msg = 'Problem connecting to LDAP server: find.' + alert_tanner(msg) logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) @@ -349,11 +352,13 @@ def create_new_member(data, user): pass else: msg = 'Problem connecting to LDAP server.' + alert_tanner(msg) logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) if utils_ldap.create_user(data) != 200: msg = 'Problem connecting to LDAP server: create.' + alert_tanner(msg) logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) From 44d50735bf8abe6540b71ccd7b65d96b41499223 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 13 Sep 2020 22:07:04 +0000 Subject: [PATCH 20/93] Add rabbit and trotec cert dates --- apiserver/apiserver/api/models.py | 2 ++ apiserver/apiserver/api/serializers.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py index ee4f5c1..a681355 100644 --- a/apiserver/apiserver/api/models.py +++ b/apiserver/apiserver/api/models.py @@ -53,6 +53,8 @@ class Member(models.Model): wood_cert_date = models.DateField(blank=True, null=True, default=None) wood2_cert_date = models.DateField(blank=True, null=True, default=None) cnc_cert_date = models.DateField(blank=True, null=True, default=None) + rabbit_cert_date = models.DateField(blank=True, null=True, default=None) + trotec_cert_date = models.DateField(blank=True, null=True, default=None) paused_date = models.DateField(blank=True, null=True) monthly_fees = models.IntegerField(default=55, blank=True, null=True) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index b6a2e54..b1024e2 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -144,6 +144,8 @@ class MemberSerializer(serializers.ModelSerializer): 'wood_cert_date', 'wood2_cert_date', 'cnc_cert_date', + 'rabbit_cert_date', + 'trotec_cert_date', ] def get_status(self, obj): From dcdfbfa953a52811079763d6cae3cb6264d82202 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:17:54 +0000 Subject: [PATCH 21/93] Change LDAP group membership based on certification --- apiserver/apiserver/api/serializers.py | 15 +++++++++++ apiserver/apiserver/api/utils_ldap.py | 35 ++++++++++++++++++++++++++ apiserver/apiserver/api/views.py | 30 +++++++++++++++++++++- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index b1024e2..54d9f52 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -195,6 +195,21 @@ class AdminMemberSerializer(MemberSerializer): 'is_staff', ] + def update(self, instance, validated_data): + if 'rabbit_cert_date' in validated_data: + if validated_data['rabbit_cert_date']: + utils_ldap.add_to_group(instance, 'Laser Users') + else: + utils_ldap.remove_from_group(instance, 'Laser Users') + + if 'trotec_cert_date' in validated_data: + if validated_data['trotec_cert_date']: + utils_ldap.add_to_group(instance, 'Trotec Users') + else: + utils_ldap.remove_from_group(instance, 'Trotec Users') + + return super().update(instance, validated_data) + # member viewing member list or search result class SearchSerializer(serializers.Serializer): diff --git a/apiserver/apiserver/api/utils_ldap.py b/apiserver/apiserver/api/utils_ldap.py index cd076f4..ac8f1d4 100644 --- a/apiserver/apiserver/api/utils_ldap.py +++ b/apiserver/apiserver/api/utils_ldap.py @@ -4,6 +4,7 @@ logger = logging.getLogger(__name__) import requests from apiserver import secrets +from apiserver.api import utils def is_configured(): return bool(secrets.LDAP_API_URL and secrets.LDAP_API_KEY) @@ -39,3 +40,37 @@ def set_password(data): password=data['password1'], ) return ldap_api('set-password', ldap_data) + +def add_to_group(member, group): + try: + ldap_data = dict(group=group) + + if member.user: + ldap_data['username'] = member.user.username + else: + ldap_data['email'] = member.old_email + + if ldap_api('add-to-group', ldap_data) != 200: raise + except BaseException as e: + logger.error('LDAP Group - {} - {}'.format(e.__class__.__name__, str(e))) + m = '{} {} ({})'.format(member.first_name, member.last_name, member.id) + msg = 'Problem adding {} to group {}!'.format(m, group) + utils.alert_tanner(msg) + logger.info(msg) + +def remove_from_group(member, group): + try: + ldap_data = dict(group=group) + + if member.user: + ldap_data['username'] = member.user.username + else: + ldap_data['email'] = member.old_email + + if ldap_api('remove-from-group', ldap_data) != 200: raise + except BaseException as e: + logger.error('LDAP Group - {} - {}'.format(e.__class__.__name__, str(e))) + m = '{} {} ({})'.format(member.first_name, member.last_name, member.id) + msg = 'Problem adding {} to group {}!'.format(m, group) + utils.alert_tanner(msg) + logger.info(msg) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 1e5f20b..cdb88f1 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -20,7 +20,7 @@ import datetime, time import requests -from . import models, serializers, utils, utils_paypal, utils_stats +from . import models, serializers, utils, utils_paypal, utils_stats, utils_ldap from .permissions import ( is_admin_director, AllowMetadata, @@ -234,6 +234,20 @@ class TrainingViewSet(Base, Retrieve, Create, Update): member.mill_cert_date = utils.today_alberta_tz() if status == 'Attended' else None elif session.course.id == 259: member.cnc_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + elif session.course.id == 247: + member.rabbit_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + + if status == 'Attended': + utils_ldap.add_to_group(member, 'Laser Users') + else: + utils_ldap.remove_from_group(member, 'Laser Users') + elif session.course.id == 321: + member.trotec_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + + if status == 'Attended': + utils_ldap.add_to_group(member, 'Trotec Users') + else: + utils_ldap.remove_from_group(member, 'Trotec Users') member.save() serializer.save(user=user, member_id=member.id, attendance_status=status) @@ -273,6 +287,20 @@ class TrainingViewSet(Base, Retrieve, Create, Update): member.mill_cert_date = utils.today_alberta_tz() if status == 'Attended' else None elif session.course.id == 259: member.cnc_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + elif session.course.id == 247: + member.rabbit_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + + if status == 'Attended': + utils_ldap.add_to_group(member, 'Laser Users') + else: + utils_ldap.remove_from_group(member, 'Laser Users') + elif session.course.id == 321: + member.trotec_cert_date = utils.today_alberta_tz() if status == 'Attended' else None + + if status == 'Attended': + utils_ldap.add_to_group(member, 'Trotec Users') + else: + utils_ldap.remove_from_group(member, 'Trotec Users') member.save() From f55d8c609e069a79035618db777d8af0ced20e4c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:19:01 +0000 Subject: [PATCH 22/93] Add scripts to import laser certs based off groups --- apiserver/import_rabbit_group.py | 59 ++++++++++++++++++++++++++++++++ apiserver/import_trotec_group.py | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 apiserver/import_rabbit_group.py create mode 100644 apiserver/import_trotec_group.py diff --git a/apiserver/import_rabbit_group.py b/apiserver/import_rabbit_group.py new file mode 100644 index 0000000..ca02f87 --- /dev/null +++ b/apiserver/import_rabbit_group.py @@ -0,0 +1,59 @@ +import django, sys, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings' +django.setup() + +import datetime +import json +import re +from apiserver.api import models, utils + +def clean(name): + return re.sub(r'[^a-z]', '', name.lower()) + +with open('ad-rabbit.json', 'r') as f: + ad_dirty = json.load(f) + +with open('ad-dump.json', 'r') as f: + ad_dump = json.load(f) + +ad = {} +for sam in ad_dirty: + try: + ad[clean(sam)] = ad_dump[sam]['mail'] + except KeyError: + continue + +members = models.Member.objects.all() + +portal = {} +for m in members: + name = m.first_name + m.last_name + portal[clean(name)] = m + +good_members = {} + +for ad_name, email in ad.items(): + if ad_name in portal: + good_members[ad_name] = portal[ad_name] + print('found ad name match', ad_name) + else: + print('cant find ad name', ad_name) + print('searching for email...') + for m in members: + if m.old_email and m.old_email.lower() == email.lower(): + good_members[ad_name] = m + print(' found email', email) + break + else: + print(' cant link email', email) + +print() +print() + +for m in good_members.values(): + if not m.rabbit_cert_date: + m.rabbit_cert_date = utils.today_alberta_tz() + print('certified', m.first_name, m.last_name) + m.save() + else: + print('skipping', m.first_name, m.last_name) diff --git a/apiserver/import_trotec_group.py b/apiserver/import_trotec_group.py new file mode 100644 index 0000000..eb63235 --- /dev/null +++ b/apiserver/import_trotec_group.py @@ -0,0 +1,59 @@ +import django, sys, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings' +django.setup() + +import datetime +import json +import re +from apiserver.api import models, utils + +def clean(name): + return re.sub(r'[^a-z]', '', name.lower()) + +with open('ad-trotec.json', 'r') as f: + ad_dirty = json.load(f) + +with open('ad-dump.json', 'r') as f: + ad_dump = json.load(f) + +ad = {} +for sam in ad_dirty: + try: + ad[clean(sam)] = ad_dump[sam]['mail'] + except KeyError: + continue + +members = models.Member.objects.all() + +portal = {} +for m in members: + name = m.first_name + m.last_name + portal[clean(name)] = m + +good_members = {} + +for ad_name, email in ad.items(): + if ad_name in portal: + good_members[ad_name] = portal[ad_name] + print('found ad name match', ad_name) + else: + print('cant find ad name', ad_name) + print('searching for email...') + for m in members: + if m.old_email and m.old_email.lower() == email.lower(): + good_members[ad_name] = m + print(' found email', email) + break + else: + print(' cant link email', email) + +print() +print() + +for m in good_members.values(): + if not m.trotec_cert_date: + m.trotec_cert_date = utils.today_alberta_tz() + print('certified', m.first_name, m.last_name) + m.save() + else: + print('skipping', m.first_name, m.last_name) From 854db4b258625dc1a1202340840597fcace44e36 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:20:24 +0000 Subject: [PATCH 23/93] Add execution permissions to scripts --- apiserver/gen_card_photos.py | 0 apiserver/import_rabbit_group.py | 0 apiserver/import_trotec_group.py | 0 apiserver/lockout_auth_update.py | 0 4 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 apiserver/gen_card_photos.py mode change 100644 => 100755 apiserver/import_rabbit_group.py mode change 100644 => 100755 apiserver/import_trotec_group.py mode change 100644 => 100755 apiserver/lockout_auth_update.py diff --git a/apiserver/gen_card_photos.py b/apiserver/gen_card_photos.py old mode 100644 new mode 100755 diff --git a/apiserver/import_rabbit_group.py b/apiserver/import_rabbit_group.py old mode 100644 new mode 100755 diff --git a/apiserver/import_trotec_group.py b/apiserver/import_trotec_group.py old mode 100644 new mode 100755 diff --git a/apiserver/lockout_auth_update.py b/apiserver/lockout_auth_update.py old mode 100644 new mode 100755 From faf6969f7efc3489f625af935908097c5fa9d11d Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:21:32 +0000 Subject: [PATCH 24/93] Add UI for Rabbit and Trotec certs --- webclient/src/AdminMembers.js | 12 ++++++++++++ webclient/src/Training.js | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index fe3ed1e..9c16d56 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -646,6 +646,18 @@ export function AdminMemberCertifications(props) { Tormach: CAM and Tormach Intro + + Rabbit Laser + {member.rabbit_cert_date ? 'Yes, ' + member.rabbit_cert_date : 'No'} + Laser: Cutting and Engraving + + + + Trotec Laser + {member.trotec_cert_date ? 'Yes, ' + member.trotec_cert_date : 'No'} + Laser: Trotec Course + + diff --git a/webclient/src/Training.js b/webclient/src/Training.js index 64d96a4..b099c02 100644 --- a/webclient/src/Training.js +++ b/webclient/src/Training.js @@ -57,6 +57,16 @@ export function CertList(props) { {member.cnc_cert_date ? 'Yes, ' + member.cnc_cert_date : 'No'} Tormach: CAM and Tormach Intro + + Rabbit Laser + {member.rabbit_cert_date ? 'Yes, ' + member.rabbit_cert_date : 'No'} + Laser: Cutting and Engraving + + + Trotec Laser + {member.trotec_cert_date ? 'Yes, ' + member.trotec_cert_date : 'No'} + Laser: Trotec Course + ); From 3b8e0097a1822534fbd9ea96a2a75a90b8b32919 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:40:29 +0000 Subject: [PATCH 25/93] Add loading spinners to buttons --- webclient/src/Classes.js | 13 ++++++++++--- webclient/src/InstructorClasses.js | 7 +++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/webclient/src/Classes.js b/webclient/src/Classes.js index b839b58..5dea796 100644 --- a/webclient/src/Classes.js +++ b/webclient/src/Classes.js @@ -92,6 +92,7 @@ export function ClassDetail(props) { const [clazz, setClass] = useState(false); const [refreshCount, refreshClass] = useReducer(x => x + 1, 0); const [error, setError] = useState(false); + const [loading, setLoading] = useState(false); const { token, user, refreshUser } = props; const { id } = useParams(); const userTraining = clazz && clazz.students.find(x => x.user == user.id); @@ -108,6 +109,7 @@ export function ClassDetail(props) { }, [refreshCount]); const handleSignup = () => { + setLoading(true); const data = { attendance_status: 'Waiting for payment', session: id }; requester('/training/', 'POST', token, data) .then(res => { @@ -120,6 +122,7 @@ export function ClassDetail(props) { }; const handleToggle = (newStatus) => { + setLoading(true); const data = { attendance_status: newStatus, session: id }; requester('/training/'+userTraining.id+'/', 'PUT', token, data) .then(res => { @@ -132,6 +135,10 @@ export function ClassDetail(props) { }); }; + useEffect(() => { + setLoading(false); + }, [userTraining]); + // TODO: calculate yesterday and lock signups return ( @@ -198,11 +205,11 @@ export function ClassDetail(props) {

Status: {userTraining.attendance_status}

{userTraining.attendance_status === 'Withdrawn' ? - : - } @@ -226,7 +233,7 @@ export function ClassDetail(props) { ((clazz.max_students && clazz.student_count >= clazz.max_students) ?

The class is full.

: - ) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index 3603dba..836f067 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -68,8 +68,10 @@ class AttendanceSheet extends React.Component { function AttendanceRow(props) { const { student, token, refreshClass } = props; const [error, setError] = useState(false); + const [loading, setLoading] = useState(false); const handleMark = (newStatus) => { + setLoading(newStatus); const data = { ...student, attendance_status: newStatus }; requester('/training/'+student.id+'/', 'PATCH', token, data) .then(res => { @@ -86,8 +88,13 @@ function AttendanceRow(props) { onClick: () => handleMark(name), toggle: true, active: student.attendance_status === name, + loading: loading === name, }); + useEffect(() => { + setLoading(false); + }, [student.attendance_status]); + return (

{student.student_name}{student.attendance_status === 'Waiting for payment' && ' (Waiting for payment)'}:

From 440d389f1615ba3323300fe947b5a0a2a49c8c78 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:42:09 +0000 Subject: [PATCH 26/93] Disable buttons while loading --- webclient/src/AdminMembers.js | 2 ++ webclient/src/Classes.js | 2 ++ webclient/src/InstructorClasses.js | 1 + 3 files changed, 5 insertions(+) diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 9c16d56..7b81b3c 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -541,6 +541,7 @@ export function AdminCert(props) { const handleCert = (e) => { e.preventDefault(); + if (loading) return; setLoading(true); let data = Object(); data[field] = moment.utc().tz('America/Edmonton').format('YYYY-MM-DD'); @@ -555,6 +556,7 @@ export function AdminCert(props) { const handleUncert = (e) => { e.preventDefault(); + if (loading) return; setLoading(true); let data = Object(); data[field] = null; diff --git a/webclient/src/Classes.js b/webclient/src/Classes.js index 5dea796..42098ec 100644 --- a/webclient/src/Classes.js +++ b/webclient/src/Classes.js @@ -109,6 +109,7 @@ export function ClassDetail(props) { }, [refreshCount]); const handleSignup = () => { + if (loading) return; setLoading(true); const data = { attendance_status: 'Waiting for payment', session: id }; requester('/training/', 'POST', token, data) @@ -122,6 +123,7 @@ export function ClassDetail(props) { }; const handleToggle = (newStatus) => { + if (loading) return; setLoading(true); const data = { attendance_status: newStatus, session: id }; requester('/training/'+userTraining.id+'/', 'PUT', token, data) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index 836f067..ea382a1 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -71,6 +71,7 @@ function AttendanceRow(props) { const [loading, setLoading] = useState(false); const handleMark = (newStatus) => { + if (loading) return; setLoading(newStatus); const data = { ...student, attendance_status: newStatus }; requester('/training/'+student.id+'/', 'PATCH', token, data) From ddb83a1b58b8887bbfafff2f683677adb64b0305 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 19:52:09 +0000 Subject: [PATCH 27/93] Only change group membership if cert changes --- apiserver/apiserver/api/serializers.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 54d9f52..fa93b4e 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -197,16 +197,20 @@ class AdminMemberSerializer(MemberSerializer): def update(self, instance, validated_data): if 'rabbit_cert_date' in validated_data: - if validated_data['rabbit_cert_date']: - utils_ldap.add_to_group(instance, 'Laser Users') - else: - utils_ldap.remove_from_group(instance, 'Laser Users') + changed = validated_data['rabbit_cert_date'] != instance.rabbit_cert_date + if changed: + if validated_data['rabbit_cert_date']: + utils_ldap.add_to_group(instance, 'Laser Users') + else: + utils_ldap.remove_from_group(instance, 'Laser Users') if 'trotec_cert_date' in validated_data: - if validated_data['trotec_cert_date']: - utils_ldap.add_to_group(instance, 'Trotec Users') - else: - utils_ldap.remove_from_group(instance, 'Trotec Users') + changed = validated_data['trotec_cert_date'] != instance.trotec_cert_date + if changed: + if validated_data['trotec_cert_date']: + utils_ldap.add_to_group(instance, 'Trotec Users') + else: + utils_ldap.remove_from_group(instance, 'Trotec Users') return super().update(instance, validated_data) From 131a8a46a6e578ee2cb55029ee8adc5fa77a598c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 20:00:53 +0000 Subject: [PATCH 28/93] Abstract updating certs to a method --- apiserver/apiserver/api/utils_ldap.py | 2 +- apiserver/apiserver/api/views.py | 128 +++++++++++--------------- 2 files changed, 54 insertions(+), 76 deletions(-) diff --git a/apiserver/apiserver/api/utils_ldap.py b/apiserver/apiserver/api/utils_ldap.py index ac8f1d4..62ad5ee 100644 --- a/apiserver/apiserver/api/utils_ldap.py +++ b/apiserver/apiserver/api/utils_ldap.py @@ -71,6 +71,6 @@ def remove_from_group(member, group): except BaseException as e: logger.error('LDAP Group - {} - {}'.format(e.__class__.__name__, str(e))) m = '{} {} ({})'.format(member.first_name, member.last_name, member.id) - msg = 'Problem adding {} to group {}!'.format(m, group) + msg = 'Problem removing {} from group {}!'.format(m, group) utils.alert_tanner(msg) logger.info(msg) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index cdb88f1..4e7d3ac 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -200,81 +200,8 @@ class TrainingViewSet(Base, Retrieve, Create, Update): else: return serializers.StudentTrainingSerializer - # TODO: turn these into @actions - # TODO: check if full, but not for instructors - # TODO: if already paid, skip to confirmed - def perform_create(self, serializer): - user = self.request.user - data = self.request.data - session_id = data['session'] - status = data['attendance_status'] - session = get_object_or_404(models.Session, id=session_id) - - if data.get('member_id', None): - if not (is_admin_director(user) or session.instructor == user): - raise exceptions.ValidationError('Not allowed to register others') - - member = get_object_or_404(models.Member, id=data['member_id']) - user = getattr(member, 'user', None) - - training1 = models.Training.objects.filter(user=user, session=session) - training2 = models.Training.objects.filter(member_id=member.id, session=session) - if (user and training1.exists()) or training2.exists(): - raise exceptions.ValidationError(dict(non_field_errors='Already registered.')) - - if session.course.id == 249: - member.orientation_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 261: - member.wood_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 401: - member.wood2_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 281: - member.lathe_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 283: - member.mill_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 259: - member.cnc_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - elif session.course.id == 247: - member.rabbit_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - - if status == 'Attended': - utils_ldap.add_to_group(member, 'Laser Users') - else: - utils_ldap.remove_from_group(member, 'Laser Users') - elif session.course.id == 321: - member.trotec_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - - if status == 'Attended': - utils_ldap.add_to_group(member, 'Trotec Users') - else: - utils_ldap.remove_from_group(member, 'Trotec Users') - member.save() - - serializer.save(user=user, member_id=member.id, attendance_status=status) - else: - training = models.Training.objects.filter(user=user, session=session) - if training.exists(): - raise exceptions.ValidationError('Already registered') - if user == session.instructor: - raise exceptions.ValidationError('You are teaching this session') - if status == 'Waiting for payment' and session.cost == 0: - status = 'Confirmed' - serializer.save(user=user, attendance_status=status) - - def perform_update(self, serializer): - session_id = self.request.data['session'] - status = self.request.data['attendance_status'] - session = get_object_or_404(models.Session, id=session_id) - if status == 'Waiting for payment' and session.cost == 0: - status = 'Confirmed' - - training = serializer.save(attendance_status=status) - - if training.user: - member = training.user.member - else: - member = models.Member.objects.get(id=training.member_id) - + def update_cert(self, session, member, status): + # always update cert date incase member is returning and gets recertified if session.course.id == 249: member.orientation_date = utils.today_alberta_tz() if status == 'Attended' else None elif session.course.id == 261: @@ -303,6 +230,57 @@ class TrainingViewSet(Base, Retrieve, Create, Update): utils_ldap.remove_from_group(member, 'Trotec Users') member.save() + # TODO: turn these into @actions + # TODO: check if full, but not for instructors + # TODO: if already paid, skip to confirmed + def perform_create(self, serializer): + user = self.request.user + data = self.request.data + session_id = data['session'] + status = data['attendance_status'] + session = get_object_or_404(models.Session, id=session_id) + + if data.get('member_id', None): + if not (is_admin_director(user) or session.instructor == user): + raise exceptions.ValidationError('Not allowed to register others') + + member = get_object_or_404(models.Member, id=data['member_id']) + user = getattr(member, 'user', None) + + training1 = models.Training.objects.filter(user=user, session=session) + training2 = models.Training.objects.filter(member_id=member.id, session=session) + if (user and training1.exists()) or training2.exists(): + raise exceptions.ValidationError(dict(non_field_errors='Already registered.')) + + self.update_cert(session, member, status) + + serializer.save(user=user, member_id=member.id, attendance_status=status) + else: + training = models.Training.objects.filter(user=user, session=session) + if training.exists(): + raise exceptions.ValidationError('Already registered') + if user == session.instructor: + raise exceptions.ValidationError('You are teaching this session') + if status == 'Waiting for payment' and session.cost == 0: + status = 'Confirmed' + serializer.save(user=user, attendance_status=status) + + def perform_update(self, serializer): + session_id = self.request.data['session'] + status = self.request.data['attendance_status'] + session = get_object_or_404(models.Session, id=session_id) + if status == 'Waiting for payment' and session.cost == 0: + status = 'Confirmed' + + training = serializer.save(attendance_status=status) + + if training.user: + member = training.user.member + else: + member = models.Member.objects.get(id=training.member_id) + + self.update_cert(session, member, status) + class TransactionViewSet(Base, List, Create, Retrieve, Update): permission_classes = [AllowMetadata | IsAuthenticated, IsObjOwnerOrAdmin] From 5e1d62caf36c9726f637f5bc394700ca1856f8e2 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 15 Sep 2020 21:01:07 +0000 Subject: [PATCH 29/93] Add more logging --- ldapserver/ldap_functions.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 91dd0c1..9de2772 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -194,6 +194,7 @@ def add_to_group(groupname, username): ldap_conn.modify_s(group_dn, mod_acct) return True else: + logger.info('Already a member, skipping') return False finally: @@ -214,6 +215,7 @@ def remove_from_group(groupname, username): ldap_conn.modify_s(group_dn, mod_acct) return True else: + logger.info('Not a member, skipping') return False finally: @@ -243,6 +245,7 @@ def is_member(groupname, username): ''' ldap_conn = init_ldap() try: + logger.info('Checking group membership...') ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) group_dn = find_group(groupname) user_dn = find_user(username).encode() @@ -308,6 +311,6 @@ if __name__ == '__main__': #print(list_group("newgroup")) #print(dump_users()) - users = list_group('Laser Users') - import json - print(json.dumps(users, indent=4)) + #users = list_group('Laser Users') + #import json + #print(json.dumps(users, indent=4)) From b45f86e0cc87d13b8f7234d55792e826e157ffc9 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 16 Sep 2020 22:52:17 +0000 Subject: [PATCH 30/93] Add Auth API connection to apiserver --- apiserver/apiserver/api/serializers.py | 19 ++++++++++++++++- apiserver/apiserver/api/utils_auth.py | 28 ++++++++++++++++++++++++++ apiserver/apiserver/secrets.py.example | 10 +++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 apiserver/apiserver/api/utils_auth.py diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index fa93b4e..9a37a20 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -473,6 +473,13 @@ class MyPasswordChangeSerializer(PasswordChangeSerializer): logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) + if utils_auth.is_configured(): + if utils_auth.set_password(data) != 200: + msg = 'Problem connecting to Auth server: set.' + utils.alert_tanner(msg) + logger.info(msg) + raise ValidationError(dict(non_field_errors=msg)) + super().save() class MyPasswordResetSerializer(PasswordResetSerializer): @@ -490,7 +497,17 @@ class MyPasswordResetConfirmSerializer(PasswordResetConfirmSerializer): if utils_ldap.is_configured(): if utils_ldap.set_password(data) != 200: - raise ValidationError(dict(non_field_errors='Problem connecting to LDAP server: set.')) + msg = 'Problem connecting to LDAP server: set.' + utils.alert_tanner(msg) + logger.info(msg) + raise ValidationError(dict(non_field_errors=msg)) + + if utils_auth.is_configured(): + if utils_auth.set_password(data) != 200: + msg = 'Problem connecting to Auth server: set.' + utils.alert_tanner(msg) + logger.info(msg) + raise ValidationError(dict(non_field_errors=msg)) super().save() diff --git a/apiserver/apiserver/api/utils_auth.py b/apiserver/apiserver/api/utils_auth.py new file mode 100644 index 0000000..cae62b0 --- /dev/null +++ b/apiserver/apiserver/api/utils_auth.py @@ -0,0 +1,28 @@ +import logging +logger = logging.getLogger(__name__) + +import requests + +from apiserver import secrets +from apiserver.api import utils + +def is_configured(): + return bool(secrets.AUTH_API_URL and secrets.AUTH_API_KEY) + + +def auth_api(route, data): + try: + headers = {'Authorization': 'Token ' + secrets.AUTH_API_KEY} + url = secrets.AUTH_API_URL + route + r = requests.post(url, data=data, headers=headers, timeout=3) + return r.status_code + except BaseException as e: + logger.error('Auth {} - {} - {}'.format(url, e.__class__.__name__, str(e))) + return None + +def set_password(data): + auth_data = dict( + username=data['username'], + password=data['password1'], + ) + return auth_api('set-password', auth_data) diff --git a/apiserver/apiserver/secrets.py.example b/apiserver/apiserver/secrets.py.example index d6179e0..c4da972 100644 --- a/apiserver/apiserver/secrets.py.example +++ b/apiserver/apiserver/secrets.py.example @@ -40,6 +40,16 @@ LDAP_API_URL = '' # spaceport/ldapserver/secrets.py LDAP_API_KEY = '' +# Auth API url +# should contain the IP and port of the script and machine connected over VPN +# with trailing slash +AUTH_API_URL = '' + +# Auth API key +# should be equal to the auth token value set in +# spaceport/authserver/secrets.py +AUTH_API_KEY = '' + # Door cards API token # Set this to random characters # For example, use the output of this: From 6603eb56ac4b6ef133bfe65f9787ebcba62e3c40 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 17 Sep 2020 22:15:48 +0000 Subject: [PATCH 31/93] Ignore class attendance buttons if no change --- webclient/src/InstructorClasses.js | 1 + 1 file changed, 1 insertion(+) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index ea382a1..a5af7b2 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -72,6 +72,7 @@ function AttendanceRow(props) { const handleMark = (newStatus) => { if (loading) return; + if (student.attendance_status == newStatus) return; setLoading(newStatus); const data = { ...student, attendance_status: newStatus }; requester('/training/'+student.id+'/', 'PATCH', token, data) From 9a105908a3afa65581e8d9fdebe36065652b5bb6 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 18 Sep 2020 05:03:05 +0000 Subject: [PATCH 32/93] Add API routes for Spaceport auth --- apiserver/apiserver/api/serializers.py | 9 ++++++++- apiserver/apiserver/api/views.py | 5 ++++- apiserver/apiserver/urls.py | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 9a37a20..b6062f1 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -7,7 +7,7 @@ from rest_framework import serializers from rest_framework.exceptions import ValidationError from rest_framework.validators import UniqueValidator from rest_auth.registration.serializers import RegisterSerializer -from rest_auth.serializers import PasswordChangeSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer +from rest_auth.serializers import PasswordChangeSerializer, PasswordResetSerializer, PasswordResetConfirmSerializer, LoginSerializer from rest_auth.serializers import UserDetailsSerializer import re @@ -545,3 +545,10 @@ class HistorySerializer(serializers.ModelSerializer): class Meta: model = models.HistoryIndex fields = '__all__' + +class SpaceportAuthSerializer(LoginSerializer): + def authenticate(self, **kwargs): + result = super().authenticate(**kwargs) + print(result) + print(self.context['request'].data) + return result diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 4e7d3ac..e48660b 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -12,7 +12,7 @@ from rest_framework import viewsets, views, mixins, generics, exceptions from rest_framework.decorators import action, api_view from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS, IsAuthenticatedOrReadOnly from rest_framework.response import Response -from rest_auth.views import PasswordChangeView, PasswordResetView, PasswordResetConfirmView +from rest_auth.views import PasswordChangeView, PasswordResetView, PasswordResetConfirmView, LoginView from rest_auth.registration.views import RegisterView from fuzzywuzzy import fuzz, process from collections import OrderedDict @@ -604,6 +604,9 @@ class PasswordResetView(PasswordResetView): class PasswordResetConfirmView(PasswordResetConfirmView): serializer_class = serializers.MyPasswordResetConfirmSerializer +class SpaceportAuthView(LoginView): + serializer_class = serializers.SpaceportAuthSerializer + @api_view() def null_view(request, *args, **kwargs): diff --git a/apiserver/apiserver/urls.py b/apiserver/apiserver/urls.py index b4839fd..cc6de42 100644 --- a/apiserver/apiserver/urls.py +++ b/apiserver/apiserver/urls.py @@ -33,6 +33,7 @@ urlpatterns = [ path('', include(router.urls)), path(ADMIN_ROUTE, admin.site.urls), url(r'^rest-auth/login/$', LoginView.as_view(), name='rest_login'), + url(r'^spaceport-auth/login/$', views.SpaceportAuthView.as_view(), name='spaceport_auth'), url(r'^rest-auth/logout/$', LogoutView.as_view(), name='rest_logout'), url(r'^password/reset/$', views.PasswordResetView.as_view(), name='rest_password_reset'), url(r'^password/reset/confirm/$', views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'), From 1cdb236a7ee35bfaa7624364c16daf5a94ed1aba Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 18 Sep 2020 05:04:00 +0000 Subject: [PATCH 33/93] Add UI for Spaceport auth --- webclient/public/wikilogo.png | Bin 0 -> 1090 bytes webclient/src/App.js | 5 ++ webclient/src/Auth.js | 132 ++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 webclient/public/wikilogo.png create mode 100644 webclient/src/Auth.js diff --git a/webclient/public/wikilogo.png b/webclient/public/wikilogo.png new file mode 100644 index 0000000000000000000000000000000000000000..228f35244c652adbd5cc8d179e3a47a6f8c57bdd GIT binary patch literal 1090 zcmV-I1ikx-P)n@&zoR8&=EW@mJDb-usAK|(`4KRsrj%`W)$FXqGd*#5bnlkj)r0k>zm)3y*00TlvL_t(|+U?tIbD}U1!0`ow z5fx4W>tk!%>%x(P@Bj5~ZoHe4NY921mTUL-x;p>a1QRS-qa_HeQ^3T%9;&&#PKtheAzfUa)}Mi{?8c$<#b+rLd23vw*>fd5Jw0k9!!%Vpkx6FJK7y z8fgv@xR*6ZRcGMZzQik3S>Y9>2X1+9&#GD;(`}GQ8A_Q1+w{0>iZ}mu<91dRc2djx z_#{9l7Qw@l!fr0R;^vK}T)cU`czCKhU*v%+r?Tt)?2%BN2-_W!NDzQSt z_X*g!T*&$aM4mFMVz3olbooFd%_UsMU@teEV5;uYGk5}=A27a#QpYhT1~)>lzLvz3w%{Z;6|ixF5-E3 z6ySzf(#}QsLw^okIno}as5Z`tAvTxFNdI + + + + {user && user.member.set_details ? diff --git a/webclient/src/Auth.js b/webclient/src/Auth.js new file mode 100644 index 0000000..dabeecc --- /dev/null +++ b/webclient/src/Auth.js @@ -0,0 +1,132 @@ +import React, { useState, useEffect, useReducer } from 'react'; +import { BrowserRouter as Router, Switch, Route, Link, useParams, useLocation } from 'react-router-dom'; +import moment from 'moment-timezone'; +import './light.css'; +import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Popup, Segment, Table } from 'semantic-ui-react'; +import { statusColor, BasicTable, staticUrl, requester, isAdmin } from './utils.js'; + +export function AuthForm(props) { + const { user } = props; + const username = user ? user.username : ''; + const [input, setInput] = useState({ username: username }); + const [error, setError] = useState({}); + const [success, setSuccess] = useState(false); + const [loading, setLoading] = useState(false); + + const handleValues = (e, v) => setInput({ ...input, [v.name]: v.value }); + const handleChange = (e) => handleValues(e, e.currentTarget); + + const handleSubmit = (e) => { + if (input.username.includes('@')) { + setError({ username: 'Username, not email.' }); + } else { + if (loading) return; + setLoading(true); + const data = { ...input, username: input.username.toLowerCase() }; + requester('/spaceport-auth/login/', 'POST', '', data) + .then(res => { + setSuccess(true); + setError({}); + }) + .catch(err => { + setLoading(false); + console.log(err); + setError(err.data); + }); + } + }; + + return ( + success ? + props.children + : + +
Log In to Spaceport
+ + {user ? + <> + + : + <> + + } + + + Authorize + + + + Forgot your password? +

Click here to reset it.

+
+ + ); +}; + +export function AuthWiki(props) { + const { user } = props; + + return ( + +
+ + Protospace Wiki +
+ +

would like to request Spaceport authentication.

+ +

URL: wiki.protospace.ca

+ + +
Success!
+

You can now log into the wiki:

+

Protospace Wiki

+
+
+ ); +} + +export function Auth(props) { + const { user } = props; + + return ( + +
Spaceport Auth
+ +

Use this page to link different applications to your Spaceport account.

+ + + + +
+ ); +} From 3bf8743e9af2cb1ac6d32567443f3fdb0523df13 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 20 Sep 2020 00:23:27 +0000 Subject: [PATCH 34/93] Display who used the lasers last --- apiserver/apiserver/api/views.py | 7 ++++++- webclient/src/Home.js | 13 ++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index e48660b..4e7ccbe 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -472,7 +472,12 @@ class StatsViewSet(viewsets.ViewSet, List): def track(self, request): if 'name' in request.data: track = cache.get('track', {}) - track[request.data['name']] = time.time() + + name = request.data['name'] + username = request.data.get('username', '') + username = username.split('.')[0] + + track[name] = dict(time=time.time(), username=username) cache.set('track', track) return Response(200) else: diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 8bd3fda..8e33403 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -153,9 +153,10 @@ export function Home(props) { const mcPlayers = stats && stats['minecraft_players'] ? stats['minecraft_players'] : []; - const getTrackStat = (x) => stats && stats.track && stats.track[x] ? moment().unix() - stats.track[x] > 60 ? 'Free' : 'In Use' : '?'; - const getTrackLast = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]).tz('America/Edmonton').format('llll') : 'Unknown'; - const getTrackAgo = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]).tz('America/Edmonton').fromNow() : ''; + const getTrackStat = (x) => stats && stats.track && stats.track[x] ? moment().unix() - stats.track[x]['time'] > 60 ? 'Free' : 'In Use' : '?'; + const getTrackLast = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').format('llll') : 'Unknown'; + const getTrackAgo = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').fromNow() : ''; + const getTrackName = (x) => stats && stats.track && stats.track[x] && stats.track[x]['username'] ? stats.track[x]['username'] : 'Unknown'; return ( @@ -238,7 +239,8 @@ export function Home(props) {

Last use:
{getTrackLast('TROTECS300')}
- {getTrackAgo('TROTECS300')} + {getTrackAgo('TROTECS300')}
+ by {getTrackName('TROTECS300')}

} trigger={[more]} /> @@ -250,7 +252,8 @@ export function Home(props) {

Last use:
{getTrackLast('FRICKIN-LASER')}
- {getTrackAgo('FRICKIN-LASER')} + {getTrackAgo('FRICKIN-LASER')}
+ by {getTrackName('TROTECS300')}

} trigger={[more]} /> From ca8083859ccde72b038b3927f5d1d0af5947bf7f Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 20 Sep 2020 00:24:28 +0000 Subject: [PATCH 35/93] Fix utils auth bugs --- apiserver/apiserver/api/serializers.py | 9 ++++++--- apiserver/apiserver/api/utils_auth.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index b6062f1..e6f79c5 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -11,7 +11,7 @@ from rest_auth.serializers import PasswordChangeSerializer, PasswordResetSeriali from rest_auth.serializers import UserDetailsSerializer import re -from . import models, fields, utils, utils_ldap +from . import models, fields, utils, utils_ldap, utils_auth from .. import settings, secrets class TransactionSerializer(serializers.ModelSerializer): @@ -549,6 +549,9 @@ class HistorySerializer(serializers.ModelSerializer): class SpaceportAuthSerializer(LoginSerializer): def authenticate(self, **kwargs): result = super().authenticate(**kwargs) - print(result) - print(self.context['request'].data) + + if result: + data = self.context['request'].data + utils_auth.set_password(data) + return result diff --git a/apiserver/apiserver/api/utils_auth.py b/apiserver/apiserver/api/utils_auth.py index cae62b0..9e3f52c 100644 --- a/apiserver/apiserver/api/utils_auth.py +++ b/apiserver/apiserver/api/utils_auth.py @@ -23,6 +23,6 @@ def auth_api(route, data): def set_password(data): auth_data = dict( username=data['username'], - password=data['password1'], + password=data['password'], ) return auth_api('set-password', auth_data) From 6aa59a46dcfbb0592145d2407484530389bf7e06 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 21 Sep 2020 18:50:01 +0000 Subject: [PATCH 36/93] Fix laser used by bug --- webclient/src/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 8e33403..7c34a13 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -253,7 +253,7 @@ export function Home(props) { Last use:
{getTrackLast('FRICKIN-LASER')}
{getTrackAgo('FRICKIN-LASER')}
- by {getTrackName('TROTECS300')} + by {getTrackName('FRICKIN-LASER')}

} trigger={[more]} /> From e263a6847f9a10223654db953b9e93e382b17f19 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 21 Sep 2020 20:01:47 +0000 Subject: [PATCH 37/93] Order upcoming classes by nearest date --- webclient/src/Classes.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/webclient/src/Classes.js b/webclient/src/Classes.js index 42098ec..dc4a802 100644 --- a/webclient/src/Classes.js +++ b/webclient/src/Classes.js @@ -72,13 +72,19 @@ export function Classes(props) {
Class List
Upcoming
+ +

Ordered by nearest date.

+ {classes ? - x.datetime > now)} /> + x.datetime > now).sort((a, b) => a.datetime > b.datetime ? 1 : -1)} /> :

Loading...

}
Recent
+ +

Ordered by nearest date.

+ {classes ? x.datetime < now)} /> : From 06fffca262c0d25a039d830f793c48d611938003 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 27 Sep 2020 04:02:07 +0000 Subject: [PATCH 38/93] Add script to delete member addresses from database --- apiserver/delete_addresses.py | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 apiserver/delete_addresses.py diff --git a/apiserver/delete_addresses.py b/apiserver/delete_addresses.py new file mode 100755 index 0000000..5b6e819 --- /dev/null +++ b/apiserver/delete_addresses.py @@ -0,0 +1,40 @@ +import django, sys, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings' +django.setup() + +from apiserver.api import models + +print('Deleting member object addresses...') + +result = models.Member.objects.update( + street_address='', + postal_code='', + city='', +) + +print(result, 'rows affected') +print() + +print('Scrubbing history...') + +result = models.Member.history.update( + street_address='', + postal_code='', + city='', +) + +print(result, 'rows affected') +print() + +print('Deleting historical changes...') + +address_fields = ['street_address', 'postal_code', 'city'] +result = models.HistoryChange.objects.filter(field__in=address_fields).update( + old='', + new='', +) + +print(result, 'rows affected') +print() + +print('Done.') From ced94ae019d9bfaacd5874797cc5a72cdded85c1 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 27 Sep 2020 04:03:24 +0000 Subject: [PATCH 39/93] Fix serializer set_password bug --- apiserver/apiserver/api/serializers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index e6f79c5..81ef20d 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -473,6 +473,11 @@ class MyPasswordChangeSerializer(PasswordChangeSerializer): logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) + data = dict( + username=self.user.username, + password=self.data['new_password1'], + ) + if utils_auth.is_configured(): if utils_auth.set_password(data) != 200: msg = 'Problem connecting to Auth server: set.' @@ -502,6 +507,11 @@ class MyPasswordResetConfirmSerializer(PasswordResetConfirmSerializer): logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) + data = dict( + username=self.user.username, + password=self.data['new_password1'], + ) + if utils_auth.is_configured(): if utils_auth.set_password(data) != 200: msg = 'Problem connecting to Auth server: set.' From 8d82b7e85bc6101a387d34fd88877196f3939cf7 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 27 Sep 2020 04:07:42 +0000 Subject: [PATCH 40/93] Don't change ldap group membership if it's not configured --- apiserver/apiserver/api/views.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 4e7ccbe..a9ba890 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -217,17 +217,19 @@ class TrainingViewSet(Base, Retrieve, Create, Update): elif session.course.id == 247: member.rabbit_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - if status == 'Attended': - utils_ldap.add_to_group(member, 'Laser Users') - else: - utils_ldap.remove_from_group(member, 'Laser Users') + if utils_ldap.is_configured(): + if status == 'Attended': + utils_ldap.add_to_group(member, 'Laser Users') + else: + utils_ldap.remove_from_group(member, 'Laser Users') elif session.course.id == 321: member.trotec_cert_date = utils.today_alberta_tz() if status == 'Attended' else None - if status == 'Attended': - utils_ldap.add_to_group(member, 'Trotec Users') - else: - utils_ldap.remove_from_group(member, 'Trotec Users') + if utils_ldap.is_configured(): + if status == 'Attended': + utils_ldap.add_to_group(member, 'Trotec Users') + else: + utils_ldap.remove_from_group(member, 'Trotec Users') member.save() # TODO: turn these into @actions From 4420a675d38929e7266d3cde0f63cf12b053d626 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 1 Oct 2020 22:14:16 +0000 Subject: [PATCH 41/93] Simplify transaction form --- apiserver/apiserver/api/serializers.py | 15 +++++++++++-- webclient/src/Transactions.js | 30 +++++++++++++------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 81ef20d..7148978 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -24,7 +24,7 @@ class TransactionSerializer(serializers.ModelSerializer): 'Square Pmt', 'Member', 'Clearing', - 'Cash' + 'Cash', ]) info_source = serializers.ChoiceField([ 'Web', @@ -38,7 +38,18 @@ class TransactionSerializer(serializers.ModelSerializer): 'IPN Trigger', 'Intranet Receipt', 'Automatic', - 'Manual' + 'Manual', + ]) + category = serializers.ChoiceField([ + 'Membership', + 'OnAcct', + 'Snacks', + 'Donation', + 'Consumables', + 'Purchases', + 'Garage Sale', + 'Reimburse', + 'Other', ]) member_id = serializers.IntegerField() member_name = serializers.SerializerMethodField() diff --git a/webclient/src/Transactions.js b/webclient/src/Transactions.js index 8b6849b..eb3e4a0 100644 --- a/webclient/src/Transactions.js +++ b/webclient/src/Transactions.js @@ -23,14 +23,14 @@ export function TransactionEditor(props) { }); const accountOptions = [ - { key: '0', text: 'Cash (CAD Lock Box)', value: 'Cash' }, + { key: '0', text: 'Cash (Lock Box)', value: 'Cash' }, { key: '1', text: 'Interac (Email) Transfer (TD)', value: 'Interac' }, - { key: '2', text: 'Square (Credit)', value: 'Square Pmt' }, - { key: '3', text: 'Dream Payments (Debit/Credit)', value: 'Dream Pmt' }, - { key: '4', text: 'Deposit to TD (Not Interac)', value: 'TD Chequing' }, - { key: '5', text: 'PayPal', value: 'PayPal' }, - { key: '6', text: 'Member Balance / Protocash', value: 'Member' }, - { key: '7', text: 'Supense (Clearing) Acct / Membership Adjustment', value: 'Clearing' }, + { key: '2', text: 'Square (Credit Card)', value: 'Square Pmt' }, + //{ key: '3', text: 'Dream Payments (Debit/Credit)', value: 'Dream Pmt' }, + { key: '4', text: 'Cheque / Deposit to TD', value: 'TD Chequing' }, + //{ key: '5', text: 'Member Balance / Protocash', value: 'Member' }, + { key: '6', text: 'Membership Adjustment / Clearing', value: 'Clearing' }, + { key: '7', text: 'PayPal', value: 'PayPal' }, ]; const sourceOptions = [ @@ -53,9 +53,9 @@ export function TransactionEditor(props) { { key: '1', text: 'Payment On Account (ie. Course Fee)', value: 'OnAcct' }, { key: '2', text: 'Snack / Pop / Coffee', value: 'Snacks' }, { key: '3', text: 'Donations', value: 'Donation' }, - { key: '4', text: 'Consumables (Specify which in memo)', value: 'Consumables' }, + { key: '4', text: 'Consumables (Explain in memo)', value: 'Consumables' }, { key: '5', text: 'Purchase of Locker / Goods / Merch / Stock', value: 'Purchases' }, - { key: '6', text: 'Auction, Garage Sale, Nearly Free Shelf', value: 'Garage Sale' }, + //{ key: '6', text: 'Auction, Garage Sale, Nearly Free Shelf', value: 'Garage Sale' }, { key: '7', text: 'Reimbursement (Enter a negative value)', value: 'Reimburse' }, { key: '8', text: 'Other (Explain in memo)', value: 'Other' }, ]; @@ -94,14 +94,14 @@ export function TransactionEditor(props) { /> - + {/* - + */} @@ -349,10 +349,10 @@ class TransactionTable extends React.Component { Account: {transaction.account_type} - + {/* Payment Method: {transaction.payment_method} - + */} Info Source: {transaction.info_source} From 7f9dd5119b5d3351f116cc63e54c3d696ef6230d Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 2 Oct 2020 23:58:45 +0000 Subject: [PATCH 42/93] Show if a member has signed up for Spaceport --- webclient/src/AdminMembers.js | 6 ++++++ webclient/src/PasswordReset.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 7b81b3c..32ba9c8 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -498,6 +498,12 @@ export function AdminMemberInfo(props) { Emergency Contact Phone: {member.emergency_contact_phone || 'None'} + + + On Spaceport: + {member.user ? 'Yes' : 'No'} + + Public Bio: diff --git a/webclient/src/PasswordReset.js b/webclient/src/PasswordReset.js index e338a95..4e6f722 100644 --- a/webclient/src/PasswordReset.js +++ b/webclient/src/PasswordReset.js @@ -48,7 +48,7 @@ function ResetForm() { Submit - {success &&
Success!
} + {success &&
Success! Be sure to check your spam folder.
} ); }; From 4afedefd89deaa279afa4fe483f75ff30b25f48f Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 3 Oct 2020 20:54:26 +0000 Subject: [PATCH 43/93] Add more detailed warning messages to signup / reset --- apiserver/apiserver/api/utils.py | 2 +- webclient/src/PasswordReset.js | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index afbcce6..4e91712 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -291,7 +291,7 @@ def link_old_member(data, user): try: member = models.Member.objects.get(old_email__iexact=data['email']) except models.Member.DoesNotExist: - msg = 'Unable to find email in old portal.' + msg = 'Unable to find email in old portal. Maybe try your other email addresses?' logger.info(msg) raise ValidationError(dict(email=msg)) except models.Member.MultipleObjectsReturned: diff --git a/webclient/src/PasswordReset.js b/webclient/src/PasswordReset.js index 4e6f722..5f8e76a 100644 --- a/webclient/src/PasswordReset.js +++ b/webclient/src/PasswordReset.js @@ -37,7 +37,7 @@ function ResetForm() { }); return ( -
+ + + Submit From 8454a8faf66878af262c5df620eb2b157bc834d8 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 20:25:56 +0000 Subject: [PATCH 44/93] Capitalize laser user's names --- apiserver/apiserver/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index a9ba890..3078c92 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -477,7 +477,7 @@ class StatsViewSet(viewsets.ViewSet, List): name = request.data['name'] username = request.data.get('username', '') - username = username.split('.')[0] + username = username.split('.')[0].title() track[name] = dict(time=time.time(), username=username) cache.set('track', track) From 127ce130a430143abfb8d753a1dfa2225b3c1c7f Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 20:26:58 +0000 Subject: [PATCH 45/93] Increase backup logging --- apiserver/apiserver/api/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 3078c92..ae78a60 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -513,14 +513,18 @@ class BackupView(views.APIView): backup_user = secrets.BACKUP_TOKENS.get(auth_token, None) if backup_user: + logger.info('Backup user: ' + backup_user['name']) backup_path = cache.get(backup_user['cache_key'], None) if not backup_path: + logger.error('Backup not found') raise Http404 if str(now().date()) not in backup_path: # sanity check - make sure it's actually today's backup - return Response('Today\'s backup not ready yet', status=400) + msg = 'Today\'s backup not ready yet' + logger.error(msg) + return Response(msg, status=503) backup_url = 'https://static.{}/backups/{}'.format( settings.PRODUCTION_HOST, From 84a479752bcfe9164c2587cac986a16a8c87c7e6 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 20:54:18 +0000 Subject: [PATCH 46/93] Allow instructors to see other member's training --- apiserver/apiserver/api/serializers.py | 19 +++++++++++++++++++ apiserver/apiserver/api/views.py | 2 ++ 2 files changed, 21 insertions(+) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 7148978..1b74b80 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -114,6 +114,7 @@ class OtherMemberSerializer(serializers.ModelSerializer): def get_status(self, obj): return 'Former Member' if obj.paused_date else obj.status + # member viewing his own details class MemberSerializer(serializers.ModelSerializer): status = serializers.SerializerMethodField() @@ -236,6 +237,24 @@ class SearchSerializer(serializers.Serializer): serializer = OtherMemberSerializer(obj) return serializer.data +# instructor viewing search result +class InstructorSearchSerializer(serializers.Serializer): + member = serializers.SerializerMethodField() + training = serializers.SerializerMethodField() + + def get_member(self, obj): + serializer = OtherMemberSerializer(obj) + return serializer.data + + def get_training(self, obj): + if obj.user: + queryset = obj.user.training + else: + queryset = models.Training.objects.filter(member_id=obj.id) + serializer = UserTrainingSerializer(data=queryset, many=True) + serializer.is_valid() + return serializer.data + # admin viewing search result class AdminSearchSerializer(serializers.Serializer): cards = serializers.SerializerMethodField() diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index ae78a60..622c14e 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -50,6 +50,8 @@ class SearchViewSet(Base, Retrieve): def get_serializer_class(self): if is_admin_director(self.request.user) and self.action == 'retrieve': return serializers.AdminSearchSerializer + elif self.request.user.member.is_instructor and self.action == 'retrieve': + return serializers.InstructorSearchSerializer else: return serializers.SearchSerializer From de68fd79b7f452bbfcdf843c02cc7c31add939b6 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 20:54:36 +0000 Subject: [PATCH 47/93] Add UI for instructions to see other member's training --- webclient/src/Members.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/webclient/src/Members.js b/webclient/src/Members.js index 89f308a..06a21c3 100644 --- a/webclient/src/Members.js +++ b/webclient/src/Members.js @@ -2,7 +2,7 @@ import React, { useState, useEffect, useReducer } from 'react'; import { BrowserRouter as Router, Switch, Route, Link, useParams } from 'react-router-dom'; import './light.css'; import { Button, Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Input, Item, Menu, Message, Segment, Table } from 'semantic-ui-react'; -import { statusColor, isAdmin, BasicTable, staticUrl, requester } from './utils.js'; +import { statusColor, isAdmin, isInstructor, BasicTable, staticUrl, requester } from './utils.js'; import { NotFound, PleaseLogin } from './Misc.js'; import { AdminMemberInfo, AdminMemberPause, AdminMemberForm, AdminMemberCards, AdminMemberTraining, AdminMemberCertifications } from './AdminMembers.js'; import { AdminMemberTransactions } from './AdminTransactions.js'; @@ -154,7 +154,7 @@ export function MemberDetail(props) {
{member.preferred_name} {member.last_name}
- +

@@ -189,7 +189,11 @@ export function MemberDetail(props) { }
- + + {isInstructor(user) && !isAdmin(user) && + + } + {isAdmin(user) && } From d5b8ffa0e2a1f3bfe65a59fab1262d219399081e Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 21:07:08 +0000 Subject: [PATCH 48/93] Link to member pages in the Instructor Attendance panel --- apiserver/apiserver/api/serializers.py | 7 +++++++ webclient/src/InstructorClasses.js | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 1b74b80..6d677ad 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -340,6 +340,7 @@ class TrainingSerializer(serializers.ModelSerializer): session = serializers.PrimaryKeyRelatedField(queryset=models.Session.objects.all()) student_name = serializers.SerializerMethodField() student_email = serializers.SerializerMethodField() + student_id = serializers.SerializerMethodField() class Meta: model = models.Training @@ -360,6 +361,12 @@ class TrainingSerializer(serializers.ModelSerializer): member = models.Member.objects.get(id=obj.member_id) return member.old_email + def get_student_id(self, obj): + if obj.user: + return obj.user.member.id + else: + return obj.member_id + class StudentTrainingSerializer(TrainingSerializer): attendance_status = serializers.ChoiceField(['Waiting for payment', 'Withdrawn']) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index a5af7b2..1ca38a1 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -99,7 +99,10 @@ function AttendanceRow(props) { return (
-

{student.student_name}{student.attendance_status === 'Waiting for payment' && ' (Waiting for payment)'}:

+

+ {student.student_name} + {student.attendance_status === 'Waiting for payment' && ' (Waiting for payment)'}: +

: - } From bf0030504a921a6308e6cd0fe04206e54f89adfd Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 5 Oct 2020 21:30:06 +0000 Subject: [PATCH 50/93] Show members application date instead of start date --- apiserver/apiserver/api/serializers.py | 1 + webclient/src/Members.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 6d677ad..69f5e6c 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -106,6 +106,7 @@ class OtherMemberSerializer(serializers.ModelSerializer): 'last_name', 'status', 'current_start_date', + 'application_date', 'photo_small', 'photo_large', 'public_bio', diff --git a/webclient/src/Members.js b/webclient/src/Members.js index 06a21c3..79698a2 100644 --- a/webclient/src/Members.js +++ b/webclient/src/Members.js @@ -107,7 +107,7 @@ export function Members(props) { {x.member.preferred_name} {x.member.last_name} Status: {x.member.status || 'Unknown'} - Joined: {x.member.current_start_date || 'Unknown'} + Joined: {x.member.application_date || 'Unknown'} ) @@ -174,7 +174,7 @@ export function MemberDetail(props) { Joined: - {member.current_start_date || 'Unknown'} + {member.application_date || 'Unknown'} Public Bio: From 982e53657f4898c392f1e676be227bfddd9ff2fa Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 8 Oct 2020 22:51:37 +0000 Subject: [PATCH 51/93] Ensure member had tour before adding cards --- webclient/src/AdminMembers.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 32ba9c8..99d906b 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -221,7 +221,15 @@ export function AdminMemberCards(props) { />
- + + + Submit {success &&
Success!
} From 7f0913006cabd624d3c0a1144b0f8091e701ebb8 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 10 Oct 2020 21:57:25 +0000 Subject: [PATCH 52/93] Improve dark mode css and contrast --- webclient/src/App.js | 1 + webclient/src/dark.css | 57 +++++++++++++++++++++++++++++++++++++++++ webclient/src/light.css | 20 --------------- 3 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 webclient/src/dark.css diff --git a/webclient/src/App.js b/webclient/src/App.js index 9a5dcc3..2172b72 100644 --- a/webclient/src/App.js +++ b/webclient/src/App.js @@ -2,6 +2,7 @@ import React, { useState, useEffect, useReducer, useContext } from 'react'; import { BrowserRouter as Router, Switch, Route, Link, useParams, useHistory } from 'react-router-dom'; import './semantic-ui/semantic.min.css'; import './light.css'; +import './dark.css'; import { Container, Divider, Dropdown, Form, Grid, Header, Icon, Image, Menu, Message, Segment, Table } from 'semantic-ui-react'; import Darkmode from 'darkmode-js'; import { isAdmin, requester } from './utils.js'; diff --git a/webclient/src/dark.css b/webclient/src/dark.css new file mode 100644 index 0000000..c2a53cf --- /dev/null +++ b/webclient/src/dark.css @@ -0,0 +1,57 @@ +.darkmode-layer, .darkmode-toggle { + z-index: 500; +} + +.darkmode--activated .ui.image { + mix-blend-mode: difference; + filter: brightness(75%); +} + +.darkmode--activated i.green.circle.icon { + mix-blend-mode: difference; + color: #21ba4582 !important; + +} + +.darkmode--activated i.yellow.circle.icon { + mix-blend-mode: difference; + color: #fbbd0882 !important; +} + +.darkmode--activated i.red.circle.icon { + mix-blend-mode: difference; + color: #db282882 !important; +} + +.darkmode--activated .footer { + mix-blend-mode: difference; +} + +.darkmode--activated .ql-toolbar.ql-snow, +.darkmode--activated .ql-container.ql-snow, +.darkmode--activated .ui.segment, +.darkmode--activated .ui.form .field input, +.darkmode--activated .ui.form .field .selection.dropdown, +.darkmode--activated .ui.form .field .ui.checkbox label::before, +.darkmode--activated .ui.form .field textarea { + border: 1px solid rgba(34,36,38,.50) !important; +} + +.darkmode--activated .ui.basic.table tbody tr { + border-bottom: 1px solid rgba(34,36,38,.50) !important; +} + +.darkmode--activated .ui.button { + background: #c9c9c9 !important; +} + +.darkmode--activated .ui.red.button { + mix-blend-mode: difference; + background: #db282882 !important; +} + +.darkmode--activated .ui.green.button, +.darkmode--activated .ui.button.toggle.active { + mix-blend-mode: difference; + background: #21ba4582 !important; +} diff --git a/webclient/src/light.css b/webclient/src/light.css index eeddb3b..999c807 100644 --- a/webclient/src/light.css +++ b/webclient/src/light.css @@ -150,23 +150,3 @@ body { margin-left: -3.5px; margin-right: 0.5em; } - - -.darkmode-layer, .darkmode-toggle { - z-index: 500; -} - -.darkmode--activated .ui.image { - mix-blend-mode: difference; - filter: brightness(50%); -} - -.darkmode--activated i.green.circle.icon, -.darkmode--activated i.yellow.circle.icon, -.darkmode--activated i.red.circle.icon { - mix-blend-mode: difference; -} - -.darkmode--activated .footer { - mix-blend-mode: difference; -} From c9482b6ec6b9172c52619024cac3d8f221ed6569 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 22 Oct 2020 21:25:31 +0000 Subject: [PATCH 53/93] Add summary of member counts to Charts page --- webclient/src/Charts.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/webclient/src/Charts.js b/webclient/src/Charts.js index 7f3c76b..7a9aca9 100644 --- a/webclient/src/Charts.js +++ b/webclient/src/Charts.js @@ -47,6 +47,25 @@ export function Charts(props) {
Charts
+
Summary
+ + {memberCount && signupCount && + <> +

+ The total member count is {memberCount.slice().reverse()[0].member_count} members, + compared to {memberCount.slice().reverse()[30].member_count} members 30 days ago. +

+

+ The green member count is {memberCount.slice().reverse()[0].green_count} members, + compared to {memberCount.slice().reverse()[30].green_count} members 30 days ago. +

+

+ There were {signupCount.slice().reverse()[0].signup_count} signups so far this month, + and {signupCount.slice().reverse()[1].signup_count} signups last month. +

+ + } +
Member Counts

Daily since March 2nd, 2020.

From 7e1385331bad6014a4b6dbfd01677b203d665edc Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 25 Oct 2020 01:30:09 +0000 Subject: [PATCH 54/93] Add fire emoji when Minecraft server is bumpin' --- webclient/src/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 7c34a13..1cc68f7 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -218,7 +218,7 @@ export function Home(props) {

Card scans today: {getZeroStat('card_scans')}

- Minecraft players: {mcPlayers.length} 5 && '🔥'}

Server IP:
From 93d3731507e2fe69fa45471188e9873fc472ea78 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 31 Oct 2020 22:53:44 +0000 Subject: [PATCH 55/93] Add Mumble user list to stats --- .../api/management/commands/run_minutely.py | 2 ++ apiserver/apiserver/api/utils_stats.py | 16 ++++++++++++++++ apiserver/apiserver/secrets.py.example | 1 + 3 files changed, 19 insertions(+) diff --git a/apiserver/apiserver/api/management/commands/run_minutely.py b/apiserver/apiserver/api/management/commands/run_minutely.py index 890b4a0..a2def88 100644 --- a/apiserver/apiserver/api/management/commands/run_minutely.py +++ b/apiserver/apiserver/api/management/commands/run_minutely.py @@ -14,6 +14,8 @@ class Command(BaseCommand): players = utils_stats.check_minecraft_server() self.stdout.write('Found Minecraft players: ' + str(players)) + users = utils_stats.check_mumble_server() + self.stdout.write('Found Mumble users: ' + str(users)) self.stdout.write('Completed tasks in {} s'.format( str(time.time() - start)[:4] diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py index fff9baf..67c4b7b 100644 --- a/apiserver/apiserver/api/utils_stats.py +++ b/apiserver/apiserver/api/utils_stats.py @@ -22,6 +22,7 @@ DEFAULTS = { 'bay_108_temp': None, 'bay_110_temp': None, 'minecraft_players': [], + 'mumble_users': [], 'card_scans': 0, 'track': {}, } @@ -114,6 +115,21 @@ def check_minecraft_server(): return [] +def check_mumble_server(): + if secrets.MUMBLE: + url = secrets.MUMBLE + + try: + r = requests.get(url, timeout=5) + r.raise_for_status() + users = r.text.split() + cache.set('mumble_users', users) + return users + except BaseException as e: + logger.error('Problem checking Mumble: {} - {}'.format(e.__class__.__name__, str(e))) + + return [] + def calc_card_scans(): date = today_alberta_tz() cards = models.Card.objects diff --git a/apiserver/apiserver/secrets.py.example b/apiserver/apiserver/secrets.py.example index c4da972..df16488 100644 --- a/apiserver/apiserver/secrets.py.example +++ b/apiserver/apiserver/secrets.py.example @@ -60,6 +60,7 @@ DOOR_API_TOKEN = '' DOOR_CODE = '' WIFI_PASS = '' MINECRAFT = '' +MUMBLE = '' # Portal Email Credentials # For sending password resets, etc. From aaa29715c67c5d6a974a53d87d4efa6564761cf9 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 31 Oct 2020 22:54:05 +0000 Subject: [PATCH 56/93] Display Mumbler users on Home page --- webclient/src/Home.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 1cc68f7..0d25fcf 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -152,6 +152,7 @@ export function Home(props) { const getDateStat = (x) => stats && stats[x] ? moment.utc(stats[x]).tz('America/Edmonton').format('ll') : '?'; const mcPlayers = stats && stats['minecraft_players'] ? stats['minecraft_players'] : []; + const mumbleUsers = stats && stats['mumble_users'] ? stats['mumble_users'] : []; const getTrackStat = (x) => stats && stats.track && stats.track[x] ? moment().unix() - stats.track[x]['time'] > 60 ? 'Free' : 'In Use' : '?'; const getTrackLast = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').format('llll') : 'Unknown'; @@ -214,7 +215,6 @@ export function Home(props) {

Next monthly clean: {getDateStat('next_clean')}

Member count: {getStat('member_count')} [more]

Green members: {getStat('green_count')}

-

Old members: {getStat('paused_count')}

Card scans today: {getZeroStat('card_scans')}

@@ -233,6 +233,21 @@ export function Home(props) { {' '}[map]

+

+ Mumble users: {mumbleUsers.length} +

+ Server IP:
+ mumble.protospace.ca +

+

+ Users:
+ {mumbleUsers.length ? mumbleUsers.map(x => {x}
) : 'None'} +

+ + } trigger={[more]} /> +

+

Trotec availability: {getTrackStat('TROTECS300')} From abcea93050f3f8f551f4b2f6d2ab66c26586a320 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 4 Nov 2020 00:39:07 +0000 Subject: [PATCH 57/93] Improve ldapserver logging --- ldapserver/gunicorn.conf.py | 10 ++++++++ ldapserver/ldap_functions.py | 34 +++++++++++++++++++-------- ldapserver/log.py | 45 ++++++++++++++++++++++++++++-------- ldapserver/requirements.txt | 1 + ldapserver/server.py | 4 ++++ 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 ldapserver/gunicorn.conf.py diff --git a/ldapserver/gunicorn.conf.py b/ldapserver/gunicorn.conf.py new file mode 100644 index 0000000..d57f600 --- /dev/null +++ b/ldapserver/gunicorn.conf.py @@ -0,0 +1,10 @@ +# Gunicorn config file +# +# By default, a file named gunicorn.conf.py will be read from the same directory where gunicorn is being run. +# Reference: https://docs.gunicorn.org/en/latest/settings.html + +import log + +logconfig_dict = log.LOG_DICT +workers = 1 +bind = ['0.0.0.0:5000'] diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 9de2772..93982d3 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -49,7 +49,7 @@ def find_user(query): criteria = '(&(objectClass=user)(|(mail={})(sAMAccountName={}))(!(objectClass=computer)))'.format(query, query) results = ldap_conn.search_s(secrets.BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email']) - logger.info(results) + logger.info(' Results: ' + str(results)) if len(results) != 1: abort(HTTP_NOTFOUND) @@ -64,10 +64,13 @@ def find_dn(dn): ''' ldap_conn = init_ldap() try: + logger.info('Finding user for dn: ' + dn) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(!(objectClass=computer)))' results = ldap_conn.search_s(dn, ldap.SCOPE_SUBTREE, criteria, ['sAMAccountName']) + logger.info(' Results: ' + str(results)) + return results[0][1]['sAMAccountName'][0].decode() finally: ldap_conn.unbind() @@ -79,6 +82,7 @@ def create_user(first, last, username, email, password): ''' ldap_conn = init_ldap() try: + logger.info('Creating user: ' + username) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) dn = 'CN={} {},{}'.format(first, last, secrets.BASE_MEMBERS) full_name = '{} {}'.format(first, last) @@ -98,7 +102,7 @@ def create_user(first, last, username, email, password): result = ldap_conn.add_s(dn, ldif) - logger.info(result) + logger.info(' Result: ' + result) # set password pass_quotes = '"{}"'.format(password) @@ -106,19 +110,20 @@ def create_user(first, last, username, email, password): change_des = [(ldap.MOD_REPLACE, 'unicodePwd', [pass_uni])] result = ldap_conn.modify_s(dn, change_des) - logger.info(result) + logger.info(' Result: ' + result) # 512 will set user account to enabled mod_acct = [(ldap.MOD_REPLACE, 'userAccountControl', b'512')] result = ldap_conn.modify_s(dn, mod_acct) - logger.info(result) + logger.info(' Result: ' + result) finally: ldap_conn.unbind() def set_password(username, password): ldap_conn = init_ldap() try: + logger.info('Setting password for: ' + username) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) criteria = '(&(objectClass=user)(sAMAccountName={})(!(objectClass=computer)))'.format(username) results = ldap_conn.search_s(secrets.BASE_MEMBERS, ldap.SCOPE_SUBTREE, criteria, ['displayName','sAMAccountName','email'] ) @@ -128,11 +133,15 @@ def set_password(username, password): dn = results[0][0] + logger.info(' Dn found: ' + dn) + # set password pass_quotes = '"{}"'.format(password) pass_uni = pass_quotes.encode('utf-16-le') change_des = [(ldap.MOD_REPLACE, 'unicodePwd', [pass_uni])] result = ldap_conn.modify_s(dn, change_des) + + logger.info(' Set password result: ' + str(result)) finally: ldap_conn.unbind() @@ -147,7 +156,7 @@ def find_group(groupname): criteria = '(&(objectClass=group)(sAMAccountName={}))'.format(groupname) results = ldap_conn.search_s(secrets.BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['name','groupType'] ) - logger.info(results) + logger.info(' Results: ' + str(results)) if len(results) != 1: abort(HTTP_NOTFOUND) @@ -185,6 +194,7 @@ def add_to_group(groupname, username): ''' ldap_conn = init_ldap() try: + logger.info('Adding ' + username + ' to group: ' + groupname) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) user_dn = find_user(username) group_dn = find_group(groupname) @@ -192,9 +202,10 @@ def add_to_group(groupname, username): if not is_member(groupname, username): mod_acct = [(ldap.MOD_ADD, 'member', user_dn.encode())] ldap_conn.modify_s(group_dn, mod_acct) + logger.info(' Added.') return True else: - logger.info('Already a member, skipping') + logger.info(' Already a member, skipping.') return False finally: @@ -245,7 +256,7 @@ def is_member(groupname, username): ''' ldap_conn = init_ldap() try: - logger.info('Checking group membership...') + logger.info('Checking if ' + username + ' is in group: ' + groupname) ldap_conn.simple_bind_s(secrets.LDAP_USERNAME, secrets.LDAP_PASSWORD) group_dn = find_group(groupname) user_dn = find_user(username).encode() @@ -254,7 +265,10 @@ def is_member(groupname, username): results = ldap_conn.search_s(secrets.BASE_GROUPS, ldap.SCOPE_SUBTREE, criteria, ['member'] ) members_tmp = results[0][1] members = members_tmp.get('member', []) - return user_dn in members + result = user_dn in members + + logger.info(' Result: ' + str(result)) + return result finally: ldap_conn.unbind() @@ -292,7 +306,7 @@ def dump_users(): if __name__ == '__main__': pass #print(create_user('Elon', 'Tusk', 'elon.tusk', 'elont@example.com', 'protospace*&^g87g6')) - #print(find_user('test.testerb')) + #print(find_user('tanner.collin')) #print(set_password('tanner.collin', 'Supersecret@@')) #print(find_dn('CN=Tanner Collin,OU=MembersOU,DC=ps,DC=protospace,DC=ca')) #print("============================================================") @@ -308,7 +322,7 @@ if __name__ == '__main__': #print(" ============== ") #print(remove_from_group('newgroup','tanner.collin')) #print(" ============== ") - #print(list_group("newgroup")) + print(list_group('Trotec Users')) #print(dump_users()) #users = list_group('Laser Users') diff --git a/ldapserver/log.py b/ldapserver/log.py index 23cd69e..7d037f7 100644 --- a/ldapserver/log.py +++ b/ldapserver/log.py @@ -1,22 +1,47 @@ import logging import logging.config -logging.config.dictConfig({ +LOG_DICT = { 'version': 1, - 'formatters': {'default': { - 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', - }}, - 'handlers': {'wsgi': { - 'class': 'logging.StreamHandler', - 'stream': 'ext://flask.logging.wsgi_errors_stream', - 'formatter': 'default' - }}, + 'formatters': { + 'default': { + 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', + }, + }, + 'handlers': { + 'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'formatter': 'default' + }, + 'null': { + 'level': 'DEBUG', + 'class': 'logging.NullHandler', + 'formatter': 'default' + }, + }, + 'loggers': { + 'gunicorn': { + 'handlers': ['console'], + 'level': 'DEBUG', + 'propagate': False, + }, + }, 'root': { 'level': 'INFO', 'handlers': ['wsgi'] } -}) +} +logging.config.dictConfig(LOG_DICT) logger = logging.getLogger(__name__) logger.info('Logging enabled.') + +from logging_tree import printout +printout() diff --git a/ldapserver/requirements.txt b/ldapserver/requirements.txt index bb11606..29aa6a7 100644 --- a/ldapserver/requirements.txt +++ b/ldapserver/requirements.txt @@ -3,6 +3,7 @@ Flask==1.1.1 gunicorn==20.0.4 itsdangerous==1.1.0 Jinja2==2.11.1 +logging-tree==1.8.1 MarkupSafe==1.1.1 pyasn1==0.4.8 pyasn1-modules==0.2.8 diff --git a/ldapserver/server.py b/ldapserver/server.py index f7f43d2..eba4a9c 100644 --- a/ldapserver/server.py +++ b/ldapserver/server.py @@ -1,3 +1,5 @@ +from log import logger + from flask import Flask, abort, request app = Flask(__name__) @@ -13,6 +15,8 @@ def check_auth(): @app.route('/') def index(): + logger.info('Index page requested') + return 'SEE YOU SPACE SAMURAI...' @app.route('/find-user', methods=['POST']) From d31d1a78d0b8d1227fa5e041f03d8a0275059e81 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 4 Nov 2020 04:03:55 +0000 Subject: [PATCH 58/93] Fix logging bug --- ldapserver/ldap_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ldapserver/ldap_functions.py b/ldapserver/ldap_functions.py index 93982d3..192a087 100644 --- a/ldapserver/ldap_functions.py +++ b/ldapserver/ldap_functions.py @@ -102,7 +102,7 @@ def create_user(first, last, username, email, password): result = ldap_conn.add_s(dn, ldif) - logger.info(' Result: ' + result) + logger.info(' Result: ' + str(result)) # set password pass_quotes = '"{}"'.format(password) @@ -110,13 +110,13 @@ def create_user(first, last, username, email, password): change_des = [(ldap.MOD_REPLACE, 'unicodePwd', [pass_uni])] result = ldap_conn.modify_s(dn, change_des) - logger.info(' Result: ' + result) + logger.info(' Result: ' + str(result)) # 512 will set user account to enabled mod_acct = [(ldap.MOD_REPLACE, 'userAccountControl', b'512')] result = ldap_conn.modify_s(dn, mod_acct) - logger.info(' Result: ' + result) + logger.info(' Result: ' + str(result)) finally: ldap_conn.unbind() From e8198f7b2abe84e48d98ec260ee16122b9c98b26 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sat, 7 Nov 2020 21:27:10 +0000 Subject: [PATCH 59/93] Filter logging /lockout/ --- apiserver/apiserver/filters.py | 7 +++++++ apiserver/apiserver/settings.py | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apiserver/apiserver/filters.py b/apiserver/apiserver/filters.py index 6f50e0b..7ec211b 100644 --- a/apiserver/apiserver/filters.py +++ b/apiserver/apiserver/filters.py @@ -8,3 +8,10 @@ class IgnoreStats(logging.Filter): return False else: return True + +class IgnoreLockout(logging.Filter): + def filter(self, record): + if 'GET /lockout/' in record.msg: + return False + else: + return True diff --git a/apiserver/apiserver/settings.py b/apiserver/apiserver/settings.py index 7e533b2..f5dfbf8 100644 --- a/apiserver/apiserver/settings.py +++ b/apiserver/apiserver/settings.py @@ -221,11 +221,14 @@ LOGGING = { 'ignore_stats': { '()': 'apiserver.filters.IgnoreStats', }, + 'ignore_lockout': { + '()': 'apiserver.filters.IgnoreLockout', + }, }, 'handlers': { 'console': { 'level': 'DEBUG', - 'filters': ['ignore_stats'], + 'filters': ['ignore_stats', 'ignore_lockout'], 'class': 'logging.StreamHandler', 'formatter': 'medium' }, From 19e2d250dee090a674167264598b2cc71ee3c68c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 12 Nov 2020 23:38:14 +0000 Subject: [PATCH 60/93] Add /ping route to ldapserver --- ldapserver/server.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ldapserver/server.py b/ldapserver/server.py index eba4a9c..881395e 100644 --- a/ldapserver/server.py +++ b/ldapserver/server.py @@ -19,6 +19,10 @@ def index(): return 'SEE YOU SPACE SAMURAI...' +@app.route('/ping') +def ping(): + return 'pong' + @app.route('/find-user', methods=['POST']) def find_user(): check_auth() From 3f4cd0e3f5ed4181fb91c340d219de80550a1075 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 12 Nov 2020 23:38:24 +0000 Subject: [PATCH 61/93] Ignore logging of ldapserver /ping route --- ldapserver/log.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ldapserver/log.py b/ldapserver/log.py index 7d037f7..332246b 100644 --- a/ldapserver/log.py +++ b/ldapserver/log.py @@ -1,6 +1,10 @@ import logging import logging.config +class IgnorePing(logging.Filter): + def filter(self, record): + return 'GET /ping' not in record.getMessage() + LOG_DICT = { 'version': 1, 'formatters': { @@ -8,19 +12,27 @@ LOG_DICT = { 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', }, }, + 'filters': { + 'ignore_ping': { + '()': 'log.IgnorePing', + }, + }, 'handlers': { 'wsgi': { 'class': 'logging.StreamHandler', + 'filters': ['ignore_ping'], 'stream': 'ext://flask.logging.wsgi_errors_stream', 'formatter': 'default' }, 'console': { 'level': 'DEBUG', + 'filters': ['ignore_ping'], 'class': 'logging.StreamHandler', 'formatter': 'default' }, 'null': { 'level': 'DEBUG', + 'filters': ['ignore_ping'], 'class': 'logging.NullHandler', 'formatter': 'default' }, From 03ebe8c7aaec6ec878952973a455ca2cb7c84b60 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 00:53:07 +0000 Subject: [PATCH 62/93] Improve development docs --- apiserver/docs/source/dev.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apiserver/docs/source/dev.rst b/apiserver/docs/source/dev.rst index 450c7b9..63a6235 100644 --- a/apiserver/docs/source/dev.rst +++ b/apiserver/docs/source/dev.rst @@ -13,7 +13,7 @@ Install dependencies: $ sudo apt install memcached # Python: - $ sudo apt install build-essential python3 python3-dev python3-pip python-virtualenv python3-virtualenv + $ sudo apt install build-essential python3 python3-dev python3-pip python3-virtualenv # Yarn / nodejs: # from https://yarnpkg.com/lang/en/docs/install/#debian-stable @@ -111,7 +111,7 @@ Point a domain to the server and reverse proxy requests according to subdomain. Domains: `portal.example.com`, `api.portal.example.com`, `static.portal.example.com`, `docs.portal.example.com` should all be reverse proxied. -Configure nginx: +Configure nginx (`/etc/nginx/sites-available/default`): .. sourcecode:: text @@ -185,7 +185,7 @@ Install certbot and run it: .. sourcecode:: bash - $ sudo apt install certbot python-certbot-nginx + $ sudo apt install certbot python3-certbot-nginx $ sudo certbot --nginx Answer the prompts, enable redirect. From 928a07d29c8f9d99b8b2366332539e4264de175c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 01:44:36 +0000 Subject: [PATCH 63/93] Display yellow banner when on development site --- webclient/src/App.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webclient/src/App.js b/webclient/src/App.js index 2172b72..c3d52e9 100644 --- a/webclient/src/App.js +++ b/webclient/src/App.js @@ -109,6 +109,10 @@ function App() {

+ + {window.location.hostname !== 'my.protospace.ca' && +

~~~~~ Development site ~~~~~

+ } From ed8df1394654e1dcf474d21d51d4f31f9593c3ac Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 01:45:07 +0000 Subject: [PATCH 64/93] Add django command to delete unused static files --- .../management/commands/delete_old_static.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 apiserver/apiserver/api/management/commands/delete_old_static.py diff --git a/apiserver/apiserver/api/management/commands/delete_old_static.py b/apiserver/apiserver/api/management/commands/delete_old_static.py new file mode 100644 index 0000000..9f77677 --- /dev/null +++ b/apiserver/apiserver/api/management/commands/delete_old_static.py @@ -0,0 +1,50 @@ +from django.core.management.base import BaseCommand, CommandError +from django.utils.timezone import now + +from apiserver import settings +from apiserver.api import models, utils, utils_stats + +import time +import os + +if settings.DEBUG: + STATIC_FOLDER = './data/static/' +else: + STATIC_FOLDER = '/opt/spaceport/apiserver/data/static/' + +class Command(BaseCommand): + help = 'Delete unused static assets' + + def delete_old_static(self): + members = models.Member.objects + + good_files = [] + for static_field in ['photo_large', 'photo_medium', 'photo_small', 'member_forms', 'card_photo']: + good_files.extend(members.values_list(static_field, flat=True)) + + count = 0 + for f in os.listdir(STATIC_FOLDER): + if len(f) != 40: + self.stdout.write('Skipping: ' + f) + continue + + if f[-3:] not in ['jpg', 'pdf', 'png']: + self.stdout.write('Skipping: ' + f) + continue + + if f not in good_files: + os.remove(STATIC_FOLDER + f) + count += 1 + + return count + + def handle(self, *args, **options): + self.stdout.write('{} - Deleting unused static files'.format(str(now()))) + start = time.time() + + count = self.delete_old_static() + self.stdout.write('Deleted {} files'.format(count)) + + self.stdout.write('Completed deletion in {} s'.format( + str(time.time() - start)[:4] + )) From 4bc2965f6021ee42e678185bfb36c6949874107c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 01:47:04 +0000 Subject: [PATCH 65/93] Increase ldap api call timeout --- apiserver/apiserver/api/utils_ldap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apiserver/apiserver/api/utils_ldap.py b/apiserver/apiserver/api/utils_ldap.py index 62ad5ee..633d6d6 100644 --- a/apiserver/apiserver/api/utils_ldap.py +++ b/apiserver/apiserver/api/utils_ldap.py @@ -14,7 +14,7 @@ def ldap_api(route, data): try: headers = {'Authorization': 'Token ' + secrets.LDAP_API_KEY} url = secrets.LDAP_API_URL + route - r = requests.post(url, data=data, headers=headers, timeout=3) + r = requests.post(url, data=data, headers=headers, timeout=5) return r.status_code except BaseException as e: logger.error('LDAP {} - {} - {}'.format(url, e.__class__.__name__, str(e))) From 67a019811b4844bea6fd5ab6c2e129556f5d82e7 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 04:35:16 +0000 Subject: [PATCH 66/93] Generate card photos on the fly instead of saving files --- .../api/management/commands/delete_old_static.py | 2 +- apiserver/apiserver/api/models.py | 1 - apiserver/apiserver/api/utils.py | 7 ++++--- apiserver/apiserver/api/views.py | 10 +++++++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apiserver/apiserver/api/management/commands/delete_old_static.py b/apiserver/apiserver/api/management/commands/delete_old_static.py index 9f77677..c123654 100644 --- a/apiserver/apiserver/api/management/commands/delete_old_static.py +++ b/apiserver/apiserver/api/management/commands/delete_old_static.py @@ -19,7 +19,7 @@ class Command(BaseCommand): members = models.Member.objects good_files = [] - for static_field in ['photo_large', 'photo_medium', 'photo_small', 'member_forms', 'card_photo']: + for static_field in ['photo_large', 'photo_medium', 'photo_small', 'member_forms']: good_files.extend(members.values_list(static_field, flat=True)) count = 0 diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py index a681355..faf6459 100644 --- a/apiserver/apiserver/api/models.py +++ b/apiserver/apiserver/api/models.py @@ -21,7 +21,6 @@ class Member(models.Model): photo_medium = models.CharField(max_length=64, blank=True, null=True) photo_small = models.CharField(max_length=64, blank=True, null=True) member_forms = models.CharField(max_length=64, blank=True, null=True) - card_photo = models.CharField(max_length=64, blank=True, null=True) set_details = models.BooleanField(default=False) first_name = models.CharField(max_length=32) diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index 4e91712..37dec74 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -247,10 +247,11 @@ def gen_card_photo(member): y = CARD_PHOTO_MARGIN_SIDE draw.text((475, y), str(member.id), (0,0,0), font=font) - file_name = str(uuid4()) + '.jpg' - card_template.save(STATIC_FOLDER + file_name, quality=95) + bio = io.BytesIO() + card_template.save(bio, 'JPEG', quality=95) + bio.seek(0) - return file_name + return bio ALLOWED_TAGS = [ diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 622c14e..caa2e83 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -4,7 +4,7 @@ logger = logging.getLogger(__name__) from django.contrib.auth.models import User, Group from django.shortcuts import get_object_or_404, redirect from django.db.models import Max -from django.http import HttpResponse, Http404 +from django.http import HttpResponse, Http404, FileResponse from django.core.files.base import File from django.core.cache import cache from django.utils.timezone import now @@ -145,6 +145,14 @@ class MemberViewSet(Base, Retrieve, Update): utils_stats.changed_card() return Response(200) + @action(detail=True, methods=['get']) + def card_photo(self, request, pk=None): + if not is_admin_director(self.request.user): + raise exceptions.PermissionDenied() + member = self.get_object() + card_photo = utils.gen_card_photo(member) + return FileResponse(card_photo, filename='card.jpg') + class CardViewSet(Base, Create, Retrieve, Update, Destroy): permission_classes = [AllowMetadata | IsAdmin] From 5ad65470284f839b1b0967caaffc59a62498c102 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 04:36:41 +0000 Subject: [PATCH 67/93] Get card photo as blob in UI --- webclient/src/AdminMembers.js | 22 +++++++++++++++++----- webclient/src/utils.js | 12 +++++++++++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/webclient/src/AdminMembers.js b/webclient/src/AdminMembers.js index 99d906b..79411a1 100644 --- a/webclient/src/AdminMembers.js +++ b/webclient/src/AdminMembers.js @@ -123,7 +123,7 @@ export function AdminMemberCards(props) { const [error, setError] = useState(false); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); - const [viewCard, setViewCard] = useState(false); + const [cardPhoto, setCardPhoto] = useState(false); const { id } = useParams(); useEffect(() => { @@ -155,6 +155,18 @@ export function AdminMemberCards(props) { }); }; + const getCardPhoto = (e) => { + e.preventDefault(); + requester('/members/' + id + '/card_photo/', 'GET', token) + .then(res => res.blob()) + .then(res => { + setCardPhoto(URL.createObjectURL(res)); + }) + .catch(err => { + console.log(err); + }); + }; + const makeProps = (name) => ({ name: name, onChange: handleChange, @@ -176,17 +188,17 @@ export function AdminMemberCards(props) {
Add a Card
- {result.member.card_photo ? + {result.member.photo_large ?

- +

:

No card image, member photo missing!

} - {viewCard && <> + {cardPhoto && <>

- +

How to Print a Card
diff --git a/webclient/src/utils.js b/webclient/src/utils.js index 6140508..5585257 100644 --- a/webclient/src/utils.js +++ b/webclient/src/utils.js @@ -70,7 +70,17 @@ export const requester = (route, method, token, data) => { if (!response.ok) { throw customError(response); } - return method === 'DELETE' ? {} : response.json(); + + if (method === 'DELETE') { + return {}; + } + + const contentType = response.headers.get('content-type'); + if (contentType && contentType.indexOf('application/json') !== -1) { + return response.json(); + } else { + return response; + } }) .catch(error => { const code = error.data ? error.data.status : null; From e69a65ae0b1eafc1e7f7843ad9bf4aa2d8079c3e Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 05:10:28 +0000 Subject: [PATCH 68/93] Stop generating card files --- apiserver/apiserver/api/serializers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 69f5e6c..77a629c 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -178,7 +178,6 @@ class MemberSerializer(serializers.ModelSerializer): instance.photo_small = small instance.photo_medium = medium instance.photo_large = large - instance.card_photo = utils.gen_card_photo(instance) return super().update(instance, validated_data) From d56a530312a217b856f9e47282bba709b166c82d Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 05:10:52 +0000 Subject: [PATCH 69/93] Add some limits to class cost and student count --- apiserver/apiserver/api/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index 77a629c..c3e8901 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -379,6 +379,8 @@ class SessionSerializer(serializers.ModelSerializer): datetime = serializers.DateTimeField() course = serializers.PrimaryKeyRelatedField(queryset=models.Course.objects.all()) students = TrainingSerializer(many=True, read_only=True) + max_students = serializers.IntegerField(min_value=1, max_value=50, allow_null=True) + cost = serializers.DecimalField(max_digits=None, decimal_places=2, min_value=0, max_value=200) class Meta: model = models.Session From a083a7814feec72405e00294a77e0b889b7684f4 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 05:33:15 +0000 Subject: [PATCH 70/93] Add limits to possible monthly_fees --- apiserver/apiserver/api/serializers.py | 1 + apiserver/apiserver/api/views.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index c3e8901..f74fee2 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -187,6 +187,7 @@ class AdminMemberSerializer(MemberSerializer): street_address = serializers.CharField(required=False) city = serializers.CharField(required=False) postal_code = serializers.CharField(required=False) + monthly_fees = serializers.ChoiceField([10, 30, 35, 50, 55]) class Meta: model = models.Member diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index caa2e83..0625279 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -139,6 +139,8 @@ class MemberViewSet(Base, Retrieve, Update): member = self.get_object() member.current_start_date = utils.today_alberta_tz() member.paused_date = None + if not member.monthly_fees: + member.monthly_fees = 55 member.save() utils.tally_membership_months(member) utils.gen_member_forms(member) From 944be50dbf6347659cbf108a19ce0dbf21193b43 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 23:01:55 +0000 Subject: [PATCH 71/93] Add checks to card photo generation --- apiserver/apiserver/api/utils.py | 8 ++++---- apiserver/apiserver/api/views.py | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index 37dec74..1b23cf2 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -229,21 +229,21 @@ def gen_card_photo(member): # check font size font_sizes = (60, 72) font = ImageFont.truetype('DejaVuSans-Bold.ttf', font_sizes[1]) - size = draw.textsize(member.last_name, font=font) + size = draw.textsize(str(member.last_name), font=font) if size[0] > CARD_TEXT_SIZE_LIMIT: font_sizes = (36, 48) font = ImageFont.truetype('DejaVuSans.ttf', font_sizes[0]) x = CARD_PHOTO_MARGIN_SIDE y = my + CARD_PHOTO_MARGIN_TOP + CARD_PHOTO_MARGIN_SIDE - draw.text((x, y), member.first_name, (0,0,0), font=font) + draw.text((x, y), str(member.first_name), (0,0,0), font=font) font = ImageFont.truetype('DejaVuSans-Bold.ttf', font_sizes[1]) y = my + CARD_PHOTO_MARGIN_TOP + CARD_PHOTO_MARGIN_SIDE + font_sizes[1] - draw.text((x, y), member.last_name, (0,0,0), font=font) + draw.text((x, y), str(member.last_name), (0,0,0), font=font) font = ImageFont.truetype('DejaVuSans.ttf', 36) - draw.text((x, 800), 'Joined: ' + str(member.application_date), (0,0,0), font=font) + draw.text((x, 800), 'Joined: ' + str(member.application_date or 'Unknown'), (0,0,0), font=font) y = CARD_PHOTO_MARGIN_SIDE draw.text((475, y), str(member.id), (0,0,0), font=font) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 0625279..e4e1282 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -152,6 +152,8 @@ class MemberViewSet(Base, Retrieve, Update): if not is_admin_director(self.request.user): raise exceptions.PermissionDenied() member = self.get_object() + if not member.photo_large: + raise Http404 card_photo = utils.gen_card_photo(member) return FileResponse(card_photo, filename='card.jpg') From a87993b9defcfa83ea669f5a978ee11d7f0199e8 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 16:30:24 -0700 Subject: [PATCH 72/93] Correct blank member form font import --- apiserver/misc/blank_member_form.pdf | Bin 45605 -> 43036 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apiserver/misc/blank_member_form.pdf b/apiserver/misc/blank_member_form.pdf index da7ef9c2f863c641e2df1bef96ff53b7cb438370..73c514a6e0e4a0dde73bab37b196284dae3b706a 100644 GIT binary patch delta 36174 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 From 3b412d35e4c51dac345a6bc50b153dbabc0391c6 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 15 Nov 2020 17:20:03 -0700 Subject: [PATCH 73/93] Update member form template --- apiserver/misc/blank_member_form.odg | Bin 0 -> 25888 bytes apiserver/misc/blank_member_form.pdf | Bin 43036 -> 43407 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 apiserver/misc/blank_member_form.odg diff --git a/apiserver/misc/blank_member_form.odg b/apiserver/misc/blank_member_form.odg new file mode 100644 index 0000000000000000000000000000000000000000..c2c205f771540785e12b0a79eaa90589f047c84d GIT binary patch literal 25888 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 delta 38702 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

W#TLfX9Pv6qkM4mQL9b71w$T0vGJWy^3LpLW= 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�VDN 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 Date: Sun, 15 Nov 2020 17:31:00 -0700 Subject: [PATCH 74/93] Update member form template --- apiserver/misc/blank_member_form.odg | Bin 25888 -> 25131 bytes apiserver/misc/blank_member_form.pdf | Bin 43407 -> 43951 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apiserver/misc/blank_member_form.odg b/apiserver/misc/blank_member_form.odg index c2c205f771540785e12b0a79eaa90589f047c84d..998b1a03fcdf0a8b48b1129bc7b2370587b69836 100644 GIT binary patch delta 23490 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 delta 24222 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 Qpxj9q7SDdq=Knzd2RcvCEdT%j diff --git a/apiserver/misc/blank_member_form.pdf b/apiserver/misc/blank_member_form.pdf index 3e39d0e0a0948fdfb8630416b8eb5ab3f2ed2b3e..d4a3fbf08b0b691184c6b1cdc65c59e28187cf2a 100644 GIT binary patch delta 31580 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

euczJy1T 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 Date: Mon, 16 Nov 2020 02:33:05 +0000 Subject: [PATCH 75/93] Adjust member form generation --- apiserver/apiserver/api/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/utils.py b/apiserver/apiserver/api/utils.py index 1b23cf2..fb3b289 100644 --- a/apiserver/apiserver/api/utils.py +++ b/apiserver/apiserver/api/utils.py @@ -389,7 +389,6 @@ def gen_member_forms(member): packet = io.BytesIO() can = canvas.Canvas(packet, pagesize=letter) - can.drawString(75, 775, '[ ] Paid [ ] Sponsored & Approved [ ] Vetted [ ] Got Card') can.drawString(34, 683, data['first_name']) can.drawString(218, 683, data['last_name']) can.drawString(403, 683, data['preferred_name']) @@ -403,7 +402,7 @@ def gen_member_forms(member): packet = io.BytesIO() can = canvas.Canvas(packet, pagesize=letter) - can.drawRightString(600, 775, '{} {} ({})'.format( + can.drawRightString(600, 770, '{} {} ({})'.format( data['first_name'], data['last_name'], data['id'], From c3238f8b75fbf3f3d603cb17132bcf817f8f7263 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Mon, 16 Nov 2020 02:51:18 +0000 Subject: [PATCH 76/93] Add some thank-yous --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ddc7daf..1e8173b 100644 --- a/README.md +++ b/README.md @@ -22,4 +22,6 @@ That means you have the right to study, change, and distribute the software and Thanks to the Protospace Portal Committee. +Thanks to Emrah for lockout certification code, Pat for LDAP code, and Murray for the blank member form PDF. + Thanks to all the devs behind Python, Django, DRF, Node, React, Quill, and Bleach. From 750eed0fb6e4db525edf892fbfcb4138ec120ea5 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 17 Nov 2020 08:14:56 +0000 Subject: [PATCH 77/93] Add API route for logging alarm light value --- apiserver/apiserver/api/views.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index e4e1282..6e016b1 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -484,6 +484,17 @@ class StatsViewSet(viewsets.ViewSet, List): except KeyError: raise exceptions.ValidationError(dict(data='This field is required.')) + @action(detail=False, methods=['post']) + def alarm(self, request): + try: + logging.info('Alarm value: ' + str(request.data['data'])) + #cache.set('alarm', int(request.data['data'])) + return Response(200) + except ValueError: + raise exceptions.ValidationError(dict(data='Invalid integer.')) + except KeyError: + raise exceptions.ValidationError(dict(data='This field is required.')) + @action(detail=False, methods=['post']) def track(self, request): if 'name' in request.data: From d990a64efb9fb6f5e40964fbd52eedfa796af6e3 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 17 Nov 2020 20:25:25 +0000 Subject: [PATCH 78/93] Return alarm light value over /stats/ API --- apiserver/apiserver/api/utils_stats.py | 1 + apiserver/apiserver/api/views.py | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py index 67c4b7b..e81eeec 100644 --- a/apiserver/apiserver/api/utils_stats.py +++ b/apiserver/apiserver/api/utils_stats.py @@ -25,6 +25,7 @@ DEFAULTS = { 'mumble_users': [], 'card_scans': 0, 'track': {}, + 'alarm': {}, } def changed_card(): diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index 6e016b1..efac0d0 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -462,6 +462,11 @@ class StatsViewSet(viewsets.ViewSet, List): cached_stats = cache.get_many(stats_keys) stats = utils_stats.DEFAULTS.copy() stats.update(cached_stats) + + user = self.request.user + if not user.is_authenticated or not user.member.vetted_date: + stats.pop('alarm', None) + return Response(stats) @action(detail=False, methods=['post']) @@ -487,8 +492,8 @@ class StatsViewSet(viewsets.ViewSet, List): @action(detail=False, methods=['post']) def alarm(self, request): try: - logging.info('Alarm value: ' + str(request.data['data'])) - #cache.set('alarm', int(request.data['data'])) + alarm = dict(time=time.time(), data=int(request.data['data'])) + cache.set('alarm', alarm) return Response(200) except ValueError: raise exceptions.ValidationError(dict(data='Invalid integer.')) From e925a184c3d16ef51f5214540bb1251f6dc7e6eb Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 17 Nov 2020 20:27:12 +0000 Subject: [PATCH 79/93] Display alarm status on home page --- webclient/src/Home.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 0d25fcf..8b6f54b 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -128,7 +128,7 @@ function MemberInfo(props) { }; export function Home(props) { - const { user } = props; + const { user, token } = props; const [stats, setStats] = useState(JSON.parse(localStorage.getItem('stats', 'false'))); const [refreshCount, refreshStats] = useReducer(x => x + 1, 0); const location = useLocation(); @@ -136,7 +136,7 @@ export function Home(props) { const bypass_code = location.hash.replace('#', ''); useEffect(() => { - requester('/stats/', 'GET') + requester('/stats/', 'GET', token) .then(res => { setStats(res); localStorage.setItem('stats', JSON.stringify(res)); @@ -159,6 +159,8 @@ export function Home(props) { const getTrackAgo = (x) => stats && stats.track && stats.track[x] ? moment.unix(stats.track[x]['time']).tz('America/Edmonton').fromNow() : ''; const getTrackName = (x) => stats && stats.track && stats.track[x] && stats.track[x]['username'] ? stats.track[x]['username'] : 'Unknown'; + const alarmStat = () => stats && stats.alarm && moment().unix() - stats.alarm['time'] < 300 ? stats.alarm['data'] > 200 ? 'Armed' : 'Disarmed' : 'Unknown'; + return ( @@ -273,6 +275,8 @@ export function Home(props) { } trigger={[more]} />

+ + {user && user.member.vetted_date &&

Alarm status: {alarmStat()}

} From e8878fc02e0f814906d20820d9f62e1ecc426ee9 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 18 Nov 2020 08:47:22 +0000 Subject: [PATCH 80/93] Refresh stats on login/logout --- webclient/src/Home.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/Home.js b/webclient/src/Home.js index 8b6f54b..429ed37 100644 --- a/webclient/src/Home.js +++ b/webclient/src/Home.js @@ -145,7 +145,7 @@ export function Home(props) { console.log(err); setStats(false); }); - }, [refreshCount]); + }, [refreshCount, token]); const getStat = (x) => stats && stats[x] ? stats[x] : '?'; const getZeroStat = (x) => stats && stats[x] ? stats[x] : '0'; From 4b74bc8ade7c60ba01d2ead5f325cd2140067db1 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 18 Nov 2020 23:54:44 +0000 Subject: [PATCH 81/93] Add stats for number of members older than six months --- apiserver/apiserver/api/management/commands/run_hourly.py | 8 ++++++-- apiserver/apiserver/api/models.py | 1 + apiserver/apiserver/api/utils_stats.py | 7 +++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apiserver/apiserver/api/management/commands/run_hourly.py b/apiserver/apiserver/api/management/commands/run_hourly.py index 443bb34..57ad634 100644 --- a/apiserver/apiserver/api/management/commands/run_hourly.py +++ b/apiserver/apiserver/api/management/commands/run_hourly.py @@ -9,13 +9,17 @@ class Command(BaseCommand): def generate_stats(self): utils_stats.calc_next_events() - member_count, green_count = utils_stats.calc_member_counts() + member_count, green_count, six_month_plus_count = utils_stats.calc_member_counts() signup_count = utils_stats.calc_signup_counts() # do this hourly in case an admin causes a change models.StatsMemberCount.objects.update_or_create( date=utils.today_alberta_tz(), - defaults=dict(member_count=member_count, green_count=green_count), + defaults=dict( + member_count=member_count, + green_count=green_count, + six_month_plus_count=six_month_plus_count, + ), ) models.StatsSignupCount.objects.update_or_create( diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py index faf6459..70c437c 100644 --- a/apiserver/apiserver/api/models.py +++ b/apiserver/apiserver/api/models.py @@ -142,6 +142,7 @@ class StatsMemberCount(models.Model): date = models.DateField(default=today_alberta_tz) member_count = models.IntegerField() green_count = models.IntegerField() + six_month_plus_count = models.IntegerField() class StatsSignupCount(models.Model): month = models.DateField() diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py index e81eeec..ddfd37d 100644 --- a/apiserver/apiserver/api/utils_stats.py +++ b/apiserver/apiserver/api/utils_stats.py @@ -2,7 +2,7 @@ import logging logger = logging.getLogger(__name__) import time -from datetime import date, datetime +from datetime import date, datetime, timedelta import requests from django.core.cache import cache from django.utils.timezone import now, pytz @@ -64,11 +64,14 @@ def calc_member_counts(): paused_count = members.count() - member_count green_count = num_current + num_prepaid + six_months_ago = today_alberta_tz() - timedelta(days=183) + six_month_plus_count = not_paused.filter(application_date__lte=six_months_ago).count() + cache.set('member_count', member_count) cache.set('paused_count', paused_count) cache.set('green_count', green_count) - return member_count, green_count + return member_count, green_count, six_month_plus_count def calc_signup_counts(): month_beginning = today_alberta_tz().replace(day=1) From af68f6b9415b2d6c5a9858e375ec2e5c1d9ac0e1 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 18 Nov 2020 23:57:37 +0000 Subject: [PATCH 82/93] Add script to import historical number of members older than six months --- apiserver/import_six_month_plus_count.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 apiserver/import_six_month_plus_count.py diff --git a/apiserver/import_six_month_plus_count.py b/apiserver/import_six_month_plus_count.py new file mode 100755 index 0000000..64c3851 --- /dev/null +++ b/apiserver/import_six_month_plus_count.py @@ -0,0 +1,21 @@ +# Expects a old_counts.csv of the historical counts in format: +# date,six_month_plus_count + +import django, sys, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings' +django.setup() + +import csv +from apiserver.api import models + +with open('old_counts.csv', newline='') as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + print('Adding', row['date'], row['six_month_plus_count']) + + models.StatsMemberCount.objects.update_or_create( + date=row['date'], + defaults=dict(six_month_plus_count=row['six_month_plus_count']), + ) + +print('Done.') From 9156bcdba30f3c41ee424637b03192c06155bd8b Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 19 Nov 2020 00:17:26 +0000 Subject: [PATCH 83/93] Add chart for older than six months member count --- webclient/src/Charts.js | 43 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/webclient/src/Charts.js b/webclient/src/Charts.js index 7a9aca9..650df43 100644 --- a/webclient/src/Charts.js +++ b/webclient/src/Charts.js @@ -59,6 +59,10 @@ export function Charts(props) { The green member count is {memberCount.slice().reverse()[0].green_count} members, compared to {memberCount.slice().reverse()[30].green_count} members 30 days ago.

+

+ The older than six months member count is {memberCount.slice().reverse()[0].six_month_plus_count} members, + compared to {memberCount.slice().reverse()[30].six_month_plus_count} members 30 days ago. +

There were {signupCount.slice().reverse()[0].signup_count} signups so far this month, and {signupCount.slice().reverse()[1].signup_count} signups last month. @@ -106,10 +110,47 @@ export function Charts(props) { }

-

Member Count: number of Prepaid, Current, Due, and Overdue members on Spaceport.

+

Member Count: number of active paying members on Spaceport.

Green Count: number of Prepaid and Current members.

+

+ {memberCount && + + + + + + + + + + + + + } +

+ +

Member Count: same as above.

+ +

Six Months+: number of memberships older than six months.

+
Space Activity

Daily since March 7th, 2020, updates hourly.

From 276e9b9b5b6c0191dfadc57c92c1f4b6a0403ac5 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 19 Nov 2020 00:52:33 +0000 Subject: [PATCH 84/93] Add stats for number of vetted members --- apiserver/apiserver/api/management/commands/run_hourly.py | 3 ++- apiserver/apiserver/api/models.py | 1 + apiserver/apiserver/api/utils_stats.py | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apiserver/apiserver/api/management/commands/run_hourly.py b/apiserver/apiserver/api/management/commands/run_hourly.py index 57ad634..7a85b47 100644 --- a/apiserver/apiserver/api/management/commands/run_hourly.py +++ b/apiserver/apiserver/api/management/commands/run_hourly.py @@ -9,7 +9,7 @@ class Command(BaseCommand): def generate_stats(self): utils_stats.calc_next_events() - member_count, green_count, six_month_plus_count = utils_stats.calc_member_counts() + member_count, green_count, six_month_plus_count, vetted_count = utils_stats.calc_member_counts() signup_count = utils_stats.calc_signup_counts() # do this hourly in case an admin causes a change @@ -19,6 +19,7 @@ class Command(BaseCommand): member_count=member_count, green_count=green_count, six_month_plus_count=six_month_plus_count, + vetted_count=vetted_count, ), ) diff --git a/apiserver/apiserver/api/models.py b/apiserver/apiserver/api/models.py index 70c437c..40e0b0c 100644 --- a/apiserver/apiserver/api/models.py +++ b/apiserver/apiserver/api/models.py @@ -143,6 +143,7 @@ class StatsMemberCount(models.Model): member_count = models.IntegerField() green_count = models.IntegerField() six_month_plus_count = models.IntegerField() + vetted_count = models.IntegerField() class StatsSignupCount(models.Model): month = models.DateField() diff --git a/apiserver/apiserver/api/utils_stats.py b/apiserver/apiserver/api/utils_stats.py index ddfd37d..d447eb4 100644 --- a/apiserver/apiserver/api/utils_stats.py +++ b/apiserver/apiserver/api/utils_stats.py @@ -67,11 +67,13 @@ def calc_member_counts(): six_months_ago = today_alberta_tz() - timedelta(days=183) six_month_plus_count = not_paused.filter(application_date__lte=six_months_ago).count() + vetted_count = not_paused.filter(vetted_date__isnull=False).count() + cache.set('member_count', member_count) cache.set('paused_count', paused_count) cache.set('green_count', green_count) - return member_count, green_count, six_month_plus_count + return member_count, green_count, six_month_plus_count, vetted_count def calc_signup_counts(): month_beginning = today_alberta_tz().replace(day=1) From e516ab12635788ab72dfebb28f7042d5e87103e0 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 19 Nov 2020 00:53:06 +0000 Subject: [PATCH 85/93] Add script to import historical number of vetted members --- apiserver/import_vetted_count.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 apiserver/import_vetted_count.py diff --git a/apiserver/import_vetted_count.py b/apiserver/import_vetted_count.py new file mode 100755 index 0000000..004b868 --- /dev/null +++ b/apiserver/import_vetted_count.py @@ -0,0 +1,21 @@ +# Expects a old_counts.csv of the historical counts in format: +# date,vetted_count + +import django, sys, os +os.environ['DJANGO_SETTINGS_MODULE'] = 'apiserver.settings' +django.setup() + +import csv +from apiserver.api import models + +with open('old_counts.csv', newline='') as csvfile: + reader = csv.DictReader(csvfile) + for row in reader: + print('Adding', row['date'], row['vetted_count']) + + models.StatsMemberCount.objects.update_or_create( + date=row['date'], + defaults=dict(vetted_count=row['vetted_count']), + ) + +print('Done.') From 21d1f3106fa13ef8b4548a442f8b20a32289ec57 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Thu, 19 Nov 2020 00:54:05 +0000 Subject: [PATCH 86/93] Add chart for vetted member count --- webclient/src/Charts.js | 43 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/webclient/src/Charts.js b/webclient/src/Charts.js index 650df43..9226124 100644 --- a/webclient/src/Charts.js +++ b/webclient/src/Charts.js @@ -63,6 +63,10 @@ export function Charts(props) { The older than six months member count is {memberCount.slice().reverse()[0].six_month_plus_count} members, compared to {memberCount.slice().reverse()[30].six_month_plus_count} members 30 days ago.

+

+ The vetted member count is {memberCount.slice().reverse()[0].vetted_count} members, + compared to {memberCount.slice().reverse()[30].vetted_count} members 30 days ago. +

There were {signupCount.slice().reverse()[0].signup_count} signups so far this month, and {signupCount.slice().reverse()[1].signup_count} signups last month. @@ -149,7 +153,44 @@ export function Charts(props) {

Member Count: same as above.

-

Six Months+: number of memberships older than six months.

+

Six Months+: number of active memberships older than six months.

+ +

+ {memberCount && + + + + + + + + + + + + + } +

+ +

Member Count: same as above.

+ +

Vetted Count: number of active vetted members.

Space Activity
From c8d5cece83f240097cd9c0b14e00173c0d17a47c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Fri, 20 Nov 2020 02:59:01 +0000 Subject: [PATCH 87/93] Fix class max_students default bug --- webclient/src/InstructorClasses.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webclient/src/InstructorClasses.js b/webclient/src/InstructorClasses.js index 131ad84..b602850 100644 --- a/webclient/src/InstructorClasses.js +++ b/webclient/src/InstructorClasses.js @@ -335,7 +335,7 @@ export function InstructorClassDetail(props) { export function InstructorClassList(props) { const { course, setCourse, token } = props; const [open, setOpen] = useState(false); - const [input, setInput] = useState({}); + const [input, setInput] = useState({ max_students: null }); const [error, setError] = useState(false); const [loading, setLoading] = useState(false); const [success, setSuccess] = useState(false); From 53ae4c31bb6c92fd4cbec4bfefd3b214a8fd88ca Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 16 Sep 2020 21:41:55 +0000 Subject: [PATCH 88/93] Initial commit of auth server --- authserver/.gitignore | 105 ++++++++++++++++++++++++++++++++++ authserver/README.md | 17 ++++++ authserver/auth_functions.py | 15 +++++ authserver/log.py | 22 +++++++ authserver/requirements.txt | 6 ++ authserver/secrets.py.example | 7 +++ authserver/server.py | 29 ++++++++++ 7 files changed, 201 insertions(+) create mode 100644 authserver/.gitignore create mode 100644 authserver/README.md create mode 100644 authserver/auth_functions.py create mode 100644 authserver/log.py create mode 100644 authserver/requirements.txt create mode 100644 authserver/secrets.py.example create mode 100644 authserver/server.py diff --git a/authserver/.gitignore b/authserver/.gitignore new file mode 100644 index 0000000..26fcc5d --- /dev/null +++ b/authserver/.gitignore @@ -0,0 +1,105 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Editor +*.swp +*.swo + +secrets.py diff --git a/authserver/README.md b/authserver/README.md new file mode 100644 index 0000000..dc3e249 --- /dev/null +++ b/authserver/README.md @@ -0,0 +1,17 @@ +# Auth Server + +Runs on Protospace's webhost and passes credentials around. + +Exposes a REST API to Spaceport that allows setting wiki, etc passwords. + +## Setup + +Basically the exact same as: + +https://docs.my.protospace.ca/ldap.html + +## License + +This program is free and open-source software licensed under the MIT License. Please see the `LICENSE` file for details. + +That means you have the right to study, change, and distribute the software and source code to anyone and for any purpose. You deserve these rights. diff --git a/authserver/auth_functions.py b/authserver/auth_functions.py new file mode 100644 index 0000000..74bdd1d --- /dev/null +++ b/authserver/auth_functions.py @@ -0,0 +1,15 @@ +from log import logger +import time +import secrets + +from flask import abort + +HTTP_NOTFOUND = 404 + +def set_password(username, password): + # TODO + print(username, password) + +if __name__ == '__main__': + print(set_password('test.test', 'password')) + pass diff --git a/authserver/log.py b/authserver/log.py new file mode 100644 index 0000000..23cd69e --- /dev/null +++ b/authserver/log.py @@ -0,0 +1,22 @@ +import logging +import logging.config + +logging.config.dictConfig({ + 'version': 1, + 'formatters': {'default': { + 'format': '[%(asctime)s] [%(process)d] [%(levelname)7s] %(message)s', + }}, + 'handlers': {'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }}, + 'root': { + 'level': 'INFO', + 'handlers': ['wsgi'] + } +}) + +logger = logging.getLogger(__name__) + +logger.info('Logging enabled.') diff --git a/authserver/requirements.txt b/authserver/requirements.txt new file mode 100644 index 0000000..139affa --- /dev/null +++ b/authserver/requirements.txt @@ -0,0 +1,6 @@ +click==7.1.2 +Flask==1.1.2 +itsdangerous==1.1.0 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +Werkzeug==1.0.1 diff --git a/authserver/secrets.py.example b/authserver/secrets.py.example new file mode 100644 index 0000000..d7312bb --- /dev/null +++ b/authserver/secrets.py.example @@ -0,0 +1,7 @@ +# Auth server secrets file, don't commit to version control! + +# Auth token, used by Spaceport to authenticate +# Set this to random characters +# For example, use the first output of this: +# head /dev/urandom | sha1sum +AUTH_TOKEN = '' diff --git a/authserver/server.py b/authserver/server.py new file mode 100644 index 0000000..8e1fdf1 --- /dev/null +++ b/authserver/server.py @@ -0,0 +1,29 @@ +from flask import Flask, abort, request +app = Flask(__name__) + +import auth_functions +import secrets + +HTTP_UNAUTHORIZED = 401 + +def check_auth(): + auth_header = request.headers.get('Authorization', '') + if auth_header != 'Token ' + secrets.AUTH_TOKEN: + abort(HTTP_UNAUTHORIZED) + +@app.route('/') +def index(): + return 'SEE YOU SPACE SAMURAI...' + +@app.route('/set-password', methods=['POST']) +def set_password(): + check_auth() + + username = request.form['username'] + password = request.form['password'] + + auth_functions.set_password(username, password) + return '' + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') From 6b841f3a78286402362488dea80e37574651268b Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 16 Sep 2020 22:14:27 +0000 Subject: [PATCH 89/93] Set a user's wiki password with auth server --- authserver/auth_functions.py | 31 +++++++++++++++++++++++++++---- authserver/secrets.py.example | 5 +++++ authserver/server.py | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/authserver/auth_functions.py b/authserver/auth_functions.py index 74bdd1d..e429384 100644 --- a/authserver/auth_functions.py +++ b/authserver/auth_functions.py @@ -1,15 +1,38 @@ from log import logger import time import secrets +import subprocess from flask import abort HTTP_NOTFOUND = 404 -def set_password(username, password): - # TODO - print(username, password) +def set_wiki_password(username, password): + # sets a user's wiki password + # creates the account if it doesn't exist + + if not username: + logger.error('Empty username, aborting') + abort(400) + + logger.info('Setting wiki password for: ' + username) + + if not password: + logger.error('Empty password, aborting') + abort(400) + + script = secrets.WIKI_MAINTENANCE + '/createAndPromote.php' + + result = subprocess.run(['php', script, '--force', username, password], + shell=False, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + output = result.stdout or result.stderr + + logger.info('Output: ' + output) + + if result.stderr: + abort(400) if __name__ == '__main__': - print(set_password('test.test', 'password')) + set_wiki_password('tanner.collin', 'protospace1') pass diff --git a/authserver/secrets.py.example b/authserver/secrets.py.example index d7312bb..9a8f8aa 100644 --- a/authserver/secrets.py.example +++ b/authserver/secrets.py.example @@ -5,3 +5,8 @@ # For example, use the first output of this: # head /dev/urandom | sha1sum AUTH_TOKEN = '' + +# Absolute path of Mediawiki maintenance directory +# Probably: +# /var/www/wiki/maintenance +WIKI_MAINTENANCE = '' diff --git a/authserver/server.py b/authserver/server.py index 8e1fdf1..69d7517 100644 --- a/authserver/server.py +++ b/authserver/server.py @@ -22,7 +22,7 @@ def set_password(): username = request.form['username'] password = request.form['password'] - auth_functions.set_password(username, password) + auth_functions.set_wiki_password(username, password) return '' if __name__ == '__main__': From 5458e4d4085e0b6fa2383839a836d3b3c930c5aa Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 22 Nov 2020 01:30:02 +0000 Subject: [PATCH 90/93] Strip command output --- authserver/auth_functions.py | 1 + authserver/server.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/authserver/auth_functions.py b/authserver/auth_functions.py index e429384..9f89b65 100644 --- a/authserver/auth_functions.py +++ b/authserver/auth_functions.py @@ -27,6 +27,7 @@ def set_wiki_password(username, password): shell=False, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output = result.stdout or result.stderr + output = output.strip() logger.info('Output: ' + output) diff --git a/authserver/server.py b/authserver/server.py index 69d7517..36d7fe3 100644 --- a/authserver/server.py +++ b/authserver/server.py @@ -13,7 +13,7 @@ def check_auth(): @app.route('/') def index(): - return 'SEE YOU SPACE SAMURAI...' + return 'LIFE IS BUT A DREAM...' @app.route('/set-password', methods=['POST']) def set_password(): From 6a6fa7d5040239df0285df9b4217b777d1afef92 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Sun, 22 Nov 2020 05:56:06 +0000 Subject: [PATCH 91/93] Show last four weeks of card scans chart by default --- webclient/src/Charts.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/webclient/src/Charts.js b/webclient/src/Charts.js index 9226124..8702fd7 100644 --- a/webclient/src/Charts.js +++ b/webclient/src/Charts.js @@ -13,6 +13,7 @@ export function Charts(props) { const [memberCount, setMemberCount] = useState(memberCountCache); const [signupCount, setSignupCount] = useState(signupCountCache); const [spaceActivity, setSpaceActivity] = useState(spaceActivityCache); + const [fullActivity, setFullActivity] = useState(false); useEffect(() => { requester('/charts/membercount/', 'GET') @@ -194,12 +195,19 @@ export function Charts(props) {
Space Activity
-

Daily since March 7th, 2020, updates hourly.

+ {fullActivity ? +

Daily since March 7th, 2020, updates hourly.

+ : +

+ Last four weeks, updates hourly. + {' '} +

+ }

{spaceActivity && - + @@ -212,7 +220,7 @@ export function Charts(props) { name='Card Scans' fill='#8884d8' maxBarSize={20} - animationDuration={1000} + isAnimationActive={false} /> From a7051f80ac26319f6d950bc18860a369949a29c1 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 24 Nov 2020 23:00:45 +0000 Subject: [PATCH 92/93] Make generate_backups function atomic, increase DB timeout --- .../apiserver/api/management/commands/generate_backups.py | 2 ++ apiserver/apiserver/settings.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/apiserver/apiserver/api/management/commands/generate_backups.py b/apiserver/apiserver/api/management/commands/generate_backups.py index d32ec50..942e0d8 100644 --- a/apiserver/apiserver/api/management/commands/generate_backups.py +++ b/apiserver/apiserver/api/management/commands/generate_backups.py @@ -1,6 +1,7 @@ from django.core.management.base import BaseCommand, CommandError from django.utils.timezone import now from django.core.cache import cache +from django.db import transaction from apiserver import secrets, settings from apiserver.api import models @@ -25,6 +26,7 @@ backup_id_string = lambda x: '{}\t{}\t{}'.format( class Command(BaseCommand): help = 'Generate backups.' + @transaction.atomic def generate_backups(self): backup_users = secrets.BACKUP_TOKENS.values() diff --git a/apiserver/apiserver/settings.py b/apiserver/apiserver/settings.py index f5dfbf8..2b1b935 100644 --- a/apiserver/apiserver/settings.py +++ b/apiserver/apiserver/settings.py @@ -116,6 +116,9 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'data/db.sqlite3'), + 'OPTIONS': { + 'timeout': 20, # increased because generate_backups.py blocks + }, }, 'old_portal': { 'ENGINE': 'django.db.backends.sqlite3', From 247e221ac6e4b35c105d9be183db866f98b2c183 Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Tue, 12 Jan 2021 02:42:53 +0000 Subject: [PATCH 93/93] Improve logging --- apiserver/apiserver/api/serializers.py | 10 ++++++++++ apiserver/apiserver/api/views.py | 1 + 2 files changed, 11 insertions(+) diff --git a/apiserver/apiserver/api/serializers.py b/apiserver/apiserver/api/serializers.py index f74fee2..a14932b 100644 --- a/apiserver/apiserver/api/serializers.py +++ b/apiserver/apiserver/api/serializers.py @@ -530,9 +530,16 @@ class MyPasswordChangeSerializer(PasswordChangeSerializer): class MyPasswordResetSerializer(PasswordResetSerializer): def validate_email(self, email): if not User.objects.filter(email__iexact=email).exists(): + logging.info('Email not found: ' + email) raise ValidationError('Not found.') return super().validate_email(email) + def save(self): + email = self.data['email'] + member = User.objects.get(email__iexact=email).member + logging.info('Password reset requested for: {} - {} {} ({})'.format(email, member.first_name, member.last_name, member.id)) + super().save() + class MyPasswordResetConfirmSerializer(PasswordResetConfirmSerializer): def save(self): data = dict( @@ -559,6 +566,9 @@ class MyPasswordResetConfirmSerializer(PasswordResetConfirmSerializer): logger.info(msg) raise ValidationError(dict(non_field_errors=msg)) + member = self.user.member + logging.info('Password reset completed for: {} {} ({})'.format(member.first_name, member.last_name, member.id)) + super().save() diff --git a/apiserver/apiserver/api/views.py b/apiserver/apiserver/api/views.py index efac0d0..d3adadc 100644 --- a/apiserver/apiserver/api/views.py +++ b/apiserver/apiserver/api/views.py @@ -84,6 +84,7 @@ class SearchViewSet(Base, Retrieve): result_objects = [queryset.get(id=x) for x in result_ids] queryset = result_objects + logging.info('Search for: {}, results: {}'.format(search, len(queryset))) elif self.action == 'create': utils.gen_search_strings() # update cache queryset = queryset.order_by('-vetted_date')