diff --git a/config/configtemplate.json b/config/configtemplate.json index fad0306..6cb58b2 100644 --- a/config/configtemplate.json +++ b/config/configtemplate.json @@ -19,6 +19,12 @@ "auto_cookie_name": "session" }, "internal": { - "internal_api_key": "CHANGE_ME_NOW" + "internal_api_key": "CHANGE_ME_NOW", + "internal_columns": [ + "_id" + ] + }, + "account_features": { + "enable_change_password": true } } \ No newline at end of file diff --git a/config/email/ChangePassword.html b/config/email/ChangePassword.html new file mode 100644 index 0000000..e69de29 diff --git a/docs/getting-started/configuration.md b/docs/getting-started/configuration.md index 8bbaea7..f4dee4d 100644 --- a/docs/getting-started/configuration.md +++ b/docs/getting-started/configuration.md @@ -48,4 +48,10 @@ Make sure that all parameters are set correctly before starting the service. | Parameter | Description | |------------|-------------| -| `internal.internal_api_key` | **Datatype:** String
**Default:** `CHANGE_ME_NOW`
This is **sensitive** information and must **never** be exposed anywhere. | \ No newline at end of file +| `internal.internal_api_key` | **Datatype:** String
**Default:** `CHANGE_ME_NOW`
This is **sensitive** information and must **never** be exposed anywhere. | +| `internal.internal_columns` | **Datatype:** List
**Default:** `["_id"]`
Columns that should only be revealed via the internal API. This example will never reveal `_id` to public endpoints, but just to the `/internal` endpoints as well as E-Mails. | + +### Account Features Configuration +| Parameter | Description | +|------------|-------------| +| `account_features.enable_change_password` | **Datatype:** Boolean
**Default:** `true`
Enable or disable the password reset feature. | \ No newline at end of file diff --git a/docs/information.md b/docs/information.md index 22a53a4..7a2afae 100644 --- a/docs/information.md +++ b/docs/information.md @@ -12,6 +12,15 @@ To guarantee the security of your user data, EZAuth uses the following security - **2FA**: Two-factor authentication can be enabled for users. - **OAuth2**: OAuth2 can be enabled for users. +## Documentation +You are reading the informative documentation for EZAuth. +If you need a documentation of all the endpoints, you can start the service and navigate to the `/docs` endpoint to find the API Documentation. +Because we utilize FastAPI, the documentation is done automatically and displayed via Swagger UI. +This also makes the API OpenAPI compliant. + +!!! Info "API Documentation" + Navigate to [`http://localhost:3250/docs`](http://localhost:3250/docs) to see the API Documentation. + ## Code Examples !!! Info "Official Libraries" diff --git a/src/api/dependencies/authenticated.py b/src/api/dependencies/authenticated.py new file mode 100644 index 0000000..d120d97 --- /dev/null +++ b/src/api/dependencies/authenticated.py @@ -0,0 +1,23 @@ +from fastapi import HTTPException, Cookie +from tools import SessionConfig, InternalConfig +from tools import users_collection, sessions_collection +import logging + + +async def get_pub_user( + session_token: str = Cookie(default=None, alias=SessionConfig.auto_cookie_name) +): + if not session_token: + logging.debug("No session token") + raise HTTPException(status_code=401) + session = sessions_collection.find_one({"session_token": session_token}) + if not session: + logging.debug("No session found") + raise HTTPException(status_code=401) + user = users_collection.find_one( + {"_id": session["user_id"]}, InternalConfig.internal_columns + ) + if not user: + logging.debug("No user for session found") + raise HTTPException(status_code=401) + return user diff --git a/src/api/internal.py b/src/api/internal.py index 0fce6e8..ad00088 100644 --- a/src/api/internal.py +++ b/src/api/internal.py @@ -51,3 +51,14 @@ async def broadcast_email( broadcast_request.mongodb_search_condition, ) return {"status": "E-Mail Task Started"} + + +@router.get("/profile") +async def profile(): + """ + # Get Profile Information + + ## Description + This endpoint is used to get the whole profile information of the user. (Including Internal Information) + """ + return {"status": "ok"} diff --git a/src/api/main.py b/src/api/main.py index 044fa78..89a3db0 100644 --- a/src/api/main.py +++ b/src/api/main.py @@ -4,6 +4,7 @@ from api.signup import router as signupRouter from api.login import router as loginRouter from api.internal import router as internalRouter +from api.profile import router as profileRouter import logging logging.basicConfig(format="%(asctime)s - %(message)s", level=logging.INFO) @@ -37,3 +38,4 @@ async def root(): app.include_router(signupRouter) app.include_router(loginRouter) app.include_router(internalRouter) +app.include_router(profileRouter) diff --git a/src/api/profile.py b/src/api/profile.py index e69de29..9f3a081 100644 --- a/src/api/profile.py +++ b/src/api/profile.py @@ -0,0 +1,20 @@ +from fastapi import APIRouter, Depends +from api.dependencies.authenticated import get_pub_user + + +router = APIRouter( + prefix="/profile", + tags=["Profile"], + dependencies=[Depends(get_pub_user)], +) + + +@router.get("/") +async def profile(user: dict = Depends(get_pub_user)): + """ + # Get Profile Information + + ## Description + This endpoint is used to get the public profile information of the user. + """ + return user diff --git a/src/tools/__init__.py b/src/tools/__init__.py index 88101fc..974fb09 100644 --- a/src/tools/__init__.py +++ b/src/tools/__init__.py @@ -1,3 +1,9 @@ -from .db import users_collection, sessions_collection, insecure_cols -from .conf import SignupConfig, EmailConfig, SessionConfig, InternalConfig +from .db import users_collection, sessions_collection +from .conf import ( + SignupConfig, + EmailConfig, + SessionConfig, + InternalConfig, + AccountFeaturesConfig, +) from .mail import send_email, broadcast_emails diff --git a/src/tools/conf.py b/src/tools/conf.py index 41ad099..93c82bf 100644 --- a/src/tools/conf.py +++ b/src/tools/conf.py @@ -1,7 +1,12 @@ import json +from collections import ChainMap config = json.load(open("/src/app/config/config.json", "rb")) +# Columns that should never leave EZAuth (maybe get more in the future) +insecure_cols = {"password": 0} +# Columns that can leave EZAuth but should only be used internally can be defined in config + class SignupConfig: enable_conf_email: bool = config["signup"]["enable_conf_email"] @@ -27,3 +32,11 @@ class SessionConfig: class InternalConfig: internal_api_key: str = config["internal"]["internal_api_key"] + internal_columns: dict = dict( + ChainMap(*[{col: 0} for col in config["internal"]["internal_columns"]]) + ) + internal_columns.update(insecure_cols) + + +class AccountFeaturesConfig: + enable_change_password: bool = config["account_features"]["enable_change_password"] diff --git a/src/tools/db.py b/src/tools/db.py index e990736..7a542bc 100644 --- a/src/tools/db.py +++ b/src/tools/db.py @@ -9,8 +9,10 @@ users_collection = db.get_collection("users") sessions_collection = db.get_collection("sessions") +# Find Users by email and username fast (id is already indexed) users_collection.create_index("email", unique=True) users_collection.create_index("username", unique=True) +# Find Sessions by session_token fast sessions_collection.create_index("session_token", unique=True) try: @@ -21,6 +23,3 @@ sessions_collection.create_index( "createdAt", expireAfterSeconds=SessionConfig.session_expiry_seconds ) - -# Columns that should never leave EZAuth (maybe get more in the future) -insecure_cols = {"password": 0} diff --git a/src/tools/mail.py b/src/tools/mail.py index 5ff5ce8..6a07952 100644 --- a/src/tools/mail.py +++ b/src/tools/mail.py @@ -4,7 +4,7 @@ from . import EmailConfig import logging from threading import Lock -from tools import users_collection, insecure_cols +from tools import users_collection, InternalConfig def load_template(template_name: str, **kwargs) -> str: @@ -39,7 +39,9 @@ def broadcast_emails( ): # Iter over all users and send them an email try: - cursor = users_collection.find(mongodb_search_condition, insecure_cols) + cursor = users_collection.find( + mongodb_search_condition, InternalConfig.internal_columns + ) for user in cursor: try: send_email(template_name, user["email"], **user)