From 8d81d1668266d71c39548ecb6bd006e0f62daf62 Mon Sep 17 00:00:00 2001 From: dvirlabs Date: Mon, 8 Dec 2025 09:24:56 +0200 Subject: [PATCH] Add admin user --- backend/__pycache__/db_utils.cpython-313.pyc | Bin 8599 -> 8599 bytes backend/__pycache__/main.cpython-313.pyc | Bin 15156 -> 15337 bytes .../__pycache__/user_db_utils.cpython-313.pyc | Bin 4994 -> 5105 bytes backend/main.py | 11 +++++++---- backend/schema.sql | 7 +++++++ backend/user_db_utils.py | 18 +++++++++--------- 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/backend/__pycache__/db_utils.cpython-313.pyc b/backend/__pycache__/db_utils.cpython-313.pyc index e224238e3caf14f1ebfd0487f1f64d008dda74d3..943fab42cfb06463ca6c6a768618fe5c3ea0b4f8 100644 GIT binary patch delta 19 ZcmbR4Jl&b=GcPX}0}wo|-pJLf2mm>%1)l%_ delta 19 ZcmbR4Jl&b=GcPX}0}wne-N@Ce2mm>N1(yH- diff --git a/backend/__pycache__/main.cpython-313.pyc b/backend/__pycache__/main.cpython-313.pyc index ad86ee89876f1fc9375488beac93c1ec2efa48e6..c6a32ef00e29413f1e71c00e47134c5465d1e464 100644 GIT binary patch delta 1579 zcmb`HdrVt(6vywc_ujVLw)91Lv=mxirHn@ec?-#;EXoKP?(iR25TRff9`)BU+{PSp zE;2U;=4i6SNth&?GsXZVF)_v{y7*2YB&%sO>immnV$DcQrY4?KF#3;wn0T9h&i8lD z`Tg$wo%6eQ{rJvQ`dyPr5%BuqR9&dEYBl|~g+<{Ty&E*v4qKn0PgWHlhE)SMj>vu^ zJZi1B9ZKj!k^X_9vCufDpx*>%(;cvA%~OInOh`{mOpLQr2MHBFToH9@8d^sCk2Y`i=|j|i*I zTrCRLZH8-QcCdK#25pa4Rb)))w(k3M3b|-wy>|G+S%1Jm^Xcl*sRl|#YuvY~sW>+6 zjp=dH)bv`G%7+z~TccXwtSg6Ek=YbBs8_*=#_~Q76r-Fw`diW_q?zWG3(B1FUZfE3cgfOT zDqKBS(4HlJ(5gp$D@$oF*4^^zNSE5$o5Y8A!%{-Xg(7k?IS#Lp3)Bb+Dv-30zTw0N5i>baf#vxEzT7YI56^@Lv} z*bsj0UJFi;;}T(xa2aCWau=YaeMR0w{Ll4%yr8tn{jc?Ilc?Sg)=<4sZ`3GlOt;aZ zL;ay-Ys-X&fRQKoAbR3g;|ren z66)*mg;X=)v&y_qeht(7GG9i+V}w-i21%tJL*%KBr9zA7jfe=3$HVVKo6wE~qy1w+ zzK(-q!0Oi66JKX8~9;PmxNNZ|>rP3q00k6c0wPeyqqh3QqJcYHJ$4RSgM zyqrKa;51 l6f_M<4XR`Z{MhJr+n9k8(X2?jWGJIHsX>)2ixoGS*`J*gn|A;J delta 1501 zcmbtUTTC2P7@jk;GqXF(vKLqi?1fzxS+=EsQf>>F6iT8{HQ5O@Z*I zg%8|809&~;i7rm?Abz=4kUXu@6^l3D@5xDW4tKt_&->RmNz-3A?MSH8N@LBW}3g*Kto-v7I0ctGbQSkhL3ZHbdK9 zv=iQ&xm&E4w6UUvc@A*;D@DDuNKT}x@U7aow%R1TaJRpv_EuT$n_0T2%U%Zu!+!gE zuT&$9vhda-HTc$VV`cDzUoW4+i4>GaQSp^qxqsIYY~PSE|q)R;$YZci`uB;&m zuAblQLaEPi!PVebdm<eSk@yHCRz!VA_CF zyZJrPL3vyIG{beAH@1HJV<7Z1J--_ z91g(e37#f+hJfnKpM`7Tc#q2IF!3V<&k;ONpb{()co1SrvA{Frc!A(3!7+HCw$XPI z<(K&7-bMJo)$~Rh;=X^aW?vIkvoAo^M14M>_3cp4M^(~Io_+ylBBN|He=Ra9DdRN5 zc^Hf)g0$qA#$Dw3=g<`7+$2wD`7E4^HhW*BSRFx@;4uOYe?~jxS5dC#;|=e~3iUHv z50C8JmY14-X6!XEnxj_p1gd-qYFZ-b>TkJEehU+w`IlQ%$!x>V!0FZ~I}8_F_pv3| zzN=TJS`FnhyUsB-57G9ph5Cl4nuB}!>!@NfKZV545}13DBxMdIadR8Y&=Puc5<(~x z;L?ZHj5_Kl=6|LL};9<@#}%; z3yFq^_svJ2d@+$U{s8?2`et~r5B>lk{R7TyTTpK@XYaXpa_*Vgd6_y%rP{i#a5P%a zr)pK5<9;*Ji(oft@9wA4$k<=(zv(C?!45d;0Jq6+A|g6`3EMtLW1L71_L%N5*$I>s z+6#J2b!4v>@|add8S~)tRe$18sx``$^{v|Wn%J95zs!`MuJ#HTp?<&UieT}{K`F+$ zINjY>@o|(Dkjrh$t_1h^L1V!cY;ls-S;RS7ElN?WxZ;uu2|UzymmaYbt}=B}WJ_c_z%^dG2c}F`{ItO31FrK2VX-FvJr~sN?Kz6gFKj^A$Q?(&D3XJ@1 zI0m_(+*7>FY@I1a0@aO&nL{t&V=&L#lsA8fE`owF(6LsGte@l$}COU^J(mW5T4VHxd@6JbDZP5?SuL7l|O?%v1$MS delta 754 zcmY+B&uZZc> z#%xC{#oqJV&iepm0`EUm+~F-e%*4J53pb0wU5I%iZd}Lkay*4(SQEo;PhEz4p3u1l zYNQ%90~cM+&7{CKo_OPuQpf9Fn3rOq+rz4bx>7{WF?MK9nnajs0$&uS_$_DQnzZWb z{efd?3&wB`mhd{f!8Z_r6Py4|PK&%duqemL9Ge7KbYH`+HVON3%H`XDck-G-szmrq zrmMOB3Au^jmJ(t)1fll=J_TaX^BM3(Da>ACOO*d0)w#@Y$#R)xk`ZZ~&vRVzkSBSm zz?Wf4Eyy+3SM~hC3jFie`rpZO zs|=_e)EICXE%e{2yQj#ejeLaDtl-#0yLpyZ8sQ!Jb?FN8%h1s-BMN{0rnta-|0k&< zI1WUe8aEg)%V7}Eg4$_a(<7Wb3B#%uMP+!X8K?zEn(5`7jN3HLj1PWm*(&9or@S4s q$R)ysC#PjD7q~>ZOfdQ8S=@Qt-0QTKXoXq+{@th04p!k~u>2np!j?Ax diff --git a/backend/main.py b/backend/main.py index ae87be5..26a2f4d 100644 --- a/backend/main.py +++ b/backend/main.py @@ -94,6 +94,7 @@ class UserResponse(BaseModel): first_name: Optional[str] = None last_name: Optional[str] = None display_name: str + is_admin: bool = False app = FastAPI( @@ -164,7 +165,7 @@ def create_recipe(recipe_in: RecipeCreate, current_user: dict = Depends(get_curr @app.put("/recipes/{recipe_id}", response_model=Recipe) def update_recipe(recipe_id: int, recipe_in: RecipeUpdate, current_user: dict = Depends(get_current_user)): - # Check ownership BEFORE updating + # Check ownership BEFORE updating (admins can edit any recipe) conn = get_conn() try: with conn.cursor() as cur: @@ -172,7 +173,8 @@ def update_recipe(recipe_id: int, recipe_in: RecipeUpdate, current_user: dict = recipe = cur.fetchone() if not recipe: raise HTTPException(status_code=404, detail="המתכון לא נמצא") - if recipe["user_id"] != current_user["user_id"]: + # Allow if user is owner OR admin + if recipe["user_id"] != current_user["user_id"] and not current_user.get("is_admin", False): raise HTTPException(status_code=403, detail="אין לך הרשאה לערוך מתכון זה") finally: conn.close() @@ -201,7 +203,7 @@ def update_recipe(recipe_id: int, recipe_in: RecipeUpdate, current_user: dict = @app.delete("/recipes/{recipe_id}", status_code=204) def delete_recipe(recipe_id: int, current_user: dict = Depends(get_current_user)): - # Get recipe first to check ownership + # Get recipe first to check ownership (admins can delete any recipe) conn = get_conn() try: with conn.cursor() as cur: @@ -209,7 +211,8 @@ def delete_recipe(recipe_id: int, current_user: dict = Depends(get_current_user) recipe = cur.fetchone() if not recipe: raise HTTPException(status_code=404, detail="המתכון לא נמצא") - if recipe["user_id"] != current_user["user_id"]: + # Allow if user is owner OR admin + if recipe["user_id"] != current_user["user_id"] and not current_user.get("is_admin", False): raise HTTPException(status_code=403, detail="אין לך הרשאה למחוק מתכון זה") finally: conn.close() diff --git a/backend/schema.sql b/backend/schema.sql index 78b2138..94b95c9 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -7,6 +7,7 @@ CREATE TABLE IF NOT EXISTS users ( first_name TEXT, last_name TEXT, display_name TEXT UNIQUE NOT NULL, + is_admin BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -41,4 +42,10 @@ CREATE INDEX IF NOT EXISTS idx_recipes_made_by CREATE INDEX IF NOT EXISTS idx_recipes_user_id ON recipes (user_id); +-- Create default admin user (password: admin123) +-- Password hash generated with bcrypt for 'admin123' +INSERT INTO users (username, email, password_hash, first_name, last_name, display_name, is_admin) +VALUES ('admin', 'admin@myrecipes.local', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewY5lE7UGf3rCvHC', 'Admin', 'User', 'מנהל', TRUE) +ON CONFLICT (username) DO NOTHING; + diff --git a/backend/user_db_utils.py b/backend/user_db_utils.py index 0b4be44..9944e84 100644 --- a/backend/user_db_utils.py +++ b/backend/user_db_utils.py @@ -14,7 +14,7 @@ def get_db_connection(): ) -def create_user(username: str, email: str, password_hash: str, first_name: str = None, last_name: str = None, display_name: str = None): +def create_user(username: str, email: str, password_hash: str, first_name: str = None, last_name: str = None, display_name: str = None, is_admin: bool = False): """Create a new user""" conn = get_db_connection() cur = conn.cursor(cursor_factory=RealDictCursor) @@ -24,11 +24,11 @@ def create_user(username: str, email: str, password_hash: str, first_name: str = cur.execute( """ - INSERT INTO users (username, email, password_hash, first_name, last_name, display_name) - VALUES (%s, %s, %s, %s, %s, %s) - RETURNING id, username, email, first_name, last_name, display_name, created_at + INSERT INTO users (username, email, password_hash, first_name, last_name, display_name, is_admin) + VALUES (%s, %s, %s, %s, %s, %s, %s) + RETURNING id, username, email, first_name, last_name, display_name, is_admin, created_at """, - (username, email, password_hash, first_name, last_name, final_display_name) + (username, email, password_hash, first_name, last_name, final_display_name, is_admin) ) user = cur.fetchone() conn.commit() @@ -44,7 +44,7 @@ def get_user_by_username(username: str): cur = conn.cursor(cursor_factory=RealDictCursor) try: cur.execute( - "SELECT id, username, email, password_hash, first_name, last_name, display_name, created_at FROM users WHERE username = %s", + "SELECT id, username, email, password_hash, first_name, last_name, display_name, is_admin, created_at FROM users WHERE username = %s", (username,) ) user = cur.fetchone() @@ -60,7 +60,7 @@ def get_user_by_email(email: str): cur = conn.cursor(cursor_factory=RealDictCursor) try: cur.execute( - "SELECT id, username, email, password_hash, first_name, last_name, display_name, created_at FROM users WHERE email = %s", + "SELECT id, username, email, password_hash, first_name, last_name, display_name, is_admin, created_at FROM users WHERE email = %s", (email,) ) user = cur.fetchone() @@ -76,7 +76,7 @@ def get_user_by_id(user_id: int): cur = conn.cursor(cursor_factory=RealDictCursor) try: cur.execute( - "SELECT id, username, email, first_name, last_name, display_name, created_at FROM users WHERE id = %s", + "SELECT id, username, email, first_name, last_name, display_name, is_admin, created_at FROM users WHERE id = %s", (user_id,) ) user = cur.fetchone() @@ -92,7 +92,7 @@ def get_user_by_display_name(display_name: str): cur = conn.cursor(cursor_factory=RealDictCursor) try: cur.execute( - "SELECT id, username, email, display_name, created_at FROM users WHERE display_name = %s", + "SELECT id, username, email, display_name, is_admin, created_at FROM users WHERE display_name = %s", (display_name,) ) user = cur.fetchone()