Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Sport competition module #621

Draft
wants to merge 78 commits into
base: schools
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
ff6822a
Feat: transform AccountType from groups to user's attribute
Rotheem Oct 27, 2024
9010267
Feat: refactoring providers and auth
Rotheem Nov 13, 2024
89416d2
Feat: refactoring base visibility
Rotheem Nov 13, 2024
2519b20
Feat: refactoring tests
Rotheem Nov 13, 2024
efe8001
Feat: add migration file
Rotheem Nov 13, 2024
5a5fe2e
Fix: remove excess lines
Rotheem Nov 13, 2024
74dd87d
Fix: correct migration
Rotheem Nov 13, 2024
d271bba
Fix: test and migration
Rotheem Nov 16, 2024
a6ee0db
Fix: migration
Rotheem Nov 16, 2024
50f33b7
Fix: auth endpoint
Rotheem Nov 16, 2024
70468f3
Fix: reorder migrations
Rotheem Nov 16, 2024
fb8856d
Feat: user dependencies refacto and minor fix
Rotheem Nov 16, 2024
b2d60d2
Fix: tools
Rotheem Nov 16, 2024
606e4b0
Fix: import
Rotheem Nov 16, 2024
7ea36f9
Fix: rename arguments
Rotheem Nov 16, 2024
7eebfa0
Fix: correct is_user dependency
Rotheem Nov 16, 2024
5b3829b
Fix: user search
Rotheem Nov 16, 2024
d9314f5
Fix: argument name
Rotheem Nov 16, 2024
f6c50df
Fix: cdr user declaration
Rotheem Nov 16, 2024
cdf92de
Fix: test auth
Rotheem Nov 17, 2024
d4ff049
Fix: auth test
Rotheem Nov 17, 2024
c798183
Fix: migrations
Rotheem Nov 17, 2024
5182fa4
Fix: migrations
Rotheem Nov 17, 2024
7582026
Fix: migrations
Rotheem Nov 17, 2024
5492a63
Fix: migrations
Rotheem Nov 17, 2024
b832e32
Fix: migrations
Rotheem Nov 17, 2024
4e536ab
Fix: migration
Rotheem Nov 17, 2024
0f19040
Fix: migration
Rotheem Nov 17, 2024
14922ac
Fix: remove unused migration
Rotheem Nov 17, 2024
9eacbc1
Fix: reorder migrations
Rotheem Nov 17, 2024
37be55c
Fix: correct enum drop
Rotheem Nov 17, 2024
b76a80c
Fix: migration
Rotheem Nov 17, 2024
a0c06f5
Feat: refactoring module visibility initialisation
Rotheem Nov 17, 2024
332ff26
Fix: initialisation
Rotheem Nov 17, 2024
6105057
Fix: migration
Rotheem Nov 17, 2024
f1d1cf0
Feat: disable external user creation separation
Rotheem Nov 17, 2024
da1ec85
Fix: migration
Rotheem Nov 17, 2024
6da8e2a
Fix: migration
Rotheem Nov 17, 2024
d3676ac
Fix: default server values
Rotheem Nov 17, 2024
5cd953b
Fix: migration
Rotheem Nov 17, 2024
5285a6f
Fix: migration
Rotheem Nov 17, 2024
338abfc
Fix: migration
Rotheem Nov 17, 2024
89d81ee
Fix: patch required parameter of CalypSSO
Rotheem Nov 17, 2024
f68a65c
Fix: clean up
Rotheem Nov 17, 2024
ec4d79d
Feat: migration test
Rotheem Nov 18, 2024
6166c49
Fix: rename account type
Rotheem Nov 18, 2024
b6aed17
Fix: student regex
Rotheem Nov 19, 2024
9caa58f
Fix: migration
Rotheem Nov 19, 2024
25e5787
Feat: add endpoint to get AccountTypes list
Rotheem Nov 24, 2024
5c620a7
Fix: change account types URL
Rotheem Nov 24, 2024
5f08b6d
Feat: add account type to groups list in providers
Rotheem Nov 26, 2024
c176e9f
Feat: refactoring providers and auth
Rotheem Nov 13, 2024
966241f
Feat: add schools
Rotheem Oct 27, 2024
06da06a
Fix: rebase
Rotheem Nov 18, 2024
8d5c035
Fix: reorder migrations
Rotheem Nov 18, 2024
ceca40f
Fix: migration
Rotheem Nov 18, 2024
eeac8b6
Fix: migration
Rotheem Nov 19, 2024
0ca2cb8
Fix: migration
Rotheem Nov 19, 2024
fe06fd0
Fix: migration
Rotheem Nov 19, 2024
38b84d6
Fix: migration
Rotheem Nov 19, 2024
3345dd5
Fix: migration
Rotheem Nov 19, 2024
2d266fc
Fix: student regex
Rotheem Nov 19, 2024
4e3d3ee
Fix: tests
Rotheem Nov 19, 2024
115f96c
Fix: remove school from core_user init
Rotheem Nov 19, 2024
c3b648f
Fix: initialise schools on launch
Rotheem Nov 19, 2024
5839c44
Fix: migration and user declaration
Rotheem Nov 19, 2024
8cebc46
Fix: add router
Rotheem Nov 19, 2024
d6d9ca5
Feat: remove school_id from user creation
Rotheem Nov 19, 2024
275b854
Feat: tests
Rotheem Nov 19, 2024
114ec31
Feat: add users to school on school creation
Rotheem Nov 19, 2024
2f85d41
Fix: add comment
Rotheem Nov 20, 2024
d09696a
Fix: correct rebase
Rotheem Nov 27, 2024
9eba273
Feat: first version of module
Rotheem Nov 28, 2024
fdbf91e
Fix: rename raid's Participant as RaidParticipant
Rotheem Nov 28, 2024
40d776b
Fix: rename raid's Team as RaidTeam
Rotheem Nov 28, 2024
cd8931d
Fix: new access system
Rotheem Nov 30, 2024
87d4528
Fix: linting
Rotheem Nov 30, 2024
b87542a
Fix: models relationship
Rotheem Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ SQLITE_DB = "test.db" # If set, the application use a SQLite database instead of
# Authorization using JWT #
ACCESS_TOKEN_SECRET_KEY="YWZOHliiI53lJMJc5BI_WbGbA4GF2T7Wbt1airIhOXEa3c021c4-1c55-4182-b141-7778bcc8fac4" # Note: modifing this token requires to update the common `test_check_settings_mocking` test
RSA_PRIVATE_PEM_STRING = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA1tpj3TZDkJakp2RygsM392pQbcmNBOGFT8FlETcRG/JVFT7k\niClJu+CVOJSVD0epfpYp93cYepfw74SezYnBCyuoLJ2yg5Qh4KlCrWmvwM7vhFIN\nx0xddIQi+Gm0T3dxGtv4Ga50TYX4SV4FE3ctJG9m3pyNF6POODp5tMJvShQWYTto\nW9qNhltZ8Z+14bq2INV/efpT47WuMT+VD/fa9/WwopAtgBcQOvq57fv5+DaPOIVR\n9BiP7F+pv+v6wQ373hI22QzCMsA4Whl+BmWFKcFoBDOBRjlW5VqhJWJkWZIRP0q+\nVAZHk2xJK+0YFc9jmaC+ExMtuyHYK0RnQK/8LQIDAQABAoIBABxJ8v4sZ+cAvrs/\nkYhAFf1gpShfck7jNr9SknEa1Aje9m7usf5vmULAhkVF4v55DAsb0HjB2JpDqTiQ\nOKyNZ7qFzAXb2aZTecZv4tScZsS3OngsqZ3FI0T1JPmaSWBxNJY5wkf3XV7btd5L\nH9X5ShtTA7Np33XuXneu01mGhEq3boLro+vfXMHV5QHyle1F4LUFWEqtP0UmZ5wA\nrro0Y7pA8R88tu5X4iWEjQPnAsbRixwFQ9LNMD8+40e1UIguobRySnP5umErHaIh\nKui7ZijLjbZh/dPS0IfpgahL1K6s9XhT3mD9WMvAvMkNtLewHIZZukG45mOQBrjF\nvvyYxoECgYEA+EY6YimGw0IKnUuf+5uZRXST7kDMENz1Flkcj8oZvo47hdX8/lDN\ni0y7gm3VNfHAK2R2KZPmSbtXA0DvS7kmx1/CFcmwkaakhuU5dyCHldWwSaTME3IE\nxjSZfTvlAiq9i6nUflgfkKo3Bdsiq8TYOUAv25S2SwYDH9Tx0fQwwGECgYEA3Ynt\nCHc8e4YRlGT65UQmEZ8cptmqVRyY4ClMU1xht7Pn0G1JwKRraiEL5/LndwscWf3h\nDygQuArJ28pp4d22FEW1LeXozXYUjJoz3anIA45IZ1OihS7Cx7tJB51/QNJeFdF4\nEX/XHaVukHyYSsAxkwCUYOw3cSgZOSEddL5Wf00CgYEA7JlIlDmMwtFR+jqSmJ3c\n//Kr8zZvAnb/Xa/IZ0MrK4yyLsYR1m48o06Ztx9iO4lKIFAZx1+563QL5P7hzOEC\nkqev90GA8hzD2AXksKEgdOrymAvjq3hSEm0YBN+qS1ldzxYmec0TL7L2wq7lqJnr\nkQuZUAG1g2OUYKZ3WSUDvKECgYEAv24NSkFuG/avfiD7w9xtYNCye2KekskROLG2\n6FltfsWQTEQDdNkekChaF2WHqRAKwaBlNymRuNZpsuhnMerZCQ9rDWwbDF86RnyA\n0MuCr7/kxJQ6XQcY/GnTIydu7F5bOlM0gzqKcW2f6m4fUohczf+0N0QmbDsQAJOi\n1lwadgkCgYEA3tkCBJIPTQecfjWiLqSocS6SrwXU+r3Jw6kI3/IB6ban/nsFdHSb\nnADST7f2zZatN6XALwsLU7f2R09R39ub0AJPyfToxo7MngR1rvaUYooF3rLlaU32\n8DqGvGpLkZkwbtcDmcX1zQoHjUo7RvoShZoapr59ihfrkiiEsXOkuGw=\n-----END RSA PRIVATE KEY-----\n"
AUTH_CLIENTS=[["AppAuthClientWithPKCE", null, ["http://127.0.0.1:8000/docs"], "AppAuthClient"], ["AppAuthClientWithClientSecret", "secret", ["http://127.0.0.1:8000/docs"], "AppAuthClient"], ["BaseAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "BaseAuthClient"], ["RalllyAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "RalllyAuthClient"], ["SynapseAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "SynapseAuthClient"], ["AcceptingOnlyECLUsersAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "NextcloudAuthClient"]]
AUTH_CLIENTS=[["AppAuthClientWithPKCE", null, ["http://127.0.0.1:8000/docs"], "AppAuthClient"], ["AppAuthClientWithClientSecret", "secret", ["http://127.0.0.1:8000/docs"], "AppAuthClient"], ["BaseAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "BaseAuthClient"], ["RalllyAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "RalllyAuthClient"], ["SynapseAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "SynapseAuthClient"], ["AcceptingOnlyECLUsersAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "NextcloudAuthClient"], ["RestrictingUsersGroupsAuthClient", "secret", ["http://127.0.0.1:8000/docs"], "DocumensoAuthClient"]]

