-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(api): project token management (#617)
* feat(api): project token management * tests + alembic revision Signed-off-by: inimaz <93inigo93@gmail.com> * fix: default access level is WRITE --------- Signed-off-by: inimaz <93inigo93@gmail.com>
- Loading branch information
Showing
11 changed files
with
460 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import abc | ||
|
||
from carbonserver.api import schemas | ||
|
||
|
||
class ProjectTokens(abc.ABC): | ||
@abc.abstractmethod | ||
def add_project_token(self, project_id: str, project: schemas.ProjectTokenCreate): | ||
raise NotImplementedError | ||
|
||
@abc.abstractmethod | ||
def delete_project_token(self, project_id: str, token_id: str): | ||
raise NotImplementedError | ||
|
||
@abc.abstractmethod | ||
def list_project_tokens(self, project_id: str): | ||
raise NotImplementedError |
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
76 changes: 76 additions & 0 deletions
76
carbonserver/carbonserver/api/infra/repositories/repository_projects_tokens.py
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,76 @@ | ||
from contextlib import AbstractContextManager | ||
|
||
from dependency_injector.providers import Callable | ||
from fastapi import HTTPException | ||
|
||
from carbonserver.api.domain.project_tokens import ProjectTokens | ||
from carbonserver.api.infra.api_key_service import generate_api_key | ||
from carbonserver.api.infra.database.sql_models import ( | ||
ProjectToken as SqlModelProjectToken, | ||
) | ||
from carbonserver.api.schemas import ProjectToken, ProjectTokenCreate | ||
|
||
|
||
class SqlAlchemyRepository(ProjectTokens): | ||
def __init__(self, session_factory) -> Callable[..., AbstractContextManager]: | ||
self.session_factory = session_factory | ||
|
||
def add_project_token(self, project_id: str, project_token: ProjectTokenCreate): | ||
token = f"pt_{generate_api_key()}" # pt stands for project token | ||
with self.session_factory() as session: | ||
db_project_token = SqlModelProjectToken( | ||
project_id=project_id, | ||
token=token, | ||
name=project_token.name, | ||
access=project_token.access, | ||
) | ||
session.add(db_project_token) | ||
session.commit() | ||
session.refresh(db_project_token) | ||
return self.map_sql_to_schema(db_project_token) | ||
|
||
def delete_project_token(self, project_id: str, token_id: str): | ||
with self.session_factory() as session: | ||
db_project_token = ( | ||
session.query(SqlModelProjectToken) | ||
.filter( | ||
SqlModelProjectToken.id == token_id | ||
and SqlModelProjectToken.project_id == project_id | ||
) | ||
.first() | ||
) | ||
if db_project_token is None: | ||
raise HTTPException( | ||
status_code=404, detail=f"Project token {token_id} not found" | ||
) | ||
session.delete(db_project_token) | ||
session.commit() | ||
|
||
def list_project_tokens(self, project_id: str): | ||
with self.session_factory() as session: | ||
db_project_tokens = ( | ||
session.query(SqlModelProjectToken) | ||
.filter(SqlModelProjectToken.project_id == project_id) | ||
.all() | ||
) | ||
return [ | ||
self.map_sql_to_schema(project_token) | ||
for project_token in db_project_tokens | ||
] | ||
|
||
@staticmethod | ||
def map_sql_to_schema(project_token: SqlModelProjectToken) -> ProjectToken: | ||
"""Convert a models.ProjectToken to a schemas.ProjectToken | ||
:project: An ProjectToken in SQLAlchemy format. | ||
:returns: An ProjectToken in pyDantic BaseModel format. | ||
:rtype: schemas.Project | ||
""" | ||
return ProjectToken( | ||
id=str(project_token.id), | ||
name=project_token.name, | ||
project_id=project_token.project_id, | ||
token=project_token.token, | ||
last_used=project_token.last_used, | ||
access=project_token.access, | ||
) |
60 changes: 60 additions & 0 deletions
60
carbonserver/carbonserver/api/routers/project_api_tokens.py
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,60 @@ | ||
from typing import List | ||
|
||
from container import ServerContainer | ||
from dependency_injector.wiring import Provide, inject | ||
from fastapi import APIRouter, Depends | ||
from starlette import status | ||
|
||
from carbonserver.api.dependencies import get_token_header | ||
from carbonserver.api.schemas import ProjectToken, ProjectTokenCreate | ||
|
||
PROJECTS_TOKENS_ROUTER_TAGS = ["Project tokens"] | ||
|
||
router = APIRouter( | ||
dependencies=[Depends(get_token_header)], | ||
) | ||
|
||
|
||
# Create project token | ||
@router.post( | ||
"/projects/{project_id}/api-tokens", | ||
tags=PROJECTS_TOKENS_ROUTER_TAGS, | ||
status_code=status.HTTP_201_CREATED, | ||
response_model=ProjectToken, | ||
) | ||
@inject | ||
def add_project_token( | ||
project_id: str, | ||
project_token: ProjectTokenCreate, | ||
project_token_service=Depends(Provide[ServerContainer.project_token_service]), | ||
) -> ProjectToken: | ||
return project_token_service.add_project_token(project_id, project_token) | ||
|
||
|
||
# Delete project token | ||
@router.delete( | ||
"/projects/{project_id}/api-tokens/{token_id}", | ||
tags=PROJECTS_TOKENS_ROUTER_TAGS, | ||
status_code=status.HTTP_204_NO_CONTENT, | ||
) | ||
@inject | ||
def delete_project_token( | ||
project_id: str, | ||
token_id: str, | ||
project_token_service=Depends(Provide[ServerContainer.project_token_service]), | ||
) -> None: | ||
return project_token_service.delete_project_token(project_id, token_id) | ||
|
||
|
||
# See all project tokens of the project | ||
@router.get( | ||
"/projects/{project_id}/api-tokens", | ||
tags=PROJECTS_TOKENS_ROUTER_TAGS, | ||
response_model=List[ProjectToken], | ||
) | ||
@inject | ||
def get_all_project_tokens( | ||
project_id: str, | ||
project_token_service=Depends(Provide[ServerContainer.project_token_service]), | ||
) -> List[ProjectToken]: | ||
return project_token_service.list_tokens_from_project(project_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
18 changes: 18 additions & 0 deletions
18
carbonserver/carbonserver/api/services/project_token_service.py
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,18 @@ | ||
from carbonserver.api.infra.repositories.repository_projects_tokens import ( | ||
SqlAlchemyRepository as ProjectTokensSqlRepository, | ||
) | ||
from carbonserver.api.schemas import ProjectTokenCreate | ||
|
||
|
||
class ProjectTokenService: | ||
def __init__(self, project_token_repository: ProjectTokensSqlRepository): | ||
self._repository = project_token_repository | ||
|
||
def add_project_token(self, project_id, project_token: ProjectTokenCreate): | ||
return self._repository.add_project_token(project_id, project_token) | ||
|
||
def delete_project_token(self, project_id, token_id): | ||
return self._repository.delete_project_token(project_id, token_id) | ||
|
||
def list_tokens_from_project(self, project_id): | ||
return self._repository.list_project_tokens(project_id) |
47 changes: 47 additions & 0 deletions
47
carbonserver/carbonserver/database/alembic/versions/caf929e09f7c_add_project_tokens.py
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,47 @@ | ||
"""add project tokens | ||
Revision ID: caf929e09f7c | ||
Revises: 7ace119b161f | ||
Create Date: 2024-07-25 19:51:43.046273 | ||
""" | ||
|
||
import sqlalchemy as sa | ||
from alembic import op | ||
from sqlalchemy.dialects.postgresql import UUID | ||
|
||
# revision identifiers, used by Alembic. | ||
revision = "caf929e09f7c" | ||
down_revision = "7ace119b161f" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade(): | ||
|
||
# Create the project_tokens table | ||
|
||
op.create_table( | ||
"project_tokens", | ||
sa.Column("id", UUID(as_uuid=True), primary_key=True, index=True), | ||
sa.Column("project_id", UUID(as_uuid=True)), | ||
sa.Column("token", sa.String, unique=True), | ||
sa.Column("name", sa.String), | ||
sa.Column("access", sa.Integer), | ||
sa.Column("last_used", sa.DateTime, nullable=True), | ||
) | ||
# Create the foreign key constraint between the project_tokens and projects tables | ||
op.create_foreign_key( | ||
"fk_project_tokens_projects", | ||
"project_tokens", | ||
"projects", | ||
["project_id"], | ||
["id"], | ||
) | ||
|
||
|
||
def downgrade(): | ||
op.drop_constraint( | ||
"fk_project_tokens_projects", "project_tokens", type_="foreignkey" | ||
) | ||
op.drop_table("project_tokens") |
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
Oops, something went wrong.