utils.py 3.72 KB
Newer Older
1
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
Timothy J. Baek's avatar
Timothy J. Baek committed
2
from fastapi import HTTPException, status, Depends, Request
Timothy J. Baek's avatar
refac  
Timothy J. Baek committed
3

4
from apps.webui.models.users import Users
liu.vaayne's avatar
liu.vaayne committed
5

6
from typing import Union, Optional
7
from constants import ERROR_MESSAGES
8
9
10
from passlib.context import CryptContext
from datetime import datetime, timedelta
import jwt
liu.vaayne's avatar
liu.vaayne committed
11
import uuid
Timothy J. Baek's avatar
Timothy J. Baek committed
12
import logging
13
14
import config

Timothy J. Baek's avatar
Timothy J. Baek committed
15
16
17
logging.getLogger("passlib").setLevel(logging.ERROR)


18
SESSION_SECRET = config.WEBUI_SECRET_KEY
19
20
21
22
23
24
ALGORITHM = "HS256"

##############
# Auth Utils
##############

Timothy J. Baek's avatar
Timothy J. Baek committed
25
bearer_security = HTTPBearer(auto_error=False)
26
27
28
29
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def verify_password(plain_password, hashed_password):
Timothy J. Baek's avatar
Timothy J. Baek committed
30
31
32
    return (
        pwd_context.verify(plain_password, hashed_password) if hashed_password else None
    )
33
34
35
36
37
38


def get_password_hash(password):
    return pwd_context.hash(password)


Timothy J. Baek's avatar
Timothy J. Baek committed
39
def create_token(data: dict, expires_delta: Union[timedelta, None] = None) -> str:
40
41
42
43
44
45
    payload = data.copy()

    if expires_delta:
        expire = datetime.utcnow() + expires_delta
        payload.update({"exp": expire})

46
    encoded_jwt = jwt.encode(payload, SESSION_SECRET, algorithm=ALGORITHM)
47
48
49
50
51
    return encoded_jwt


def decode_token(token: str) -> Optional[dict]:
    try:
52
        decoded = jwt.decode(token, SESSION_SECRET, algorithms=[ALGORITHM])
53
        return decoded
54
    except Exception:
55
56
57
58
        return None


def extract_token_from_auth_header(auth_header: str):
Timothy J. Baek's avatar
Timothy J. Baek committed
59
    return auth_header[len("Bearer ") :]
60
61


liu.vaayne's avatar
liu.vaayne committed
62
63
64
65
66
def create_api_key():
    key = str(uuid.uuid4()).replace("-", "")
    return f"sk-{key}"


Timothy J. Baek's avatar
Timothy J. Baek committed
67
68
69
def get_http_authorization_cred(auth_header: str):
    try:
        scheme, credentials = auth_header.split(" ")
Timothy J. Baek's avatar
Timothy J. Baek committed
70
        return HTTPAuthorizationCredentials(scheme=scheme, credentials=credentials)
71
    except Exception:
Timothy J. Baek's avatar
Timothy J. Baek committed
72
73
74
        raise ValueError(ERROR_MESSAGES.INVALID_TOKEN)


Timothy J. Baek's avatar
Timothy J. Baek committed
75
def get_current_user(
Timothy J. Baek's avatar
Timothy J. Baek committed
76
    request: Request,
Timothy J. Baek's avatar
Timothy J. Baek committed
77
78
    auth_token: HTTPAuthorizationCredentials = Depends(bearer_security),
):
Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
79
    token = None
Timothy J. Baek's avatar
Timothy J. Baek committed
80
81
82
83

    if auth_token is not None:
        token = auth_token.credentials

Timothy J. Baek's avatar
fix  
Timothy J. Baek committed
84
85
86
87
88
89
    if token is None and "token" in request.cookies:
        token = request.cookies.get("token")

    if token is None:
        raise HTTPException(status_code=403, detail="Not authenticated")

liu.vaayne's avatar
liu.vaayne committed
90
    # auth by api key
Timothy J. Baek's avatar
Timothy J. Baek committed
91
    if token.startswith("sk-"):
92
        return get_current_user_by_api_key(token)
Timothy J. Baek's avatar
Timothy J. Baek committed
93

liu.vaayne's avatar
liu.vaayne committed
94
    # auth by jwt token
Timothy J. Baek's avatar
Timothy J. Baek committed
95
    data = decode_token(token)
96
    if data is not None and "id" in data:
97
        user = Users.get_user_by_id(data["id"])
98
99
100
101
        if user is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail=ERROR_MESSAGES.INVALID_TOKEN,
102
            )
Timothy J. Baek's avatar
Timothy J. Baek committed
103
        else:
104
            Users.update_user_last_active_by_id(user.id)
105
        return user
106
107
108
109
110
    else:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=ERROR_MESSAGES.UNAUTHORIZED,
        )
111

Timothy J. Baek's avatar
Timothy J. Baek committed
112

Jonathan Rohde's avatar
Jonathan Rohde committed
113
114
def get_current_user_by_api_key(api_key: str):
    user = Users.get_user_by_api_key(api_key)
Timothy J. Baek's avatar
Timothy J. Baek committed
115

liu.vaayne's avatar
liu.vaayne committed
116
117
118
119
120
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=ERROR_MESSAGES.INVALID_TOKEN,
        )
Timothy J. Baek's avatar
Timothy J. Baek committed
121
    else:
Jonathan Rohde's avatar
Jonathan Rohde committed
122
        Users.update_user_last_active_by_id(user.id)
Timothy J. Baek's avatar
Timothy J. Baek committed
123

liu.vaayne's avatar
liu.vaayne committed
124
    return user
125

Timothy J. Baek's avatar
Timothy J. Baek committed
126

Timothy J. Baek's avatar
Timothy J. Baek committed
127
def get_verified_user(user=Depends(get_current_user)):
128
129
130
131
132
    if user.role not in {"user", "admin"}:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
        )
Timothy J. Baek's avatar
Timothy J. Baek committed
133
    return user
134
135


Timothy J. Baek's avatar
Timothy J. Baek committed
136
def get_admin_user(user=Depends(get_current_user)):
137
138
139
140
141
    if user.role != "admin":
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=ERROR_MESSAGES.ACCESS_PROHIBITED,
        )
Timothy J. Baek's avatar
Timothy J. Baek committed
142
    return user