Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix and refactor open api #330

Merged
merged 1 commit into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 17 additions & 19 deletions oidc-controller/api/clientConfigurations/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@
from typing import List
from pymongo import ReturnDocument
from pymongo.database import Database
from fastapi import HTTPException
from fastapi import status as http_status
from fastapi.encoders import jsonable_encoder

from ..core.http_exception_util import raise_appropriate_http_exception, check_and_raise_not_found_http_exception
from ..core.oidc.provider import init_provider
from ..db.session import COLLECTION_NAMES

from .models import (
ClientConfiguration,
ClientConfigurationCreate,
ClientConfigurationPatch,
ClientConfigurationRead,
)
from ..db.session import COLLECTION_NAMES
from api.core.oidc.provider import init_provider


logger: structlog.typing.FilteringBoundLogger = structlog.getLogger(__name__)

Expand All @@ -25,32 +22,31 @@ def __init__(self, db: Database):
self._db = db

async def create(
self, client_config: ClientConfigurationCreate
self, client_config: ClientConfiguration
) -> ClientConfiguration:
col = self._db.get_collection(COLLECTION_NAMES.CLIENT_CONFIGURATIONS)
col.insert_one(jsonable_encoder(client_config))
try:
col.insert_one(jsonable_encoder(client_config))
except Exception as err:
raise_appropriate_http_exception(
err, exists_msg="Client configuration already exists")

# remake provider instance to refresh provider client
await init_provider(self._db)
return ClientConfiguration(
**col.find_one({"client_id": client_config.client_id})
)

async def get(self, client_id: str) -> ClientConfigurationRead:
async def get(self, client_id: str) -> ClientConfiguration:
col = self._db.get_collection(COLLECTION_NAMES.CLIENT_CONFIGURATIONS)
obj = col.find_one({"client_id": client_id})

if obj is None:
raise HTTPException(
status_code=http_status.HTTP_404_NOT_FOUND,
detail="The client_config hasn't been found!",
)
check_and_raise_not_found_http_exception(obj)

return ClientConfiguration(**obj)

async def get_all(self) -> List[ClientConfigurationRead]:
async def get_all(self) -> List[ClientConfiguration]:
col = self._db.get_collection(COLLECTION_NAMES.CLIENT_CONFIGURATIONS)
return [ClientConfigurationRead(**cc) for cc in col.find()]
return [ClientConfiguration(**cc) for cc in col.find()]

