Sistema limpio
This commit is contained in:
104
backend/auth_backup.ts
Normal file
104
backend/auth_backup.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import type { Writable } from 'svelte/store';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
export interface User {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
tenant_id: string;
|
||||||
|
role: 'CLIENT_ADMIN' | 'CLIENT_USER';
|
||||||
|
is_active: boolean;
|
||||||
|
is_two_factor_enabled: boolean;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
export interface AuthState {
|
||||||
|
user: User | null;
|
||||||
|
token: string | null;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
export interface LoginRequest {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
tenant_slug: string;
|
||||||
|
totp_code?: string;
|
||||||
|
}
|
||||||
|
export interface LoginResponse {
|
||||||
|
access_token: string;
|
||||||
|
token_type: string;
|
||||||
|
expires_in: number;
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
const initialState: AuthState = {
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
isAuthenticated: false,
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
function createAuthStore() {
|
||||||
|
const { subscribe, set, update }: Writable<AuthState> = writable(initialState);
|
||||||
|
let _state = initialState;
|
||||||
|
subscribe(s => { _state = s; });
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
init: async () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/v1/auth/me', {
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'X-App': 'client', 'X-Tenant-Slug': 'aduanasoft' }
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const user = await response.json();
|
||||||
|
set({ user, token: null, isAuthenticated: true, isLoading: false });
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
login: async (credentials: LoginRequest): Promise<void> => {
|
||||||
|
update(state => ({ ...state, isLoading: true }));
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/v1/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Tenant-Slug': credentials.tenant_slug,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(credentials)
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json();
|
||||||
|
throw new Error(error.detail || 'Login failed');
|
||||||
|
}
|
||||||
|
const data: LoginResponse = await response.json();
|
||||||
|
set({ user: data.user, token: data.access_token, isAuthenticated: true, isLoading: false });
|
||||||
|
} catch (error) {
|
||||||
|
update(state => ({ ...state, isLoading: false }));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout: async () => {
|
||||||
|
try {
|
||||||
|
const token = _state.token;
|
||||||
|
await fetch('/api/v1/auth/logout', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'X-App': 'client',
|
||||||
|
'X-Tenant-Slug': 'aduanasoft',
|
||||||
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
set(initialState);
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateUser: (user: User) => { update(state => ({ ...state, user })); },
|
||||||
|
setUser: (user: User) => { set({ user, token: null, isAuthenticated: true, isLoading: false }); },
|
||||||
|
setLoading: (isLoading: boolean) => { update(state => ({ ...state, isLoading })); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export const auth = createAuthStore();
|
||||||
6
backend/check_lines.py
Normal file
6
backend/check_lines.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if "tenant_id == current_user.tenant_id" in line:
|
||||||
|
print(f"Linea {i+1}: {line.rstrip()}")
|
||||||
14
backend/check_user.py
Normal file
14
backend/check_user.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import asyncio
|
||||||
|
from app.core.database import AsyncSessionLocal
|
||||||
|
from app.models.user import User
|
||||||
|
from sqlalchemy import select
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
async def check():
|
||||||
|
async with AsyncSessionLocal() as db:
|
||||||
|
result = await db.execute(select(User))
|
||||||
|
users = result.scalars().all()
|
||||||
|
for u in users:
|
||||||
|
print(f"ID: {u.id} | Email: {u.email} | Tenant: {u.tenant_id} | Rol: {u.role}")
|
||||||
|
|
||||||
|
asyncio.run(check())
|
||||||
16
backend/fix_response.py
Normal file
16
backend/fix_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/auth.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Linea 286 (0-indexed 285): "tenant_id": str(user.tenant_id),
|
||||||
|
# Agregar tenant_slug despues de tenant_id
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if '"tenant_id": str(user.tenant_id),' in line:
|
||||||
|
indent = " "
|
||||||
|
new_line = indent + '"tenant_slug": tenant.slug if tenant else str(user.tenant_id),\n'
|
||||||
|
lines.insert(i + 1, new_line)
|
||||||
|
print(f"OK: tenant_slug agregado en linea {i+2}")
|
||||||
|
break
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/auth.py", "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
print("Listo")
|
||||||
18
backend/fix_syntax.py
Normal file
18
backend/fix_syntax.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
new_block = [
|
||||||
|
" if current_user.role.value == 'ADMIN':\n",
|
||||||
|
" query = select(User).where(User.id == user_id)\n",
|
||||||
|
" else:\n",
|
||||||
|
" query = select(User).where(\n",
|
||||||
|
" User.id == user_id,\n",
|
||||||
|
" User.tenant_id == current_user.tenant_id\n",
|
||||||
|
" )\n",
|
||||||
|
]
|
||||||
|
|
||||||
|
lines[150:161] = new_block
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
print("Listo")
|
||||||
28
backend/fix_users.py
Normal file
28
backend/fix_users.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
old1 = " User.tenant_id == current_user.tenant_id # " + "\u2705" + " Seguridad multi-tenant"
|
||||||
|
new1 = """ from app.models.user import UserRole as _UserRole
|
||||||
|
if current_user.role == _UserRole.ADMIN:
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)"""
|
||||||
|
|
||||||
|
if old1 in content:
|
||||||
|
content = content.replace(old1, new1)
|
||||||
|
print("OK bloque 1")
|
||||||
|
else:
|
||||||
|
print("SKIP bloque 1 - buscando alternativa")
|
||||||
|
old1b = " User.tenant_id == current_user.tenant_id\n )\n result = await db.execute(query)\n if not user:"
|
||||||
|
new1b = " User.tenant_id == current_user.tenant_id\n )\n result = await db.execute(query)\n if not user:"
|
||||||
|
print("Lineas con tenant_id encontradas:")
|
||||||
|
for i, line in enumerate(content.split("\n")):
|
||||||
|
if "tenant_id == current_user.tenant_id" in line:
|
||||||
|
print(f" Linea {i}: {line}")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print("Listo")
|
||||||
60
backend/fix_users2.py
Normal file
60
backend/fix_users2.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
from app.models.user import UserRole as _UserRole
|
||||||
|
|
||||||
|
# Reemplazar el patron comun de query con filtro de tenant
|
||||||
|
# por una version que permite a ADMIN ver todos los tenants
|
||||||
|
|
||||||
|
old_get_user = """ query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
if not user:"""
|
||||||
|
|
||||||
|
new_get_user = """ if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
if not user:"""
|
||||||
|
|
||||||
|
old_update_user = """ query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
new_update_user = """ if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for old, new in [(old_get_user, new_get_user), (old_update_user, new_update_user)]:
|
||||||
|
occurrences = content.count(old)
|
||||||
|
if occurrences > 0:
|
||||||
|
content = content.replace(old, new)
|
||||||
|
count += occurrences
|
||||||
|
print(f"OK: {occurrences} ocurrencia(s) reemplazada(s)")
|
||||||
|
else:
|
||||||
|
print(f"SKIP: bloque no encontrado")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"Total: {count} reemplazos aplicados")
|
||||||
36
backend/fix_users3.py
Normal file
36
backend/fix_users3.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
old1 = """ # Buscar usuario
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
new1 = """ # Buscar usuario - ADMIN global puede editar cualquier tenant
|
||||||
|
if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
count = content.count(old1)
|
||||||
|
if count > 0:
|
||||||
|
content = content.replace(old1, new1)
|
||||||
|
print(f"OK: {count} bloques reemplazados")
|
||||||
|
else:
|
||||||
|
print("ERROR: bloque no encontrado")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print("Listo")
|
||||||
38
backend/fix_users4.py
Normal file
38
backend/fix_users4.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
admin_check = [
|
||||||
|
" # Buscar usuario - ADMIN global puede editar cualquier tenant\n",
|
||||||
|
" if current_user.role.value == \"ADMIN\":\n",
|
||||||
|
" query = select(User).where(User.id == user_id)\n",
|
||||||
|
" else:\n",
|
||||||
|
" query = select(User).where(\n",
|
||||||
|
" User.id == user_id,\n",
|
||||||
|
" User.tenant_id == current_user.tenant_id\n",
|
||||||
|
" )\n",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Reemplazar bloques en lineas 198, 305, 382 (0-indexed: 197, 304, 381)
|
||||||
|
replaced = 0
|
||||||
|
new_lines = lines[:]
|
||||||
|
i = 0
|
||||||
|
while i < len(new_lines):
|
||||||
|
if (new_lines[i].strip() == "# Buscar usuario" and
|
||||||
|
i+1 < len(new_lines) and "select(User).where(" in new_lines[i+1] and
|
||||||
|
i+2 < len(new_lines) and "User.id == user_id," in new_lines[i+2] and
|
||||||
|
i+3 < len(new_lines) and "User.tenant_id == current_user.tenant_id" in new_lines[i+3]):
|
||||||
|
|
||||||
|
indent = " "
|
||||||
|
new_block = admin_check[:]
|
||||||
|
# Remove old 4 lines of query block (comment + query 4 lines)
|
||||||
|
new_lines[i:i+5] = new_block
|
||||||
|
replaced += 1
|
||||||
|
i += len(new_block)
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print(f"Reemplazos realizados: {replaced}")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
|
print("Listo")
|
||||||
11
backend/show_context.py
Normal file
11
backend/show_context.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Mostrar contexto alrededor de lineas con tenant_id
|
||||||
|
targets = [51, 92, 159, 200, 307, 384]
|
||||||
|
for t in targets:
|
||||||
|
print(f"\n=== Linea {t} ===")
|
||||||
|
start = max(0, t-5)
|
||||||
|
end = min(len(lines), t+5)
|
||||||
|
for i in range(start, end):
|
||||||
|
print(f"{i+1}: {lines[i].rstrip()}")
|
||||||
6
check_lines.py
Normal file
6
check_lines.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if "tenant_id == current_user.tenant_id" in line:
|
||||||
|
print(f"Linea {i+1}: {line.rstrip()}")
|
||||||
14
check_user.py
Normal file
14
check_user.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import asyncio
|
||||||
|
from app.core.database import AsyncSessionLocal
|
||||||
|
from app.models.user import User
|
||||||
|
from sqlalchemy import select
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
async def check():
|
||||||
|
async with AsyncSessionLocal() as db:
|
||||||
|
result = await db.execute(select(User))
|
||||||
|
users = result.scalars().all()
|
||||||
|
for u in users:
|
||||||
|
print(f"ID: {u.id} | Email: {u.email} | Tenant: {u.tenant_id} | Rol: {u.role}")
|
||||||
|
|
||||||
|
asyncio.run(check())
|
||||||
16
fix_response.py
Normal file
16
fix_response.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/auth.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Linea 286 (0-indexed 285): "tenant_id": str(user.tenant_id),
|
||||||
|
# Agregar tenant_slug despues de tenant_id
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if '"tenant_id": str(user.tenant_id),' in line:
|
||||||
|
indent = " "
|
||||||
|
new_line = indent + '"tenant_slug": tenant.slug if tenant else str(user.tenant_id),\n'
|
||||||
|
lines.insert(i + 1, new_line)
|
||||||
|
print(f"OK: tenant_slug agregado en linea {i+2}")
|
||||||
|
break
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/auth.py", "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
print("Listo")
|
||||||
18
fix_syntax.py
Normal file
18
fix_syntax.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
new_block = [
|
||||||
|
" if current_user.role.value == 'ADMIN':\n",
|
||||||
|
" query = select(User).where(User.id == user_id)\n",
|
||||||
|
" else:\n",
|
||||||
|
" query = select(User).where(\n",
|
||||||
|
" User.id == user_id,\n",
|
||||||
|
" User.tenant_id == current_user.tenant_id\n",
|
||||||
|
" )\n",
|
||||||
|
]
|
||||||
|
|
||||||
|
lines[150:161] = new_block
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
print("Listo")
|
||||||
28
fix_users.py
Normal file
28
fix_users.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
old1 = " User.tenant_id == current_user.tenant_id # " + "\u2705" + " Seguridad multi-tenant"
|
||||||
|
new1 = """ from app.models.user import UserRole as _UserRole
|
||||||
|
if current_user.role == _UserRole.ADMIN:
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)"""
|
||||||
|
|
||||||
|
if old1 in content:
|
||||||
|
content = content.replace(old1, new1)
|
||||||
|
print("OK bloque 1")
|
||||||
|
else:
|
||||||
|
print("SKIP bloque 1 - buscando alternativa")
|
||||||
|
old1b = " User.tenant_id == current_user.tenant_id\n )\n result = await db.execute(query)\n if not user:"
|
||||||
|
new1b = " User.tenant_id == current_user.tenant_id\n )\n result = await db.execute(query)\n if not user:"
|
||||||
|
print("Lineas con tenant_id encontradas:")
|
||||||
|
for i, line in enumerate(content.split("\n")):
|
||||||
|
if "tenant_id == current_user.tenant_id" in line:
|
||||||
|
print(f" Linea {i}: {line}")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print("Listo")
|
||||||
60
fix_users2.py
Normal file
60
fix_users2.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
from app.models.user import UserRole as _UserRole
|
||||||
|
|
||||||
|
# Reemplazar el patron comun de query con filtro de tenant
|
||||||
|
# por una version que permite a ADMIN ver todos los tenants
|
||||||
|
|
||||||
|
old_get_user = """ query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
if not user:"""
|
||||||
|
|
||||||
|
new_get_user = """ if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
user = result.scalar_one_or_none()
|
||||||
|
if not user:"""
|
||||||
|
|
||||||
|
old_update_user = """ query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
new_update_user = """ if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for old, new in [(old_get_user, new_get_user), (old_update_user, new_update_user)]:
|
||||||
|
occurrences = content.count(old)
|
||||||
|
if occurrences > 0:
|
||||||
|
content = content.replace(old, new)
|
||||||
|
count += occurrences
|
||||||
|
print(f"OK: {occurrences} ocurrencia(s) reemplazada(s)")
|
||||||
|
else:
|
||||||
|
print(f"SKIP: bloque no encontrado")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f"Total: {count} reemplazos aplicados")
|
||||||
36
fix_users3.py
Normal file
36
fix_users3.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
old1 = """ # Buscar usuario
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
new1 = """ # Buscar usuario - ADMIN global puede editar cualquier tenant
|
||||||
|
if current_user.role.value == "ADMIN":
|
||||||
|
query = select(User).where(User.id == user_id)
|
||||||
|
else:
|
||||||
|
query = select(User).where(
|
||||||
|
User.id == user_id,
|
||||||
|
User.tenant_id == current_user.tenant_id
|
||||||
|
)
|
||||||
|
result = await db.execute(query)
|
||||||
|
db_user = result.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not db_user:"""
|
||||||
|
|
||||||
|
count = content.count(old1)
|
||||||
|
if count > 0:
|
||||||
|
content = content.replace(old1, new1)
|
||||||
|
print(f"OK: {count} bloques reemplazados")
|
||||||
|
else:
|
||||||
|
print("ERROR: bloque no encontrado")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
print("Listo")
|
||||||
38
fix_users4.py
Normal file
38
fix_users4.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
admin_check = [
|
||||||
|
" # Buscar usuario - ADMIN global puede editar cualquier tenant\n",
|
||||||
|
" if current_user.role.value == \"ADMIN\":\n",
|
||||||
|
" query = select(User).where(User.id == user_id)\n",
|
||||||
|
" else:\n",
|
||||||
|
" query = select(User).where(\n",
|
||||||
|
" User.id == user_id,\n",
|
||||||
|
" User.tenant_id == current_user.tenant_id\n",
|
||||||
|
" )\n",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Reemplazar bloques en lineas 198, 305, 382 (0-indexed: 197, 304, 381)
|
||||||
|
replaced = 0
|
||||||
|
new_lines = lines[:]
|
||||||
|
i = 0
|
||||||
|
while i < len(new_lines):
|
||||||
|
if (new_lines[i].strip() == "# Buscar usuario" and
|
||||||
|
i+1 < len(new_lines) and "select(User).where(" in new_lines[i+1] and
|
||||||
|
i+2 < len(new_lines) and "User.id == user_id," in new_lines[i+2] and
|
||||||
|
i+3 < len(new_lines) and "User.tenant_id == current_user.tenant_id" in new_lines[i+3]):
|
||||||
|
|
||||||
|
indent = " "
|
||||||
|
new_block = admin_check[:]
|
||||||
|
# Remove old 4 lines of query block (comment + query 4 lines)
|
||||||
|
new_lines[i:i+5] = new_block
|
||||||
|
replaced += 1
|
||||||
|
i += len(new_block)
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
print(f"Reemplazos realizados: {replaced}")
|
||||||
|
|
||||||
|
with open("/app/app/api/v1/endpoints/users.py", "w") as f:
|
||||||
|
f.writelines(new_lines)
|
||||||
|
print("Listo")
|
||||||
104
frontend-client/src/lib/stores/auth.ts.bak
Normal file
104
frontend-client/src/lib/stores/auth.ts.bak
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
import type { Writable } from 'svelte/store';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
export interface User {
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
tenant_id: string;
|
||||||
|
role: 'CLIENT_ADMIN' | 'CLIENT_USER';
|
||||||
|
is_active: boolean;
|
||||||
|
is_two_factor_enabled: boolean;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
export interface AuthState {
|
||||||
|
user: User | null;
|
||||||
|
token: string | null;
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
export interface LoginRequest {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
tenant_slug: string;
|
||||||
|
totp_code?: string;
|
||||||
|
}
|
||||||
|
export interface LoginResponse {
|
||||||
|
access_token: string;
|
||||||
|
token_type: string;
|
||||||
|
expires_in: number;
|
||||||
|
user: User;
|
||||||
|
}
|
||||||
|
const initialState: AuthState = {
|
||||||
|
user: null,
|
||||||
|
token: null,
|
||||||
|
isAuthenticated: false,
|
||||||
|
isLoading: false
|
||||||
|
};
|
||||||
|
function createAuthStore() {
|
||||||
|
const { subscribe, set, update }: Writable<AuthState> = writable(initialState);
|
||||||
|
let _state = initialState;
|
||||||
|
subscribe(s => { _state = s; });
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
init: async () => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/v1/auth/me', {
|
||||||
|
credentials: 'include',
|
||||||
|
headers: { 'X-App': 'client', 'X-Tenant-Slug': 'aduanasoft' }
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
const user = await response.json();
|
||||||
|
set({ user, token: null, isAuthenticated: true, isLoading: false });
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
login: async (credentials: LoginRequest): Promise<void> => {
|
||||||
|
update(state => ({ ...state, isLoading: true }));
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/v1/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-Tenant-Slug': credentials.tenant_slug,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(credentials)
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const error = await response.json();
|
||||||
|
throw new Error(error.detail || 'Login failed');
|
||||||
|
}
|
||||||
|
const data: LoginResponse = await response.json();
|
||||||
|
set({ user: data.user, token: data.access_token, isAuthenticated: true, isLoading: false });
|
||||||
|
} catch (error) {
|
||||||
|
update(state => ({ ...state, isLoading: false }));
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
logout: async () => {
|
||||||
|
try {
|
||||||
|
const token = _state.token;
|
||||||
|
await fetch('/api/v1/auth/logout', {
|
||||||
|
method: 'POST',
|
||||||
|
credentials: 'include',
|
||||||
|
headers: {
|
||||||
|
'X-App': 'client',
|
||||||
|
'X-Tenant-Slug': 'aduanasoft',
|
||||||
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
set(initialState);
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.location.href = '/login';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateUser: (user: User) => { update(state => ({ ...state, user })); },
|
||||||
|
setUser: (user: User) => { set({ user, token: null, isAuthenticated: true, isLoading: false }); },
|
||||||
|
setLoading: (isLoading: boolean) => { update(state => ({ ...state, isLoading })); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export const auth = createAuthStore();
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
import { writable } from 'svelte/store';
|
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
// Types
|
export interface User {
|
||||||
export interface InternalUser {
|
|
||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name: string;
|
last_name: string;
|
||||||
role: 'ADMIN' | 'SUPPORT_MANAGER' | 'AGENT' | 'AUDITOR';
|
tenant_id: string;
|
||||||
|
tenant_slug: string;
|
||||||
|
role: 'CLIENT_ADMIN' | 'CLIENT_USER';
|
||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
is_two_factor_enabled: boolean;
|
is_two_factor_enabled: boolean;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
tenant_id: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AuthState {
|
export interface AuthState {
|
||||||
user: InternalUser | null;
|
user: User | null;
|
||||||
token: string | null;
|
token: string | null;
|
||||||
refreshToken: string | null;
|
|
||||||
isAuthenticated: boolean;
|
isAuthenticated: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}
|
}
|
||||||
@@ -25,181 +24,90 @@ export interface AuthState {
|
|||||||
export interface LoginRequest {
|
export interface LoginRequest {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
tenant_slug: string;
|
tenant_slug?: string;
|
||||||
totp_code?: string;
|
totp_code?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginResponse {
|
export interface LoginResponse {
|
||||||
access_token: string;
|
access_token: string;
|
||||||
refresh_token: string;
|
|
||||||
token_type: string;
|
token_type: string;
|
||||||
expires_in: number;
|
expires_in: number;
|
||||||
user: InternalUser;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RefreshTokenRequest {
|
|
||||||
refresh_token: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TokenResponse {
|
|
||||||
access_token: string;
|
|
||||||
token_type: string;
|
|
||||||
expires_in: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial state
|
|
||||||
const initialState: AuthState = {
|
const initialState: AuthState = {
|
||||||
user: null,
|
user: null,
|
||||||
token: null,
|
token: null,
|
||||||
refreshToken: null,
|
|
||||||
isAuthenticated: false,
|
isAuthenticated: false,
|
||||||
isLoading: false
|
isLoading: false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create auth store
|
|
||||||
function createAuthStore() {
|
function createAuthStore() {
|
||||||
const { subscribe, set, update } = writable<AuthState>(initialState);
|
const { subscribe, set, update }: Writable<AuthState> = writable(initialState);
|
||||||
|
|
||||||
// Track current state for uso interno (evita dependencias circulares)
|
|
||||||
let _state = initialState;
|
let _state = initialState;
|
||||||
subscribe(s => { _state = s; });
|
subscribe(s => { _state = s; });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
subscribe,
|
subscribe,
|
||||||
|
|
||||||
// Rehidrata sesión desde cookie HttpOnly (no toca localStorage)
|
|
||||||
init: async () => {
|
init: async () => {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/v1/auth/me', {
|
const response = await fetch('/api/v1/auth/me', {
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: { 'X-App': 'internal' }
|
headers: { 'X-App': 'client' }
|
||||||
});
|
});
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const user = await response.json();
|
const user = await response.json();
|
||||||
set({
|
set({ user, token: null, isAuthenticated: true, isLoading: false });
|
||||||
user,
|
|
||||||
token: null,
|
|
||||||
refreshToken: null,
|
|
||||||
isAuthenticated: true,
|
|
||||||
isLoading: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 401/400 es esperado cuando no hay sesión activa — no es un error
|
|
||||||
} catch (error) {
|
|
||||||
// Ignorar errores de red en init
|
|
||||||
}
|
}
|
||||||
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Login
|
|
||||||
login: async (credentials: LoginRequest): Promise<void> => {
|
login: async (credentials: LoginRequest): Promise<void> => {
|
||||||
update(state => ({ ...state, isLoading: true }));
|
update(state => ({ ...state, isLoading: true }));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/v1/auth/login', {
|
const response = await fetch('/api/v1/auth/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'X-Tenant-Slug': credentials.tenant_slug,
|
'X-App': 'client',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(credentials)
|
body: JSON.stringify(credentials)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const error = await response.json();
|
const error = await response.json();
|
||||||
throw new Error(error.detail || 'Login failed');
|
throw new Error(error.detail || 'Login failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
const data: LoginResponse = await response.json();
|
const data: LoginResponse = await response.json();
|
||||||
|
set({ user: data.user, token: data.access_token, isAuthenticated: true, isLoading: false });
|
||||||
set({
|
|
||||||
user: data.user,
|
|
||||||
token: data.access_token,
|
|
||||||
refreshToken: data.refresh_token,
|
|
||||||
isAuthenticated: true,
|
|
||||||
isLoading: false
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
update(state => ({ ...state, isLoading: false }));
|
update(state => ({ ...state, isLoading: false }));
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Refresh Session
|
|
||||||
refreshSession: async (): Promise<void> => {
|
|
||||||
const currentRefreshToken = _state.refreshToken;
|
|
||||||
|
|
||||||
if (!currentRefreshToken) {
|
|
||||||
throw new Error("No refresh token available");
|
|
||||||
}
|
|
||||||
|
|
||||||
update(state => ({ ...state, isLoading: true }));
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('/api/v1/auth/refresh', {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ refresh_token: currentRefreshToken })
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
// If refresh fails, logout
|
|
||||||
if (response.status === 401 || response.status === 403) {
|
|
||||||
auth.logout();
|
|
||||||
}
|
|
||||||
const error = await response.json();
|
|
||||||
throw new Error(error.detail || 'Refresh failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
const data: TokenResponse = await response.json();
|
|
||||||
|
|
||||||
update(state => ({
|
|
||||||
...state,
|
|
||||||
token: data.access_token,
|
|
||||||
isLoading: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
update(state => ({ ...state, isLoading: false }));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Logout
|
|
||||||
logout: async () => {
|
logout: async () => {
|
||||||
// Llamar al backend para que borre la cookie HttpOnly
|
|
||||||
try {
|
try {
|
||||||
|
const token = _state.token;
|
||||||
|
const slug = _state.user?.tenant_slug || _state.user?.tenant_id || '';
|
||||||
await fetch('/api/v1/auth/logout', {
|
await fetch('/api/v1/auth/logout', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: { 'X-App': 'internal' }
|
headers: {
|
||||||
|
'X-App': 'client',
|
||||||
|
'X-Tenant-Slug': slug,
|
||||||
|
...(token ? { 'Authorization': `Bearer ${token}` } : {})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch { /* ignorar errores de red */ }
|
} catch {}
|
||||||
set(initialState);
|
set(initialState);
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.location.href = '/login';
|
window.location.href = '/login';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
updateUser: (user: User) => { update(state => ({ ...state, user })); },
|
||||||
// Update user data
|
setUser: (user: User) => { set({ user, token: null, isAuthenticated: true, isLoading: false }); },
|
||||||
updateUser: (user: InternalUser) => {
|
setLoading: (isLoading: boolean) => { update(state => ({ ...state, isLoading })); }
|
||||||
update(state => ({ ...state, user }));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set user from SSR pre-load (no fetch required)
|
|
||||||
setUser: (user: InternalUser) => {
|
|
||||||
set({ user, token: null, refreshToken: null, isAuthenticated: true, isLoading: false });
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set loading state
|
|
||||||
setLoading: (isLoading: boolean) => {
|
|
||||||
update(state => ({ ...state, isLoading }));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
11
show_context.py
Normal file
11
show_context.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
with open("/app/app/api/v1/endpoints/users.py", "r") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Mostrar contexto alrededor de lineas con tenant_id
|
||||||
|
targets = [51, 92, 159, 200, 307, 384]
|
||||||
|
for t in targets:
|
||||||
|
print(f"\n=== Linea {t} ===")
|
||||||
|
start = max(0, t-5)
|
||||||
|
end = min(len(lines), t+5)
|
||||||
|
for i in range(start, end):
|
||||||
|
print(f"{i+1}: {lines[i].rstrip()}")
|
||||||
Reference in New Issue
Block a user