From 418e2a22c92135cb3c615fdfe059004a886ebe50 Mon Sep 17 00:00:00 2001 From: ebby Date: Mon, 14 Sep 2026 19:24:20 +0200 Subject: [PATCH] zwischenstand --- .../__pycache__/dependencies.cpython-314.pyc | Bin 1183 -> 0 bytes .../__pycache__/user_code_gen.cpython-314.pyc | Bin 731 -> 689 bytes .../src/backend/internal/authentication.py | 35 ++++++++++++++++++ backend/src/backend/internal/user_code_gen.py | 3 +- .../__pycache__/comment.cpython-314.pyc | Bin 1783 -> 1840 bytes .../__pycache__/subscription.cpython-314.pyc | Bin 1401 -> 0 bytes .../models/__pycache__/user.cpython-314.pyc | Bin 2262 -> 3516 bytes backend/src/backend/models/comment.py | 6 ++- backend/src/backend/models/user.py | 32 ++++++++++++++-- .../__pycache__/subscriptions.cpython-314.pyc | Bin 1411 -> 0 bytes ...subscriptions.cpython-314-pytest-9.1.1.pyc | Bin 2326 -> 0 bytes .../test_users.cpython-314-pytest-9.1.1.pyc | Bin 8804 -> 10816 bytes .../routers/__pycache__/users.cpython-314.pyc | Bin 1552 -> 2296 bytes backend/src/backend/routers/test_users.py | 30 ++++++++++++--- backend/src/backend/routers/users.py | 33 +++++++++++------ 15 files changed, 116 insertions(+), 23 deletions(-) delete mode 100644 backend/src/backend/__pycache__/dependencies.cpython-314.pyc create mode 100644 backend/src/backend/internal/authentication.py delete mode 100644 backend/src/backend/models/__pycache__/subscription.cpython-314.pyc delete mode 100644 backend/src/backend/routers/__pycache__/subscriptions.cpython-314.pyc delete mode 100644 backend/src/backend/routers/__pycache__/test_subscriptions.cpython-314-pytest-9.1.1.pyc diff --git a/backend/src/backend/__pycache__/dependencies.cpython-314.pyc b/backend/src/backend/__pycache__/dependencies.cpython-314.pyc deleted file mode 100644 index 8fe1123d2f5b6c796a4e261bc87f787141b625f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1183 zcmcJOUr*Ce7{;H|ZtM6rh$st4WC}59VD3UhVu*<*Cd0^tN{AN=OV=J)VYK!13>a5L z{QzA00Wg8YJN+QLn2BlhN)yAKgc#zTIj0@bJ8wKm+qX@7`aHkqeJ3-0-2m_{^LFD6 z0ltcx&Qjvy>>hx9timAkC=yP_M6Jv#MU|_?1W$~?P_r-+S=0x3(nw}uq-32kv~lR^ z6S0v_!mbCd>v5A)TUWwVk(xGTVPA21dGXP%MSbpgu0;TdL#!kK6~I|Z3RuCaGW!~^ zRbUiLq(%%n2xR3BSQx}m4E1h|tVWE)IPRzp(UW`G zHFJ~ZgN9F8KA;w(JTD2I91vh{=y`rw@XFO>dO644HtUXktb{!QH+dtdSe{M8lufzm z)Jw;N2~|2_X< z^uno%^wh625z@=-F!`%vgdy266aUaLQ6Df($Vo3|yp$`mktZQO7`g~( zNd~k;4hKalON90PmsCP+g9d?PnXDhc-f+pwJ&qpar0|^2yluy(HX8s?VuO*3Y{x|m zy{>kIfzbCuC4_vz=y&KpQ=;R~yDvcg23Mr{ AF#rGn diff --git a/backend/src/backend/internal/__pycache__/user_code_gen.cpython-314.pyc b/backend/src/backend/internal/__pycache__/user_code_gen.cpython-314.pyc index 7a5dca95e5b89e750d47c29a5a96045e37d7b0e8..fa720351079efc810468ef82c0d436aad872cea3 100644 GIT binary patch delta 237 zcmcc3x{;Mvn~#@^fq{WRbJo(Vx{bUtjMgj+3=G9$3=9kn4Br_T866lug2_3~5c)Gn zbux&60d@uk24)5ZhR->ZQy6o3l^HadZZT*w-eSzwWS#trQ9*z|u{b$1GrqK-Ahjqt zu{d?IK9i!m2m=EH*eWHkRX4bK`^`Jer?~+W=2NF+YG$78F;_5fZPw(3IHm( BG0y-1 delta 279 zcmdnUdYhG3n~#@^fq{YH=Jcgm%^P`T7_FHZ7#NDh7#J8D7``(wGP*H-1e0@|A@paE z>SPcB1MCb83?NOPJ0_qaxm_=?xE6u@qsd$($iTo*#1A4sCKZW*2#{0}2Ll7cEe@O9{FKt1 SRJ$Uv$^A?cTudOJg5?1vxjDN4 diff --git a/backend/src/backend/internal/authentication.py b/backend/src/backend/internal/authentication.py new file mode 100644 index 0000000..68f4549 --- /dev/null +++ b/backend/src/backend/internal/authentication.py @@ -0,0 +1,35 @@ +from sqlmodel import select +from backend.models.user import User +from backend.internal.database import get_session +from sqlmodel import Session +from fastapi import HTTPException +from fastapi.security.oauth2 import OAuth2PasswordBearer +from fastapi import Depends +from datetime import datetime +from datetime import timedelta + + +SECRET_KEY = "secret-key" +ALGORITHM = "HS256" +ACCESS_TOKEN_EXPIRE_DAYS = 360 + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + +def create_access_token(data: dict, expires_delta: timedelta | None = None): + to_encode = data.copy() + expire = datetime.utcnow() + (expires_delta or timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)) + to_encode.update({"exp": expire}) + return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) + +async def get_current_user(token: str = Depends(oauth2_scheme), session: Session = Depends(get_session)): + try: + payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) + user_code: str = payload.get("sub") + if user_code is None: + raise HTTPException(status_code=401, detail="Invalid token") + except JWTError: + raise HTTPException(status_code=401, detail="Invalid token") + user = session.exec(select(User).where(User.user_code == user_code)) + if user is None: + raise HTTPException(status_code=401, detail="User not found") + return user \ No newline at end of file diff --git a/backend/src/backend/internal/user_code_gen.py b/backend/src/backend/internal/user_code_gen.py index bf82c3d..4c098a2 100644 --- a/backend/src/backend/internal/user_code_gen.py +++ b/backend/src/backend/internal/user_code_gen.py @@ -1,4 +1,5 @@ import string import random + def generate_user_code() -> str: - return ''.join(random.choices(string.ascii_letters, k=4)).upper() + return ''.join(random.choices(string.ascii_uppercase, k=4)) diff --git a/backend/src/backend/models/__pycache__/comment.cpython-314.pyc b/backend/src/backend/models/__pycache__/comment.cpython-314.pyc index e3d9eaa80284d8157c1e6eb2622e49349802215b..45f2bc9df58d7d09c584f2089e34821cd10d082d 100644 GIT binary patch delta 813 zcmX|9OKa3%6g@YuNhXi!(>`cBPCHP7+e*c@x)Di0s)TMzosrQQw05S|%vYqVsS0XW zRk9MHxX_)8iVJtezc8Si6p`Y}U8fdwDK}}&;&9H%_uYGLZvNQ2PV1awX8~aL&HMYW zCEzEE(?>rczdr!jL>U43@JLXex#UZpB&3APzAWT20>xK6Rj7%l`kI#!nS^V;=IKID zc*fT}Ll}9K)XCOKhiV2G;i9^q^-N*r(T@TGV*o+6WK7BFF=3S`@y!7QR>=zNz?oLk zcT*>ogdNBeaC5&{yA&JMim2SFSDHBbIh73ay zK&-B>FIT6-F*foHXBqg5#`5agqp-kooI#5ot0UAM?Wo1kUN-tqA(`uXI@cx+q95vn zv8#W!K3ZRg=OSAhSmIbukp|;y3ogZ~sNAmCTqidF@2A!bdCy7vG@d3l9xSik4S5f- z-h5bZtW|6EP~%g{5PHx$!JbT)VgU0%7W39SguU!iH7a`t)58zNHqVFg~ gU$o-*p$5r3mNc4qvGTU>7#hv|mLZu(Y$dh+0V8Fg=Kufz delta 789 zcmXw1O-~b16g{`|=Bv|=Krws-sFfz9F<~R55&YPYII|&fbVIP1LW?4W;&d9L8yZbO zHlmrtxO8JebfF3HH|WwPxahbtu4F~Uh)Z*4%3Yk?bMKq`&Utq}nkUK43uZD70NXd; z-d~OZKbf3{+DVRI0cc_dC}<@PHYr8 z^B2>XhZ|)0^R!Ba|C#RCY|3RY0MujvTlg+CxBIld)R7Se_c@Ng04Sn?0z7iy#THDz z8oMWDFyViVZRvqpDlZr76>ooD>Lt?r4KYlT|6F9L$KMxYnS;=Oq=m~YsvfswsS2LqFh>#bCJ z@9{xusxfoIQlBCc%cE}PD3HD4Vx^o*gaRUgiuY(^W&N(pTMe|@gUVX9RIa$ZlE?=C zg+XeuxU^bcFO7%Y+W574t?arQ9|~6;YUL-1?qmIdc6qH@s#nTa-HQN-SCa>DBoUE+ WsAN-r6osart-k3!fkM;am;V73>Yk$j diff --git a/backend/src/backend/models/__pycache__/subscription.cpython-314.pyc b/backend/src/backend/models/__pycache__/subscription.cpython-314.pyc deleted file mode 100644 index c0fd5b7cf19265c79be0b10f3294ca62733d6029..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1401 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 delta 1163 zcmZuw&udgy6#njeZ{Cl2^X5l_^AclC#?j)dsX{woQ>(PtmKl*C7l|7u!#MMZXNBwfJJcDK%Gf8O|j81>ZUYxY&I-uErMH(Ox>2Y z&Se@--IcD6?S@nLq^D!&?qF)oZTR(^%uV9}D&SrSo^57pCY1$bptF7mo@)j?;Q7b*h;UTg+?H)^A8$O1R3h)Pdr9kJoqQoI_Awv1c-G#0H+uNy~f=ylz;YG!_f$2cv%? zsDNjm3y>x5RS{*?k7ms-(~A>o)0|R0zszo_$KIs+$+y+d z_N9Z|o;|qR6M!=Ycisk2MdYg6)~SN<01VwuZ)GK3m8tsEnzV;jvYbk_X;s*`dSxB8 z3)I0Cb>F!d7sl_*ecyX%o!<_?*r*w5a6TFn0rAKa&;?N>unDSEBh&9McWdG;N*pGb zCLp843_*p!AvmJe+;FZ$mrAXcR=2y{k)1e6TCHE1h=>k}^RXDG^9S}o^PE@zxSu35 zv%i1-@67o@{RQ#-4kM;?@^Zw68QH#;#GN;~0LJcOXLi3Sp)||rL7Tv|2^KeGW&zUH zGh(T~yt2CC&(9qqB5!)5jv_-Gqb!LY8R9s_nm^S$G8i@Rj=GwiTnni!I`DEG-{P^K zzj{wV|$>2o8Nl zlcqn!$S_@;MrULVIXQGcTIqHBNqkOx2!Je3uK;*zGR7X^>?2gvji7qcW}mW+5AM~T cgQBNJ82&TEWZ}m39hfZKF=6=Il+aJV1`Y7r{Qv*} diff --git a/backend/src/backend/models/comment.py b/backend/src/backend/models/comment.py index e1fb5dc..2e61a7e 100644 --- a/backend/src/backend/models/comment.py +++ b/backend/src/backend/models/comment.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from datetime import datetime import uuid from typing import TYPE_CHECKING @@ -11,10 +12,11 @@ if TYPE_CHECKING: class CommentBase(SQLModel): pass +@dataclass class Comment(CommentBase, table=True): - id: uuid.UUID | None = Field(default_factory=uuid.uuid4, primary_key=True) + id: uuid.UUID | None = Field(default_factory=uuid.uuid7, primary_key=True) slug: str = Field(index=True) content: str = Field() - date_time: datetime = Field(default=datetime.now()) + date_time: datetime = Field(default_factory=datetime.now) user_id: uuid.UUID = Field(foreign_key="user.id") user: "User" = Relationship(back_populates="comments") \ No newline at end of file diff --git a/backend/src/backend/models/user.py b/backend/src/backend/models/user.py index 844b898..3d922fc 100644 --- a/backend/src/backend/models/user.py +++ b/backend/src/backend/models/user.py @@ -1,5 +1,6 @@ +from uuid import uuid7 +from datetime import datetime from backend.internal.user_code_gen import generate_user_code -from dataclasses import dataclass import uuid from typing import TYPE_CHECKING from sqlmodel import Field @@ -13,15 +14,40 @@ class UserBase(SQLModel): pass class User(UserBase, table=True): - id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + id: uuid.UUID = Field(default_factory=uuid.uuid7, primary_key=True) email: str = Field(unique=True) name: str = Field(unique=True) user_code: str = Field(default_factory=generate_user_code,unique=True) subscription: bool = Field() + created_at: datetime = Field(default_factory=datetime.now) + comments: list["Comment"] = Relationship(back_populates="user") -@dataclass + def factory(self, + id: uuid.UUID = uuid.uuid7(), + email: str = "Email@Test.mail", + name: str = "TestName", + user_code: str = "WYZZ", + subscription: bool = False, + ) -> User: + self.id=id + self.email=email + self.name=name + self.user_code=user_code + self.subscription=subscription + return self + class NewUser(UserBase): email: str name: str subscription: bool + + def factory(self, + email: str = "Email@Test.mail", + name: str = "TestName", + subscription: bool = False + ) -> NewUser: + self.email = email + self.name = name + self.subscription = subscription + return self diff --git a/backend/src/backend/routers/__pycache__/subscriptions.cpython-314.pyc b/backend/src/backend/routers/__pycache__/subscriptions.cpython-314.pyc deleted file mode 100644 index cfa77d66be5749bf822f320e3ef67e403f9d3058..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1411 zcmZ8h&u`;Y5T0i{cAQ_kNsE#;+s&3r)mmM}AXWmT6%s!ViHfqpLA|hPE_QYJEym3~)=W-z_2H{XZ{Uz?SAjH1ZO7UA(S!P0HLxp7?; z+jVt2u?=lGe)|rxaz(K26k%|8fL2|y*4-U2@_$PJU?84pjl0w#-iUdDZy=sN!mL9v zrBSGTf=AeQse!ouVY6-pV}^-=xYmkFPI4x_(SY*ot6L?4Ct z7b!?&&h{t!=a~y-Yo?^n8k5F_vM#)*lc(pOUMSnMbaAQ_!&Nkl9A6s-E*{Y8t+s+9 z&wD<@#J2}k*JgItrkIEi1J`;sW%kG;f+!Ix;Ys?{AnX%fkE@XoxqO6KC^@c-0?I

7UO_U2HY!`2+`{h=3T>GCYKG1WI``L)@d z!g>4W{ptE!)7;yy^h8QJRTnvsl4qNf%@-ekU%8YJx_>2ulwSyMp^992Qhw2TiSm~! Wlqx@c`?9$AuMmkSEBqtHBKaSqtv&Vt diff --git a/backend/src/backend/routers/__pycache__/test_subscriptions.cpython-314-pytest-9.1.1.pyc b/backend/src/backend/routers/__pycache__/test_subscriptions.cpython-314-pytest-9.1.1.pyc deleted file mode 100644 index 1704d6f85df96ace85e51aa3aa405caface1b80d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2326 zcmds3&u<$=6n^7f|8|`=0Sc(Zu|h*vE#B02nnc005LI#xM9~(xm$b`zJ#jXyy_=ae z!A6yG;E<|^@+aiNffIiQ9Gxa4)rtyn=FCq%?#cXK6ni!c1#6}N)-Yzy`3w40)$_IhB{S&|ySWj@&T8e8UDS~pL zAykl(zdv4yRReAXDY130giO|QBRrQPR0DMp0NU@JTPe?RBPl{7+y~FFb@^P}#SxxM zH10E`#1tX2qnG0mua_de78~POS-25a3cwbXxRPjyGCJdpuTSv2BFt}a-$;tkh;wE> zZXzYQglx2gNVx%PZ;jAgwl9t9Y70tANy}(kZzR0F?(nL$U!1GjtNNO(AZ6_St^Z+P zGm+}Q{0s=Z>Pq6Z*aXMQ;@NI1StZwqpF0Do|8NFUoSBcKGhho#hPcb0P-Mp*SObo4K z#^+BQjpB|x(hwh2j>YmRyPl<-o8kwLunXUPNA-_t$#1G*bxQ>A5W~T4I&`_oz(>Ye zOfG0vsiPTP5@W9M4UIaQZIoD3{NDv(9|2^Hy;y600CJV-(+r;asAIKyCjOXw2mtIO lgiZp$p8`VYH-K;97r62Qq~FsM-z`5W>?Zya1yu3k{{@34*~I_= diff --git a/backend/src/backend/routers/__pycache__/test_users.cpython-314-pytest-9.1.1.pyc b/backend/src/backend/routers/__pycache__/test_users.cpython-314-pytest-9.1.1.pyc index 799726371812118f05fa4476e7a8ac6c71e83a94..ede2373f05abb74c155df7234fd5a100590c2757 100644 GIT binary patch delta 2772 zcmd^>Uu;uV7{I^N-u}7$KU#LzZtdFP{xo9_*kp{c2{LprR?5vNB7>%`w@~cb?%Z3) zM##p4afFABoCo*d10foji3wyr81(_-gYqQG)TNOJ6O8d~gpkD;jrnf7wId4Xt0&FZ z@0{~}U(Pw-cYf#I@4Y!_YqnWU005~k-#_z`>9VbgynLf^C#i4?AbJEyITWu*HbJnO zCCDt9UsxeO6H>+QY45uP0LWXS(O5d2QDTY|jZ*KzcU*uFfvxC{5g$9O-`P|L0Mrct zvK9amem_)Ps5O3KB#wn8$8V&i8JDL^B&n=W2b#8E7;`Cn-k6rgqB&Wjw2l3_ zVISGSdYu6xu!Pe`!V7OX;~L^)SE@sGP3Rtc;woONEUdy!uCjM4>zue*wY3-j!#eg; zrLzJ=t^@Y~09kDW!Uk3$1PIRpUJq9b5yG|#E>6@%H0-e8V&?_EUf-c9+H(;ujED1B z%j!e}i+SC|!7lT@=R|wN&_+bZ(MX%7oh#UiMv)(Kt@?#4*&g1-W{ghaV1EccHc*Am ztyN9N0V49EX$U*DEafw4%|(6A=V`Wx)`+o{Y}T&^0?6`1m|+m1KK8rM z%=}iaPuE50o1$$K1S|4fmHrZ4*+1Ig2D{{}uCQj6qu}NRXCWC1Fw`|(a zcaf}GKM^kMU%yLiqelX3=a@B!rqK@~rX76%qn|YvjZfVJsDYs(cWn`~%H4@F?*8Y! z6}f9QtR_n|7Izym^SfHP^$->j&HQ6%w)_K{_wXBuh27wtoy7yl8oCJmROAbQMgT^K zGjVCc(u%s=n#!C?rh^)~MFnI$ro>pf*4}!7;>0#QywLMjs&ju7>6!?BpB&>R5G=LiNuJE~N+%=Mk=TcG+od5vjmps9K3CQ+G O8w{jzWrKqh==c|gOF$U_ delta 1564 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 diff --git a/backend/src/backend/routers/test_users.py b/backend/src/backend/routers/test_users.py index 78e2219..e31b0d1 100644 --- a/backend/src/backend/routers/test_users.py +++ b/backend/src/backend/routers/test_users.py @@ -39,7 +39,7 @@ def test_register_user_saved(session:Session, client: TestClient): "subscription": True } response = client.post(url="/users/", json=new_user) - assert response.status_code == 200 + assert response.status_code == 201 db_user = session.exec(select(User).where(User.email == "Test@Test.test")).first() assert db_user @@ -48,19 +48,37 @@ def test_register_user_saved(session:Session, client: TestClient): assert db_user.subscription == True def test_email_not_unique_returns_400(session: Session, client: TestClient): - new_user = NewUser(email="Not@Unique.de",name="Tim", subscription=True) + new_user = NewUser(email="NotUnique",name="Müller", subscription=True) # TODO: durch factory ersetzen - user = User.model_validate(new_user.model_copy(update={'name':'Different'})) + user = User().factory(email="NotUnique") session.add(user) session.commit() response = client.post(url="/users/", json=new_user.model_dump()) assert response.status_code == 400 def test_name_not_unique_returns_400(session: Session, client: TestClient): - new_user = NewUser(email="Absolutly@Unique.de",name="Chiara", subscription=True) + new_user = NewUser(email="wewqe@2ewew",name="Chiara", subscription=True) # TODO: durch factory ersetzen - user = User.model_validate(new_user.model_copy(update={'email':'Different@Different.de'})) + user = User().factory(name="Chiara") session.add(user) session.commit() response = client.post(url="/users/", json=new_user.model_dump()) - assert response.status_code == 400 \ No newline at end of file + assert response.status_code == 400 + +def test_login_successful(session: Session, client: TestClient): + user = User().factory() + session.add(user) + session.commit() + session.refresh(user) + + payload = { + "user_code": user.user_code + } + + response = client.post( + url="/users/login", + data=payload + ) + + assert response.status_code == 200 + assert response.json() == "token" diff --git a/backend/src/backend/routers/users.py b/backend/src/backend/routers/users.py index 8b8cef1..bc502e0 100644 --- a/backend/src/backend/routers/users.py +++ b/backend/src/backend/routers/users.py @@ -1,13 +1,5 @@ -# CreateUser <- mail, Name -# Login Rate Limit! -> token for everything else -# Forgot Code <- email (rate limit) -# Delete User <- token -# change mail <- token, new mail -# subscribe -# unsubscribe - -# delete all deactivated users after 48h - +from fastapi import Form +from sqlmodel import select from starlette.status import HTTP_400_BAD_REQUEST from fastapi import HTTPException from sqlalchemy.exc import IntegrityError @@ -25,13 +17,32 @@ router = APIRouter( responses={404: {"description": "Not found"}}, ) -@router.post("/") +@router.post("/", status_code=201) def register_user(new_user: NewUser, session: Session = Depends(get_session)): user = User.model_validate(new_user) try: session.add(user) session.commit() + # TODO: send mail except IntegrityError as e: session.rollback() raise HTTPException(HTTP_400_BAD_REQUEST) + +@router.post("/login") # TODO: rate limit +def login(user_code: str = Form(), session: Session = Depends(get_session)) -> str: + user = session.exec(select(User).where(User.user_code == user_code)).one + + if not user: + raise HTTPException() + return "token" + + +# Login Rate Limit! -> token for everything else +# Forgot Code <- email (rate limit) +# Delete User <- token +# change mail <- token, new mail +# subscribe +# unsubscribe + +# delete all deactivated users after 48h \ No newline at end of file