From 98b9aa2e76ed440153652ace37f63d1c39742d09 Mon Sep 17 00:00:00 2001 From: ablogo Date: Mon, 22 Dec 2025 17:05:49 -0600 Subject: [PATCH 1/3] change the way to get the env variables --- .env | 23 +++++++++++++---------- .github/workflows/docker-image.yml | 2 +- Dockerfile | 4 +--- README.md | 24 +++++++++++++++++++++--- config.ini | 12 ------------ src/dependencies.py | 10 +++++----- src/dependency_injection/containers.py | 15 +++++++++------ src/main.py | 8 ++++---- src/services/chat_service.py | 13 +++++++------ src/services/jwt_service.py | 16 ++++++++-------- src/services/mongodb_service.py | 2 +- src/services/user_service.py | 9 +++++---- 12 files changed, 75 insertions(+), 63 deletions(-) delete mode 100644 config.ini diff --git a/.env b/.env index 19d794b..ce2dcad 100644 --- a/.env +++ b/.env @@ -1,10 +1,13 @@ -MONGO_URL = -DB_NAME = development -DB_USERS_COLLECTION = "users" -DB_USERS_PICTURES_COLLECTION = "users.pictures" -DB_USERS_CONTACTS_COLLECTION = "users.contacts" -DB_USERS_MESSAGES_COLLECTION = "users.messages" -SECRET_KEY = "" -ALGORITHM = "HS256" -EXPIRE_MINUTES = 15 -CORS_ALLOWED_HOSTS = "http://localhost:8081" \ No newline at end of file +DB_URL= +DB_NAME=development +DB_USERS_COLLECTION=users +DB_USERS_PICTURES_COLLECTION=users.pictures +DB_USERS_CONTACTS_COLLECTION=users.contacts +DB_USERS_MESSAGES_COLLECTION=users.messages +LOG_DB_URL= +LOG_DATABASE_NAME=auth-logs +LOG_LEVEL=DEBUG +JWT_SECRET_KEY= +JWT_ALGORITHM=HS256 +JWT_EXPIRE_MINUTES=15 +CORS_ALLOWED_HOSTS="http://localhost:8081" \ No newline at end of file diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index adb73aa..0ab7e9a 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -38,5 +38,5 @@ jobs: with: context: . push: true - tags: ${{ steps.meta.outputs.tags }} + tags: auth-service:latest labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile index dca04c4..9f78b36 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,9 @@ -FROM python:3.13-slim +FROM python:3.12-slim WORKDIR /app COPY ./requirements.txt ./ COPY ./*.pem ./ -COPY .env ./ -COPY *.ini ./ RUN pip install --no-cache-dir --upgrade -r /app/requirements.txt diff --git a/README.md b/README.md index 7ea1d13..c81f151 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ It is a microservice for user administration and authenication, using JWT tokens ## Requirements - Python 3.12+ -- FastApi 0.117+ +- FastApi 0.124+ ## Installing 1. Create a virtual environment @@ -35,9 +35,9 @@ Use the private key file to extract the public key in PEM format ```bash openssl rsa -in private_key.pem -pubout -out public_key.pem ``` -5. Set configurations files (.env, config.ini) for different purposes (mongodb, JWT, CORS, logs) +5. Set configurations files (.env) for different purposes (mongodb, JWT, CORS, logs) -The microservice uses mongoDB as its database, so the connection string and other configurations must be included in the configuration files +The microservice uses mongoDB as its database, so the connection string and other configurations must be included in the configuration file 6. Run local development server ```bash @@ -48,5 +48,23 @@ uvicorn src.main:app --reload http://127.0.0.1:8000/docs ``` +## Using with Docker + +1. Create the image +```bash +docker build -t auth-service:latest . +``` + Or download the image hosted in this repository. +```bash +docker pull ghcr.io/ablogo/authfastapi:latest +``` +2. Run a container from the image previously created +```bash +docker run -p 8000:80 --env-file .env auth-service:latest +``` + +> [!IMPORTANT] +> It is necessary to complete the configuration file(.env), and create the PEM files and place them in the root folder + > [!NOTE] > Since the project is used for learning, it does not strictly follow the concept of microservices, where each microservice should have its own realm of responsability diff --git a/config.ini b/config.ini deleted file mode 100644 index bb9ac36..0000000 --- a/config.ini +++ /dev/null @@ -1,12 +0,0 @@ -[database] -url= -name=development - -[log] -db_url= -db_database=auth-logs -db_collection= -level=DEBUG - -[auth] -token_ttl=3600 \ No newline at end of file diff --git a/src/dependencies.py b/src/dependencies.py index 44ab972..eb7598d 100644 --- a/src/dependencies.py +++ b/src/dependencies.py @@ -1,11 +1,11 @@ -from dotenv import dotenv_values from pymongo import AsyncMongoClient from pymongo.server_api import ServerApi +from dotenv import load_dotenv +import os -config = dotenv_values(".env") - -client = AsyncMongoClient(config["MONGO_URL"], server_api= ServerApi(version='1', strict=True, deprecation_errors=True)) -database = client.get_database(config["DB_NAME"]) +load_dotenv() +client = AsyncMongoClient(os.environ["DB_URL"], server_api= ServerApi(version='1', strict=True, deprecation_errors=True)) +database = client.get_database(os.environ["DB_NAME"]) async def get_db(): try: diff --git a/src/dependency_injection/containers.py b/src/dependency_injection/containers.py index e9ff6f3..5e517d2 100644 --- a/src/dependency_injection/containers.py +++ b/src/dependency_injection/containers.py @@ -1,9 +1,12 @@ from dependency_injector import containers, providers +import os +from dotenv import load_dotenv from src.services import mongodb_service from src.services.crypto_service import CryptoService from src.logging.mongo_logging import MongoLogger +load_dotenv() class Container(containers.DeclarativeContainer): @@ -18,20 +21,20 @@ class Container(containers.DeclarativeContainer): "src.routers.products_router" ]) - config = providers.Configuration(ini_files=["config.ini"]) + #config = providers.Configuration(ini_files=["config.ini"]) logging = providers.Singleton( MongoLogger, - config.log.db_url, - config.log.db_database, + os.environ["LOG_DB_URL"], #config.log.db_url, + os.environ["LOG_DATABASE_NAME"], #config.log.db_database, "", - config.log.level + os.environ["LOG_LEVEL"], #config.log.level ) database_client = providers.Singleton( mongodb_service.MongoAsyncService, - config.database.url, - config.database.name + os.environ["DB_URL"], #config.database.url, + os.environ["DB_NAME"], #config.database.name ) crypto_service = providers.Singleton( diff --git a/src/main.py b/src/main.py index 7fa65e3..caf71ec 100644 --- a/src/main.py +++ b/src/main.py @@ -1,7 +1,8 @@ from fastapi import FastAPI from contextlib import asynccontextmanager -from dotenv import dotenv_values from fastapi.middleware.cors import CORSMiddleware +from dotenv import load_dotenv +import os from src.routers import auth_router, products_router, users_router, chat_router from src.routers.admin import users_router as admin_user_router @@ -10,9 +11,8 @@ from src.dependency_injection.containers import Container from src.dependencies import close_db -config = dotenv_values(".env") - -origins = config["CORS_ALLOWED_HOSTS"].split(',') if config["CORS_ALLOWED_HOSTS"] else [] +load_dotenv() +origins = os.environ["CORS_ALLOWED_HOSTS"].split(',') if os.environ["CORS_ALLOWED_HOSTS"] else [] @asynccontextmanager async def lifespan(app: FastAPI): diff --git a/src/services/chat_service.py b/src/services/chat_service.py index 54b778a..8222399 100644 --- a/src/services/chat_service.py +++ b/src/services/chat_service.py @@ -1,6 +1,7 @@ from datetime import datetime from dependency_injector.wiring import Provide, inject -from dotenv import dotenv_values +from dotenv import load_dotenv +import os from src.services.user_service import get_user from src.services.crypto_service import CryptoService @@ -10,15 +11,15 @@ from src.logging.mongo_logging import MongoLogger from src.dependency_injection.containers import Container +load_dotenv() crypto_service: CryptoService = Provide[Container.crypto_service] db_dependency: MongoAsyncService = Provide[Container.database_client] logger: MongoLogger = Provide[Container.logging] -config = dotenv_values(".env") -users_collection = str(config["DB_USERS_COLLECTION"]) -users_contacts_collection = str(config["DB_USERS_CONTACTS_COLLECTION"]) -users_messages_collection = str(config["DB_USERS_MESSAGES_COLLECTION"]) +users_collection = str(os.environ["DB_USERS_COLLECTION"]) +users_contacts_collection = str(os.environ["DB_USERS_CONTACTS_COLLECTION"]) +users_messages_collection = str(os.environ["DB_USERS_MESSAGES_COLLECTION"]) + - @inject async def get_contacts(email: str, db = db_dependency, log = logger) -> list[Contact] | None: try: diff --git a/src/services/jwt_service.py b/src/services/jwt_service.py index 719dc22..6765d17 100644 --- a/src/services/jwt_service.py +++ b/src/services/jwt_service.py @@ -1,8 +1,8 @@ from datetime import datetime, timedelta, timezone from dependency_injector.wiring import Provide, inject -from dotenv import dotenv_values -from fastapi import HTTPException, Header, Request -import jwt +from fastapi import HTTPException, Request +from dotenv import load_dotenv +import os, jwt from src.services.crypto_service import CryptoService from src.logging.mongo_logging import MongoLogger @@ -10,17 +10,17 @@ crypto_service: CryptoService = Provide[Container.crypto_service] log_service: MongoLogger = Provide[Container.logging] -config = dotenv_values(".env") +load_dotenv() @inject -async def create_token(data: dict, expire_time: timedelta = timedelta(minutes=int(str(config["EXPIRE_MINUTES"]))), crypto = crypto_service, log = log_service): +async def create_token(data: dict, expire_time: timedelta = timedelta(minutes=int(str(os.environ["JWT_EXPIRE_MINUTES"]))), crypto = crypto_service, log = log_service): try: for item in data: data[item] = await crypto.encrypt_text(data[item]) expire = datetime.now(timezone.utc) + expire_time data.update({ "exp": expire }) - encode_jwt = jwt.encode(data, str(config["SECRET_KEY"]), algorithm= config["ALGORITHM"]) + encode_jwt = jwt.encode(data, str(os.environ["JWT_SECRET_KEY"]), algorithm= os.environ["JWT_ALGORITHM"]) return encode_jwt except Exception as e: log.logger.error(e) @@ -53,7 +53,7 @@ async def verify_token_from_requests(request: Request): @inject async def verify(request_token: str, log = log_service): try: - payload = jwt.decode(request_token, str(config["SECRET_KEY"]), config["ALGORITHM"]) + payload = jwt.decode(request_token, str(os.environ["JWT_SECRET_KEY"]), os.environ["JWT_ALGORITHM"]) return payload except jwt.ExpiredSignatureError as e: log.logger.error(e) @@ -68,7 +68,7 @@ async def verify(request_token: str, log = log_service): @inject async def get_email(token, crypto = crypto_service, log = log_service): try: - payload = jwt.decode(token, str(config["SECRET_KEY"]), config["ALGORITHM"]) + payload = jwt.decode(token, str(os.environ["JWT_SECRET_KEY"]), os.environ["JWT_ALGORITHM"]) email = await crypto.decrypt_text(payload.get("sub")) if email is None: return None diff --git a/src/services/mongodb_service.py b/src/services/mongodb_service.py index 7798997..4e37ed0 100644 --- a/src/services/mongodb_service.py +++ b/src/services/mongodb_service.py @@ -40,4 +40,4 @@ def close_db(self): try: self.client.close() except Exception as e: - raise e \ No newline at end of file + raise e diff --git a/src/services/user_service.py b/src/services/user_service.py index b02e826..60a7120 100644 --- a/src/services/user_service.py +++ b/src/services/user_service.py @@ -2,7 +2,8 @@ from fastapi import UploadFile from pymongo.asynchronous.database import AsyncDatabase from bson import ObjectId, Binary -from dotenv import dotenv_values +from dotenv import load_dotenv +import os from src.services.crypto_service import CryptoService from src.services.jwt_service import get_email @@ -14,9 +15,9 @@ crypto_service: CryptoService = Provide[Container.crypto_service] log_service: MongoLogger = Provide[Container.logging] -config = dotenv_values(".env") -users_collection = str(config["DB_USERS_COLLECTION"]) -users_pics_collection = str(config["DB_USERS_PICTURES_COLLECTION"]) +load_dotenv() +users_collection = str(os.environ["DB_USERS_COLLECTION"]) +users_pics_collection = str(os.environ["DB_USERS_PICTURES_COLLECTION"]) @inject async def get_user(email: str, db: AsyncDatabase, log = log_service) -> User | None: From 96ae04216411837805cf285c2975c5a7745fa8ca Mon Sep 17 00:00:00 2001 From: ablogo Date: Mon, 22 Dec 2025 17:21:39 -0600 Subject: [PATCH 2/3] add permissions on docker action --- .github/workflows/docker-image.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 0ab7e9a..fc995db 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -15,6 +15,8 @@ jobs: permissions: packages: write contents: read + attestations: write + id-token: write steps: - name: Check out the repo uses: actions/checkout@v5 From 6e8e2928cb87ec61e19d3f5ba3303947c09de8fb Mon Sep 17 00:00:00 2001 From: ablogo Date: Mon, 22 Dec 2025 17:42:37 -0600 Subject: [PATCH 3/3] fix docker action --- .github/workflows/docker-image.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index fc995db..adb73aa 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -15,8 +15,6 @@ jobs: permissions: packages: write contents: read - attestations: write - id-token: write steps: - name: Check out the repo uses: actions/checkout@v5 @@ -40,5 +38,5 @@ jobs: with: context: . push: true - tags: auth-service:latest + tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}