Compare commits
12 commits
88a64d2047
...
71f26a4c9b
| Author | SHA1 | Date | |
|---|---|---|---|
| 71f26a4c9b | |||
| 00dcf7ce35 | |||
| 33e78d4a9b | |||
| 82c2b13a7f | |||
| 9a1975c389 | |||
| 7a0f43d34f | |||
| 0e169af456 | |||
| 0c1c9f62ee | |||
| 39d3f2d560 | |||
| 42349b0182 | |||
| a86cfea65a | |||
| d8abe17618 |
48 changed files with 168 additions and 284 deletions
10
example.env
Normal file
10
example.env
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
SECRET_KEY=""
|
||||
OIDC_CONFIG="https://sso.sr2.uk/realms/sr2/.well-known/openid-configuration"
|
||||
OIDC_ISSUER="https://sso.sr2.uk/realms/sr2"
|
||||
OIDC_AUDIENCE="account"
|
||||
CLIENT_ID=""
|
||||
|
||||
DATABASE_NAME="cloud-api"
|
||||
DATABASE_PORT="5432"
|
||||
DATABASE_HOSTNAME="localhost"
|
||||
DATABASE_CREDENTIALS="user:password"
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Configurations for the admin module
|
||||
|
||||
Configurations:
|
||||
- List: Description
|
||||
- Configs: Description
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Constants and error codes for the admin module
|
||||
|
||||
Constants:
|
||||
- List: Description
|
||||
- Consts: Description
|
||||
Constants for the admin module
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Router dependencies for the admin module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Dependencies for the admin module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Module specific exceptions for the admin module
|
||||
|
||||
Exceptions:
|
||||
- List: Description
|
||||
- Exceptions: Description
|
||||
Custom exceptions for the admin module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Database models for the admin module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
"""
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
"""
|
||||
Router endpoints for the admin module
|
||||
|
||||
Endpoints:
|
||||
- List: Description
|
||||
- Endpoints: Description
|
||||
Exports:
|
||||
- router: fastapi.APIRouter
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Pydantic models for the admin module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Module specific business logic for the admin module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Non-business logic reusable functions and classes for the admin module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
"""
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
"""
|
||||
Configurations for auth module, import auth_settings
|
||||
Configurations for the auth module
|
||||
|
||||
Configurations:
|
||||
- List: Description
|
||||
- Configs: Description
|
||||
Exports:
|
||||
- auth_settings: Contains OIDC information
|
||||
"""
|
||||
from src.config import CustomBaseSettings
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Constants and error codes for auth module
|
||||
|
||||
Constants:
|
||||
- List: Description
|
||||
- Consts: Description
|
||||
Constants for the auth module
|
||||
"""
|
||||
|
|
@ -1,18 +1,17 @@
|
|||
"""
|
||||
Router dependencies for auth module
|
||||
Auth dependencies
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Exports:
|
||||
- org_query_user_claims_dependency: bool: Verifies user belongs to org
|
||||
- org_model_root_claim_query_dependency: org_model: verifies org exists and user is either root or su, gets org from query
|
||||
- org_model_root_claim_body_dependency: org_model: verifies org exists and user is either root or su, gets org from body
|
||||
- super_admin_dependency: user_model: verifies the user is a super admin
|
||||
"""
|
||||
from typing import Annotated
|
||||
from fastapi import Depends
|
||||
|
||||
from src.user.dependencies import user_model_claims_dependency
|
||||
from src.user.models import User
|
||||
from src.organisation.dependencies import org_model_query_dependency, org_model_body_dependency
|
||||
from src.organisation.models import Organisation as Org
|
||||
|
||||
|
|
@ -69,4 +68,4 @@ async def user_model_super_admin(user_model: user_model_claims_dependency):
|
|||
raise UnauthorizedException()
|
||||
|
||||
|
||||
super_admin_dependency = Annotated[bool, Depends(user_model_super_admin)]
|
||||
super_admin_dependency = Annotated[type[User], Depends(user_model_super_admin)]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
"""
|
||||
Module specific exceptions for auth module
|
||||
Module specific exceptions for the auth module
|
||||
|
||||
Exceptions:
|
||||
- List: Description
|
||||
- Exceptions: Description
|
||||
- UnauthorizedException: Takes an optional message string
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Database models for auth module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
Database models for the auth module
|
||||
"""
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
"""
|
||||
Router endpoints for auth module
|
||||
Contains oauth registration
|
||||
Router endpoints for the auth module
|
||||
|
||||
Endpoints:
|
||||
Exports:
|
||||
- router: fastapi.APIRouter
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Pydantic models for auth module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
Pydantic models for the auth module
|
||||
"""
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
"""
|
||||
Module specific business logic for auth module
|
||||
Module specific business logic for the auth module
|
||||
|
||||
Exports:
|
||||
- claims_dependency
|
||||
- claims_dependency: Dict[str, Any] containing OIDC claims and database ID
|
||||
"""
|
||||
import json
|
||||
import requests
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Non-business logic reusable functions and classes for auth module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Non-business logic reusable functions and classes for the auth module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Configurations for contact module
|
||||
|
||||
Configurations:
|
||||
- List: Description
|
||||
- Configs: Description
|
||||
Configurations for the contact module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Constants and error codes for contact module
|
||||
|
||||
Constants:
|
||||
- List: Description
|
||||
- Consts: Description
|
||||
Constants for the contact module
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Router dependencies for contact module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Dependencies for the contact module
|
||||
"""
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
"""
|
||||
Module specific exceptions for contact module
|
||||
Exceptions related to the contact module
|
||||
|
||||
Exceptions:
|
||||
- List: Description
|
||||
- Exceptions: Description
|
||||
Exports:
|
||||
- ContactNotFoundException: Takes an optional contact ID int
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
Database models for contact module
|
||||
Database models for the contact module
|
||||
|
||||
Models:
|
||||
- Contact: id[pk], email, first_name, last_name, phonenumber, vat_number
|
||||
street_address, post_office_box_number, address_locality, country_code, address_region, postal_code
|
||||
street_address, street_address_line_2, post_office_box_number, address_locality, country_code, address_region, postal_code
|
||||
"""
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
"""
|
||||
Router endpoints for contact module
|
||||
|
||||
Endpoints:
|
||||
- [get]/{contact_id} - Returns non-address type details for contact
|
||||
- [get]/{contact_id}/address - Returns address details for contact
|
||||
- [get]/{contact_id}/orgs - Returns a list of orgs which the contact is assigned to, and what they are assigned as
|
||||
- [post]/ - Creates a new contact
|
||||
- [patch]/{contact_id} - Updates the details of an existing contact
|
||||
- [delete]/{contact_id} - Deletes a contact by ID
|
||||
Router endpoints for the contact module
|
||||
"""
|
||||
from fastapi import APIRouter
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
Pydantic models for contact module
|
||||
Pydantic models for the contact module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
- ContactAddress
|
||||
- ContactModel: Contains ContactAddress as a property
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Module specific business logic for contact module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Module specific business logic for the contact module
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Non-business logic reusable functions and classes for contact module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Non-business logic reusable functions and classes for the contact module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Configurations for <this module>
|
||||
|
||||
Configurations:
|
||||
- List: Description
|
||||
- Configs: Description
|
||||
Configurations for the IAM module
|
||||
"""
|
||||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Constants and error codes for <this module>
|
||||
|
||||
Constants:
|
||||
- List: Description
|
||||
- Consts: Description
|
||||
Constants for the IAM module
|
||||
"""
|
||||
|
|
@ -1,13 +1,10 @@
|
|||
"""
|
||||
Router dependencies for the IAM module
|
||||
Dependencies for the IAM module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Exports:
|
||||
- group_model_query_dependency: group_model: Gets group model from db, if it exists. Uses group_id from query param.
|
||||
- group_model_body_dependency: group_model: Gets group model from db, if it exists. Uses group_id from request body.
|
||||
- perm_model_body_dependency: perm_model: Gets perm model from db, if it exists. Uses perm_id from request body.
|
||||
"""
|
||||
from typing import Annotated, Optional
|
||||
|
||||
|
|
@ -15,7 +12,7 @@ from fastapi import Depends, Query
|
|||
|
||||
from src.database import db_dependency
|
||||
|
||||
from src.iam.models import Group
|
||||
from src.iam.models import Group, Permission
|
||||
from src.iam.exceptions import GroupNotFoundException, PermNotFoundException
|
||||
from src.iam.schemas import GroupIDMixin, PermIDMixin
|
||||
|
||||
|
|
@ -43,14 +40,14 @@ def get_group_model_body(db: db_dependency, request_model: Optional[GroupIDMixin
|
|||
group_model_body_dependency = Annotated[type[Group], Depends(get_group_model_body)]
|
||||
|
||||
|
||||
def get_perm_model_body(db: db_dependency, request_model: Optional[PermIDMixin] = None) -> type[Group]:
|
||||
def get_perm_model_body(db: db_dependency, request_model: Optional[PermIDMixin] = None) -> type[Permission]:
|
||||
perm_id = getattr(request_model, "permission_id", None)
|
||||
if perm_id is None:
|
||||
raise PermNotFoundException
|
||||
group_model = db.get(Group, perm_id)
|
||||
if group_model is None:
|
||||
perm_model = db.get(Permission, perm_id)
|
||||
if perm_model is None:
|
||||
raise PermNotFoundException(perm_id)
|
||||
|
||||
return group_model
|
||||
return perm_model
|
||||
|
||||
perm_model_body_dependency = Annotated[type[Group], Depends(get_perm_model_body)]
|
||||
perm_model_body_dependency = Annotated[type[Permission], Depends(get_perm_model_body)]
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
Module specific exceptions for the IAM module
|
||||
Exceptions related to the IAM module
|
||||
|
||||
Exceptions:
|
||||
- List: Description
|
||||
- Exceptions: Description
|
||||
- GroupNotFoundException: Takes an optional group_id int
|
||||
- PermNotFoundException: Takes an optional perm_id int
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
Database models for the IAM module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
- Permission:
|
||||
- id[PK], resource[U1], action[U1], service_id[FK][U1]
|
||||
- service_rel: ORM relationship over service_id FK
|
||||
- group_rel: ORM relationship backpops to Group.permission_rel
|
||||
- service_name: Calc property service_rel.name
|
||||
"""
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
|
||||
from sqlalchemy.orm import relationship
|
||||
|
|
|
|||
|
|
@ -1,9 +1,19 @@
|
|||
"""
|
||||
Router endpoints for <this module>
|
||||
Router endpoints for IAM
|
||||
|
||||
Endpoints:
|
||||
- List: Description
|
||||
- Endpoints: Description
|
||||
- [POST](/iam/can_act_on_resource): [API key & user claim]: Service access point to verify user permissions
|
||||
- [GET](/iam/group/permissions): [root user]: Gets list of perms(service, resource, action) the given group(id) has
|
||||
- [DELETE](/iam/group/permissions): [root user]: Removes a given perm(id) from the given group(id)
|
||||
- [GET](/iam/group/users): [root user]: Gets a list of users(id, name, email) that are assigned to the given group(id)
|
||||
- [POST](/iam/group): [root user]: Creates a new group for the given org(id)
|
||||
- [PUT](/iam/group/permission): [root user]: Assigns a perm(id) to the given group(id)
|
||||
- [PUT](/iam/group/user): [root user]: Assigns a user(id) to a group(id)
|
||||
- [DELETE](/iam/group/user): [root user]: Removes a user(id) from the given group(id)
|
||||
- [GET](/iam/permissions): [root user]: Gets a list of all permissions
|
||||
- [POST](/iam/permission): [super admin]: Creates a new permission
|
||||
- [DELETE](/iam/permission): [super admin]: Removes a permission
|
||||
- [GET](/iam/permissions/search): [root user]: Returns a list of permissions matching a filter(service|resource|action)
|
||||
"""
|
||||
from fastapi import APIRouter, status
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
|
@ -25,10 +35,10 @@ from src.iam.service import service_key_dependency
|
|||
from src.iam.models import Permission as Perm, GroupPermissions as GPerms, Group, UserGroups
|
||||
from src.iam.dependencies import group_model_query_dependency, group_model_body_dependency, perm_model_body_dependency
|
||||
from src.iam.schemas import IAMGetGroupPermissionsResponse, IAMGetGroupUsersResponse, IAMPostGroupRequest, \
|
||||
GroupResponse, IAMPostGroupResponse, IAMPutGroupPermissionRequest, IAMPutGroupPermissionResponse, \
|
||||
GroupSchema, IAMPostGroupResponse, IAMPutGroupPermissionRequest, IAMPutGroupPermissionResponse, \
|
||||
IAMPutGroupUserRequest, IAMPutGroupUserResponse, IAMDeleteGroupPermissionRequest, IAMDeleteGroupPermissionResponse, \
|
||||
IAMDeleteGroupUserRequest, IAMDeleteGroupUserResponse, IAMGetPermissionsResponse, IAMPostPermissionRequest, \
|
||||
IAMPostPermissionResponse, PermissionResponse, IAMDeletePermissionRequest, IAMGetPermissionsSearchRequest, IAMGetPermissionsSearchResponse
|
||||
IAMPostPermissionResponse, PermissionSchema, IAMDeletePermissionRequest, IAMGetPermissionsSearchRequest, IAMGetPermissionsSearchResponse
|
||||
|
||||
router = APIRouter(
|
||||
tags=["IAM"],
|
||||
|
|
@ -91,7 +101,7 @@ async def create_group(db: db_dependency, org_model: org_model_root_claim_body_d
|
|||
except IntegrityError as e:
|
||||
if isinstance(e.orig, errors.UniqueViolation):
|
||||
raise Conflict("Group with this name already exists")
|
||||
response = GroupResponse(**group_model.__dict__)
|
||||
response = GroupSchema(**group_model.__dict__)
|
||||
db.commit()
|
||||
return {"group": response}
|
||||
|
||||
|
|
@ -107,7 +117,7 @@ async def add_group_permission(db: db_dependency, group_model: group_model_body_
|
|||
group_model.permission_rel.append(perm_model)
|
||||
|
||||
db.flush()
|
||||
response = IAMPutGroupPermissionResponse(group=GroupResponse(**group_model.__dict__), permissions=group_model.permission_rel)
|
||||
response = IAMPutGroupPermissionResponse(group=GroupSchema(**group_model.__dict__), permissions=group_model.permission_rel)
|
||||
db.commit()
|
||||
return response
|
||||
|
||||
|
|
@ -122,7 +132,7 @@ async def add_group_user(db: db_dependency, group_model: group_model_body_depend
|
|||
|
||||
group_model.user_rel.append(user_model)
|
||||
db.flush()
|
||||
response = IAMPutGroupUserResponse(group=GroupResponse(**group_model.__dict__), users=group_model.user_rel)
|
||||
response = IAMPutGroupUserResponse(group=GroupSchema(**group_model.__dict__), users=group_model.user_rel)
|
||||
db.commit()
|
||||
return response
|
||||
|
||||
|
|
@ -134,8 +144,8 @@ async def remove_group_permissions(db: db_dependency, group_model: group_model_b
|
|||
|
||||
group_model.permission_rel.remove(perm_model)
|
||||
db.flush()
|
||||
response = IAMDeleteGroupPermissionResponse(group=GroupResponse(**group_model.__dict__),
|
||||
permissions=group_model.permission_rel)
|
||||
response = IAMDeleteGroupPermissionResponse(group=GroupSchema(**group_model.__dict__),
|
||||
permissions=group_model.permission_rel)
|
||||
db.commit()
|
||||
return response
|
||||
|
||||
|
|
@ -147,7 +157,7 @@ async def remove_group_user(db: db_dependency, group_model: group_model_body_dep
|
|||
|
||||
user_model.group_rel.remove(group_model)
|
||||
db.flush()
|
||||
response = IAMDeleteGroupUserResponse(group=GroupResponse(**group_model.__dict__), users=group_model.user_rel)
|
||||
response = IAMDeleteGroupUserResponse(group=GroupSchema(**group_model.__dict__), users=group_model.user_rel)
|
||||
db.commit()
|
||||
|
||||
return response
|
||||
|
|
@ -169,7 +179,7 @@ async def create_new_permission(db: db_dependency, su: super_admin_dependency, r
|
|||
if isinstance(e.orig, errors.UniqueViolation):
|
||||
raise Conflict(message="Permission already exists")
|
||||
db.flush()
|
||||
response = IAMPostPermissionResponse(permission=PermissionResponse(**perm_model.__dict__))
|
||||
response = IAMPostPermissionResponse(permission=PermissionSchema(**perm_model.__dict__))
|
||||
db.commit()
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
Pydantic models for the IAM module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
Models follow the nomenclature of:
|
||||
- Sub-models: "<Resource><Opt:>Schema"
|
||||
- Mixins: "<Attribute>Mixin"
|
||||
- Models: "<Module><Method><Resource><Opt:Resource><Direction>" ie "IAMGetGroupPermissionsResponse"
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -14,20 +15,20 @@ from src.schemas import CustomBaseModel
|
|||
from user.schemas import UserIDMixin
|
||||
|
||||
|
||||
class UserResponse(CustomBaseModel):
|
||||
class UserSchema(CustomBaseModel):
|
||||
id: int
|
||||
first_name: str
|
||||
last_name: str
|
||||
email: EmailStr
|
||||
|
||||
class PermissionResponse(CustomBaseModel):
|
||||
class PermissionSchema(CustomBaseModel):
|
||||
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
||||
|
||||
service_name: str
|
||||
resource: str
|
||||
action: str
|
||||
|
||||
class GroupResponse(CustomBaseModel):
|
||||
class GroupSchema(CustomBaseModel):
|
||||
id: int
|
||||
name: str
|
||||
|
||||
|
|
@ -38,47 +39,47 @@ class PermIDMixin(CustomBaseModel):
|
|||
permission_id: int
|
||||
|
||||
class IAMGetGroupPermissionsResponse(CustomBaseModel):
|
||||
permissions: list[PermissionResponse]
|
||||
permissions: list[PermissionSchema]
|
||||
|
||||
class IAMGetGroupUsersResponse(CustomBaseModel):
|
||||
users : list[UserResponse]
|
||||
users : list[UserSchema]
|
||||
|
||||
class IAMPostGroupRequest(OrgIDMixin):
|
||||
name: str
|
||||
|
||||
class IAMPostGroupResponse(CustomBaseModel):
|
||||
group: GroupResponse
|
||||
group: GroupSchema
|
||||
|
||||
class IAMPutGroupPermissionRequest(GroupIDMixin, PermIDMixin):
|
||||
pass
|
||||
|
||||
class IAMPutGroupPermissionResponse(CustomBaseModel):
|
||||
group: GroupResponse
|
||||
permissions: list[PermissionResponse]
|
||||
group: GroupSchema
|
||||
permissions: list[PermissionSchema]
|
||||
|
||||
class IAMPutGroupUserRequest(GroupIDMixin, UserIDMixin):
|
||||
pass
|
||||
|
||||
class IAMPutGroupUserResponse(CustomBaseModel):
|
||||
group: GroupResponse
|
||||
users: list[UserResponse]
|
||||
group: GroupSchema
|
||||
users: list[UserSchema]
|
||||
|
||||
class IAMDeleteGroupPermissionRequest(GroupIDMixin, PermIDMixin):
|
||||
pass
|
||||
|
||||
class IAMDeleteGroupPermissionResponse(CustomBaseModel):
|
||||
group: GroupResponse
|
||||
permissions: list[PermissionResponse]
|
||||
group: GroupSchema
|
||||
permissions: list[PermissionSchema]
|
||||
|
||||
class IAMDeleteGroupUserRequest(GroupIDMixin, UserIDMixin):
|
||||
pass
|
||||
|
||||
class IAMDeleteGroupUserResponse(CustomBaseModel):
|
||||
group: GroupResponse
|
||||
users: list[UserResponse]
|
||||
group: GroupSchema
|
||||
users: list[UserSchema]
|
||||
|
||||
class IAMGetPermissionsResponse(CustomBaseModel):
|
||||
permissions: list[PermissionResponse]
|
||||
permissions: list[PermissionSchema]
|
||||
|
||||
class IAMPostPermissionRequest(CustomBaseModel):
|
||||
service_id: int
|
||||
|
|
@ -86,7 +87,7 @@ class IAMPostPermissionRequest(CustomBaseModel):
|
|||
action: str
|
||||
|
||||
class IAMPostPermissionResponse(CustomBaseModel):
|
||||
permission: PermissionResponse
|
||||
permission: PermissionSchema
|
||||
|
||||
class IAMDeletePermissionRequest(PermIDMixin):
|
||||
pass
|
||||
|
|
@ -97,4 +98,4 @@ class IAMGetPermissionsSearchRequest(CustomBaseModel):
|
|||
action: Optional[str] = None
|
||||
|
||||
class IAMGetPermissionsSearchResponse(CustomBaseModel):
|
||||
permissions: list[PermissionResponse]
|
||||
permissions: list[PermissionSchema]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
"""
|
||||
Module specific business logic for <this module>
|
||||
Business logic reusable functions related to IAM
|
||||
|
||||
Exports service_key_dependency
|
||||
Exports:
|
||||
- service_key_dependency: bool: verifies request headers contain the correct api key for the service
|
||||
"""
|
||||
from typing import Annotated
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Non-business logic reusable functions and classes for <this module>
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Non-business logic reusable functions and classes for the IAM module
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
"""
|
||||
Configurations for organisation module
|
||||
|
||||
Configurations:
|
||||
- List: Description
|
||||
- Configs: Description
|
||||
Configurations for the organisation module
|
||||
"""
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Constants and error codes for organisation module
|
||||
Constants for the organisation module
|
||||
|
||||
Classes:
|
||||
- Status(StrEnum): PARTIAL, SUBMITTED, REMEDIATION, APPROVED, REJECTED, REMOVED
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
"""
|
||||
Router dependencies for organisation module
|
||||
Dependencies related to the organisation module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Exports:
|
||||
- org_model_query_dependency: org_model: Gets org model from db, if it exists. Uses org_id from query param. Also verifies if the org has been approved.
|
||||
- org_model_body_dependency: org_model: Gets org model from db, if it exists. Uses org_id from request body. Also verifies if the org has been approved.
|
||||
"""
|
||||
from typing import Annotated, Optional
|
||||
|
||||
|
|
@ -26,7 +22,7 @@ def get_org_model(db, request: Request, org_id: int):
|
|||
if org_model is None:
|
||||
raise OrgNotFoundException(org_id)
|
||||
|
||||
pre_approval_endpoints = ["PATCH/org/status", "PATCH/org/questionnaire", "GET/org/id"]
|
||||
pre_approval_endpoints = ["PATCH/org/status", "PATCH/org/questionnaire", "GET/org/id", "GET/org/contact", "PATCH/org/contact"]
|
||||
current_request = f"{request.method}{request.url.path}"
|
||||
if current_request not in pre_approval_endpoints and org_model.status != OrgStatus.APPROVED:
|
||||
raise AwaitingApprovalException(org_id)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
Module specific exceptions for organisation module
|
||||
Exceptions related to the organisation module
|
||||
|
||||
Exceptions:
|
||||
- List: Description
|
||||
- Exceptions: Description
|
||||
- OrgNotFoundException: Takes an optional org_id int
|
||||
- AwaitingApprovalException: Takes an optional org_id int
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,16 @@
|
|||
Database models for organisation module
|
||||
|
||||
Models:
|
||||
- Organisation: id[pk], name, status, intake_questionnaire,
|
||||
billing_contact_id[fk], security_contact_id[fk], owner_contact_id[fk]
|
||||
- OrgUsers: org_id[fk][cpk], user_id[fk][cpk], is_admin
|
||||
- Organisation:
|
||||
- id[PK], name, status, intake_questionnaire, root_user_id[FK], billing_contact_id[FK], security_contact_id[FK], owner_contact_id[FK]
|
||||
- user_rel: ORM relationship to User via OrgUsers relationship table
|
||||
- group_rel: ORM relationship to Group, backprops Group.org_rel
|
||||
- root_user_rel: ORM relationship to User with root_user_id FK
|
||||
- root_user_email: Calc property root_user_rel.email
|
||||
- billing_contact_rel: ORM relationship to Contact with billing_contact FK
|
||||
- security_contact_rel: ORM relationship to Contact with security_contact FK
|
||||
- owner_contact_rel: ORM relationship to Contact with owner_contact FK
|
||||
- OrgUsers: org_id[FK][PK], user_id[FK][PK]
|
||||
"""
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, JSON
|
||||
from sqlalchemy.orm import relationship
|
||||
|
|
|
|||
|
|
@ -2,15 +2,18 @@
|
|||
Router endpoints for organisation module
|
||||
|
||||
Endpoints:
|
||||
- [get]/id/{org_id} - Retrieves an organisation by its ID
|
||||
- [post]/ - Creates a new organisation
|
||||
- [patch]/{org_id}/questionnaire - Updates the questionnaire data for an organisation (can be partial or final submission)
|
||||
- [patch]/{org_id}/status - Updates the status of an organisation
|
||||
- [patch]/{org_id}/contact - Assigns a contact to an organisation (as billing, security, or owner)
|
||||
- [get]/{org_id}/users - Retrieves all users associated with an organisation
|
||||
- [post]/{org_id}/users - Adds a new user to an organisation
|
||||
- [delete]/{org_id} - Deletes an organisation by ID
|
||||
- [get]/{org_id}/contact/{contact_type} - Retrieves the contact of a specific type (owner, billing, security) for an organisation
|
||||
- [GET](/org/id): [root user]: Get details about an organisation(id)
|
||||
- [POST](/org/): [oidc claim]: Creates an organisation, adds the current user as a user and sets them to be the root user
|
||||
- [PATCH](/org/questionnaire): [root user]: Updates the org's intake questionnaire and optionally be submitted for review
|
||||
- [PATCH](/org/status): [super admin]: Allows a super admin to update an org(id) status(Status enum)
|
||||
- [GET](/org/users): [root user]: Gets a list of the org(id) users(email)
|
||||
- [POST](/org/users): [root user]: Adds a new user(id) to the org(id)
|
||||
- [DELETE](/org/): [super admin]: Deletes an organisation(id)
|
||||
- [PATCH](/org/root_user): [super admin]: Updates an org(id) root user(id)
|
||||
- [GET](/org/groups): [root user]: Gets a list of the org(id) groups(name)
|
||||
- [DELETE](/org/user): [root user]: Removes a user(id) from an org(id)
|
||||
- [GET](/org/contact): [root user]: Gets the (contact_type) contact for an org(id)
|
||||
- [PATCH](/org/contact): [root user]: Updates the (contact_type) contact for an org(id). Any number of details can be changed.
|
||||
"""
|
||||
from typing import Annotated, Optional
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
"""
|
||||
Pydantic models for organisation module
|
||||
|
||||
Models:
|
||||
- List: Description
|
||||
- Models: Description
|
||||
Models follow the nomenclature of:
|
||||
- Sub-models: "<Resource><Opt:>Schema"
|
||||
- Mixins: "<Attribute>Mixin"
|
||||
- Models: "<Module><Method><Resource><Opt:Resource><Direction>" ie "OrgPostOrgRequest"
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
|
|
@ -16,7 +17,7 @@ from src.user.schemas import UserIDMixin
|
|||
from src.organisation.constants import Status, ContactType
|
||||
|
||||
|
||||
class OrgQuestionnaire(CustomBaseModel):
|
||||
class Questionnaire(CustomBaseModel):
|
||||
question_one: str
|
||||
question_two: str
|
||||
question_three: str
|
||||
|
|
@ -27,10 +28,10 @@ class OrgIDMixin(CustomBaseModel):
|
|||
|
||||
class OrgPostOrgRequest(CustomBaseModel):
|
||||
name: str
|
||||
intake_questionnaire: Optional[OrgQuestionnaire] = None
|
||||
intake_questionnaire: Optional[Questionnaire] = None
|
||||
|
||||
class OrgPatchQuestionnaireRequest(OrgIDMixin):
|
||||
intake_questionnaire: OrgQuestionnaire
|
||||
intake_questionnaire: Questionnaire
|
||||
partial: bool
|
||||
|
||||
class OrgPatchStatusRequest(OrgIDMixin):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Module specific business logic for organisation module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Reusable business logic functions for the organisation module
|
||||
"""
|
||||
|
|
@ -1,11 +1,3 @@
|
|||
"""
|
||||
Non-business logic reusable functions and classes for organisation module
|
||||
|
||||
Classes:
|
||||
- List: Description
|
||||
- Classes: Description
|
||||
|
||||
Functions:
|
||||
- List: Description
|
||||
- Functions: Description
|
||||
Non-business logic reusable functions and classes for the organisation module
|
||||
"""
|
||||
|
|
@ -17,7 +17,7 @@ from src.service.models import Service
|
|||
from src.service.utils import generate_api_key
|
||||
from src.service.dependencies import service_model_body_dependency
|
||||
from src.service.schemas import ServiceGetServiceResponse, ServicePostServiceRequest, ServicePostServiceResponse, \
|
||||
ServiceWithKeyResponse, ServicePatchKeyResponse, ServicePatchKeyRequest, ServiceDeleteServiceRequest
|
||||
ServiceWithKeySchema, ServicePatchKeyResponse, ServicePatchKeyRequest, ServiceDeleteServiceRequest
|
||||
|
||||
router = APIRouter(
|
||||
tags=["Service"],
|
||||
|
|
@ -42,7 +42,7 @@ async def register_service(db: db_dependency, su: super_admin_dependency, servic
|
|||
if isinstance(e.orig, UniqueViolation):
|
||||
raise Conflict(message="Service with this name already exists")
|
||||
db.commit()
|
||||
response = ServiceWithKeyResponse(**service_model.__dict__)
|
||||
response = ServiceWithKeySchema(**service_model.__dict__)
|
||||
db.commit()
|
||||
return {"service": response}
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ async def regenerate_api_key(db: db_dependency, su: super_admin_dependency, serv
|
|||
service_model.api_key = key
|
||||
|
||||
db.flush()
|
||||
response = ServiceWithKeyResponse(**service_model.__dict__)
|
||||
response = ServiceWithKeySchema(**service_model.__dict__)
|
||||
db.commit()
|
||||
return {"service": response}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,29 +12,29 @@ from src.schemas import CustomBaseModel
|
|||
class ServiceIDMixin(CustomBaseModel):
|
||||
service_id: int
|
||||
|
||||
class ServiceResponse(CustomBaseModel):
|
||||
class ServiceSchema(CustomBaseModel):
|
||||
model_config = ConfigDict(from_attributes=True, extra="ignore")
|
||||
|
||||
id: int
|
||||
name: str
|
||||
|
||||
class ServiceWithKeyResponse(ServiceResponse):
|
||||
class ServiceWithKeySchema(ServiceSchema):
|
||||
api_key: str
|
||||
|
||||
class ServiceGetServiceResponse(CustomBaseModel):
|
||||
services: list[ServiceResponse]
|
||||
services: list[ServiceSchema]
|
||||
|
||||
class ServicePostServiceRequest(CustomBaseModel):
|
||||
name: str
|
||||
|
||||
class ServicePostServiceResponse(CustomBaseModel):
|
||||
service: ServiceWithKeyResponse
|
||||
service: ServiceWithKeySchema
|
||||
|
||||
class ServicePatchKeyRequest(ServiceIDMixin):
|
||||
pass
|
||||
|
||||
class ServicePatchKeyResponse(CustomBaseModel):
|
||||
service: ServiceWithKeyResponse
|
||||
service: ServiceWithKeySchema
|
||||
|
||||
class ServiceDeleteServiceRequest(ServiceIDMixin):
|
||||
pass
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue