-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from exactpro/release-8.1.0
Release 8.1.0
- Loading branch information
Showing
287 changed files
with
10,771 additions
and
7,210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
FROM python:3.8-slim | ||
|
||
WORKDIR /app | ||
|
||
USER root | ||
|
||
RUN apt-get -y update && apt-get install -y libpq-dev gcc \ | ||
&& pip install --upgrade pip | ||
|
||
RUN pip3 install poetry==1.1.4 | ||
|
||
COPY poetry.lock pyproject.toml /app/ | ||
|
||
RUN poetry install --no-dev | ||
|
||
COPY . /app/ |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
from passlib.context import CryptContext | ||
|
||
from models.User import User | ||
from models.UserSettings import UserSettings | ||
from models.UserFilter import UserFilter | ||
from models.UserQAMetricsFilter import UserQAMetricsFilter | ||
from models.UserPredictionsTable import UserPredictionsTable | ||
|
||
from serializers import UserSerializer | ||
|
||
from settings.settings import init_filters, init_predictions_table | ||
|
||
|
||
async def create_user(user: UserSerializer) -> None: | ||
"""Creates a new User with default settings. | ||
:param user: New user. | ||
""" | ||
user = user.dict() | ||
user["password"] = CryptContext(schemes=["sha256_crypt"]).hash( | ||
user["password"] | ||
) | ||
user["email"] = user["email"].lower().strip() | ||
user["name"] = user["name"].strip() | ||
user = await User(**user).create() | ||
user_settings = await UserSettings(user_id=user.id).create() | ||
|
||
await init_filters(UserFilter, user_settings.id) | ||
await init_filters(UserQAMetricsFilter, user_settings.id) | ||
await init_predictions_table(UserPredictionsTable, user_settings.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import json | ||
from typing import Dict, Union | ||
|
||
|
||
from fastapi import HTTPException | ||
from passlib.context import CryptContext | ||
|
||
from authentication.token import create_token | ||
from models.User import User | ||
|
||
from serializers import UserCredentialsSerializer | ||
from database import create_session | ||
|
||
|
||
DEFAULT_ERROR_CODE = 500 | ||
|
||
|
||
def auth_user( | ||
user_data: UserCredentialsSerializer, | ||
) -> Dict[str, Union[str, int]]: | ||
"""Authenticates User. | ||
:param user_data: User credentials. | ||
:return: Authenticated User with JWT. | ||
""" | ||
with create_session() as db: | ||
user = ( | ||
db.query(User) | ||
.filter(User.email == user_data.credentials.lower().strip()) | ||
.first() | ||
) | ||
if not user: | ||
user = ( | ||
db.query(User) | ||
.filter( | ||
User.name == user_data.credentials.strip(), | ||
) | ||
.first() | ||
) | ||
if not user: | ||
raise HTTPException( | ||
status_code=DEFAULT_ERROR_CODE, | ||
detail="Incorrect username or password.", | ||
) | ||
if not CryptContext(schemes=["sha256_crypt"]).verify( | ||
user_data.password, user.password | ||
): | ||
raise HTTPException( | ||
status_code=DEFAULT_ERROR_CODE, | ||
detail="Incorrect username or password.", | ||
) | ||
|
||
response = { | ||
"id": str(user.id), | ||
"name": user.name, | ||
"email": user.email, | ||
"token": create_token(user), | ||
} | ||
return response |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import os | ||
|
||
import jwt | ||
from datetime import datetime, timedelta | ||
from fastapi import HTTPException | ||
|
||
from database import create_session | ||
from models.User import User | ||
|
||
ACCESS_TOKEN_EXPIRE_WEEKS = 15 | ||
|
||
SECRET_KEY_DEFAULT = "v$1bx=+6#ibt4a$4i&5i8stwjqzm+3=tjsde9iku1a0w(u6bfy" | ||
SECRET_KEY = os.environ.get("SECRET_KEY", default=SECRET_KEY_DEFAULT) | ||
|
||
|
||
def create_token(user: User) -> str: | ||
"""Generates JSON Web Token. | ||
:param user: User to be authenticated. | ||
:return: JSON Web Token. | ||
""" | ||
raw_token = { | ||
"exp": datetime.utcnow() + timedelta(weeks=ACCESS_TOKEN_EXPIRE_WEEKS), | ||
"id": str(user.id), | ||
} | ||
token = jwt.encode(raw_token, key=SECRET_KEY).decode("UTF-8") | ||
return token | ||
|
||
|
||
def decode_jwt(token: str) -> str: | ||
"""Checks JSON Web Token and return user id. | ||
:param token: JSON Web Token. | ||
:return: User id. | ||
""" | ||
# To avoid a circular dependency | ||
from authentication.sign_in import DEFAULT_ERROR_CODE | ||
|
||
try: | ||
decoded_token = jwt.decode(jwt=token, key=SECRET_KEY) | ||
except jwt.DecodeError: | ||
raise HTTPException(status_code=DEFAULT_ERROR_CODE, detail="Token is invalid") | ||
except jwt.ExpiredSignatureError: | ||
raise HTTPException(status_code=DEFAULT_ERROR_CODE, detail="Token expired") | ||
with create_session() as db: | ||
user = db.query(User).filter(User.id == decoded_token.get("id")).first() | ||
|
||
if not user: | ||
raise HTTPException( | ||
status_code=DEFAULT_ERROR_CODE, detail="User not found", | ||
) | ||
return str(user.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import os | ||
from contextlib import contextmanager | ||
|
||
from sqlalchemy import create_engine | ||
from sqlalchemy.ext.declarative import declarative_base | ||
from sqlalchemy.orm import sessionmaker, Session | ||
|
||
|
||
DB_NAME = os.environ.get("POSTGRES_DB", "users") | ||
DB_USER = os.environ.get("POSTGRES_USER", "postgres_user") | ||
DB_PASSWORD = os.environ.get("POSTGRES_PASSWORD", "postgres_password") | ||
DB_HOST = os.environ.get("POSTGRES_HOST", "localhost") | ||
DB_PORT = os.environ.get("POSTGRES_PORT", "5432") | ||
DB_URL = f"postgresql://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}" | ||
|
||
engine = create_engine(DB_URL) | ||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | ||
|
||
Base = declarative_base() | ||
|
||
|
||
@contextmanager | ||
def create_session() -> Session: | ||
"""Create database session. | ||
:return: Database session. | ||
""" | ||
session = SessionLocal() | ||
try: | ||
yield session | ||
finally: | ||
session.close() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
from fastapi import FastAPI | ||
from fastapi.encoders import jsonable_encoder | ||
from fastapi.exceptions import RequestValidationError, HTTPException | ||
from fastapi.responses import JSONResponse | ||
from starlette.requests import Request | ||
from starlette.responses import Response | ||
|
||
from authentication.token import decode_jwt | ||
from authentication.sign_in import auth_user | ||
from authentication.register import create_user | ||
|
||
from database import engine, Base | ||
|
||
from serializers import ( | ||
UserSerializer, | ||
UserCredentialsSerializer, | ||
AuthResponseSerializer, | ||
VerifyTokenResponse, | ||
) | ||
|
||
|
||
TABLES_TO_CREATE = ( | ||
Base.metadata.tables["users"], | ||
Base.metadata.tables["user_settings"], | ||
Base.metadata.tables["user_filter"], | ||
Base.metadata.tables["user_qa_metrics_filter"], | ||
Base.metadata.tables["user_predictions_table"], | ||
) | ||
|
||
Base.metadata.create_all(bind=engine, tables=TABLES_TO_CREATE) | ||
|
||
app = FastAPI() | ||
|
||
|
||
@app.exception_handler(HTTPException) | ||
async def http_exception_handler(request, exc): | ||
return JSONResponse( | ||
content={"exception": {"detail": exc.detail}}, status_code=exc.status_code, | ||
) | ||
|
||
|
||
@app.exception_handler(RequestValidationError) | ||
async def validation_exception_handler(request: Request, exc: RequestValidationError): | ||
exception_detail = [ | ||
{"name": error.get("loc")[1], "errors": [error.get("msg")]} | ||
for error in exc.errors() | ||
] | ||
|
||
return JSONResponse( | ||
status_code=400, content=jsonable_encoder({"exception": exception_detail}), | ||
) | ||
|
||
|
||
@app.post("/sign_in/", response_model=AuthResponseSerializer) | ||
def sign_in(user: UserCredentialsSerializer): | ||
user = auth_user(user) | ||
return JSONResponse(user) | ||
|
||
|
||
@app.post("/register/") | ||
async def register(user: UserSerializer): | ||
await create_user(user) | ||
return Response(status_code=200) | ||
|
||
|
||
@app.get("/verify_token/", response_model=VerifyTokenResponse) | ||
def verify_token(token: str): | ||
response = {"id": decode_jwt(token)} | ||
return JSONResponse(response) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
from sqlalchemy import Column, String, Integer | ||
from sqlalchemy.orm import relationship | ||
|
||
from database import Base, create_session | ||
|
||
|
||
class User(Base): | ||
__tablename__ = "users" | ||
|
||
id = Column( | ||
Integer, primary_key=True, index=True, unique=True, autoincrement=True | ||
) | ||
email = Column(String, unique=True) | ||
name = Column( | ||
String, | ||
unique=True, | ||
) | ||
password = Column(String) | ||
|
||
user_settings = relationship( | ||
"UserSettings", | ||
uselist=False, | ||
back_populates="user", | ||
cascade="all, delete, delete-orphan", | ||
) | ||
|
||
def __repr__(self): | ||
return ( | ||
f"<User(id='{self.id}', email='{self.email}', name='{self.name}')>" | ||
) | ||
|
||
async def create(self): | ||
with create_session() as db: | ||
db.add(self) | ||
db.commit() | ||
db.refresh(self) | ||
return self |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from sqlalchemy import Column, ForeignKey, String, Integer | ||
from sqlalchemy.orm import relationship | ||
|
||
from database import Base, create_session | ||
|
||
|
||
class UserFilter(Base): | ||
__tablename__ = "user_filter" | ||
|
||
id = Column( | ||
Integer, primary_key=True, index=True, unique=True, autoincrement=True | ||
) | ||
settings_id = Column( | ||
Integer, ForeignKey("user_settings.id", ondelete="CASCADE") | ||
) | ||
name = Column(String) | ||
type = Column(String) | ||
|
||
user_settings = relationship("UserSettings", back_populates="user_filter") | ||
|
||
def __repr__(self): | ||
return ( | ||
f"<UserFilter(id='{self.id}', name='{self.name}', " | ||
f"type='{self.type}', settings_id='{self.settings_id}')>" | ||
) | ||
|
||
async def create(self): | ||
with create_session() as db: | ||
db.add(self) | ||
db.commit() | ||
db.refresh(self) | ||
return self |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean | ||
from sqlalchemy.orm import relationship | ||
|
||
from database import Base, create_session | ||
|
||
|
||
class UserPredictionsTable(Base): | ||
__tablename__ = "user_predictions_table" | ||
|
||
id = Column( | ||
Integer, primary_key=True, index=True, unique=True, autoincrement=True | ||
) | ||
settings_id = Column( | ||
Integer, ForeignKey("user_settings.id", ondelete="CASCADE") | ||
) | ||
name = Column(String) | ||
is_default = Column(Boolean) | ||
position = Column(Integer) | ||
|
||
user_settings = relationship( | ||
"UserSettings", back_populates="user_predictions_table" | ||
) | ||
|
||
def __repr__(self): | ||
return ( | ||
f"<UserPredictionsTable(id='{self.id}', name='{self.name}', " | ||
f"is_default='{self.is_default}', position='{self.position}', " | ||
f"settings_id='{self.settings_id}')>" | ||
) | ||
|
||
async def create(self): | ||
with create_session() as db: | ||
db.add(self) | ||
db.commit() | ||
db.refresh(self) | ||
return self |
Oops, something went wrong.