Skip to content

Commit

Permalink
user and oauth normalized + required crud
Browse files Browse the repository at this point in the history
  • Loading branch information
Winston-Hsiao committed Aug 3, 2024
1 parent 666b047 commit d6971d0
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 23 deletions.
19 changes: 12 additions & 7 deletions store/app/crud/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ async def _create_user_from_email(self, email: str, password: str) -> User:
await self._add_item(user, unique_fields=["email"])
return user

async def _create_user_from_auth_key(self, auth_key: str, email: str) -> User:
user = await self._create_user_from_email(email)
key = OAuthKey.create(auth_key, user.id)
await self._add_item(key, unique_fields=["user_token"])
async def _create_user_from_oauth(self, email: str, provider: str, token: str) -> User:
user = User.create(email=email, password=None)
if provider == "github":
user.github_id = token
elif provider == "google":
user.google_id = token
await self._add_item(user, unique_fields=["email"])
oauth_key = OAuthKey.create(user_id=user.id, provider=provider, token=token)
await self._add_item(oauth_key, unique_fields=["user_token"])
return user

@overload
Expand All @@ -71,7 +76,7 @@ async def get_user_from_github_token(self, token: str, email: str) -> User:
user = await self._get_user_from_auth_key(auth_key)
if user is not None:
return user
return await self._create_user_from_auth_key(auth_key, email)
return await self._create_user_from_oauth(email, "github", auth_key)

async def delete_github_token(self, github_id: str) -> None:
await self._delete_item(await self._get_oauth_key(github_auth_key(github_id), throw_if_missing=True))
Expand All @@ -81,7 +86,7 @@ async def get_user_from_google_token(self, token: str, email: str) -> User | Non
user = await self._get_user_from_auth_key(auth_key)
if user is not None:
return user
return await self._create_user_from_auth_key(auth_key, email)
return await self._create_user_from_oauth(email, "google", auth_key)

async def delete_google_token(self, google_id: str) -> None:
await self._delete_item(await self._get_oauth_key(google_auth_key(google_id), throw_if_missing=True))
Expand Down Expand Up @@ -126,7 +131,7 @@ async def delete_api_key(self, token: APIKey | str) -> None:

async def test_adhoc() -> None:
async with UserCrud() as crud:
await crud._create_user_from_email(email="[email protected]")
await crud._create_user_from_email(email="[email protected]", password="examplepas$w0rd")


if __name__ == "__main__":
Expand Down
47 changes: 31 additions & 16 deletions store/app/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@

import time
from datetime import datetime, timedelta
from typing import Literal, Self
from typing import Optional, Self, Set, Literal

from pydantic import BaseModel
from pydantic import BaseModel, EmailStr

from store.settings import settings
from store.store.app.utils.password import hash_password
Expand All @@ -33,28 +33,38 @@ class RobolistBaseModel(BaseModel):
class User(RobolistBaseModel):
"""Defines the user model for the API.
Users are defined by their email and hashed_password. This is the
simplest form of authentication, and is used for users who sign up with
their email and password.
Users are defined by their id and email (both unique).
Hashed password is set if user signs up with email and password, and is
left empty if the user signed up with Google or Github OAuth.
"""

email: str
hashed_password: str
permissions: set[UserPermission] | None = None
email: EmailStr
hashed_password: Optional[str] = None
permissions: Optional[Set[UserPermission]] = None
created_at: int
updated_at: int
email_verified_at: int | None = None
email_verified_at: Optional[int] = None
github_id: Optional[str] = None
google_id: Optional[str] = None

@classmethod
def create(cls, email: str, password: str) -> Self:
def create(
cls,
email: str,
password: Optional[str] = None,
github_id: Optional[str] = None,
google_id: Optional[str] = None,
) -> Self:
now = int(time.time())
hashed_pw = hash_password(password) if password else None
return cls(
id=new_uuid(),
email=email,
hashed_password=hash_password(password),
permissions=None,
hashed_password=hashed_pw,
created_at=now,
updated_at=now,
github_id=github_id,
google_id=google_id,
)

def update_timestamp(self) -> None:
Expand All @@ -66,13 +76,18 @@ def verify_email(self) -> None:

class OAuthKey(RobolistBaseModel):
"""Keys for OAuth providers which identify users."""

user_id: str
user_token: str
provider: str
token: str

@classmethod
def create(cls, user_token: str, user_id: str) -> Self:
return cls(id=new_uuid(), user_id=user_id, user_token=user_token)
def create(cls, user_id: str, provider: str, token: str) -> Self:
return cls(
id=new_uuid(),
user_id=user_id,
provider=provider,
token=token
)


APIKeySource = Literal["user", "oauth"]
Expand Down

0 comments on commit d6971d0

Please sign in to comment.