async def patch(
self, client_id: str, data: ClientConfigurationPatch
Expand All @@ -61,15 +57,17 @@ async def patch(
{"$set": data.dict(exclude_unset=True)},
return_document=ReturnDocument.AFTER,
)
check_and_raise_not_found_http_exception(obj)

# remake provider instance to refresh provider client
await init_provider(self._db)
return obj

async def delete(self, client_id: str) -> bool:
col = self._db.get_collection(COLLECTION_NAMES.CLIENT_CONFIGURATIONS)
obj = col.find_one_and_delete({"client_id": client_id})
check_and_raise_not_found_http_exception(obj)

# remake provider instance to refresh provider client
await init_provider(self._db)

return bool(obj)
2 changes: 1 addition & 1 deletion oidc-controller/api/clientConfigurations/examples.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from api.core.config import settings

ex_client_config_create = {
ex_client_config = {
"client_id": settings.OIDC_CLIENT_ID,
"client_name": settings.OIDC_CLIENT_NAME,
"client_secret": "**********",
Expand Down
16 changes: 6 additions & 10 deletions oidc-controller/api/clientConfigurations/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from enum import Enum
from typing import List, Optional
from typing import Optional, List
from pydantic import BaseModel, Field

from api.core.config import settings

from .examples import ex_client_config_create
from .examples import ex_client_config
from ..core.config import settings


class TOKENENDPOINTAUTHMETHODS(str, Enum):
Expand All @@ -24,6 +23,7 @@ class ClientConfigurationBase(BaseModel):

class Config:
allow_population_by_field_name = True
schema_extra = {"example": ex_client_config}


class ClientConfiguration(ClientConfigurationBase):
Expand All @@ -34,16 +34,12 @@ class ClientConfigurationRead(ClientConfigurationBase):
pass


class ClientConfigurationCreate(ClientConfigurationBase):
class Config:
schema_extra = {"example": ex_client_config_create}


class ClientConfigurationPatch(ClientConfigurationBase):
client_id: Optional[str]
client_name: Optional[str]
response_types: Optional[List[str]]
redirect_uris: Optional[List[str]]
token_endpoint_auth_method: Optional[TOKENENDPOINTAUTHMETHODS]

client_secret: Optional[str]

pass
52 changes: 27 additions & 25 deletions oidc-controller/api/clientConfigurations/router.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
from typing import List
from pymongo.database import Database

from fastapi import APIRouter, HTTPException, Depends
from fastapi import APIRouter, Depends
from fastapi import status as http_status

from ..core.models import StatusMessage

from .crud import ClientConfigurationCRUD
from .models import (
ClientConfigurationRead,
ClientConfiguration,
ClientConfigurationPatch,
ClientConfigurationCreate,
ClientConfigurationRead,
)
from ..core.auth import get_api_key
from ..core.models import GenericErrorMessage, StatusMessage
from ..db.session import get_db


router = APIRouter()


@router.post(
"/",
response_description="Add new verification configuration",
response_description="Add new client configuration",
status_code=http_status.HTTP_201_CREATED,
response_model=ClientConfigurationCreate,
response_model=ClientConfiguration,
responses={http_status.HTTP_409_CONFLICT: {"model": GenericErrorMessage}},
response_model_exclude_unset=True,
dependencies=[Depends(get_api_key)],
)
async def create_client_config(
ver_config: ClientConfigurationCreate, db: Database = Depends(get_db)
client_config: ClientConfiguration, db: Database = Depends(get_db)
):
return await ClientConfigurationCRUD(db).create(ver_config)
return await ClientConfigurationCRUD(db).create(client_config)


@router.get(
"/",
status_code=http_status.HTTP_200_OK,
response_model=List[ClientConfigurationRead],
response_model_exclude_unset=True,
dependencies=[Depends(get_api_key)],
)
Expand All @@ -42,43 +45,42 @@ async def get_all_client_configs(db: Database = Depends(get_db)):


@router.get(
"/{client_config_id}",
"/{client_id}",
status_code=http_status.HTTP_200_OK,
response_model=ClientConfigurationRead,
responses={http_status.HTTP_404_NOT_FOUND: {"model": GenericErrorMessage}},
response_model_exclude_unset=True,
dependencies=[Depends(get_api_key)],
)
async def get_client_config(client_config_id: str, db: Database = Depends(get_db)):
return await ClientConfigurationCRUD(db).get(client_config_id)
async def get_client_config(client_id: str, db: Database = Depends(get_db)):
return await ClientConfigurationCRUD(db).get(client_id)


@router.patch(
"/{client_config_id}",
"/{client_id}",
status_code=http_status.HTTP_200_OK,
response_model=ClientConfigurationRead,
responses={http_status.HTTP_404_NOT_FOUND: {"model": GenericErrorMessage}},
response_model_exclude_unset=True,
dependencies=[Depends(get_api_key)],
)
async def patch_client_config(
client_config_id: str,
client_id: str,
data: ClientConfigurationPatch,
db: Database = Depends(get_db),
):
return await ClientConfigurationCRUD(db).patch(id=client_config_id, data=data)
return await ClientConfigurationCRUD(db).patch(
client_id=client_id, data=data
)


@router.delete(
"/{client_config_id}",
"/{client_id}",
status_code=http_status.HTTP_200_OK,
response_model=StatusMessage,
responses={http_status.HTTP_404_NOT_FOUND: {"model": GenericErrorMessage}},
dependencies=[Depends(get_api_key)],
)
async def delete_client_config(client_config_id: str, db: Database = Depends(get_db)):
status = await ClientConfigurationCRUD(db).delete(id=client_config_id)

if not status:
raise HTTPException(
status_code=http_status.HTTP_404_NOT_FOUND,
detail="client_config does not exist",
)
return StatusMessage(status=status, message="The client_config was deleted")
async def delete_client_config(client_id: str, db: Database = Depends(get_db)):
status = await ClientConfigurationCRUD(db).delete(client_id)
return StatusMessage(status=status, message="The client configuration was deleted")
28 changes: 28 additions & 0 deletions oidc-controller/api/core/http_exception_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from pymongo.errors import WriteError
from fastapi import HTTPException
from fastapi import status as http_status
import structlog

logger = structlog.getLogger(__name__)


def raise_appropriate_http_exception(err: WriteError, exists_msg: str = None):
if err.code == 11000:
raise HTTPException(
status_code=http_status.HTTP_409_CONFLICT,
detail=exists_msg,
)
else:
logger.error("Unknown error", err=err)
raise HTTPException(
status_code=http_status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="The server was unable to process the request",
)


def check_and_raise_not_found_http_exception(resp):
if resp is None:
raise HTTPException(
status_code=http_status.HTTP_404_NOT_FOUND,
detail="The requested resource wasn't found",
)
4 changes: 4 additions & 0 deletions oidc-controller/api/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ class Config:
class TimestampModel(BaseModel):
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)


class GenericErrorMessage(BaseModel):
detail: str
30 changes: 19 additions & 11 deletions oidc-controller/api/verificationConfigs/crud.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from fastapi import HTTPException
from fastapi import status as http_status
from typing import List
from fastapi.encoders import jsonable_encoder

from pymongo import ReturnDocument
from pymongo.database import Database

from ..core.http_exception_util import raise_appropriate_http_exception, check_and_raise_not_found_http_exception
from ..db.session import COLLECTION_NAMES

from .models import (
Expand All @@ -21,36 +21,44 @@ def __init__(self, db: Database):

async def create(self, ver_config: VerificationConfig) -> VerificationConfig:
ver_confs = self._db.get_collection(COLLECTION_NAMES.VER_CONFIGS)
ver_confs.insert_one(jsonable_encoder(ver_config))

try:
ver_confs.insert_one(jsonable_encoder(ver_config))
except Exception as err:
raise_appropriate_http_exception(
err, exists_msg="Verification configuration already exists")
return ver_confs.find_one({"ver_config_id": ver_config.ver_config_id})

async def get(self, ver_config_id: str) -> VerificationConfig:
ver_confs = self._db.get_collection(COLLECTION_NAMES.VER_CONFIGS)
ver_conf = ver_confs.find_one({"ver_config_id": ver_config_id})

if ver_conf is None:
raise HTTPException(
status_code=http_status.HTTP_404_NOT_FOUND,
detail="The verification_config hasn't been found!",
)
check_and_raise_not_found_http_exception(ver_conf)

return VerificationConfig(**ver_conf)

async def get_all(self) -> List[VerificationConfig]:
ver_confs = self._db.get_collection(COLLECTION_NAMES.VER_CONFIGS)
return [VerificationConfig(**vc) for vc in ver_confs.find()]

async def patch(
self, ver_config_id: str, data: VerificationConfigPatch
) -> VerificationConfig:
if not isinstance(data, VerificationConfigPatch):
raise Exception("please provide an instance of the <document> PATCH class")
raise Exception(
"please provide an instance of the <document> PATCH class")
ver_confs = self._db.get_collection(COLLECTION_NAMES.VER_CONFIGS)
ver_conf = ver_confs.find_one_and_update(
{"ver_config_id": ver_config_id},
{"$set": data.dict(exclude_unset=True)},
return_document=ReturnDocument.AFTER,
)
check_and_raise_not_found_http_exception(ver_conf)

return ver_conf

async def delete(self, ver_config_id: str) -> bool:
ver_confs = self._db.get_collection(COLLECTION_NAMES.VER_CONFIGS)
ver_conf = ver_confs.find_one_and_delete({"ver_config_id": ver_config_id})
ver_conf = ver_confs.find_one_and_delete(
{"ver_config_id": ver_config_id})
check_and_raise_not_found_http_exception(ver_conf)
return bool(ver_conf)
15 changes: 1 addition & 14 deletions oidc-controller/api/verificationConfigs/examples.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
ex_ver_config_read = {
"ver_config_id": "test-request-config",
"subject_identifier": "first_name",
"proof_request": {
"name": "Basic Proof",
"version": "1.0",
"requested_attributes": [
{"names": ["first_name", "last_name"], "restrictions": []},
],
"requested_predicates": [],
},
}

ex_ver_config_create = {
ex_ver_config = {
"ver_config_id": "test-request-config",
"subject_identifier": "first_name",
"proof_request": {
Expand Down
Loading