Skip to content

Commit a374c74

Browse files
authored
Вход в приложение через Твой ФФ (#84)
* Auth with TvoyFF * Fix tests * CI
1 parent 1993f62 commit a374c74

20 files changed

Lines changed: 121 additions & 128 deletions

File tree

.github/workflows/build_and_publish.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ jobs:
8787
--network=web \
8888
--volume com_profcomff_api_timetable_test_static:/app/static \
8989
--env DB_DSN=${{ secrets.DB_DSN }} \
90+
--env AUTH_URL='https://api.test.profcomff.com/auth' \
9091
--env ROOT_PATH='/timetable' \
9192
--env GUNICORN_CMD_ARGS='--log-config logging_test.conf' \
9293
--env GOOGLE_CLIENT_SECRET='${{ secrets.GOOGLE_CLIENT_SECRET }}' \
@@ -132,6 +133,7 @@ jobs:
132133
--network=web \
133134
--volume com_profcomff_api_timetable_static:/app/static \
134135
--env DB_DSN='${{ secrets.DB_DSN }}' \
136+
--env AUTH_URL='https://api.profcomff.com/auth' \
135137
--env ROOT_PATH='/timetable' \
136138
--env ADMIN_SECRET='${{ secrets.ADMIN_SECRET }}' \
137139
--env REDIRECT_URL='https://www.profcomff.com/timetable/google' \

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
run:
22
source ./venv/bin/activate && uvicorn --reload --log-level debug calendar_backend.routes.base:app
33

4+
configure: venv
5+
source ./venv/bin/activate && pip install -r requirements.dev.txt -r requirements.txt
6+
7+
venv:
8+
python3.11 -m venv venv
9+
10+
format:
11+
autoflake -r --in-place --remove-all-unused-imports ./calendar_backend
12+
isort ./calendar_backend
13+
black ./calendar_backend
14+
415
db:
516
docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-timetable_api postgres:15
617

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
from . import auth, utils
1+
from . import utils
22

33

4-
__all__ = [
5-
"utils",
6-
"auth",
7-
]
4+
__all__ = ("utils",)

calendar_backend/methods/auth.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

calendar_backend/routes/auth.py

Lines changed: 0 additions & 27 deletions
This file was deleted.

calendar_backend/routes/base.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
from calendar_backend.exceptions import ForbiddenAction, NotEnoughCriteria, ObjectNotFound
1818
from calendar_backend.settings import get_settings
1919

20-
from .auth import auth_router # TODO: Replace with PKFF Auth
2120
from .event import event_comment_review_router as old_event_comment_review_router # DEPRICATED TODO: Drop 2023-04-01
2221
from .event import event_comment_router as old_event_comment_router # DEPRICATED TODO: Drop 2023-04-01
2322
from .event import event_router as old_event_router # DEPRICATED TODO: Drop 2023-04-01
@@ -52,22 +51,17 @@
5251
description=dedent(
5352
"""
5453
API для работы с календарем физфака.
54+
5555
Пример работы на питоне(Создание Room):
5656
```python
5757
import reqests, json
5858
url=f"https://api.test.profcomff.com/timetable"
5959
60-
# Авторизация
61-
beaver = requests.post(f"{url}/token", {"username": "...", "password": "..."})
62-
63-
# Парсинг ответа
64-
auth_data=json.loads(beaver.content)
65-
6660
# Создание
6761
create_room = requests.post(
6862
f"{url}/room",
6963
json={"name": "test", "direction": "South"},
70-
headers={"Authorization": f"Bearer {auth_data.get('access_token')}"}
64+
headers={"Authorization": f"ТокенАвторизацииТвойФФ"}
7165
)
7266
```
7367
"""
@@ -141,7 +135,6 @@ async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -
141135
# region DEPRICATED
142136
# TODO: Drop 2023-04-01
143137
app.include_router(gcal)
144-
app.include_router(auth_router)
145138
app.include_router(old_lecturer_router)
146139
app.include_router(old_lecturer_comment_router)
147140
app.include_router(old_lecturer_comment_review_router)

calendar_backend/routes/event/comment.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
from auth_lib.fastapi import UnionAuth
12
from fastapi import APIRouter, Depends
23
from fastapi_sqlalchemy import db
34

45
from calendar_backend.exceptions import ForbiddenAction, ObjectNotFound
5-
from calendar_backend.methods import auth
66
from calendar_backend.models import ApproveStatuses
77
from calendar_backend.models import CommentEvent as DbCommentEvent
88
from calendar_backend.routes.models.event import CommentEventGet, EventCommentPatch, EventCommentPost, EventComments
@@ -11,12 +11,12 @@
1111

1212
settings = get_settings()
1313
# DEPRICATED TODO: Drop 2023-04-01
14-
event_comment_router = APIRouter(prefix="/timetable/event/{event_id}", tags=["Event: Comment"], deprecated=True)
15-
router = APIRouter(prefix="/event/{event_id}", tags=["Event: Comment"])
14+
event_comment_router = APIRouter(prefix="/timetable/event/{event_id}/comment", tags=["Event: Comment"], deprecated=True)
15+
router = APIRouter(prefix="/event/{event_id}/comment", tags=["Event: Comment"])
1616

1717

18-
@event_comment_router.post("/comment/", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
19-
@router.post("/comment/", response_model=CommentEventGet)
18+
@event_comment_router.post("/", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
19+
@router.post("/", response_model=CommentEventGet)
2020
async def comment_event(event_id: int, comment: EventCommentPost) -> CommentEventGet:
2121
approve_status = ApproveStatuses.APPROVED if not settings.REQUIRE_REVIEW_EVENT_COMMENT else ApproveStatuses.PENDING
2222
comment_event = DbCommentEvent.create(
@@ -26,8 +26,8 @@ async def comment_event(event_id: int, comment: EventCommentPost) -> CommentEven
2626
return CommentEventGet.from_orm(comment_event)
2727

2828

29-
@event_comment_router.patch("/comment/{id}", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
30-
@router.patch("/comment/{id}", response_model=CommentEventGet)
29+
@event_comment_router.patch("/{id}", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
30+
@router.patch("/{id}", response_model=CommentEventGet)
3131
async def update_comment(id: int, event_id: int, comment_inp: EventCommentPatch) -> CommentEventGet:
3232
comment = DbCommentEvent.get(id, only_approved=False, session=db.session)
3333
if comment.event_id != event_id:
@@ -39,18 +39,20 @@ async def update_comment(id: int, event_id: int, comment_inp: EventCommentPatch)
3939
return CommentEventGet.from_orm(comment_event)
4040

4141

42-
@event_comment_router.get("/comment/{id}", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
43-
@router.get("/comment/{id}", response_model=CommentEventGet)
42+
@event_comment_router.get("/{id}", response_model=CommentEventGet) # DEPRICATED TODO: Drop 2023-04-01
43+
@router.get("/{id}", response_model=CommentEventGet)
4444
async def get_comment(id: int, event_id: int) -> CommentEventGet:
4545
comment = DbCommentEvent.get(id, session=db.session)
4646
if not comment.event_id == event_id or comment.approve_status != ApproveStatuses.APPROVED:
4747
raise ObjectNotFound(DbCommentEvent, id)
4848
return CommentEventGet.from_orm(comment)
4949

5050

51-
@event_comment_router.delete("/comment/{id}", response_model=None) # DEPRICATED TODO: Drop 2023-04-01
52-
@router.delete("/comment/{id}", response_model=None)
53-
async def delete_comment(id: int, event_id: int, _: auth.User = Depends(auth.get_current_user)) -> None:
51+
@event_comment_router.delete("/{id}", response_model=None) # DEPRICATED TODO: Drop 2023-04-01
52+
@router.delete("/{id}", response_model=None)
53+
async def delete_comment(
54+
id: int, event_id: int, _=Depends(UnionAuth(scopes=["timetable.event.comment.delete"]))
55+
) -> None:
5456
comment = DbCommentEvent.get(id, only_approved=False, session=db.session)
5557
if comment.event_id != event_id or comment.approve_status != ApproveStatuses.APPROVED:
5658
raise ObjectNotFound(DbCommentEvent, id)
@@ -59,8 +61,8 @@ async def delete_comment(id: int, event_id: int, _: auth.User = Depends(auth.get
5961
return None
6062

6163

62-
@event_comment_router.get("/comment/", response_model=EventComments) # DEPRICATED TODO: Drop 2023-04-01
63-
@router.get("/comment/", response_model=EventComments)
64+
@event_comment_router.get("/", response_model=EventComments) # DEPRICATED TODO: Drop 2023-04-01
65+
@router.get("/", response_model=EventComments)
6466
async def get_event_comments(event_id: int, limit: int = 10, offset: int = 0) -> EventComments:
6567
res = DbCommentEvent.get_all(session=db.session).filter(DbCommentEvent.event_id == event_id)
6668
if limit:

calendar_backend/routes/event/comment_review.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
from typing import Literal
22

3+
from auth_lib.fastapi import UnionAuth
34
from fastapi import APIRouter, Depends
45
from fastapi_sqlalchemy import db
56
from pydantic import parse_obj_as
67

78
from calendar_backend.exceptions import ObjectNotFound
8-
from calendar_backend.methods import auth
99
from calendar_backend.models import ApproveStatuses
1010
from calendar_backend.models import CommentEvent as DbCommentEvent
1111
from calendar_backend.routes.models.event import CommentEventGet
@@ -23,7 +23,7 @@
2323
@event_comment_review_router.get("/review/", response_model=list[CommentEventGet]) # DEPRICATED TODO: Drop 2023-04-01
2424
@router.get("/review/", response_model=list[CommentEventGet])
2525
async def get_unreviewed_comments(
26-
event_id: int, _: auth.User = Depends(auth.get_current_user)
26+
event_id: int, _=Depends(UnionAuth(scopes=["timetable.event.comment.review"]))
2727
) -> list[CommentEventGet]:
2828
comments = (
2929
DbCommentEvent.get_all(session=db.session, only_approved=False)
@@ -39,7 +39,7 @@ async def review_comment(
3939
id: int,
4040
event_id: int,
4141
action: Literal[ApproveStatuses.APPROVED, ApproveStatuses.DECLINED] = ApproveStatuses.DECLINED,
42-
_: auth.User = Depends(auth.get_current_user),
42+
_=Depends(UnionAuth(scopes=["timetable.event.comment.review"])),
4343
) -> CommentEventGet:
4444
comment = DbCommentEvent.get(id, only_approved=False, session=db.session)
4545
if comment.event_id != event_id or comment.approve_status is not ApproveStatuses.PENDING:

calendar_backend/routes/event/event.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
from datetime import date, timedelta
33
from typing import Literal
44

5+
from auth_lib.fastapi import UnionAuth
56
from fastapi import APIRouter, Depends, Query
67
from fastapi.responses import FileResponse
78
from fastapi_sqlalchemy import db
89
from pydantic import parse_obj_as
910

1011
from calendar_backend.exceptions import NotEnoughCriteria
11-
from calendar_backend.methods import auth, list_calendar
12+
from calendar_backend.methods import list_calendar
1213
from calendar_backend.models import Event, EventsLecturers, EventsRooms, Group, Lecturer, Room
1314
from calendar_backend.routes.models import EventGet
1415
from calendar_backend.routes.models.event import EventPatch, EventPost, GetListEvent
@@ -88,7 +89,7 @@ async def get_events(
8889

8990
@event_router.post("/", response_model=EventGet) # DEPRICATED TODO: Drop 2023-04-01
9091
@router.post("/", response_model=EventGet)
91-
async def create_event(event: EventPost, _: auth.User = Depends(auth.get_current_user)) -> EventGet:
92+
async def create_event(event: EventPost, _=Depends(UnionAuth(scopes=["timetable.event.create"]))) -> EventGet:
9293
event_dict = event.dict()
9394
rooms = [Room.get(room_id, session=db.session) for room_id in event_dict.pop("room_id", [])]
9495
lecturers = [Lecturer.get(lecturer_id, session=db.session) for lecturer_id in event_dict.pop("lecturer_id", [])]
@@ -106,7 +107,9 @@ async def create_event(event: EventPost, _: auth.User = Depends(auth.get_current
106107

107108
@event_router.post("/bulk", response_model=list[EventGet]) # DEPRICATED TODO: Drop 2023-04-01
108109
@router.post("/bulk", response_model=list[EventGet])
109-
async def create_events(events: list[EventPost], _: auth.User = Depends(auth.get_current_user)) -> list[EventGet]:
110+
async def create_events(
111+
events: list[EventPost], _=Depends(UnionAuth(scopes=["timetable.event.create"]))
112+
) -> list[EventGet]:
110113
result = []
111114
for event in events:
112115
event_dict = event.dict()
@@ -128,21 +131,23 @@ async def create_events(events: list[EventPost], _: auth.User = Depends(auth.get
128131

129132
@event_router.patch("/{id}", response_model=EventGet) # DEPRICATED TODO: Drop 2023-04-01
130133
@router.patch("/{id}", response_model=EventGet)
131-
async def patch_event(id: int, event_inp: EventPatch, _: auth.User = Depends(auth.get_current_user)) -> EventGet:
134+
async def patch_event(
135+
id: int, event_inp: EventPatch, _=Depends(UnionAuth(scopes=["timetable.event.update"]))
136+
) -> EventGet:
132137
patched = Event.update(id, session=db.session, **event_inp.dict(exclude_unset=True))
133138
db.session.commit()
134139
return EventGet.from_orm(patched)
135140

136141

137142
@event_router.delete("/bulk", response_model=None) # DEPRICATED TODO: Drop 2023-04-01
138143
@router.delete("/bulk", response_model=None)
139-
async def delete_events(start: date, end: date, _: auth.User = Depends(auth.get_current_user)) -> None:
144+
async def delete_events(start: date, end: date, _=Depends(UnionAuth(scopes=["timetable.event.delete"]))) -> None:
140145
db.session.query(Event).filter(Event.start_ts >= start, Event.end_ts < end).update(values={"is_deleted": True})
141146
db.session.commit()
142147

143148

144149
@event_router.delete("/{id}", response_model=None) # DEPRICATED TODO: Drop 2023-04-01
145150
@router.delete("/{id}", response_model=None)
146-
async def delete_event(id: int, _: auth.User = Depends(auth.get_current_user)) -> None:
151+
async def delete_event(id: int, _=Depends(UnionAuth(scopes=["timetable.event.delete"]))) -> None:
147152
Event.delete(id, session=db.session)
148153
db.session.commit()

calendar_backend/routes/group/group.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import logging
22

3+
from auth_lib.fastapi import UnionAuth
34
from fastapi import APIRouter, Depends, HTTPException
45
from fastapi_sqlalchemy import db
56

6-
from calendar_backend.methods import auth
77
from calendar_backend.models import Group
88
from calendar_backend.routes.models import GetListGroup, GroupGet, GroupPatch, GroupPost
99
from calendar_backend.settings import get_settings
@@ -42,7 +42,7 @@ async def get_groups(query: str = "", limit: int = 10, offset: int = 0) -> GetLi
4242

4343
@group_router.post("/", response_model=GroupGet) # DEPRICATED TODO: Drop 2023-04-01
4444
@router.post("/", response_model=GroupGet)
45-
async def create_group(group: GroupPost, _: auth.User = Depends(auth.get_current_user)) -> GroupGet:
45+
async def create_group(group: GroupPost, _=Depends(UnionAuth(scopes=["timetable.group.create"]))) -> GroupGet:
4646
if db.session.query(Group).filter(Group.number == group.number).one_or_none():
4747
raise HTTPException(status_code=423, detail="Already exists")
4848
group = Group.create(**group.dict(), session=db.session)
@@ -55,7 +55,7 @@ async def create_group(group: GroupPost, _: auth.User = Depends(auth.get_current
5555
async def patch_group(
5656
id: int,
5757
group_inp: GroupPatch,
58-
_: auth.User = Depends(auth.get_current_user),
58+
_=Depends(UnionAuth(scopes=["timetable.group.update"])),
5959
) -> GroupGet:
6060
if (
6161
bool(query := Group.get_all(session=db.session).filter(Group.number == group_inp.number).one_or_none())
@@ -69,6 +69,6 @@ async def patch_group(
6969

7070
@group_router.delete("/{id}", response_model=None) # DEPRICATED TODO: Drop 2023-04-01
7171
@router.delete("/{id}", response_model=None)
72-
async def delete_group(id: int, _: auth.User = Depends(auth.get_current_user)) -> None:
72+
async def delete_group(id: int, _=Depends(UnionAuth(scopes=["timetable.group.delete"]))) -> None:
7373
Group.delete(id, session=db.session)
7474
db.session.commit()

0 commit comments

Comments
 (0)