From e3a58741968cf0c228fef1d4c29d7ec1d99b07b3 Mon Sep 17 00:00:00 2001
From: bsquillari <bsquillari@itba.edu.ar>
Date: Mon, 4 Dec 2023 14:41:39 +0000
Subject: [PATCH] Add role fields (user, airlie, admin)

---
 auth-domain/user-manager/manage.py            | 14 ++++++++++---
 auth-domain/user-manager/src/api/auth.py      |  4 ++--
 .../user-manager/src/api/models/users.py      | 21 ++++++++++++-------
 auth-domain/user-manager/src/api/users.py     |  2 +-
 .../src/tests/functional/test_auth.py         |  2 +-
 .../user-manager/src/tests/unit/test_users.py |  2 +-
 browser-domain/src/Types.d.ts                 |  2 +-
 browser-domain/src/useAuth.tsx                | 13 +++++++-----
 gateway/src/api/routes/auth.py                |  2 +-
 gateway/src/api/schemas/user.py               |  4 ++--
 10 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/auth-domain/user-manager/manage.py b/auth-domain/user-manager/manage.py
index 9f5ee0f..782c64a 100644
--- a/auth-domain/user-manager/manage.py
+++ b/auth-domain/user-manager/manage.py
@@ -1,7 +1,7 @@
 from flask.cli import FlaskGroup
 
 from src import create_app, db
-from src.api.models.users import User
+from src.api.models.users import Roles, User
 
 app = create_app()
 cli = FlaskGroup(create_app=create_app)
@@ -21,7 +21,7 @@ def seed_db():
             username="lufthansa",
             email="info@lufthansa.com",
             password="password1234",
-            airline=True,
+            role=Roles.airline,
         )
     )
     db.session.add(
@@ -29,7 +29,15 @@ def seed_db():
             username="ryanair",
             email="info@ryanair.com",
             password="password1234",
-            airline=True,
+            role=Roles.airline,
+        )
+    )
+    db.session.add(
+        User(
+            username="admin",
+            email="admin",
+            password="password1234",
+            role=Roles.admin,
         )
     )
     db.session.add(
diff --git a/auth-domain/user-manager/src/api/auth.py b/auth-domain/user-manager/src/api/auth.py
index a33575a..017ded9 100644
--- a/auth-domain/user-manager/src/api/auth.py
+++ b/auth-domain/user-manager/src/api/auth.py
@@ -34,7 +34,7 @@ class Login(Resource):
         if not user or not bcrypt.check_password_hash(user.password, password):
             auth_namespace.abort(404, "User does not exist")
 
-        access_token = user.encode_token(user.id, "access", user.airline)
+        access_token = user.encode_token(user.id, "access", user.role)
         refresh_token = user.encode_token(user.id, "refresh")
 
         response_object = {
@@ -62,7 +62,7 @@ class Refresh(Resource):
             if not user:
                 auth_namespace.abort(401, "Invalid token")
 
-            access_token = user.encode_token(user.id, "access", user.airline)
+            access_token = user.encode_token(user.id, "access", user.role)
             refresh_token = user.encode_token(user.id, "refresh")
 
             response_object = {
diff --git a/auth-domain/user-manager/src/api/models/users.py b/auth-domain/user-manager/src/api/models/users.py
index 992d775..1205617 100644
--- a/auth-domain/user-manager/src/api/models/users.py
+++ b/auth-domain/user-manager/src/api/models/users.py
@@ -1,4 +1,5 @@
 import datetime
+from enum import Enum
 
 import jwt
 from flask import current_app
@@ -8,6 +9,12 @@ from sqlalchemy.sql import func
 from src import bcrypt, db
 
 
+class Roles(Enum):
+    user = "user"
+    airline = "airline"
+    admin = "admin"
+
+
 class User(db.Model):
     __tablename__ = "users"
 
@@ -17,18 +24,18 @@ class User(db.Model):
     password = db.Column(db.String(255), nullable=False)
     active = db.Column(db.Boolean(), default=True, nullable=False)
     created_date = db.Column(db.DateTime, default=func.now(), nullable=False)
-    airline = db.Column(db.Boolean(), default=False, nullable=False)
+    role = db.Column(db.String(128), default=Roles.user.value, nullable=False)
 
-    def __init__(self, username, email, password, airline=False):
+    def __init__(self, username, email, password, role=Roles.user):
         self.username = username
         self.email = email
         self.password = bcrypt.generate_password_hash(
             password, current_app.config.get("BCRYPT_LOG_ROUNDS")
         ).decode()
-        self.airline = airline
+        self.role = role.value
 
     @staticmethod
-    def encode_token(user_id, token_type, airline=False):
+    def encode_token(user_id, token_type, role="user"):
         if token_type == "access":
             seconds = current_app.config.get("ACCESS_TOKEN_EXPIRATION")
         else:
@@ -38,7 +45,7 @@ class User(db.Model):
             "exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=seconds),
             "iat": datetime.datetime.utcnow(),
             "sub": user_id,
-            "airline": airline,
+            "role": role,
         }
         return jwt.encode(
             payload, current_app.config.get("SECRET_KEY"), algorithm="HS256"
@@ -60,7 +67,7 @@ class User(db.Model):
                 "username": fields.String(required=True),
                 "email": fields.String(required=True),
                 "created_date": fields.DateTime,
-                "airline": fields.Boolean(readOnly=True),
+                "role": fields.String(readOnly=True),
             },
         )
 
@@ -91,7 +98,7 @@ class User(db.Model):
             "User",
             {
                 "id": fields.Integer(required=True),
-                "airline": fields.Boolean(readOnly=True),
+                "role": fields.String(required=True),
             },
         )
 
