Renamed AuthUsers -> Users & completed user gql #181
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from api.src.queries.cities import CityGraphType, CityFilter, CitySort
|
||||
from api.src.queries.hello import UserGraphType#, AuthUserFilter, AuthUserSort, AuthUserGraphType
|
||||
from api.src.queries.hello import UserGraphType#, UserFilter, UserSort, UserGraphType
|
||||
from api.src.queries.user import UserFilter, UserSort
|
||||
from cpl.api.api_module import ApiModule
|
||||
from cpl.application.application_builder import ApplicationBuilder
|
||||
from cpl.auth.schema import AuthUser, Role
|
||||
from cpl.auth.schema import User, Role
|
||||
from cpl.core.configuration import Configuration
|
||||
from cpl.core.console import Console
|
||||
from cpl.core.environment import Environment
|
||||
from cpl.core.utils.cache import Cache
|
||||
from cpl.database.mysql.mysql_module import MySQLModule
|
||||
from cpl.graphql.application.graphql_app import GraphQLApp
|
||||
from cpl.graphql.auth.graphql_auth_module import GraphQLAuthModule
|
||||
from cpl.graphql.graphql_module import GraphQLModule
|
||||
from model.author_dao import AuthorDao
|
||||
from model.author_query import AuthorGraphType, AuthorFilter, AuthorSort
|
||||
@@ -38,8 +39,9 @@ def main():
|
||||
.add_module(MySQLModule)
|
||||
.add_module(ApiModule)
|
||||
.add_module(GraphQLModule)
|
||||
.add_module(GraphQLAuthModule)
|
||||
.add_scoped(ScopedService)
|
||||
.add_cache(AuthUser)
|
||||
.add_cache(User)
|
||||
.add_cache(Role)
|
||||
.add_transient(CityGraphType)
|
||||
.add_transient(CityFilter)
|
||||
@@ -47,9 +49,9 @@ def main():
|
||||
.add_transient(UserGraphType)
|
||||
.add_transient(UserFilter)
|
||||
.add_transient(UserSort)
|
||||
# .add_transient(AuthUserGraphType)
|
||||
# .add_transient(AuthUserFilter)
|
||||
# .add_transient(AuthUserSort)
|
||||
# .add_transient(UserGraphType)
|
||||
# .add_transient(UserFilter)
|
||||
# .add_transient(UserSort)
|
||||
.add_transient(HelloQuery)
|
||||
# test data
|
||||
.add_singleton(TestDataSeeder)
|
||||
@@ -100,7 +102,7 @@ def main():
|
||||
app.with_permissions(PostPermissions)
|
||||
|
||||
provider = builder.service_provider
|
||||
user_cache = provider.get_service(Cache[AuthUser])
|
||||
user_cache = provider.get_service(Cache[User])
|
||||
role_cache = provider.get_service(Cache[Role])
|
||||
|
||||
if role_cache == user_cache:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from api.src.queries.cities import CityFilter, CitySort, CityGraphType, City
|
||||
from api.src.queries.user import User, UserFilter, UserSort, UserGraphType
|
||||
from cpl.api.middleware.request import get_request
|
||||
from cpl.auth.schema import AuthUserDao, AuthUser
|
||||
from cpl.auth.schema import UserDao, User
|
||||
from cpl.graphql.schema.filter.filter import Filter
|
||||
from cpl.graphql.schema.graph_type import GraphType
|
||||
from cpl.graphql.schema.query import Query
|
||||
@@ -11,20 +11,20 @@ from cpl.graphql.schema.sort.sort_order import SortOrder
|
||||
users = [User(i, f"User {i}") for i in range(1, 101)]
|
||||
cities = [City(i, f"City {i}") for i in range(1, 101)]
|
||||
|
||||
# class AuthUserFilter(Filter[AuthUser]):
|
||||
# class UserFilter(Filter[User]):
|
||||
# def __init__(self):
|
||||
# Filter.__init__(self)
|
||||
# self.field("id", int)
|
||||
# self.field("username", str)
|
||||
#
|
||||
#
|
||||
# class AuthUserSort(Sort[AuthUser]):
|
||||
# class UserSort(Sort[User]):
|
||||
# def __init__(self):
|
||||
# Sort.__init__(self)
|
||||
# self.field("id", SortOrder)
|
||||
# self.field("username", SortOrder)
|
||||
#
|
||||
# class AuthUserGraphType(GraphType[AuthUser]):
|
||||
# class UserGraphType(GraphType[User]):
|
||||
#
|
||||
# def __init__(self):
|
||||
# GraphType.__init__(self)
|
||||
@@ -61,9 +61,9 @@ class HelloQuery(Query):
|
||||
resolver=lambda: cities,
|
||||
)
|
||||
# self.dao_collection_field(
|
||||
# AuthUserGraphType,
|
||||
# AuthUserDao,
|
||||
# "authUsers",
|
||||
# AuthUserFilter,
|
||||
# AuthUserSort,
|
||||
# UserGraphType,
|
||||
# UserDao,
|
||||
# "Users",
|
||||
# UserFilter,
|
||||
# UserSort,
|
||||
# )
|
||||
|
||||
@@ -214,6 +214,9 @@ class WebApp(WebAppABC):
|
||||
self.with_middleware(AuthorizationMiddleware)
|
||||
return self
|
||||
|
||||
async def _log_before_startup(self):
|
||||
self._logger.info(f"Start API on {self._api_settings.host}:{self._api_settings.port}")
|
||||
|
||||
async def main(self):
|
||||
self._logger.debug(f"Preparing API")
|
||||
self._validate_policies()
|
||||
@@ -237,7 +240,7 @@ class WebApp(WebAppABC):
|
||||
else:
|
||||
app = self._app
|
||||
|
||||
self._logger.info(f"Start API on {self._api_settings.host}:{self._api_settings.port}")
|
||||
await self._log_before_startup()
|
||||
|
||||
config = uvicorn.Config(
|
||||
app, host=self._api_settings.host, port=self._api_settings.port, log_config=None, loop="asyncio"
|
||||
|
||||
@@ -7,13 +7,13 @@ from cpl.api.logger import APILogger
|
||||
from cpl.api.middleware.request import get_request
|
||||
from cpl.api.router import Router
|
||||
from cpl.auth.keycloak import KeycloakClient
|
||||
from cpl.auth.schema import AuthUserDao, AuthUser
|
||||
from cpl.auth.schema import UserDao, User
|
||||
from cpl.core.ctx import set_user
|
||||
|
||||
|
||||
class AuthenticationMiddleware(ASGIMiddleware):
|
||||
|
||||
def __init__(self, app, logger: APILogger, keycloak: KeycloakClient, user_dao: AuthUserDao):
|
||||
def __init__(self, app, logger: APILogger, keycloak: KeycloakClient, user_dao: UserDao):
|
||||
ASGIMiddleware.__init__(self, app)
|
||||
|
||||
self._logger = logger
|
||||
@@ -72,12 +72,12 @@ class AuthenticationMiddleware(ASGIMiddleware):
|
||||
|
||||
return await self._call_next(scope, receive, send)
|
||||
|
||||
async def _get_or_crate_user(self, keycloak_id: str) -> AuthUser:
|
||||
async def _get_or_crate_user(self, keycloak_id: str) -> User:
|
||||
existing = await self._user_dao.find_by_keycloak_id(keycloak_id)
|
||||
if existing is not None:
|
||||
return existing
|
||||
|
||||
user = AuthUser(0, keycloak_id)
|
||||
user = User(0, keycloak_id)
|
||||
uid = await self._user_dao.create(user)
|
||||
return await self._user_dao.get_by_id(uid)
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ from cpl.api.middleware.request import get_request
|
||||
from cpl.api.model.validation_match import ValidationMatch
|
||||
from cpl.api.registry.policy import PolicyRegistry
|
||||
from cpl.api.router import Router
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
from cpl.core.ctx.user_context import get_user
|
||||
|
||||
|
||||
class AuthorizationMiddleware(ASGIMiddleware):
|
||||
|
||||
def __init__(self, app, logger: APILogger, policies: PolicyRegistry, user_dao: AuthUserDao):
|
||||
def __init__(self, app, logger: APILogger, policies: PolicyRegistry, user_dao: UserDao):
|
||||
ASGIMiddleware.__init__(self, app)
|
||||
|
||||
self._logger = logger
|
||||
|
||||
@@ -10,8 +10,8 @@ from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
||||
from cpl.api.logger import APILogger
|
||||
from cpl.api.typing import TRequest
|
||||
from cpl.auth.keycloak.keycloak_client import KeycloakClient
|
||||
from cpl.auth.schema import AuthUser
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema import User
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
from cpl.core.ctx import set_user
|
||||
from cpl.dependency.inject import inject
|
||||
from cpl.dependency.service_provider import ServiceProvider
|
||||
@@ -22,7 +22,7 @@ _request_context: ContextVar[Union[TRequest, None]] = ContextVar("request", defa
|
||||
class RequestMiddleware(ASGIMiddleware):
|
||||
|
||||
def __init__(
|
||||
self, app, provider: ServiceProvider, logger: APILogger, keycloak: KeycloakClient, user_dao: AuthUserDao
|
||||
self, app, provider: ServiceProvider, logger: APILogger, keycloak: KeycloakClient, user_dao: UserDao
|
||||
):
|
||||
ASGIMiddleware.__init__(self, app)
|
||||
|
||||
@@ -80,7 +80,7 @@ class RequestMiddleware(ASGIMiddleware):
|
||||
|
||||
user = await self._user_dao.find_by_keycloak_id(keycloak_id)
|
||||
if not user:
|
||||
user = AuthUser(0, keycloak_id)
|
||||
user = User(0, keycloak_id)
|
||||
uid = await self._user_dao.create(user)
|
||||
user = await self._user_dao.get_by_id(uid)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from starlette.types import ASGIApp
|
||||
from starlette.websockets import WebSocket
|
||||
|
||||
from cpl.api.abc.asgi_middleware_abc import ASGIMiddleware
|
||||
from cpl.auth.schema import AuthUser
|
||||
from cpl.auth.schema import User
|
||||
|
||||
TRequest = Union[Request, WebSocket]
|
||||
TEndpoint = Callable[[TRequest, ...], Awaitable[Response]] | Callable[[TRequest, ...], Response]
|
||||
@@ -18,5 +18,5 @@ PartialMiddleware = Union[
|
||||
Middleware,
|
||||
Callable[[ASGIApp], ASGIApp],
|
||||
]
|
||||
PolicyResolver = Callable[[AuthUser], bool | Awaitable[bool]]
|
||||
PolicyResolver = Callable[[User], bool | Awaitable[bool]]
|
||||
PolicyInput = Union[dict[str, PolicyResolver], "Policy"]
|
||||
|
||||
@@ -12,7 +12,7 @@ from cpl.dependency.service_provider import ServiceProvider
|
||||
from .keycloak.keycloak_admin import KeycloakAdmin
|
||||
from .keycloak.keycloak_client import KeycloakClient
|
||||
from .schema._administration.api_key_dao import ApiKeyDao
|
||||
from .schema._administration.auth_user_dao import AuthUserDao
|
||||
from .schema._administration.user_dao import UserDao
|
||||
from .schema._permission.api_key_permission_dao import ApiKeyPermissionDao
|
||||
from .schema._permission.permission_dao import PermissionDao
|
||||
from .schema._permission.role_dao import RoleDao
|
||||
@@ -26,7 +26,7 @@ class AuthModule(Module):
|
||||
singleton = [
|
||||
KeycloakClient,
|
||||
KeycloakAdmin,
|
||||
AuthUserDao,
|
||||
UserDao,
|
||||
ApiKeyDao,
|
||||
ApiKeyPermissionDao,
|
||||
PermissionDao,
|
||||
|
||||
@@ -6,7 +6,7 @@ from cpl.auth.schema import (
|
||||
RolePermissionDao,
|
||||
ApiKeyDao,
|
||||
ApiKeyPermissionDao,
|
||||
AuthUserDao,
|
||||
UserDao,
|
||||
RoleUserDao,
|
||||
RoleUser,
|
||||
)
|
||||
@@ -23,7 +23,7 @@ class RoleSeeder(DataSeederABC):
|
||||
role_permission_dao: RolePermissionDao,
|
||||
api_key_dao: ApiKeyDao,
|
||||
api_key_permission_dao: ApiKeyPermissionDao,
|
||||
user_dao: AuthUserDao,
|
||||
user_dao: UserDao,
|
||||
role_user_dao: RoleUserDao,
|
||||
):
|
||||
DataSeederABC.__init__(self)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from ._administration.api_key import ApiKey
|
||||
from ._administration.api_key_dao import ApiKeyDao
|
||||
from ._administration.auth_user import AuthUser
|
||||
from ._administration.auth_user_dao import AuthUserDao
|
||||
from ._administration.user import User
|
||||
from ._administration.user_dao import UserDao
|
||||
|
||||
from ._permission.api_key_permission import ApiKeyPermission
|
||||
from ._permission.api_key_permission_dao import ApiKeyPermissionDao
|
||||
|
||||
@@ -13,7 +13,7 @@ from cpl.database.logger import DBLogger
|
||||
from cpl.dependency import get_provider
|
||||
|
||||
|
||||
class AuthUser(DbModelABC[Self]):
|
||||
class User(DbModelABC[Self]):
|
||||
def __init__(
|
||||
self,
|
||||
id: SerialId,
|
||||
@@ -69,21 +69,21 @@ class AuthUser(DbModelABC[Self]):
|
||||
|
||||
@async_property
|
||||
async def permissions(self):
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
|
||||
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||
return await auth_user_dao.get_permissions(self.id)
|
||||
user_dao: UserDao = get_provider().get_service(UserDao)
|
||||
return await user_dao.get_permissions(self.id)
|
||||
|
||||
async def has_permission(self, permission: Permissions) -> bool:
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
|
||||
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||
return await auth_user_dao.has_permission(self.id, permission)
|
||||
user_dao: UserDao = get_provider().get_service(UserDao)
|
||||
return await user_dao.has_permission(self.id, permission)
|
||||
|
||||
async def anonymize(self):
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
|
||||
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||
user_dao: UserDao = get_provider().get_service(UserDao)
|
||||
|
||||
self._keycloak_id = str(uuid.UUID(int=0))
|
||||
await auth_user_dao.update(self)
|
||||
await user_dao.update(self)
|
||||
@@ -3,21 +3,21 @@ from typing import Optional, Union
|
||||
from cpl.auth.permission.permissions import Permissions
|
||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||
from cpl.auth.schema._permission.permission import Permission
|
||||
from cpl.auth.schema._administration.auth_user import AuthUser
|
||||
from cpl.auth.schema._administration.user import User
|
||||
from cpl.database import TableManager
|
||||
from cpl.database.abc import DbModelDaoABC
|
||||
from cpl.database.external_data_temp_table_builder import ExternalDataTempTableBuilder
|
||||
from cpl.dependency.context import get_provider
|
||||
|
||||
|
||||
class AuthUserDao(DbModelDaoABC[AuthUser]):
|
||||
class UserDao(DbModelDaoABC[User]):
|
||||
|
||||
def __init__(self, permission_dao: PermissionDao):
|
||||
DbModelDaoABC.__init__(self, AuthUser, TableManager.get("auth_users"))
|
||||
DbModelDaoABC.__init__(self, User, TableManager.get("users"))
|
||||
|
||||
self._permissions = permission_dao
|
||||
|
||||
self.attribute(AuthUser.keycloak_id, str)
|
||||
self.attribute(User.keycloak_id, str)
|
||||
|
||||
async def get_users():
|
||||
return [(x.id, x.username, x.email) for x in await self.get_all()]
|
||||
@@ -31,11 +31,11 @@ class AuthUserDao(DbModelDaoABC[AuthUser]):
|
||||
.with_value_getter(get_users)
|
||||
)
|
||||
|
||||
async def get_by_keycloak_id(self, keycloak_id: str) -> AuthUser:
|
||||
return await self.get_single_by({AuthUser.keycloak_id: keycloak_id})
|
||||
async def get_by_keycloak_id(self, keycloak_id: str) -> User:
|
||||
return await self.get_single_by({User.keycloak_id: keycloak_id})
|
||||
|
||||
async def find_by_keycloak_id(self, keycloak_id: str) -> Optional[AuthUser]:
|
||||
return await self.find_single_by({AuthUser.keycloak_id: keycloak_id})
|
||||
async def find_by_keycloak_id(self, keycloak_id: str) -> Optional[User]:
|
||||
return await self.find_single_by({User.keycloak_id: keycloak_id})
|
||||
|
||||
async def has_permission(self, user_id: int, permission: Union[Permissions, str]) -> bool:
|
||||
from cpl.auth.schema._permission.permission_dao import PermissionDao
|
||||
@@ -29,10 +29,10 @@ class RoleUser(DbJoinModelABC):
|
||||
|
||||
@async_property
|
||||
async def user(self):
|
||||
from cpl.auth.schema._administration.auth_user_dao import AuthUserDao
|
||||
from cpl.auth.schema._administration.user_dao import UserDao
|
||||
|
||||
auth_user_dao: AuthUserDao = get_provider().get_service(AuthUserDao)
|
||||
return await auth_user_dao.get_by_id(self._user_id)
|
||||
user_dao: UserDao = get_provider().get_service(UserDao)
|
||||
return await user_dao.get_by_id(self._user_id)
|
||||
|
||||
@property
|
||||
def role_id(self) -> int:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS administration_auth_users
|
||||
CREATE TABLE IF NOT EXISTS administration_users
|
||||
(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
keycloakId CHAR(36) NOT NULL,
|
||||
@@ -9,10 +9,10 @@ CREATE TABLE IF NOT EXISTS administration_auth_users
|
||||
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT UC_KeycloakId UNIQUE (keycloakId),
|
||||
CONSTRAINT FK_EditorId FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_EditorId FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS administration_auth_users_history
|
||||
CREATE TABLE IF NOT EXISTS administration_users_history
|
||||
(
|
||||
id INT NOT NULL,
|
||||
keycloakId CHAR(36) NOT NULL,
|
||||
@@ -23,22 +23,22 @@ CREATE TABLE IF NOT EXISTS administration_auth_users_history
|
||||
updated TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TRIGGER TR_administration_auth_usersUpdate
|
||||
CREATE TRIGGER TR_administration_usersUpdate
|
||||
AFTER UPDATE
|
||||
ON administration_auth_users
|
||||
ON administration_users
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO administration_auth_users_history
|
||||
INSERT INTO administration_users_history
|
||||
(id, keycloakId, deleted, editorId, created, updated)
|
||||
VALUES (OLD.id, OLD.keycloakId, OLD.deleted, OLD.editorId, OLD.created, NOW());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER TR_administration_auth_usersDelete
|
||||
CREATE TRIGGER TR_administration_usersDelete
|
||||
AFTER DELETE
|
||||
ON administration_auth_users
|
||||
ON administration_users
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO administration_auth_users_history
|
||||
INSERT INTO administration_users_history
|
||||
(id, keycloakId, deleted, editorId, created, updated)
|
||||
VALUES (OLD.id, OLD.keycloakId, 1, OLD.editorId, OLD.created, NOW());
|
||||
END;
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS administration_api_keys
|
||||
|
||||
CONSTRAINT UC_Identifier_Key UNIQUE (identifier, keyString),
|
||||
CONSTRAINT UC_Key UNIQUE (keyString),
|
||||
CONSTRAINT FK_ApiKeys_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_ApiKeys_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS administration_api_keys_history
|
||||
|
||||
@@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS permission_permissions
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT UQ_PermissionName UNIQUE (name),
|
||||
CONSTRAINT FK_Permissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_Permissions_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_permissions_history
|
||||
@@ -52,7 +52,7 @@ CREATE TABLE IF NOT EXISTS permission_roles
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT UQ_RoleName UNIQUE (name),
|
||||
CONSTRAINT FK_Roles_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_Roles_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_roles_history
|
||||
@@ -98,7 +98,7 @@ CREATE TABLE IF NOT EXISTS permission_role_permissions
|
||||
CONSTRAINT UQ_RolePermission UNIQUE (roleId, permissionId),
|
||||
CONSTRAINT FK_RolePermissions_Role FOREIGN KEY (roleId) REFERENCES permission_roles (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_RolePermissions_Permission FOREIGN KEY (permissionId) REFERENCES permission_permissions (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_RolePermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_RolePermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_role_permissions_history
|
||||
@@ -132,7 +132,7 @@ BEGIN
|
||||
VALUES (OLD.id, OLD.roleId, OLD.permissionId, 1, OLD.editorId, OLD.created, NOW());
|
||||
END;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_role_auth_users
|
||||
CREATE TABLE IF NOT EXISTS permission_role_users
|
||||
(
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
roleId INT NOT NULL,
|
||||
@@ -142,12 +142,12 @@ CREATE TABLE IF NOT EXISTS permission_role_auth_users
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
CONSTRAINT UQ_RoleUser UNIQUE (roleId, userId),
|
||||
CONSTRAINT FK_Roleauth_users_Role FOREIGN KEY (roleId) REFERENCES permission_roles (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_Roleauth_users_User FOREIGN KEY (userId) REFERENCES administration_auth_users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_Roleauth_users_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_Roleusers_Role FOREIGN KEY (roleId) REFERENCES permission_roles (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_Roleusers_User FOREIGN KEY (userId) REFERENCES administration_users (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_Roleusers_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_role_auth_users_history
|
||||
CREATE TABLE IF NOT EXISTS permission_role_users_history
|
||||
(
|
||||
id INT NOT NULL,
|
||||
roleId INT NOT NULL,
|
||||
@@ -158,22 +158,22 @@ CREATE TABLE IF NOT EXISTS permission_role_auth_users_history
|
||||
updated TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TRIGGER TR_Roleauth_usersUpdate
|
||||
CREATE TRIGGER TR_RoleusersUpdate
|
||||
AFTER UPDATE
|
||||
ON permission_role_auth_users
|
||||
ON permission_role_users
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO permission_role_auth_users_history
|
||||
INSERT INTO permission_role_users_history
|
||||
(id, roleId, userId, deleted, editorId, created, updated)
|
||||
VALUES (OLD.id, OLD.roleId, OLD.userId, OLD.deleted, OLD.editorId, OLD.created, NOW());
|
||||
END;
|
||||
|
||||
CREATE TRIGGER TR_Roleauth_usersDelete
|
||||
CREATE TRIGGER TR_RoleusersDelete
|
||||
AFTER DELETE
|
||||
ON permission_role_auth_users
|
||||
ON permission_role_users
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
INSERT INTO permission_role_auth_users_history
|
||||
INSERT INTO permission_role_users_history
|
||||
(id, roleId, userId, deleted, editorId, created, updated)
|
||||
VALUES (OLD.id, OLD.roleId, OLD.userId, 1, OLD.editorId, OLD.created, NOW());
|
||||
END;
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS permission_api_key_permissions
|
||||
CONSTRAINT UQ_ApiKeyPermission UNIQUE (apiKeyId, permissionId),
|
||||
CONSTRAINT FK_ApiKeyPermissions_ApiKey FOREIGN KEY (apiKeyId) REFERENCES administration_api_keys (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_ApiKeyPermissions_Permission FOREIGN KEY (permissionId) REFERENCES permission_permissions (id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_ApiKeyPermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_auth_users (id)
|
||||
CONSTRAINT FK_ApiKeyPermissions_Editor FOREIGN KEY (editorId) REFERENCES administration_users (id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS permission_api_key_permissions_history
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
CREATE SCHEMA IF NOT EXISTS administration;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS administration.auth_users
|
||||
CREATE TABLE IF NOT EXISTS administration.users
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
keycloakId UUID NOT NULL,
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
|
||||
CONSTRAINT UC_KeycloakId UNIQUE (keycloakId)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS administration.auth_users_history
|
||||
CREATE TABLE IF NOT EXISTS administration.users_history
|
||||
(
|
||||
LIKE administration.auth_users
|
||||
LIKE administration.users
|
||||
);
|
||||
|
||||
CREATE TRIGGER users_history_trigger
|
||||
BEFORE INSERT OR UPDATE OR DELETE
|
||||
ON administration.auth_users
|
||||
ON administration.users
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION public.history_trigger_function();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ CREATE TABLE IF NOT EXISTS administration.api_keys
|
||||
keyString VARCHAR(255) NOT NULL,
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ CREATE TABLE permission.permissions
|
||||
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT UQ_PermissionName UNIQUE (name)
|
||||
@@ -35,7 +35,7 @@ CREATE TABLE permission.roles
|
||||
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT UQ_RoleName UNIQUE (name)
|
||||
@@ -61,7 +61,7 @@ CREATE TABLE permission.role_permissions
|
||||
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT UQ_RolePermission UNIQUE (RoleId, permissionId)
|
||||
@@ -79,27 +79,27 @@ CREATE TRIGGER versioning_trigger
|
||||
EXECUTE PROCEDURE public.history_trigger_function();
|
||||
|
||||
-- Role user
|
||||
CREATE TABLE permission.role_auth_users
|
||||
CREATE TABLE permission.role_users
|
||||
(
|
||||
id SERIAL PRIMARY KEY,
|
||||
RoleId INT NOT NULL REFERENCES permission.roles (id) ON DELETE CASCADE,
|
||||
UserId INT NOT NULL REFERENCES administration.auth_users (id) ON DELETE CASCADE,
|
||||
UserId INT NOT NULL REFERENCES administration.users (id) ON DELETE CASCADE,
|
||||
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT UQ_RoleUser UNIQUE (RoleId, UserId)
|
||||
);
|
||||
|
||||
CREATE TABLE permission.role_auth_users_history
|
||||
CREATE TABLE permission.role_users_history
|
||||
(
|
||||
LIKE permission.role_auth_users
|
||||
LIKE permission.role_users
|
||||
);
|
||||
|
||||
CREATE TRIGGER versioning_trigger
|
||||
BEFORE INSERT OR UPDATE OR DELETE
|
||||
ON permission.role_auth_users
|
||||
ON permission.role_users
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE public.history_trigger_function();
|
||||
@@ -6,7 +6,7 @@ CREATE TABLE permission.api_key_permissions
|
||||
|
||||
-- for history
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
editorId INT NULL REFERENCES administration.auth_users (id),
|
||||
editorId INT NULL REFERENCES administration.users (id),
|
||||
created timestamptz NOT NULL DEFAULT NOW(),
|
||||
updated timestamptz NOT NULL DEFAULT NOW(),
|
||||
CONSTRAINT UQ_ApiKeyPermission UNIQUE (apiKeyId, permissionId)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from contextvars import ContextVar
|
||||
from typing import Optional
|
||||
|
||||
from cpl.auth.schema._administration.auth_user import AuthUser
|
||||
from cpl.auth.schema._administration.user import User
|
||||
from cpl.dependency import get_provider
|
||||
|
||||
_user_context: ContextVar[Optional[AuthUser]] = ContextVar("user", default=None)
|
||||
_user_context: ContextVar[Optional[User]] = ContextVar("user", default=None)
|
||||
|
||||
|
||||
def set_user(user: Optional[AuthUser]):
|
||||
def set_user(user: Optional[User]):
|
||||
from cpl.core.log.logger_abc import LoggerABC
|
||||
|
||||
logger = get_provider().get_service(LoggerABC)
|
||||
@@ -15,5 +15,5 @@ def set_user(user: Optional[AuthUser]):
|
||||
_user_context.set(user)
|
||||
|
||||
|
||||
def get_user() -> Optional[AuthUser]:
|
||||
def get_user() -> Optional[User]:
|
||||
return _user_context.get()
|
||||
|
||||
@@ -49,11 +49,11 @@ class DbModelABC(ABC, Generic[T]):
|
||||
if self._editor_id is None:
|
||||
return None
|
||||
|
||||
from cpl.auth.schema import AuthUserDao
|
||||
from cpl.auth.schema import UserDao
|
||||
|
||||
auth_user_dao = get_provider().get_service(AuthUserDao)
|
||||
user_dao = get_provider().get_service(UserDao)
|
||||
|
||||
return await auth_user_dao.get_by_id(self._editor_id)
|
||||
return await user_dao.get_by_id(self._editor_id)
|
||||
|
||||
@property
|
||||
def created(self) -> datetime:
|
||||
|
||||
@@ -18,7 +18,7 @@ class DbModelDaoABC[T_DBM](DataAccessObjectABC[T_DBM]):
|
||||
self.attribute(DbModelABC.editor_id, int, db_name="editorId", ignore=True) # handled by db trigger
|
||||
|
||||
self.reference(
|
||||
"editor", "id", DbModelABC.editor_id, TableManager.get("auth_users")
|
||||
"editor", "id", DbModelABC.editor_id, TableManager.get("users")
|
||||
) # not relevant for updates due to editor_id
|
||||
|
||||
self.attribute(DbModelABC.created, datetime, ignore=True) # handled by db trigger
|
||||
|
||||
@@ -7,9 +7,9 @@ class TableManager:
|
||||
ServerTypes.POSTGRES: "system._executed_migrations",
|
||||
ServerTypes.MYSQL: "system__executed_migrations",
|
||||
},
|
||||
"auth_users": {
|
||||
ServerTypes.POSTGRES: "administration.auth_users",
|
||||
ServerTypes.MYSQL: "administration_auth_users",
|
||||
"users": {
|
||||
ServerTypes.POSTGRES: "administration.users",
|
||||
ServerTypes.MYSQL: "administration_users",
|
||||
},
|
||||
"api_keys": {
|
||||
ServerTypes.POSTGRES: "administration.api_keys",
|
||||
@@ -32,8 +32,8 @@ class TableManager:
|
||||
ServerTypes.MYSQL: "permission_role_permissions",
|
||||
},
|
||||
"role_users": {
|
||||
ServerTypes.POSTGRES: "permission.role_auth_users",
|
||||
ServerTypes.MYSQL: "permission_role_auth_users",
|
||||
ServerTypes.POSTGRES: "permission.role_users",
|
||||
ServerTypes.MYSQL: "permission_role_users",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ class GraphQLApp(WebApp):
|
||||
def __init__(self, services: ServiceProvider, modules: Modules):
|
||||
WebApp.__init__(self, services, modules, [GraphQLModule])
|
||||
|
||||
self._with_graphiql = False
|
||||
self._with_playground = False
|
||||
|
||||
def with_graphql(
|
||||
self,
|
||||
authentication: bool = False,
|
||||
@@ -57,6 +60,7 @@ class GraphQLApp(WebApp):
|
||||
policies=policies,
|
||||
match=match,
|
||||
)
|
||||
self._with_graphiql = True
|
||||
return self
|
||||
|
||||
def with_playground(
|
||||
@@ -77,4 +81,13 @@ class GraphQLApp(WebApp):
|
||||
policies=policies,
|
||||
match=match,
|
||||
)
|
||||
self._with_playground = True
|
||||
return self
|
||||
|
||||
|
||||
async def _log_before_startup(self):
|
||||
self._logger.info(f"Start API on {self._api_settings.host}:{self._api_settings.port}")
|
||||
if self._with_graphiql:
|
||||
self._logger.warning(f"GraphiQL available at http://{self._api_settings.host}:{self._api_settings.port}/api/graphiql")
|
||||
if self._with_playground:
|
||||
self._logger.warning(f"GraphQL Playground available at http://{self._api_settings.host}:{self._api_settings.port}/api/playground")
|
||||
@@ -1,12 +0,0 @@
|
||||
from cpl.auth.schema import AuthUser
|
||||
from cpl.graphql.schema.db_model_graph_type import DbModelGraphType
|
||||
|
||||
|
||||
class AuthUserGraphType(DbModelGraphType):
|
||||
|
||||
def __init__(self):
|
||||
DbModelGraphType.__init__(self)
|
||||
|
||||
self.string_field(AuthUser.keycloak_id, lambda root: root.keycloak_id)
|
||||
self.string_field(AuthUser.username, lambda root: root.username)
|
||||
self.string_field(AuthUser.email, lambda root: root.email)
|
||||
@@ -1,9 +1,9 @@
|
||||
from cpl.auth.schema import AuthUser
|
||||
from cpl.auth.schema import User
|
||||
from cpl.graphql.schema.filter.db_model_filter import DbModelFilter
|
||||
from cpl.graphql.schema.filter.string_filter import StringFilter
|
||||
|
||||
|
||||
class AuthUserFilter(DbModelFilter[AuthUser]):
|
||||
class UserFilter(DbModelFilter[User]):
|
||||
def __init__(self, public: bool = False):
|
||||
DbModelFilter.__init__(self, public)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from cpl.auth.schema import User
|
||||
from cpl.graphql.schema.db_model_graph_type import DbModelGraphType
|
||||
|
||||
|
||||
class UserGraphType(DbModelGraphType):
|
||||
|
||||
def __init__(self):
|
||||
DbModelGraphType.__init__(self)
|
||||
|
||||
self.string_field(User.keycloak_id, lambda root: root.keycloak_id)
|
||||
self.string_field(User.username, lambda root: root.username)
|
||||
self.string_field(User.email, lambda root: root.email)
|
||||
@@ -0,0 +1,23 @@
|
||||
from cpl.auth.schema import User
|
||||
from cpl.core.typing import SerialId
|
||||
from cpl.graphql.schema.input import Input
|
||||
|
||||
|
||||
class UserCreateInput(Input[User]):
|
||||
keycloak_id: str
|
||||
roles: list[SerialId]
|
||||
|
||||
def __init__(self):
|
||||
Input.__init__(self)
|
||||
self.string_field("keycloak_id").with_required()
|
||||
self.list_field("roles", SerialId)
|
||||
|
||||
|
||||
class UserUpdateInput(Input[User]):
|
||||
id: SerialId
|
||||
roles: list[SerialId]
|
||||
|
||||
def __init__(self):
|
||||
Input.__init__(self)
|
||||
self.int_field("id").with_required()
|
||||
self.list_field("roles", SerialId)
|
||||
@@ -0,0 +1,112 @@
|
||||
from cpl.api import APILogger
|
||||
from cpl.auth.keycloak import KeycloakAdmin
|
||||
from cpl.auth.permission import Permissions
|
||||
from cpl.auth.schema import UserDao, User, RoleUser, RoleUserDao, RoleDao
|
||||
from cpl.core.ctx.user_context import get_user
|
||||
from cpl.graphql.auth.administration.user.user_input import UserCreateInput, UserUpdateInput
|
||||
from cpl.graphql.schema.mutation import Mutation
|
||||
|
||||
|
||||
class UserMutation(Mutation):
|
||||
def __init__(
|
||||
self,
|
||||
logger: APILogger,
|
||||
user_dao: UserDao,
|
||||
role_user_dao: RoleUserDao,
|
||||
role_dao: RoleDao,
|
||||
keycloak_admin: KeycloakAdmin,
|
||||
):
|
||||
Mutation.__init__(self)
|
||||
self._logger = logger
|
||||
self._user_dao = user_dao
|
||||
self._role_user_dao = role_user_dao
|
||||
self._role_dao = role_dao
|
||||
self._keycloak_admin = keycloak_admin
|
||||
|
||||
self.int_field(
|
||||
"create",
|
||||
self.resolve_create,
|
||||
).with_require_any_permission(Permissions.users_create).with_argument(
|
||||
"input",
|
||||
UserCreateInput,
|
||||
).with_required()
|
||||
|
||||
self.bool_field(
|
||||
"update",
|
||||
self.resolve_update,
|
||||
).with_require_any_permission(Permissions.users_update).with_argument(
|
||||
"input",
|
||||
UserUpdateInput,
|
||||
).with_required()
|
||||
|
||||
self.bool_field(
|
||||
"delete",
|
||||
self.resolve_delete,
|
||||
).with_require_any_permission(Permissions.users_delete).with_argument(
|
||||
"id",
|
||||
int,
|
||||
).with_required()
|
||||
|
||||
self.bool_field(
|
||||
"restore",
|
||||
self.resolve_restore,
|
||||
).with_require_any_permission(Permissions.users_delete).with_argument(
|
||||
"id",
|
||||
int,
|
||||
).with_required()
|
||||
|
||||
async def resolve_create(self, input: UserCreateInput):
|
||||
self._logger.debug(f"create user: {input.__dict__}")
|
||||
|
||||
# ensure keycloak knows a user with this keycloak_id
|
||||
# get_user should raise an exception if the user does not exist
|
||||
kc_user = self._keycloak_admin.get_user(input.keycloak_id)
|
||||
if kc_user is None:
|
||||
raise ValueError(f"Keycloak user with id {input.keycloak_id} does not exist")
|
||||
|
||||
user = User(0, input.keycloak_id, input.license)
|
||||
user_id = await self._user_dao.create(user)
|
||||
user = await self._user_dao.get_by_id(user_id)
|
||||
await self._role_user_dao.create_many([RoleUser(0, user.id, x) for x in set(input.roles)])
|
||||
|
||||
return user
|
||||
|
||||
async def resolve_update(self, input: UserUpdateInput):
|
||||
self._logger.debug(f"update user: {input.__dict__}")
|
||||
user = await self._user_dao.get_by_id(input.id)
|
||||
|
||||
if input.license:
|
||||
user.license = input.license
|
||||
|
||||
await self._user_dao.update(user)
|
||||
await self._resolve_assignments(
|
||||
input.roles or [],
|
||||
user,
|
||||
RoleUser.user_id,
|
||||
RoleUser.role_id,
|
||||
self._user_dao,
|
||||
self._role_user_dao,
|
||||
RoleUser,
|
||||
self._role_dao,
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
async def resolve_delete(self, id: int):
|
||||
self._logger.debug(f"delete user: {id}")
|
||||
user = await self._user_dao.get_by_id(id)
|
||||
await self._user_dao.delete(user)
|
||||
try:
|
||||
active_user = get_user()
|
||||
if active_user is not None and active_user.id == user.id:
|
||||
# await broadcast.publish("userLogout", user.id)
|
||||
self._keycloak_admin.user_logout(user_id=user.keycloak_id)
|
||||
except Exception as e:
|
||||
self._logger.error(f"Failed to logout user from Keycloak", e)
|
||||
return True
|
||||
|
||||
async def resolve_restore(self, id: int):
|
||||
self._logger.debug(f"restore user: {id}")
|
||||
user = await self._user_dao.get_by_id(id)
|
||||
await self._user_dao.restore(user)
|
||||
return True
|
||||
@@ -1,7 +1,23 @@
|
||||
from cpl.core.configuration import Configuration
|
||||
from cpl.dependency import ServiceProvider
|
||||
from cpl.dependency.module.module import Module
|
||||
from cpl.graphql.auth.administration.auth_user_filter import AuthUserFilter
|
||||
from cpl.graphql.auth.administration.auth_user_graph_type import AuthUserGraphType
|
||||
from cpl.dependency.service_collection import ServiceCollection
|
||||
from cpl.graphql.auth.administration.user.user_filter import UserFilter
|
||||
from cpl.graphql.auth.administration.user.user_graph_type import UserGraphType
|
||||
from cpl.graphql.auth.administration.user.user_mutation import UserMutation
|
||||
from cpl.graphql.graphql_module import GraphQLModule
|
||||
from cpl.graphql.service.schema import Schema
|
||||
|
||||
|
||||
class GraphQLAuthModule(Module):
|
||||
transient = [AuthUserGraphType, AuthUserFilter]
|
||||
dependencies = [GraphQLModule]
|
||||
transient = [UserGraphType, UserMutation, UserFilter]
|
||||
|
||||
@staticmethod
|
||||
def register(collection: ServiceCollection):
|
||||
Configuration.set("GraphQLAuthModuleEnabled", True)
|
||||
|
||||
@staticmethod
|
||||
def configure(provider: ServiceProvider):
|
||||
schema = provider.get_service(Schema)
|
||||
schema.with_type(UserGraphType)
|
||||
@@ -1,8 +1,6 @@
|
||||
from cpl.api.api_module import ApiModule
|
||||
from cpl.dependency import ServiceCollection
|
||||
from cpl.dependency.module.module import Module
|
||||
from cpl.dependency.service_provider import ServiceProvider
|
||||
from cpl.graphql.auth.graphql_auth_module import GraphQLAuthModule
|
||||
from cpl.graphql.schema.filter.bool_filter import BoolFilter
|
||||
from cpl.graphql.schema.filter.date_filter import DateFilter
|
||||
from cpl.graphql.schema.filter.filter import Filter
|
||||
@@ -20,10 +18,6 @@ class GraphQLModule(Module):
|
||||
scoped = [GraphQLService]
|
||||
transient = [Filter, StringFilter, IntFilter, BoolFilter, DateFilter]
|
||||
|
||||
@staticmethod
|
||||
def register(collection: ServiceCollection):
|
||||
collection.add_module(GraphQLAuthModule)
|
||||
|
||||
@staticmethod
|
||||
def configure(services: ServiceProvider) -> None:
|
||||
schema = services.get_service(Schema)
|
||||
|
||||
@@ -3,7 +3,7 @@ from typing import Optional
|
||||
|
||||
from graphql import GraphQLResolveInfo
|
||||
|
||||
from cpl.auth.schema import AuthUser, Permission
|
||||
from cpl.auth.schema import User, Permission
|
||||
from cpl.core.ctx import get_user
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class QueryContext:
|
||||
self._is_mutation = is_mutation
|
||||
|
||||
@property
|
||||
def user(self) -> AuthUser:
|
||||
def user(self) -> User:
|
||||
return self._user
|
||||
|
||||
@property
|
||||
|
||||
@@ -2,6 +2,7 @@ from typing import Type, Optional, Generic, Annotated
|
||||
|
||||
import strawberry
|
||||
|
||||
from cpl.core.configuration import Configuration
|
||||
from cpl.core.typing import T
|
||||
from cpl.database.abc.data_access_object_abc import DataAccessObjectABC
|
||||
from cpl.graphql.schema.graph_type import GraphType
|
||||
@@ -23,9 +24,9 @@ class DbModelGraphType(GraphType[T], Generic[T]):
|
||||
self.int_field("id", lambda root: root.id).with_public(public)
|
||||
self.bool_field("deleted", lambda root: root.deleted).with_public(public)
|
||||
|
||||
from cpl.graphql.auth.administration.auth_user_graph_type import AuthUserGraphType
|
||||
|
||||
self.object_field("editor", lambda: AuthUserGraphType, lambda root: root.editor).with_public(public)
|
||||
if Configuration.get("GraphQLAuthModuleEnabled", False):
|
||||
from cpl.graphql.auth.administration.user.user_graph_type import UserGraphType
|
||||
self.object_field("editor", lambda: UserGraphType, lambda root: root.editor).with_public(public)
|
||||
|
||||
self.string_field("created", lambda root: root.created).with_public(public)
|
||||
self.string_field("updated", lambda root: root.updated).with_public(public)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Generic
|
||||
|
||||
from cpl.core.configuration.configuration import Configuration
|
||||
from cpl.core.typing import T
|
||||
from cpl.graphql.schema.filter.bool_filter import BoolFilter
|
||||
from cpl.graphql.schema.filter.date_filter import DateFilter
|
||||
@@ -13,8 +14,9 @@ class DbModelFilter(Filter[T], Generic[T]):
|
||||
|
||||
self.field("id", IntFilter).with_public(public)
|
||||
self.field("deleted", BoolFilter).with_public(public)
|
||||
from cpl.graphql.auth.administration.auth_user_filter import AuthUserFilter
|
||||
if Configuration.get("GraphQLAuthModuleEnabled", False):
|
||||
from cpl.graphql.auth.administration.user.user_filter import UserFilter
|
||||
self.field("editor", lambda: UserFilter).with_public(public)
|
||||
|
||||
self.field("editor", lambda: AuthUserFilter).with_public(public)
|
||||
self.field("created", DateFilter).with_public(public)
|
||||
self.field("updated", DateFilter).with_public(public)
|
||||
|
||||
@@ -13,16 +13,16 @@ class Filter(Input[T]):
|
||||
Input.__init__(self)
|
||||
|
||||
def filter_field(self, name: str, filter_type: Type["Filter"]):
|
||||
self.field(name, filter_type())
|
||||
self.field(name, filter_type)
|
||||
|
||||
def string_field(self, name: str):
|
||||
self.field(name, StringFilter())
|
||||
self.field(name, StringFilter)
|
||||
|
||||
def int_field(self, name: str):
|
||||
self.field(name, IntFilter())
|
||||
self.field(name, IntFilter)
|
||||
|
||||
def bool_field(self, name: str):
|
||||
self.field(name, BoolFilter())
|
||||
self.field(name, BoolFilter)
|
||||
|
||||
def date_field(self, name: str):
|
||||
self.field(name, DateFilter())
|
||||
self.field(name, DateFilter)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from typing import Type
|
||||
from typing import Type, Union
|
||||
|
||||
from cpl.core.typing import T
|
||||
from cpl.database.abc import DataAccessObjectABC, DbJoinModelABC
|
||||
from cpl.dependency.inject import inject
|
||||
from cpl.dependency.service_provider import ServiceProvider
|
||||
from cpl.graphql.abc.query_abc import QueryABC
|
||||
@@ -23,3 +25,75 @@ class Mutation(QueryABC):
|
||||
raise ValueError(f"Mutation '{cls.__name__}' not registered in service provider")
|
||||
|
||||
return self.field(name, sub.to_strawberry(), lambda: sub)
|
||||
|
||||
@staticmethod
|
||||
async def _resolve_assignments(
|
||||
foreign_objs: list[int],
|
||||
resolved_obj: T,
|
||||
reference_key_own: Union[str, property],
|
||||
reference_key_foreign: Union[str, property],
|
||||
source_dao: DataAccessObjectABC[T],
|
||||
join_dao: DataAccessObjectABC[T],
|
||||
join_type: Type[DbJoinModelABC],
|
||||
foreign_dao: DataAccessObjectABC[T],
|
||||
):
|
||||
if foreign_objs is None:
|
||||
return
|
||||
|
||||
reference_key_foreign_attr = reference_key_foreign
|
||||
if isinstance(reference_key_foreign, property):
|
||||
reference_key_foreign_attr = reference_key_foreign.fget.__name__
|
||||
|
||||
foreign_list = await join_dao.find_by(
|
||||
[{reference_key_own: resolved_obj.id}, {"deleted": False}]
|
||||
)
|
||||
|
||||
to_delete = (
|
||||
foreign_list
|
||||
if len(foreign_objs) == 0
|
||||
else await join_dao.find_by(
|
||||
[
|
||||
{reference_key_own: resolved_obj.id},
|
||||
{reference_key_foreign: {"notIn": foreign_objs}},
|
||||
]
|
||||
)
|
||||
)
|
||||
foreign_ids = [getattr(x, reference_key_foreign_attr) for x in foreign_list]
|
||||
deleted_foreign_ids = [
|
||||
getattr(x, reference_key_foreign_attr)
|
||||
for x in await join_dao.find_by(
|
||||
[{reference_key_own: resolved_obj.id}, {"deleted": True}]
|
||||
)
|
||||
]
|
||||
|
||||
to_create = [
|
||||
join_type(0, resolved_obj.id, x)
|
||||
for x in foreign_objs
|
||||
if x not in foreign_ids and x not in deleted_foreign_ids
|
||||
]
|
||||
to_restore = [
|
||||
await join_dao.get_single_by(
|
||||
[
|
||||
{reference_key_own: resolved_obj.id},
|
||||
{reference_key_foreign: x},
|
||||
]
|
||||
)
|
||||
for x in foreign_objs
|
||||
if x not in foreign_ids and x in deleted_foreign_ids
|
||||
]
|
||||
|
||||
if len(to_delete) > 0:
|
||||
await join_dao.delete_many(to_delete)
|
||||
|
||||
if len(to_create) > 0:
|
||||
await join_dao.create_many(to_create)
|
||||
|
||||
if len(to_restore) > 0:
|
||||
await join_dao.restore_many(to_restore)
|
||||
|
||||
foreign_changes = [*to_delete, *to_create, *to_restore]
|
||||
if len(foreign_changes) > 0:
|
||||
await source_dao.touch(resolved_obj)
|
||||
await foreign_dao.touch_many_by_id(
|
||||
[getattr(x, reference_key_foreign_attr) for x in foreign_changes]
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import strawberry
|
||||
from cpl.api.logger import APILogger
|
||||
from cpl.dependency.service_provider import ServiceProvider
|
||||
from cpl.graphql.abc.strawberry_protocol import StrawberryProtocol
|
||||
from cpl.graphql.auth.administration.auth_user_graph_type import AuthUserGraphType
|
||||
from cpl.graphql.schema.root_mutation import RootMutation
|
||||
from cpl.graphql.schema.root_query import RootQuery
|
||||
|
||||
@@ -17,9 +16,7 @@ class Schema:
|
||||
self._logger = logger
|
||||
self._provider = provider
|
||||
|
||||
self._types: dict[str, Type[StrawberryProtocol]] = {
|
||||
"AuthUserGraphType": AuthUserGraphType,
|
||||
}
|
||||
self._types: dict[str, Type[StrawberryProtocol]] = {}
|
||||
|
||||
self._schema = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user