Renamed AgencyConf to User
This commit is contained in:
parent
6f019020ea
commit
6af3250bea
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -144,7 +144,7 @@ data/trash/
|
||||||
data/gtfs/
|
data/gtfs/
|
||||||
data/grfs
|
data/grfs
|
||||||
data/tmp
|
data/tmp
|
||||||
data/agencyconf/**
|
data/users/**
|
||||||
|
|
||||||
#these files are under app/static but they get copied to the outside directory on startup
|
#these files are under app/static but they get copied to the outside directory on startup
|
||||||
logging.conf
|
logging.conf
|
||||||
|
|
|
||||||
|
|
@ -29,14 +29,14 @@ There is a special *admin* user.
|
||||||
For this user, the API-Key must be passed in as an environment variable when
|
For this user, the API-Key must be passed in as an environment variable when
|
||||||
Amarillo is started.
|
Amarillo is started.
|
||||||
|
|
||||||
The admin can create additional API-Keys in the `/agencyconf` endpoint. This
|
The admin can create additional API-Keys in the `/users` endpoint. This
|
||||||
endpoint is always available but not always shown in `/docs`, especially not
|
endpoint is always available but not always shown in `/docs`, especially not
|
||||||
when running in production.
|
when running in production.
|
||||||
The Swagger docs for `/agencyconf` can be seen on the MFDZ demo server.
|
The Swagger docs for `/users` can be seen on the MFDZ demo server.
|
||||||
|
|
||||||
Permissions work this way
|
Permissions work this way
|
||||||
- the admin is allowed to call all operations on all resources. Only the admin
|
- the admin is allowed to call all operations on all resources. Only the admin
|
||||||
can create new API-Keys by POSTing an `AgencyConf` JSON object to `/agencyconf`.
|
can create new API-Keys by POSTing an `users` JSON object to `/users`.
|
||||||
- API-Keys for agencies are allowed to POST/PUT/GET/DELETE their own
|
- API-Keys for agencies are allowed to POST/PUT/GET/DELETE their own
|
||||||
resources and GET some public resources.
|
resources and GET some public resources.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
from amarillo.utils.container import container
|
from amarillo.utils.container import container
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from amarillo.services.agencyconf import AgencyConfService, agency_conf_directory
|
from amarillo.services.users import UserService, user_conf_directory
|
||||||
from amarillo.services.agencies import AgencyService
|
from amarillo.services.agencies import AgencyService
|
||||||
from amarillo.services.regions import RegionService
|
from amarillo.services.regions import RegionService
|
||||||
|
|
||||||
|
|
@ -27,12 +27,12 @@ def create_required_directories():
|
||||||
assert_folder_exists(f'data/{subdir}/{agency_id}')
|
assert_folder_exists(f'data/{subdir}/{agency_id}')
|
||||||
|
|
||||||
# Agency configurations
|
# Agency configurations
|
||||||
assert_folder_exists(agency_conf_directory)
|
assert_folder_exists(user_conf_directory)
|
||||||
|
|
||||||
|
|
||||||
def configure_services():
|
def configure_services():
|
||||||
container['agencyconf'] = AgencyConfService()
|
container['users'] = UserService()
|
||||||
logger.info("Loaded %d agency configuration(s)", len(container['agencyconf'].agency_id_to_agency_conf))
|
logger.info("Loaded %d user configuration(s)", len(container['users'].user_id_to_user_conf))
|
||||||
|
|
||||||
container['agencies'] = AgencyService()
|
container['agencies'] = AgencyService()
|
||||||
logger.info("Loaded %d agencies", len(container['agencies'].agencies))
|
logger.info("Loaded %d agencies", len(container['agencies'].agencies))
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ copy_static_files(["conf", "static", "templates", "logging.conf", "config"])
|
||||||
|
|
||||||
import amarillo.plugins
|
import amarillo.plugins
|
||||||
from amarillo.configuration import configure_services, configure_admin_token
|
from amarillo.configuration import configure_services, configure_admin_token
|
||||||
from amarillo.routers import carpool, agency, agencyconf, region
|
from amarillo.routers import carpool, agency, region, users
|
||||||
import amarillo.services.oauth2 as oauth2
|
import amarillo.services.oauth2 as oauth2
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
@ -85,7 +85,7 @@ app = FastAPI(title="Amarillo - The Carpooling Intermediary",
|
||||||
|
|
||||||
app.include_router(carpool.router)
|
app.include_router(carpool.router)
|
||||||
app.include_router(agency.router)
|
app.include_router(agency.router)
|
||||||
app.include_router(agencyconf.router)
|
app.include_router(users.router)
|
||||||
app.include_router(region.router)
|
app.include_router(region.router)
|
||||||
app.include_router(oauth2.router)
|
app.include_router(oauth2.router)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pydantic import ConfigDict, BaseModel, Field
|
from pydantic import ConfigDict, BaseModel, Field
|
||||||
|
|
||||||
|
class User(BaseModel):
|
||||||
|
#TODO: add attributes admin, permissions, fullname, email
|
||||||
|
|
||||||
class AgencyConf(BaseModel):
|
user_id: str = Field(
|
||||||
agency_id: str = Field(
|
|
||||||
description="ID of the agency that uses this token.",
|
description="ID of the agency that uses this token.",
|
||||||
min_length=1,
|
min_length=1,
|
||||||
max_length=20,
|
max_length=20,
|
||||||
|
|
@ -5,7 +5,7 @@ from typing import List
|
||||||
from fastapi import APIRouter, HTTPException, status, Depends
|
from fastapi import APIRouter, HTTPException, status, Depends
|
||||||
|
|
||||||
from amarillo.models.Carpool import Carpool, Agency
|
from amarillo.models.Carpool import Carpool, Agency
|
||||||
from amarillo.routers.agencyconf import verify_permission_for_same_agency_or_admin
|
from amarillo.routers.users import verify_permission_for_same_agency_or_admin
|
||||||
from amarillo.services.oauth2 import get_current_agency
|
from amarillo.services.oauth2 import get_current_agency
|
||||||
# TODO should move this to service
|
# TODO should move this to service
|
||||||
from amarillo.routers.carpool import store_carpool, delete_agency_carpools_older_than
|
from amarillo.routers.carpool import store_carpool, delete_agency_carpools_older_than
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from fastapi import APIRouter, Body, HTTPException, status, Depends
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from amarillo.models.Carpool import Carpool
|
from amarillo.models.Carpool import Carpool
|
||||||
from amarillo.routers.agencyconf import verify_permission_for_same_agency_or_admin
|
from amarillo.routers.users import verify_permission_for_same_agency_or_admin
|
||||||
from amarillo.services.oauth2 import get_current_agency
|
from amarillo.services.oauth2 import get_current_agency
|
||||||
from amarillo.tests.sampledata import examples
|
from amarillo.tests.sampledata import examples
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from typing import List
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, status, Header, Depends
|
from fastapi import APIRouter, HTTPException, status, Header, Depends
|
||||||
|
|
||||||
from amarillo.models.AgencyConf import AgencyConf
|
from amarillo.models.User import User
|
||||||
from amarillo.services.agencyconf import AgencyConfService
|
from amarillo.services.users import UserService
|
||||||
from amarillo.services.oauth2 import get_current_agency, verify_admin
|
from amarillo.services.oauth2 import get_current_agency, verify_admin
|
||||||
from amarillo.services.config import config
|
from amarillo.services.config import config
|
||||||
from amarillo.utils.container import container
|
from amarillo.utils.container import container
|
||||||
|
|
@ -12,8 +12,8 @@ from amarillo.utils.container import container
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
router = APIRouter(
|
router = APIRouter(
|
||||||
prefix="/agencyconf",
|
prefix="/users",
|
||||||
tags=["agencyconf"]
|
tags=["users"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# This endpoint is not shown in PROD installations, only in development
|
# This endpoint is not shown in PROD installations, only in development
|
||||||
|
|
@ -39,48 +39,48 @@ async def verify_permission_for_same_agency_or_admin(agency_id_in_path_or_body,
|
||||||
|
|
||||||
@router.get("/",
|
@router.get("/",
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
operation_id="getAgencyIdsWhichHaveAConfiguration",
|
operation_id="getUserIdsWhichHaveAConfiguration",
|
||||||
summary="Get agency_ids which have a configuration",
|
summary="Get user which have a configuration",
|
||||||
response_model=List[str],
|
response_model=List[str],
|
||||||
description="Returns the agency_ids but not the details.",
|
description="Returns the user_ids but not the details.",
|
||||||
status_code=status.HTTP_200_OK)
|
status_code=status.HTTP_200_OK)
|
||||||
async def get_agency_ids(admin_api_key: str = Depends(get_current_agency)) -> [str]:
|
async def get_user_ids(admin_api_key: str = Depends(get_current_agency)) -> [str]:
|
||||||
return container['agencyconf'].get_agency_ids()
|
return container['users'].get_user_ids()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/",
|
@router.post("/",
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
operation_id="postNewAgencyConf",
|
operation_id="postNewUserConf",
|
||||||
summary="Post a new AgencyConf")
|
summary="Post a new User")
|
||||||
async def post_agency_conf(agency_conf: AgencyConf, admin_api_key: str = Depends(verify_admin)):
|
async def post_user_conf(user_conf: User, admin_api_key: str = Depends(verify_admin)):
|
||||||
agency_conf_service: AgencyConfService = container['agencyconf']
|
user_service: UserService = container['users']
|
||||||
agency_conf_service.add(agency_conf)
|
user_service.add(user_conf)
|
||||||
|
|
||||||
# TODO 400->403
|
# TODO 400->403
|
||||||
@router.delete("/{agency_id}",
|
@router.delete("/{user_id}",
|
||||||
include_in_schema=include_in_schema,
|
include_in_schema=include_in_schema,
|
||||||
operation_id="deleteAgencyConf",
|
operation_id="deleteUser",
|
||||||
status_code=status.HTTP_200_OK,
|
status_code=status.HTTP_200_OK,
|
||||||
summary="Delete configuration of an agency. Returns true if the token for the agency existed, "
|
summary="Delete configuration of a user. Returns true if the token for the user existed, "
|
||||||
"false if it didn't exist."
|
"false if it didn't exist."
|
||||||
)
|
)
|
||||||
async def delete_agency_conf(agency_id: str, requesting_agency_id: str = Depends(get_current_agency)):
|
async def delete_user(user_id: str, requesting_user_id: str = Depends(get_current_agency)):
|
||||||
agency_may_delete_own = requesting_agency_id == agency_id
|
agency_may_delete_own = requesting_user_id == user_id
|
||||||
admin_may_delete_everything = requesting_agency_id == "admin"
|
admin_may_delete_everything = requesting_user_id == "admin"
|
||||||
is_permitted = agency_may_delete_own or admin_may_delete_everything
|
is_permitted = agency_may_delete_own or admin_may_delete_everything
|
||||||
|
|
||||||
if not is_permitted:
|
if not is_permitted:
|
||||||
message = f"The API key for {requesting_agency_id} can not delete the configuration for {agency_id}"
|
message = f"The API key for {requesting_user_id} can not delete the configuration for {user_id}"
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||||
|
|
||||||
agency_conf_service: AgencyConfService = container['agencyconf']
|
user_service: UserService = container['users']
|
||||||
|
|
||||||
agency_exists = agency_id in agency_conf_service.get_agency_ids()
|
agency_exists = user_id in user_service.get_user_ids()
|
||||||
|
|
||||||
if not agency_exists:
|
if not agency_exists:
|
||||||
message = f"No config for {agency_id}"
|
message = f"No config for {user_id}"
|
||||||
logger.error(message)
|
logger.error(message)
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||||
|
|
||||||
agency_conf_service.delete(agency_id)
|
user_service.delete(user_id)
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
import json
|
|
||||||
import os
|
|
||||||
from glob import glob
|
|
||||||
from typing import Dict, List
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from fastapi import HTTPException, status
|
|
||||||
|
|
||||||
from amarillo.models.AgencyConf import AgencyConf
|
|
||||||
from amarillo.services.config import config
|
|
||||||
from amarillo.services.passwords import get_password_hash
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
agency_conf_directory = 'data/agencyconf'
|
|
||||||
|
|
||||||
|
|
||||||
class AgencyConfService:
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
# Both Dicts to be kept in sync always. The second api_key_to_agency_id is like a reverse
|
|
||||||
# cache for the first for fast lookup of valid api keys, which happens on *every* request.
|
|
||||||
self.agency_id_to_agency_conf: Dict[str, AgencyConf] = {}
|
|
||||||
self.api_key_to_agency_id: Dict[str, str] = {}
|
|
||||||
|
|
||||||
for agency_conf_file_name in glob(f'{agency_conf_directory}/*.json'):
|
|
||||||
with open(agency_conf_file_name) as agency_conf_file:
|
|
||||||
dictionary = json.load(agency_conf_file)
|
|
||||||
|
|
||||||
agency_conf = AgencyConf(**dictionary)
|
|
||||||
|
|
||||||
agency_id = agency_conf.agency_id
|
|
||||||
api_key = agency_conf.api_key
|
|
||||||
|
|
||||||
self.agency_id_to_agency_conf[agency_id] = agency_conf
|
|
||||||
if api_key is not None:
|
|
||||||
self.api_key_to_agency_id[api_key] = agency_conf.agency_id
|
|
||||||
|
|
||||||
def get_agency_conf(self, agency_id: str) -> AgencyConf:
|
|
||||||
agency_conf = self.agency_id_to_agency_conf.get(agency_id)
|
|
||||||
return agency_conf
|
|
||||||
|
|
||||||
def check_api_key(self, api_key: str) -> str:
|
|
||||||
"""Check if the API key is valid
|
|
||||||
|
|
||||||
The agencies' api keys are checked first, and the admin's key.
|
|
||||||
|
|
||||||
The agency_id or "admin" is returned for further checks in the caller if the
|
|
||||||
request is permitted, like {agency_id} == agency_id.
|
|
||||||
"""
|
|
||||||
|
|
||||||
agency_id = self.api_key_to_agency_id.get(api_key)
|
|
||||||
|
|
||||||
is_agency = agency_id is not None
|
|
||||||
|
|
||||||
if is_agency:
|
|
||||||
return agency_id
|
|
||||||
|
|
||||||
is_admin = api_key == config.admin_token
|
|
||||||
|
|
||||||
if is_admin:
|
|
||||||
return "admin"
|
|
||||||
|
|
||||||
message = "X-API-Key header invalid"
|
|
||||||
logger.error(message)
|
|
||||||
raise HTTPException(status_code=400, detail=message)
|
|
||||||
|
|
||||||
def add(self, agency_conf: AgencyConf):
|
|
||||||
|
|
||||||
agency_id = agency_conf.agency_id
|
|
||||||
api_key = agency_conf.api_key
|
|
||||||
|
|
||||||
agency_id_exists_already = self.agency_id_to_agency_conf.get(agency_id) is not None
|
|
||||||
|
|
||||||
if agency_id_exists_already:
|
|
||||||
message = f"Agency {agency_id} exists already. To update, delete it first."
|
|
||||||
logger.error(message)
|
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
|
||||||
|
|
||||||
agency_using_this_api_key_already = self.api_key_to_agency_id.get(api_key)
|
|
||||||
a_different_agency_is_using_this_api_key_already = \
|
|
||||||
agency_using_this_api_key_already is not None and \
|
|
||||||
agency_using_this_api_key_already != agency_id
|
|
||||||
|
|
||||||
if a_different_agency_is_using_this_api_key_already:
|
|
||||||
message = f"Duplicate API Key for {agency_id} not permitted. Use a different key."
|
|
||||||
logger.error(message)
|
|
||||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
|
||||||
|
|
||||||
agency_conf.password = get_password_hash(agency_conf.password)
|
|
||||||
|
|
||||||
with open(f'{agency_conf_directory}/{agency_id}.json', 'w', encoding='utf-8') as f:
|
|
||||||
f.write(agency_conf.json())
|
|
||||||
|
|
||||||
self.agency_id_to_agency_conf[agency_id] = agency_conf
|
|
||||||
self.api_key_to_agency_id[api_key] = agency_id
|
|
||||||
|
|
||||||
logger.info(f"Added configuration for agency {agency_id}.")
|
|
||||||
|
|
||||||
def get_agency_ids(self) -> List[str]:
|
|
||||||
return list(self.agency_id_to_agency_conf.keys())
|
|
||||||
|
|
||||||
def delete(self, agency_id):
|
|
||||||
|
|
||||||
agency_conf = self.agency_id_to_agency_conf.get(agency_id)
|
|
||||||
|
|
||||||
api_key = agency_conf.api_key
|
|
||||||
|
|
||||||
del self.api_key_to_agency_id[api_key]
|
|
||||||
|
|
||||||
del self.agency_id_to_agency_conf[agency_id]
|
|
||||||
|
|
||||||
os.remove(f'{agency_conf_directory}/{agency_id}.json')
|
|
||||||
|
|
||||||
logger.info(f"Deleted configuration for agency {agency_id}.")
|
|
||||||
|
|
@ -11,7 +11,7 @@ from pydantic import BaseModel
|
||||||
from amarillo.services.passwords import verify_password
|
from amarillo.services.passwords import verify_password
|
||||||
from amarillo.utils.container import container
|
from amarillo.utils.container import container
|
||||||
from amarillo.services.agencies import AgencyService
|
from amarillo.services.agencies import AgencyService
|
||||||
from amarillo.services.agencyconf import AgencyConfService
|
from amarillo.services.users import UserService
|
||||||
from amarillo.models.Carpool import Agency
|
from amarillo.models.Carpool import Agency
|
||||||
|
|
||||||
from amarillo.services.secrets import secrets
|
from amarillo.services.secrets import secrets
|
||||||
|
|
@ -38,13 +38,12 @@ async def verify_optional_api_key(X_API_Key: Optional[str] = Header(None)):
|
||||||
return await verify_api_key(X_API_Key)
|
return await verify_api_key(X_API_Key)
|
||||||
|
|
||||||
def authenticate_agency(agency_id: str, password: str):
|
def authenticate_agency(agency_id: str, password: str):
|
||||||
agency_conf_service : AgencyConfService = container['agencyconf']
|
user_service : UserService = container['users']
|
||||||
agency_conf = agency_conf_service.agency_id_to_agency_conf.get(agency_id, None)
|
user_conf = user_service.user_id_to_user_conf.get(agency_id, None)
|
||||||
if not agency_conf:
|
if not user_conf:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
agency_password = agency_conf.password
|
if not verify_password(password, user_conf.password):
|
||||||
if not verify_password(password, agency_password):
|
|
||||||
return False
|
return False
|
||||||
return agency_id
|
return agency_id
|
||||||
|
|
||||||
|
|
@ -103,9 +102,9 @@ async def verify_admin(agency: str = Depends(get_current_agency)):
|
||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
# X_API_Key is upper case for OpenAPI
|
# X_API_Key is upper case for OpenAPI
|
||||||
async def verify_api_key(X_API_Key: str = Header(...)):
|
async def verify_api_key(X_API_Key: str = Header(...)):
|
||||||
agency_conf_service: AgencyConfService = container['agencyconf']
|
user_service: UserService = container['users']
|
||||||
|
|
||||||
return agency_conf_service.check_api_key(X_API_Key)
|
return user_service.check_api_key(X_API_Key)
|
||||||
|
|
||||||
@router.post("/token")
|
@router.post("/token")
|
||||||
async def login_for_access_token(
|
async def login_for_access_token(
|
||||||
|
|
|
||||||
115
amarillo/services/users.py
Normal file
115
amarillo/services/users.py
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from glob import glob
|
||||||
|
from typing import Dict, List
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
|
||||||
|
from amarillo.models.User import User
|
||||||
|
from amarillo.services.config import config
|
||||||
|
from amarillo.services.passwords import get_password_hash
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
user_conf_directory = 'data/users'
|
||||||
|
|
||||||
|
|
||||||
|
class UserService:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
# Both Dicts to be kept in sync always. The second api_key_to_agency_id is like a reverse
|
||||||
|
# cache for the first for fast lookup of valid api keys, which happens on *every* request.
|
||||||
|
self.user_id_to_user_conf: Dict[str, User] = {}
|
||||||
|
self.api_key_to_user_id: Dict[str, str] = {}
|
||||||
|
|
||||||
|
for user_conf_file_name in glob(f'{user_conf_directory}/*.json'):
|
||||||
|
with open(user_conf_file_name) as user_conf_file:
|
||||||
|
dictionary = json.load(user_conf_file)
|
||||||
|
|
||||||
|
user_conf = User(**dictionary)
|
||||||
|
|
||||||
|
agency_id = user_conf.user_id
|
||||||
|
api_key = user_conf.api_key
|
||||||
|
|
||||||
|
self.user_id_to_user_conf[agency_id] = user_conf
|
||||||
|
if api_key is not None:
|
||||||
|
self.api_key_to_user_id[api_key] = user_conf.user_id
|
||||||
|
|
||||||
|
def get_user(self, user_id: str) -> User:
|
||||||
|
user_conf = self.user_id_to_user_conf.get(user_id)
|
||||||
|
return user_conf
|
||||||
|
|
||||||
|
def check_api_key(self, api_key: str) -> str:
|
||||||
|
"""Check if the API key is valid
|
||||||
|
|
||||||
|
The agencies' api keys are checked first, and the admin's key.
|
||||||
|
|
||||||
|
The agency_id or "admin" is returned for further checks in the caller if the
|
||||||
|
request is permitted, like {agency_id} == agency_id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
agency_id = self.api_key_to_user_id.get(api_key)
|
||||||
|
|
||||||
|
is_agency = agency_id is not None
|
||||||
|
|
||||||
|
if is_agency:
|
||||||
|
return agency_id
|
||||||
|
|
||||||
|
is_admin = api_key == config.admin_token
|
||||||
|
|
||||||
|
if is_admin:
|
||||||
|
return "admin"
|
||||||
|
|
||||||
|
message = "X-API-Key header invalid"
|
||||||
|
logger.error(message)
|
||||||
|
raise HTTPException(status_code=400, detail=message)
|
||||||
|
|
||||||
|
def add(self, user_conf: User):
|
||||||
|
|
||||||
|
user_id = user_conf.user_id
|
||||||
|
api_key = user_conf.api_key
|
||||||
|
|
||||||
|
agency_id_exists_already = self.user_id_to_user_conf.get(user_id) is not None
|
||||||
|
|
||||||
|
if agency_id_exists_already:
|
||||||
|
message = f"Agency {user_id} exists already. To update, delete it first."
|
||||||
|
logger.error(message)
|
||||||
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||||
|
|
||||||
|
agency_using_this_api_key_already = self.api_key_to_user_id.get(api_key)
|
||||||
|
a_different_agency_is_using_this_api_key_already = \
|
||||||
|
agency_using_this_api_key_already is not None and \
|
||||||
|
agency_using_this_api_key_already != user_id
|
||||||
|
|
||||||
|
if a_different_agency_is_using_this_api_key_already:
|
||||||
|
message = f"Duplicate API Key for {user_id} not permitted. Use a different key."
|
||||||
|
logger.error(message)
|
||||||
|
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
|
||||||
|
|
||||||
|
user_conf.password = get_password_hash(user_conf.password)
|
||||||
|
|
||||||
|
with open(f'{user_conf_directory}/{user_id}.json', 'w', encoding='utf-8') as f:
|
||||||
|
f.write(user_conf.json())
|
||||||
|
|
||||||
|
self.user_id_to_user_conf[user_id] = user_conf
|
||||||
|
self.api_key_to_user_id[api_key] = user_id
|
||||||
|
|
||||||
|
logger.info(f"Added configuration for user {user_id}.")
|
||||||
|
|
||||||
|
def get_user_ids(self) -> List[str]:
|
||||||
|
return list(self.user_id_to_user_conf.keys())
|
||||||
|
|
||||||
|
def delete(self, user_id):
|
||||||
|
|
||||||
|
user_conf = self.user_id_to_user_conf.get(user_id)
|
||||||
|
|
||||||
|
api_key = user_conf.api_key
|
||||||
|
|
||||||
|
del self.api_key_to_user_id[api_key]
|
||||||
|
|
||||||
|
del self.user_id_to_user_conf[user_id]
|
||||||
|
|
||||||
|
os.remove(f'{user_conf_directory}/{user_id}.json')
|
||||||
|
|
||||||
|
logger.info(f"Deleted configuration for {user_id}.")
|
||||||
Loading…
Reference in a new issue