diff --git a/auth-domain/user-manager/src/api/users.py b/auth-domain/user-manager/src/api/users.py
index 81e5566..05b927d 100644
--- a/auth-domain/user-manager/src/api/users.py
+++ b/auth-domain/user-manager/src/api/users.py
@@ -88,7 +88,7 @@ class Users(Resource):
             "username": user.username,
             "email": user.email,
             "created_date": user.created_date.strftime("%Y-%m-%d %H:%M:%S"),
-            "airline": user.airline,
+            "role": user.role,
         }
         return response_object, 200
 
diff --git a/auth-domain/user-manager/src/tests/functional/test_auth.py b/auth-domain/user-manager/src/tests/functional/test_auth.py
index 2f29906..096506a 100644
--- a/auth-domain/user-manager/src/tests/functional/test_auth.py
+++ b/auth-domain/user-manager/src/tests/functional/test_auth.py
@@ -110,7 +110,7 @@ def test_user_status(test_app, test_database, add_user):
     data = json.loads(resp.data.decode())
     assert resp.status_code == 200
     assert resp.content_type == "application/json"
-    assert data["role"] == 0
+    assert data["role"] == "user"
     assert "password" not in data
 
 
diff --git a/auth-domain/user-manager/src/tests/unit/test_users.py b/auth-domain/user-manager/src/tests/unit/test_users.py
index 5baffc9..5465894 100644
--- a/auth-domain/user-manager/src/tests/unit/test_users.py
+++ b/auth-domain/user-manager/src/tests/unit/test_users.py
@@ -190,7 +190,7 @@ def test_update_user(test_app, monkeypatch):
                 "username": username,
                 "email": email,
                 "created_date": datetime.now(),
-                "airline": False,
+                "role": "user",
             }
         )
         return d
diff --git a/browser-domain/src/Types.d.ts b/browser-domain/src/Types.d.ts
index 81b8ce7..574d4c3 100644
--- a/browser-domain/src/Types.d.ts
+++ b/browser-domain/src/Types.d.ts
@@ -11,7 +11,7 @@ export interface Token {
 
 export interface TokenData {
     sub: string;
-    airline: boolean;
+    role: string;
 }
 
 export interface User {
diff --git a/browser-domain/src/useAuth.tsx b/browser-domain/src/useAuth.tsx
index 9d8e993..4415a47 100644
--- a/browser-domain/src/useAuth.tsx
+++ b/browser-domain/src/useAuth.tsx
@@ -30,6 +30,7 @@ export function AuthProvider({
     const [loading, setLoading] = useState<boolean>(false);
     const [loadingInitial, setLoadingInitial] = useState<boolean>(true);
     const [isAirline, setIsAirline] = useState(false);
+    const [isAdmin, setIsAdmin] = useState(false);
     const navigate = useNavigate();
 
     useEffect(() => {
@@ -39,10 +40,11 @@ export function AuthProvider({
     useEffect(() => {
         const existingToken = localStorage.getItem("token");
         if (existingToken) {
-            let airline
+            let role
             try {
-                airline = (jwt_decode(existingToken) as TokenData).airline;
-                setIsAirline(airline)
+                role = (jwt_decode(existingToken) as TokenData).role;
+                setIsAirline(role == "airline")
+                setIsAdmin(role == "admin")
             } catch (err) {
                 setLoadingInitial(false);
                 logout()
@@ -73,8 +75,9 @@ export function AuthProvider({
         const tokens = logIn(credentials)
             .then((x) => {
                 localStorage.setItem("token", x.access_token);
-                const airline = (jwt_decode(x.access_token) as TokenData).airline;
-                setIsAirline(airline)
+                const role = (jwt_decode(x.access_token) as TokenData).role;
+                setIsAirline(role == "airline")
+                setIsAdmin(role == "admin")
                 const user = fetchUserById(x.user_id as number, x.access_token)
                     .then(y => {
                         setUser(y);
diff --git a/gateway/src/api/routes/auth.py b/gateway/src/api/routes/auth.py
index b404917..782f457 100644
--- a/gateway/src/api/routes/auth.py
+++ b/gateway/src/api/routes/auth.py
@@ -55,7 +55,7 @@ async def checkAuth(
 ):
     response = await status(req, authorization)
     if isAirline:
-        if response["role"] == 1:
+        if response["role"] == "airline":
             return response["id"]
         else:
             raise HTTPException(
diff --git a/gateway/src/api/schemas/user.py b/gateway/src/api/schemas/user.py
index 8f49615..daee193 100644
--- a/gateway/src/api/schemas/user.py
+++ b/gateway/src/api/schemas/user.py
@@ -6,7 +6,7 @@ class User(BaseModel):
     username: str
     email: str
     created_date: str
-    airline: bool
+    role: str
 
 
 class UserMin(BaseModel):
@@ -17,7 +17,7 @@ class UserMin(BaseModel):
 
 class UserStatus(BaseModel):
     id: int
-    airline: bool
+    role: str
 
 
 class UserRegister(BaseModel):