Spaces:
Building
Building
Update admin_routes.py
Browse files- admin_routes.py +38 -29
admin_routes.py
CHANGED
@@ -37,6 +37,10 @@ class LoginResponse(BaseModel):
|
|
37 |
token: str
|
38 |
username: str
|
39 |
|
|
|
|
|
|
|
|
|
40 |
class EnvironmentUpdate(BaseModel):
|
41 |
work_mode: str
|
42 |
cloud_token: Optional[str] = None
|
@@ -100,22 +104,19 @@ class TestRequest(BaseModel):
|
|
100 |
test_type: str # "all", "ui", "backend", "integration", "spark"
|
101 |
|
102 |
# ===================== Helpers =====================
|
103 |
-
def hash_password(password: str) -> str:
|
104 |
-
"""
|
105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
106 |
|
107 |
-
def
|
108 |
-
"""Verify
|
109 |
-
|
110 |
-
payload = jwt.decode(credentials.credentials, JWT_SECRET, algorithms=[JWT_ALGORITHM])
|
111 |
-
username = payload.get("sub")
|
112 |
-
if username is None:
|
113 |
-
raise HTTPException(status_code=401, detail="Invalid token")
|
114 |
-
return username
|
115 |
-
except jwt.ExpiredSignatureError:
|
116 |
-
raise HTTPException(status_code=401, detail="Token expired")
|
117 |
-
except jwt.JWTError:
|
118 |
-
raise HTTPException(status_code=401, detail="Invalid token")
|
119 |
|
120 |
def load_config() -> Dict[str, Any]:
|
121 |
"""Load service_config.jsonc"""
|
@@ -171,9 +172,11 @@ async def login(request: LoginRequest):
|
|
171 |
if not user:
|
172 |
raise HTTPException(status_code=401, detail="Invalid credentials")
|
173 |
|
174 |
-
# Verify password
|
175 |
-
if
|
176 |
-
|
|
|
|
|
177 |
|
178 |
# Create JWT token
|
179 |
expire = datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS)
|
@@ -185,14 +188,10 @@ async def login(request: LoginRequest):
|
|
185 |
|
186 |
log(f"β
User '{request.username}' logged in")
|
187 |
return LoginResponse(token=token, username=request.username)
|
188 |
-
|
189 |
-
log(f"β
User '{request.username}' logged in")
|
190 |
-
return LoginResponse(token=token, username=request.username)
|
191 |
-
|
192 |
@router.post("/change-password")
|
193 |
async def change_password(
|
194 |
-
|
195 |
-
new_password: str,
|
196 |
username: str = Depends(verify_token)
|
197 |
):
|
198 |
"""Change user password"""
|
@@ -200,21 +199,31 @@ async def change_password(
|
|
200 |
|
201 |
# Find user
|
202 |
users = config.get("config", {}).get("users", [])
|
203 |
-
|
204 |
|
205 |
-
if
|
206 |
raise HTTPException(status_code=404, detail="User not found")
|
207 |
|
|
|
|
|
208 |
# Verify current password
|
209 |
-
if
|
210 |
raise HTTPException(status_code=401, detail="Current password is incorrect")
|
211 |
|
212 |
-
#
|
213 |
-
|
|
|
|
|
|
|
|
|
214 |
|
215 |
# Save config
|
216 |
save_config(config)
|
217 |
|
|
|
|
|
|
|
|
|
218 |
log(f"β
Password changed for user '{username}'")
|
219 |
return {"success": True, "message": "Password changed successfully"}
|
220 |
|
|
|
37 |
token: str
|
38 |
username: str
|
39 |
|
40 |
+
class ChangePasswordRequest(BaseModel):
|
41 |
+
current_password: str
|
42 |
+
new_password: str
|
43 |
+
|
44 |
class EnvironmentUpdate(BaseModel):
|
45 |
work_mode: str
|
46 |
cloud_token: Optional[str] = None
|
|
|
104 |
test_type: str # "all", "ui", "backend", "integration", "spark"
|
105 |
|
106 |
# ===================== Helpers =====================
|
107 |
+
def hash_password(password: str, salt: str = None) -> tuple[str, str]:
|
108 |
+
"""Hash password with bcrypt. Returns (hash, salt)"""
|
109 |
+
if salt is None:
|
110 |
+
salt = bcrypt.gensalt().decode('utf-8')
|
111 |
+
else:
|
112 |
+
salt = salt.encode('utf-8')
|
113 |
+
|
114 |
+
hashed = bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8')
|
115 |
+
return hashed, salt.decode('utf-8') if isinstance(salt, bytes) else salt
|
116 |
|
117 |
+
def verify_password(password: str, hashed: str, salt: str) -> bool:
|
118 |
+
"""Verify password against hash"""
|
119 |
+
return bcrypt.checkpw(password.encode('utf-8'), hashed.encode('utf-8'))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
|
121 |
def load_config() -> Dict[str, Any]:
|
122 |
"""Load service_config.jsonc"""
|
|
|
172 |
if not user:
|
173 |
raise HTTPException(status_code=401, detail="Invalid credentials")
|
174 |
|
175 |
+
# Verify password with bcrypt
|
176 |
+
if not verify_password(request.password, user["password_hash"], user.get("salt", "")):
|
177 |
+
# Fallback to simple hash for backward compatibility
|
178 |
+
if hash_password(request.password)[0] != user["password_hash"]:
|
179 |
+
raise HTTPException(status_code=401, detail="Invalid credentials")
|
180 |
|
181 |
# Create JWT token
|
182 |
expire = datetime.utcnow() + timedelta(hours=JWT_EXPIRATION_HOURS)
|
|
|
188 |
|
189 |
log(f"β
User '{request.username}' logged in")
|
190 |
return LoginResponse(token=token, username=request.username)
|
191 |
+
|
|
|
|
|
|
|
192 |
@router.post("/change-password")
|
193 |
async def change_password(
|
194 |
+
request: ChangePasswordRequest,
|
|
|
195 |
username: str = Depends(verify_token)
|
196 |
):
|
197 |
"""Change user password"""
|
|
|
199 |
|
200 |
# Find user
|
201 |
users = config.get("config", {}).get("users", [])
|
202 |
+
user_index = next((i for i, u in enumerate(users) if u["username"] == username), None)
|
203 |
|
204 |
+
if user_index is None:
|
205 |
raise HTTPException(status_code=404, detail="User not found")
|
206 |
|
207 |
+
user = users[user_index]
|
208 |
+
|
209 |
# Verify current password
|
210 |
+
if not verify_password(request.current_password, user["password_hash"], user.get("salt", "")):
|
211 |
raise HTTPException(status_code=401, detail="Current password is incorrect")
|
212 |
|
213 |
+
# Generate new hash with new salt
|
214 |
+
new_hash, new_salt = hash_password(request.new_password)
|
215 |
+
|
216 |
+
# Update user
|
217 |
+
users[user_index]["password_hash"] = new_hash
|
218 |
+
users[user_index]["salt"] = new_salt
|
219 |
|
220 |
# Save config
|
221 |
save_config(config)
|
222 |
|
223 |
+
# Add activity log
|
224 |
+
add_activity_log(config, username, "CHANGE_PASSWORD", "user", username, username, "Password changed")
|
225 |
+
save_config(config)
|
226 |
+
|
227 |
log(f"β
Password changed for user '{username}'")
|
228 |
return {"success": True, "message": "Password changed successfully"}
|
229 |
|