# OIDC #
# Host or url of the API, used for Openid connect discovery endpoint
Expand Down
2 changes: 2 additions & 0 deletions app/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from app.core.groups import endpoints_groups
from app.core.notification import endpoints_notification
from app.core.payment import endpoints_payment
from app.core.schools import endpoints_schools
from app.core.users import endpoints_users
from app.modules.module_list import module_list

Expand All @@ -24,6 +25,7 @@
api_router.include_router(endpoints_notification.router)
api_router.include_router(endpoints_payment.router)
api_router.include_router(endpoints_users.router)
api_router.include_router(endpoints_schools.router)

for module in module_list:
api_router.include_router(module.router)
113 changes: 91 additions & 22 deletions app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from app.core.google_api.google_api import GoogleAPI
from app.core.groups.groups_type import GroupType
from app.core.log import LogConfig
from app.core.schools.schools_type import SchoolType
from app.dependencies import (
get_db,
get_redis_client,
Expand Down Expand Up @@ -185,40 +186,104 @@ def initialize_groups(
)


def initialize_module_visibility(
def initialize_schools(
sync_engine: Engine,
hyperion_error_logger: logging.Logger,
) -> None:
"""Add the default module visibilities for Titan"""
"""Add the necessary shools"""

hyperion_error_logger.info("Startup: Adding new groups to the database")
with Session(sync_engine) as db:
# Is run to create default module visibilities or when the table is empty
haveBeenInitialized = (
len(initialization.get_all_module_visibility_membership_sync(db)) > 0
)
if haveBeenInitialized:
hyperion_error_logger.info(
"Startup: Modules visibility settings have already been initialized",
)
return

hyperion_error_logger.info(
"Startup: Modules visibility settings are empty, initializing them",
)
for module in module_list:
for default_group_id in module.default_allowed_groups_ids:
module_visibility = models_core.ModuleVisibility(
root=module.root,
allowed_group_id=default_group_id.value,
for school in SchoolType:
exists = initialization.get_school_by_id_sync(school_id=school, db=db)
# We don't want to recreate the groups if they already exist
if not exists:
db_school = models_core.CoreSchool(
id=school,
name=school.name,
email_regex=".*",
)

try:
initialization.create_module_visibility_sync(module_visibility, db)
initialization.create_school_sync(school=db_school, db=db)
except IntegrityError as error:
hyperion_error_logger.fatal(
f"Startup: Could not add module visibility {module.root}<{default_group_id}> in the database: {error}",
f"Startup: Could not add group {db_school.name}<{db_school.id}> in the database: {error}",
)


def initialize_module_visibility(
sync_engine: Engine,
hyperion_error_logger: logging.Logger,
) -> None:
"""Add the default module visibilities for Titan"""

with Session(sync_engine) as db:
module_awareness = initialization.get_core_data_sync(
"module_awareness",
db,
)
awareness_list = []
if module_awareness is not None:
awareness_list = module_awareness.data.split(",")

# Is run to create default module visibilities or when the table is empty
if awareness_list != [module.root for module in module_list]:
hyperion_error_logger.info(
"Startup: Some modules visibility settings are empty, initializing them",
)
to_add = [
module for module in module_list if module.root not in awareness_list
]
for module in to_add:
if module.default_allowed_groups_ids is not None:
for group_id in module.default_allowed_groups_ids:
module_group_visibility = models_core.ModuleGroupVisibility(
root=module.root,
allowed_group_id=group_id,
)
try:
initialization.create_module_group_visibility_sync(
module_visibility=module_group_visibility,
db=db,
)
except ValueError as error:
hyperion_error_logger.fatal(
f"Startup: Could not add module visibility {module.root} in the database: {error}",
)
if module.default_allowed_account_types is not None:
for account_type in module.default_allowed_account_types:
module_account_type_visibility = (
models_core.ModuleAccountTypeVisibility(
root=module.root,
allowed_account_type=account_type,
)
)
try:
initialization.create_module_account_type_visibility_sync(
module_visibility=module_account_type_visibility,
db=db,
)
except ValueError as error:
hyperion_error_logger.fatal(
f"Startup: Could not add module visibility {module.root} in the database: {error}",
)
initialization.set_core_data_sync(
models_core.CoreData(
"module_awareness",
",".join([module.root for module in module_list]),
),
db,
)
hyperion_error_logger.info(
f"Startup: Modules visibility settings initialized for {[module.root for module in to_add]}",
)
else:
hyperion_error_logger.info(
"Startup: Modules visibility settings already initialized",
)


def use_route_path_as_operation_ids(app: FastAPI) -> None:
"""
Simplify operation IDs so that generated API clients have simpler function names.
Expand Down Expand Up @@ -261,6 +326,10 @@ def init_db(
sync_engine=sync_engine,
hyperion_error_logger=hyperion_error_logger,
)
initialize_schools(
sync_engine=sync_engine,
hyperion_error_logger=hyperion_error_logger,
)
initialize_module_visibility(
sync_engine=sync_engine,
hyperion_error_logger=hyperion_error_logger,
Expand Down
14 changes: 7 additions & 7 deletions app/core/auth/endpoints_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from app.types.exceptions import AuthHTTPException
from app.types.scopes_type import ScopeType
from app.utils.auth.providers import BaseAuthClient
from app.utils.tools import is_user_external, is_user_member_of_an_allowed_group
from app.utils.tools import is_user_member_of_an_allowed_group

router = APIRouter(tags=["Auth"])

Expand Down Expand Up @@ -310,7 +310,7 @@ async def authorize_validation(
if auth_client.allowed_groups is not None:
if not is_user_member_of_an_allowed_group(
user=user,
allowed_groups=auth_client.allowed_groups,
allowed_groups=auth_client.allowed_groups, # type: ignore
):
hyperion_access_logger.warning(
f"Authorize-validation: user is not member of an allowed group {authorizereq.email} ({request_id})",
Expand All @@ -322,19 +322,19 @@ async def authorize_validation(
),
status_code=status.HTTP_302_FOUND,
)
if not auth_client.allow_external_users:
if is_user_external(user):
if auth_client.allowed_account_types is not None:
if user.account_type not in auth_client.allowed_account_types:
# TODO We should show an HTML page explaining the issue
hyperion_access_logger.warning(
f"Authorize-validation: external users are disabled for this auth provider {auth_client.client_id} ({request_id})",
f"Authorize-validation: user account type is not allowed {authorizereq.email} ({request_id})",
)
return RedirectResponse(
settings.CLIENT_URL
+ calypsso.get_error_relative_url(
message="External users are not allowed",
message="User account type is not allowed",
),
status_code=status.HTTP_302_FOUND,
)

# We generate a new authorization_code
# The authorization code MUST expire
# shortly after it is issued to mitigate the risk of leaks. A
Expand Down
113 changes: 81 additions & 32 deletions app/core/cruds_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,48 @@
from sqlalchemy.ext.asyncio import AsyncSession

from app.core import models_core


async def get_all_module_visibility_membership(
db: AsyncSession,
):
"""Return the every module with their visibility"""
result = await db.execute(select(models_core.ModuleVisibility))
return result.unique().scalars().all()
from app.core.groups.groups_type import AccountType


async def get_modules_by_user(
user: models_core.CoreUser,
db: AsyncSession,
) -> Sequence[str]:
) -> list[str]:
"""Return the modules a user has access to"""

userGroupIds = [group.id for group in user.groups]

result = await db.execute(
select(models_core.ModuleVisibility.root)
.where(models_core.ModuleVisibility.allowed_group_id.in_(userGroupIds))
.group_by(models_core.ModuleVisibility.root),
result_group = list(
(
await db.execute(
select(models_core.ModuleGroupVisibility.root)
.where(
models_core.ModuleGroupVisibility.allowed_group_id.in_(
userGroupIds,
),
)
.group_by(models_core.ModuleGroupVisibility.root),
)
)
.unique()
.scalars()
.all(),
)
result_account_type = list(
(
await db.execute(
select(models_core.ModuleAccountTypeVisibility.root).where(
models_core.ModuleAccountTypeVisibility.allowed_account_type
== user.account_type,
),
)
)
.unique()
.scalars()
.all(),
)

return result.unique().scalars().all()
return result_group + result_account_type


async def get_allowed_groups_by_root(
Expand All @@ -40,35 +57,36 @@ async def get_allowed_groups_by_root(

result = await db.execute(
select(
models_core.ModuleVisibility.allowed_group_id,
).where(models_core.ModuleVisibility.root == root),
models_core.ModuleGroupVisibility.allowed_group_id,
).where(models_core.ModuleGroupVisibility.root == root),
)

resultList = result.unique().scalars().all()

return resultList


async def get_module_visibility(
async def get_allowed_account_types_by_root(
root: str,
group_id: str,
db: AsyncSession,
) -> models_core.ModuleVisibility | None:
"""Return module visibility by root and group id"""
) -> Sequence[str]:
"""Return the groups allowed to access to a specific root"""

result = await db.execute(
select(models_core.ModuleVisibility).where(
models_core.ModuleVisibility.allowed_group_id == group_id,
models_core.ModuleVisibility.root == root,
),
select(
models_core.ModuleAccountTypeVisibility.allowed_account_type,
).where(models_core.ModuleAccountTypeVisibility.root == root),
)
return result.unique().scalars().first()

resultList = result.unique().scalars().all()

return resultList


async def create_module_visibility(
module_visibility: models_core.ModuleVisibility,
async def create_module_group_visibility(
module_visibility: models_core.ModuleGroupVisibility,
db: AsyncSession,
) -> models_core.ModuleVisibility:
) -> models_core.ModuleGroupVisibility:
"""Create a new module visibility in database and return it"""

db.add(module_visibility)
Expand All @@ -81,15 +99,46 @@ async def create_module_visibility(
return module_visibility


async def delete_module_visibility(
async def create_module_account_type_visibility(
module_visibility: models_core.ModuleAccountTypeVisibility,
db: AsyncSession,
) -> models_core.ModuleAccountTypeVisibility:
"""Create a new module visibility in database and return it"""

db.add(module_visibility)
try:
await db.commit()
except IntegrityError:
await db.rollback()
raise
else:
return module_visibility


async def delete_module_group_visibility(
root: str,
allowed_group_id: str,
db: AsyncSession,
):
await db.execute(
delete(models_core.ModuleVisibility).where(
models_core.ModuleVisibility.root == root,
models_core.ModuleVisibility.allowed_group_id == allowed_group_id,
delete(models_core.ModuleGroupVisibility).where(
models_core.ModuleGroupVisibility.root == root,
models_core.ModuleGroupVisibility.allowed_group_id == allowed_group_id,
),
)
await db.commit()


async def delete_module_account_type_visibility(
root: str,
allowed_account_type: AccountType,
db: AsyncSession,
):
await db.execute(
delete(models_core.ModuleAccountTypeVisibility).where(
models_core.ModuleAccountTypeVisibility.root == root,
models_core.ModuleAccountTypeVisibility.allowed_account_type
== allowed_account_type,
),
)
await db.commit()
Expand Down
Loading