From e0e145288618d49a0eac38d35489eae4671654ca Mon Sep 17 00:00:00 2001 From: Jonathan Zhang Date: Fri, 15 Apr 2022 18:41:47 -0700 Subject: [PATCH 1/5] feat: add renew endpoint --- app/routes/account/renew.py | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 app/routes/account/renew.py diff --git a/app/routes/account/renew.py b/app/routes/account/renew.py new file mode 100644 index 0000000..60bde40 --- /dev/null +++ b/app/routes/account/renew.py @@ -0,0 +1,59 @@ +from fastapi import Depends, HTTPException, status +from pydantic import BaseModel, Field +import pexpect + +from routes import router +import ocflib.account.validators as validators + +class ExpiredPasswordInput(BaseModel): + username: str = Field( + min_length=3, + max_length=16, + ) + old_password: str = Field( + min_length=5, + max_length=256, + ) + new_password: str = Field( + min_length=12, + max_length=256, + ) + + + +class ExpiredPasswordOutput(BaseModel): + output: str + error: str + + +@router.post("/account/renew", tags=["account"], response_model=ExpiredPasswordOutput) +def reset_password(data: ExpiredPasswordInput): + try: + validators.validate_username(data.username) + validators.validate_password(data.username, data.old_password, strength_check=False) + validators.validate_password(data.username, data.new_password, strength_check=True) + except ValueError as ex: + raise HTTPException(status_code=400, detail=str(ex)) + cmd = 'kinit --no-forwardable -l0 {}@OCF.BERKELEY.EDU'.format(data.username) + child = pexpect.spawn(cmd, timeout=10) + child.expect("{}@OCF.BERKELEY.EDU's Password:".format(data.username)) + child.sendline(data.old_password) + try: + result = child.expect(['incorrect', 'unknown', pexpect.EOF, 'expired']) + if (result == 0): + raise HTTPException(status_code=403, detail="Authentication failed") + elif (result == 1): + raise HTTPException(status_code=400, detail="Unknown user") + elif (result == 2): + raise HTTPException(status_code=400, detail="Password not expired") + else: + child.sendline(data.new_password) + child.expect('\r\nRepeat new password:') + child.sendline(data.new_password) + child.expect('\r\nSuccess: Password changed\r\n') + output = 'Password successfully updated!' + error = '' + except pexpect.exceptions.TIMEOUT: + raise HTTPException(status_code=400, detail="Please double check your credentials") + + return {"output": output, "error": error} From 9247894e1d67afee24e74dd7da99fe6a75c3ca9f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Apr 2022 01:42:14 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- app/routes/account/renew.py | 38 ++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/app/routes/account/renew.py b/app/routes/account/renew.py index 60bde40..9a72a72 100644 --- a/app/routes/account/renew.py +++ b/app/routes/account/renew.py @@ -1,9 +1,12 @@ +import pexpect + +import ocflib.account.validators as validators + from fastapi import Depends, HTTPException, status from pydantic import BaseModel, Field -import pexpect from routes import router -import ocflib.account.validators as validators + class ExpiredPasswordInput(BaseModel): username: str = Field( @@ -20,7 +23,6 @@ class ExpiredPasswordInput(BaseModel): ) - class ExpiredPasswordOutput(BaseModel): output: str error: str @@ -30,30 +32,36 @@ class ExpiredPasswordOutput(BaseModel): def reset_password(data: ExpiredPasswordInput): try: validators.validate_username(data.username) - validators.validate_password(data.username, data.old_password, strength_check=False) - validators.validate_password(data.username, data.new_password, strength_check=True) + validators.validate_password( + data.username, data.old_password, strength_check=False + ) + validators.validate_password( + data.username, data.new_password, strength_check=True + ) except ValueError as ex: raise HTTPException(status_code=400, detail=str(ex)) - cmd = 'kinit --no-forwardable -l0 {}@OCF.BERKELEY.EDU'.format(data.username) + cmd = "kinit --no-forwardable -l0 {}@OCF.BERKELEY.EDU".format(data.username) child = pexpect.spawn(cmd, timeout=10) child.expect("{}@OCF.BERKELEY.EDU's Password:".format(data.username)) child.sendline(data.old_password) try: - result = child.expect(['incorrect', 'unknown', pexpect.EOF, 'expired']) - if (result == 0): + result = child.expect(["incorrect", "unknown", pexpect.EOF, "expired"]) + if result == 0: raise HTTPException(status_code=403, detail="Authentication failed") - elif (result == 1): + elif result == 1: raise HTTPException(status_code=400, detail="Unknown user") - elif (result == 2): + elif result == 2: raise HTTPException(status_code=400, detail="Password not expired") else: child.sendline(data.new_password) - child.expect('\r\nRepeat new password:') + child.expect("\r\nRepeat new password:") child.sendline(data.new_password) - child.expect('\r\nSuccess: Password changed\r\n') - output = 'Password successfully updated!' - error = '' + child.expect("\r\nSuccess: Password changed\r\n") + output = "Password successfully updated!" + error = "" except pexpect.exceptions.TIMEOUT: - raise HTTPException(status_code=400, detail="Please double check your credentials") + raise HTTPException( + status_code=400, detail="Please double check your credentials" + ) return {"output": output, "error": error} From 09bd039cbf6e573b98ee0a7183a973775d32bbee Mon Sep 17 00:00:00 2001 From: Jonathan Zhang Date: Fri, 15 Apr 2022 18:43:39 -0700 Subject: [PATCH 3/5] fix: remove unused imports --- app/routes/account/renew.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/account/renew.py b/app/routes/account/renew.py index 9a72a72..e8a9e83 100644 --- a/app/routes/account/renew.py +++ b/app/routes/account/renew.py @@ -2,7 +2,7 @@ import ocflib.account.validators as validators -from fastapi import Depends, HTTPException, status +from fastapi import HTTPException from pydantic import BaseModel, Field from routes import router From d8b41d95ad383a07d400fbc883d803894c2649f7 Mon Sep 17 00:00:00 2001 From: Jonathan Zhang Date: Sat, 16 Apr 2022 01:08:16 -0700 Subject: [PATCH 4/5] fix: misc fixes --- .../account/{renew.py => renew_password.py} | 22 +++++++++---------- requirements-minimal.txt | 1 + 2 files changed, 11 insertions(+), 12 deletions(-) rename app/routes/account/{renew.py => renew_password.py} (69%) diff --git a/app/routes/account/renew.py b/app/routes/account/renew_password.py similarity index 69% rename from app/routes/account/renew.py rename to app/routes/account/renew_password.py index e8a9e83..a2e10c6 100644 --- a/app/routes/account/renew.py +++ b/app/routes/account/renew_password.py @@ -2,13 +2,13 @@ import ocflib.account.validators as validators -from fastapi import HTTPException +from fastapi import HTTPException, status from pydantic import BaseModel, Field from routes import router -class ExpiredPasswordInput(BaseModel): +class RenewPasswordInput(BaseModel): username: str = Field( min_length=3, max_length=16, @@ -23,13 +23,12 @@ class ExpiredPasswordInput(BaseModel): ) -class ExpiredPasswordOutput(BaseModel): +class RenewPasswordOutput(BaseModel): output: str - error: str -@router.post("/account/renew", tags=["account"], response_model=ExpiredPasswordOutput) -def reset_password(data: ExpiredPasswordInput): +@router.post("/account/renew-password", tags=["account"], response_model=RenewPasswordOutput) +def renew_password(data: RenewPasswordInput): try: validators.validate_username(data.username) validators.validate_password( @@ -39,7 +38,7 @@ def reset_password(data: ExpiredPasswordInput): data.username, data.new_password, strength_check=True ) except ValueError as ex: - raise HTTPException(status_code=400, detail=str(ex)) + raise HTTPException(status.HTTP_400_BAD_REQUEST, detail=str(ex)) cmd = "kinit --no-forwardable -l0 {}@OCF.BERKELEY.EDU".format(data.username) child = pexpect.spawn(cmd, timeout=10) child.expect("{}@OCF.BERKELEY.EDU's Password:".format(data.username)) @@ -47,21 +46,20 @@ def reset_password(data: ExpiredPasswordInput): try: result = child.expect(["incorrect", "unknown", pexpect.EOF, "expired"]) if result == 0: - raise HTTPException(status_code=403, detail="Authentication failed") + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed") elif result == 1: - raise HTTPException(status_code=400, detail="Unknown user") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown user") elif result == 2: - raise HTTPException(status_code=400, detail="Password not expired") + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Password not expired") else: child.sendline(data.new_password) child.expect("\r\nRepeat new password:") child.sendline(data.new_password) child.expect("\r\nSuccess: Password changed\r\n") output = "Password successfully updated!" - error = "" except pexpect.exceptions.TIMEOUT: raise HTTPException( status_code=400, detail="Please double check your credentials" ) - return {"output": output, "error": error} + return {"output": output} diff --git a/requirements-minimal.txt b/requirements-minimal.txt index a73132c..ae05eda 100644 --- a/requirements-minimal.txt +++ b/requirements-minimal.txt @@ -2,6 +2,7 @@ celery[redis]==5.2.6 fastapi==0.75.1 ocflib paramiko==2.7.2 +pexpect==4.8.0 python-dateutil==2.8.2 python-dotenv==0.19.2 python-jose==3.3.0 From 17b55400d9b90d5e331179090440d558da461f97 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 16 Apr 2022 08:08:28 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- app/routes/account/renew_password.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/routes/account/renew_password.py b/app/routes/account/renew_password.py index a2e10c6..93b9fa3 100644 --- a/app/routes/account/renew_password.py +++ b/app/routes/account/renew_password.py @@ -27,7 +27,9 @@ class RenewPasswordOutput(BaseModel): output: str -@router.post("/account/renew-password", tags=["account"], response_model=RenewPasswordOutput) +@router.post( + "/account/renew-password", tags=["account"], response_model=RenewPasswordOutput +) def renew_password(data: RenewPasswordInput): try: validators.validate_username(data.username) @@ -46,11 +48,17 @@ def renew_password(data: RenewPasswordInput): try: result = child.expect(["incorrect", "unknown", pexpect.EOF, "expired"]) if result == 0: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed") + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication failed" + ) elif result == 1: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown user") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Unknown user" + ) elif result == 2: - raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Password not expired") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, detail="Password not expired" + ) else: child.sendline(data.new_password) child.expect("\r\nRepeat new password:")