Skip to content

Commit

Permalink
Replace shared username/password with AD auth
Browse files Browse the repository at this point in the history
  • Loading branch information
campb303 committed Jan 22, 2021
1 parent a647639 commit 75e8cd4
Showing 1 changed file with 57 additions and 4 deletions.
61 changes: 57 additions & 4 deletions api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
)
from werkzeug.security import check_password_hash
import os, dotenv
from easyad import EasyAD
from ldap.filter import escape_filter_chars
from ldap import INVALID_CREDENTIALS as LDAP_INVALID_CREDENTIALS
import ECNQueue

# Load envrionment variables for ./.env
Expand Down Expand Up @@ -47,6 +50,58 @@



def user_is_valid(username: str, password: str) -> bool:
"""Checks if user is valid and in webqueue2 login group.
Args:
username (str): Career account username.
password (str): Career account passphrase.
Returns:
bool: True if user is valid, otherwise False.
"""

# Check for empty arguments
if (username == "" or password == ""):
return False

# Initialize EasyAD
config = {
"AD_SERVER": "boilerad.purdue.edu",
"AD_DOMAIN": "boilerad.purdue.edu"
}
ad = EasyAD(config)

# Prepare search critiera for Active Directory
credentials = {
"username": escape_filter_chars(username),
"password": password
}
attributes = [ 'cn', "memberOf" ]
filter_string = f'(&(objectClass=user)(|(sAMAccountName={username})))'

# Do user search
try:
user = ad.search(credentials=credentials, attributes=attributes, filter_string=filter_string)[0]
# pylint says this is an error but it works so ¯\_(ツ)_/¯
except LDAP_INVALID_CREDENTIALS:
return False

# Isolate group names
# Example:
# 'CN=00000227-ECNStuds,OU=BoilerADGroups,DC=BoilerAD,DC=Purdue,DC=edu' becomes
# `00000227-ECNStuds`
user_groups = [ group.split(',')[0].split('=')[1] for group in user["memberOf"] ]

# Check group membership
webqueue_login_group = "00000227-ECN-webqueue"
if webqueue_login_group not in user_groups:
return False

return True



class Login(Resource):
def post(self) -> tuple:
"""Validates username/password and returns both access and refresh tokens.
Expand Down Expand Up @@ -76,10 +131,8 @@ def post(self) -> tuple:
if field not in data.keys():
return ({ "message": f"{field} missing from request body"}, 422)

if data["username"] != os.environ.get("SHARED_USERNAME"):
return ({ "message": "Username invalid"}, 401)
if not check_password_hash(os.environ.get("SHARED_PASSWORD_HASH"), data["password"]):
return ({ "message": "Password invalid"}, 401)
if not user_is_valid(data["username"], data["password"]):
return ({ "message": "Username or password is invalid"}, 401)

access_token = create_access_token(data["username"])
refresh_token = create_refresh_token(data["username"])
Expand Down

0 comments on commit 75e8cd4

Please sign in to comment.