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' ?
-