Skip to content

Commit

Permalink
feat(backend): Initial account creation endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
IscoRuta98 committed Feb 22, 2023
1 parent e48e28c commit 3f4ffbd
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 1 deletion.
13 changes: 12 additions & 1 deletion backend-services/src/common/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
from typing import NewType
from typing import NewType, TypeAlias
from odmantic import AIOEngine

WalletAddress = NewType("WalletAddress", str)
"""
A type for vault wallet addresses.
"""

Engine: TypeAlias = AIOEngine
"""
A database engine instance.
"""

HashString = NewType("HashString", str)
"""
HashString
"""
3 changes: 3 additions & 0 deletions backend-services/src/user_auth_service/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
User Authentication service for the Nautilus Vault backend.
"""
3 changes: 3 additions & 0 deletions backend-services/src/user_auth_service/operations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
The collection of operations provided by the user authenticatioin service.
"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from common.types import Engine
from create_new_user import argon2_context
from fastapi import HTTPException
from user_auth_service.schema.actions import AuthenticateUser, AuthenticateUserResult, AuthenticateUserSuccess, AuthenticateUserFailure
from user_auth_service.schema.entites import UserDetailsStorable

def verify_password(password_attempt: str, hashed_password: str):
return argon2_context.verify(password_attempt, hashed_password)

async def authenticate_user(engine: Engine, params: AuthenticateUser) -> AuthenticateUserResult:
"""
Look through DB to see if any user matches to the above.
If user exists, verify password.
"""
existing_user = await engine.find_one(UserDetailsStorable, UserDetailsStorable.email_address == AuthenticateUser.email_address)
if existing_user is None:
return AuthenticateUserFailure(
Failed = 'Username does not exist.'
)

if not verify_password(AuthenticateUser.password,existing_user.hashed_password):
return AuthenticateUserFailure(
Failed = 'Invalid Password'
)
return existing_user
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from user_auth_service.schema.actions import CreateNewUser, CreateNewUserResult, CreateNewUserSuccess, CreateNewUserFailure
from user_auth_service.schema.entites import UserDetailsStorable, UserDisplay
from common.types import Engine
from passlib.context import CryptContext

argon2_context = CryptContext(schemes=['argon2'], depricated='auto')

def password_hash(password: str):
return argon2_context.hash(password)


async def create_new_user(engine: Engine, params: CreateNewUser) -> CreateNewUserResult:
"""
User Creation.
"""
hash_password = password_hash(params.password)

new_user = UserDetailsStorable(
email_address = params.email_address,
full_name = params.full_name,
phone_number = params.phone_number,
password_hash_string = hash_password
)
try:
await engine.save(new_user)
user_display = UserDisplay(
user_id = UserDetailsStorable.id,
email_address = params.email_address,
full_name = params.full_name,
phone_number = params.phone_number
)
return CreateNewUserSuccess(created=user_display)
except:
return CreateNewUserFailure(
Failed = 'Unable to save user credentials.'
)
3 changes: 3 additions & 0 deletions backend-services/src/user_auth_service/schema/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Internals and abstractions for the User Authentication service.
"""
55 changes: 55 additions & 0 deletions backend-services/src/user_auth_service/schema/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from user_auth_service.schema.entites import UserDisplay
from pydantic import BaseModel

from common.types import WalletAddress
from datetime import datetime
from typing import TypeAlias


class CreateNewUser(BaseModel):
"""
User creation parameters.
"""

full_name: str
phone_number: str
email_address: str
password: str

class CreateNewUserSuccess(BaseModel):
"""
Return email address, full name, and phone number.
"""

Created: UserDisplay

class CreateNewUserFailure(BaseModel):
"""
Return Failure if user's credentials is not created.
"""
Failed: str

CreateNewUserResult: TypeAlias = CreateNewUserSuccess | CreateNewUserFailure

class AuthenticateUser(BaseModel):
"""
Authentic user parameters.
"""
email_address: str
password: str

class AuthenticateUserSuccess(BaseModel):
"""
Successfully authenticated user.
"""

Opened: str

class AuthenticateUserFailure(BaseModel):
"""
Failed to authenticate user.
"""

Failed: str

class AuthenticateUserResult: TypeAlias = AuthenticateUserSuccess | AuthenticateUserFailure
31 changes: 31 additions & 0 deletions backend-services/src/user_auth_service/schema/entites.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from typing import TypeAlias
from common.types import HashString

from odmantic import Model
from pydantic import BaseModel

from common.types import HashString

class UserDetailsStorable(Model):
"""
Storing new ueser's credentials.
"""

email_address: str
full_name: str;
phone_number: str
password_hash_string: HashString;

class Config:
collection = 'user'

class UserDisplay(BaseModel):
"""
Return User credentials when user is created or opened.
"""

user_id: str
email_address: str
owner_name: str
phone_number: str

18 changes: 18 additions & 0 deletions backend-services/src/web_asgi/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
from data_service.schema.entities import Dataset, DatasetList, Datapool, DatapoolList, Dataschema

from data_service.operations.datapool import datapools, create_datapool, delete_datapool
from data_service.operations.dataschema import create_dataschema

from user_auth_service.schema.actions import CreateNewUser, CreateNewUserResult, AuthenticateUser, AuthenticateUserResult
from user_auth_service.operations import create_new_user, authenticate_user

from web_asgi.settings import AppSettings

Expand All @@ -38,6 +42,20 @@
)


@app.post(
"/auth/create", response_model=CreateNewUserResult, status_code=status.HTTP_201_CREATED
)
async def post_create_new_user(request: CreateNewUser) -> CreateNewUserResult:
return await create_new_user(mongo_engine, request)


@app.post(
"/auth/login", response_model=AuthenticateUserResult, status_code=status.HTTP_201_CREATED
)
async def post_authenticate_user(request: AuthenticateUser) -> AuthenticateUserResult:
return await authenticate_user(mongo_engine, request)


@app.get("/datasets", response_model=DatasetList, status_code=status.HTTP_200_OK)
async def get_datasets(wallet_id: WalletAddress) -> DatasetList:
return await datasets(mongo_engine, wallet_id)
Expand Down

0 comments on commit 3f4ffbd

Please sign in to comment.