From 0a04060527b54c5f71e65f5f0af380a1fd360f0f Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Mon, 19 Dec 2022 12:12:22 -0300 Subject: [PATCH] Fix lots of bugs Co-authored-by: Ezequiel Bellver Co-authored-by: Juan Barmasch --- bsition/api/endpoints/documents.py | 37 +++++++++++++++---- bsition/api/endpoints/tables.py | 39 ++++++++++++++++---- bsition/api/endpoints/token.py | 7 ++-- bsition/api/endpoints/users.py | 2 +- bsition/api/main.py | 9 +++++ bsition/api/models/document.py | 4 +++ bsition/api/models/user.py | 1 + bsition/api/utils/security.py | 3 +- bsition/backend/elastic/search.py | 18 +++++----- bsition/backend/mongo/documents.py | 7 +++- bsition/backend/postgres/relations.py | 52 ++++++++++++++++++++++++++- bsition/backend/redis/tokens.py | 5 +-- 12 files changed, 149 insertions(+), 35 deletions(-) diff --git a/bsition/api/endpoints/documents.py b/bsition/api/endpoints/documents.py index 8775ada..3a9eb2c 100644 --- a/bsition/api/endpoints/documents.py +++ b/bsition/api/endpoints/documents.py @@ -1,11 +1,13 @@ import json -from fastapi import APIRouter +from fastapi import APIRouter, Depends from fastapi.responses import JSONResponse -from bsition.api.models.document import Document, DocumentUpdate +from bsition.api.models.document import Document, DocumentUpdate, Access +from bsition.api.utils.security import get_current_user from bsition.backend.elastic import search as elastic from bsition.backend.mongo import documents as mongo +from bsition.backend.postgres import relations as postgres router = APIRouter() @@ -17,14 +19,14 @@ def create(aux: Document): @router.get("") -def get_documents(query: str = None): +def get_documents(query: str = None, user: tuple = Depends(get_current_user)): if query is not None: - return elastic.search("test-index", query) - return mongo.get_documents() + return elastic.search("test-index", query, user[0]) + return mongo.get_documents(user[0]) @router.get("/{id}") -def get_by_id(id: str): +def get_document(id: str): return mongo.get_document_by_id(id) @@ -37,3 +39,26 @@ def edit_data(aux: DocumentUpdate, id: str): if aux.name is not None: mongo.edit_name(id, aux.name) return JSONResponse(content={"detail": "Document updated."}, status_code=202) + + +@router.get("/{id}/access") +def get_access(id: str, user: tuple = Depends(get_current_user)): + return postgres.has_access_doc(user[0], id) + + +@router.post("/{id}/access") +def give_access(id: str, access_type: Access, user: tuple = Depends(get_current_user)): + postgres.give_access_doc(user[0], id, access_type.access_type) + return JSONResponse(content={"detail": "Access created"}, status_code=201) + + +@router.put("/{id}/access") +def update_access(id: str, access_type: Access, user: tuple = Depends(get_current_user)): + postgres.give_access_doc(user[0], id, access_type.access_type) + return JSONResponse(content={"detail": "Access updated"}, status_code=202) + + +@router.delete("/{id}/access") +def deny_access(id: str, user: tuple = Depends(get_current_user)): + postgres.deny_access_doc(user[0], id) + return JSONResponse(content={"detail": "Access deleted"}, status_code=201) diff --git a/bsition/api/endpoints/tables.py b/bsition/api/endpoints/tables.py index 75c9809..b4f0cf6 100644 --- a/bsition/api/endpoints/tables.py +++ b/bsition/api/endpoints/tables.py @@ -1,9 +1,12 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Depends from fastapi.responses import JSONResponse +from bsition.api.models.document import Access from bsition.api.models.table import Filter, Sort, Table +from bsition.api.utils.security import get_current_user from bsition.backend.mongo import tables as mongo -from bsition.backend.postgres import tables as postgres +from bsition.backend.postgres import tables as postgres_t +from bsition.backend.postgres import relations as postgres_r router = APIRouter() @@ -27,13 +30,13 @@ def edit_table(aux: Table, name: str): @router.post("/{name}/sort") def create_sort(name: str): - postgres.create_sort() + postgres_t.create_sort() return JSONResponse(content={"detail": "Sort created."}, status_code=201) @router.put("/{name}/sort") def add_sort(aux: Sort, name: str): - postgres.add_sort(name, aux.property, aux.order, aux.priority) + postgres_t.add_sort(name, aux.property, aux.order, aux.priority) return JSONResponse(content={"detail": "Sort updated."}, status_code=202) @@ -44,17 +47,39 @@ def sort(name: str): @router.post("/{name}/filter") def create_filter(name: str): - postgres.create_filter() - # postgres.add_filter_trigger(name) + postgres_t.create_filter() return JSONResponse(content={"detail": "Filter created."}, status_code=201) @router.put("/{name}/filter") def add_filter(aux: Filter, name: str): - postgres.add_filter(name, aux.property, aux.value, aux.function) + postgres_t.add_filter(name, aux.property, aux.value, aux.function) return JSONResponse(content={"detail": "Filter updated."}, status_code=202) @router.get("/{name}/filter") def filter(name: str): return mongo.filter(name) + + +@router.get("/{id}/access") +def get_access(id: str, user: tuple = Depends(get_current_user)): + return postgres_r.has_access_table(user[0], id) + + +@router.post("/{id}/access") +def give_access(id: str, access_type: Access, user: tuple = Depends(get_current_user)): + postgres_r.give_access_table(user[0], id, access_type.access_type) + return JSONResponse(content={"detail": "Access created"}, status_code=201) + + +@router.put("/{id}/access") +def update_access(id: str, access_type: Access, user: tuple = Depends(get_current_user)): + postgres_r.give_access_table(user[0], id, access_type.access_type) + return JSONResponse(content={"detail": "Access updated"}, status_code=202) + + +@router.delete("/{id}/access") +def deny_access(id: str, user: tuple = Depends(get_current_user)): + postgres_r.deny_access_table(user[0], id) + return JSONResponse(content={"detail": "Access deleted"}, status_code=201) diff --git a/bsition/api/endpoints/token.py b/bsition/api/endpoints/token.py index bd90c96..e619ff1 100644 --- a/bsition/api/endpoints/token.py +++ b/bsition/api/endpoints/token.py @@ -21,7 +21,7 @@ def login(form: OAuth2PasswordRequestForm = Depends()): detail="User not found.", ) - token, expire = write_token({"sub": form.username}) + token, expire = write_token({"sub": form.username + ":" + str(user[0])}) add_token(token, form.username, expire) clean_tokens(form.username) return JSONResponse( @@ -34,7 +34,6 @@ def login(form: OAuth2PasswordRequestForm = Depends()): @router.delete("/token") -def logout(token: str = Depends(oauth2_scheme), user: User = Depends(get_current_user)): +def logout(token: str = Depends(oauth2_scheme), user: tuple = Depends(get_current_user)): remove_token(user[1], token) - return JSONResponse(content={"detail": "Token deleted."}, status_code=201) - + return JSONResponse(content={"detail": "Token deleted."}, status_code=202) diff --git a/bsition/api/endpoints/users.py b/bsition/api/endpoints/users.py index 2c3d445..be05a32 100644 --- a/bsition/api/endpoints/users.py +++ b/bsition/api/endpoints/users.py @@ -24,7 +24,7 @@ def create_user(user: User): @router.get("/me") -def get_logged_user(user: User = Depends(get_current_user)): +def get_logged_user(user: tuple = Depends(get_current_user)): return user diff --git a/bsition/api/main.py b/bsition/api/main.py index a25cc31..06d588d 100644 --- a/bsition/api/main.py +++ b/bsition/api/main.py @@ -1,9 +1,18 @@ from dotenv import load_dotenv from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware from bsition.api.api import router app = FastAPI(title="BSition API") app.include_router(router, prefix="/api") +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["POST", "GET", "PUT", "DELETE", "OPTIONS"], + allow_headers=["*"], + max_age=3600, +) load_dotenv() diff --git a/bsition/api/models/document.py b/bsition/api/models/document.py index bbffa74..70b4829 100644 --- a/bsition/api/models/document.py +++ b/bsition/api/models/document.py @@ -13,3 +13,7 @@ class DocumentUpdate(BaseModel): name: Optional[str] access: Optional[list] data: Optional[str] + + +class Access(BaseModel): + access_type: int diff --git a/bsition/api/models/user.py b/bsition/api/models/user.py index 26353ea..6b33a46 100644 --- a/bsition/api/models/user.py +++ b/bsition/api/models/user.py @@ -3,5 +3,6 @@ from pydantic.types import Optional class User(BaseModel): + id: Optional[str] username: str password: Optional[str] diff --git a/bsition/api/utils/security.py b/bsition/api/utils/security.py index 8ab8382..b6d67f1 100644 --- a/bsition/api/utils/security.py +++ b/bsition/api/utils/security.py @@ -3,6 +3,7 @@ from fastapi.security import OAuth2PasswordBearer from jwt import exceptions from starlette import status +from bsition.api.models.user import User from bsition.api.utils.jwt import validate_token from bsition.backend.postgres.users import get_user_by_username from bsition.backend.redis.tokens import valid_token @@ -19,7 +20,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): try: payload = validate_token(token) - username: str = payload.get("sub") + username: str = payload.get("sub").split(":")[0] if username is None: raise credentials_exception except exceptions.DecodeError: diff --git a/bsition/backend/elastic/search.py b/bsition/backend/elastic/search.py index 9dc4456..b97b306 100644 --- a/bsition/backend/elastic/search.py +++ b/bsition/backend/elastic/search.py @@ -1,25 +1,22 @@ from bsition.backend.elastic.utils import get_client +from bsition.backend.postgres import relations as postgres -def search(index, query): +def search(index, query, user_id): client = get_client() + acc_doc = postgres.get_accesible_documents(user_id) + resp = client.search( index=index, size=100, query={ - # "query_string": { - # "query": "*puan*", - # "default_field": "data" - # } "bool": { "must": [ {"query_string": {"query": query, "default_field": "data"}}, - # { - # "match": { - # "id": "1", - # } - # } + ], + "filter": [ + {"terms": {"_id": acc_doc}} ] } }, @@ -31,5 +28,6 @@ def search(index, query): hits.append( str(hit["highlight"]) + " ---- " + "%(name)s: %(data)s" % hit["_source"] ) + print(hit['_id'], hit['_source']) return hits diff --git a/bsition/backend/mongo/documents.py b/bsition/backend/mongo/documents.py index 5ac054f..064fa86 100644 --- a/bsition/backend/mongo/documents.py +++ b/bsition/backend/mongo/documents.py @@ -1,13 +1,18 @@ from bson import ObjectId from bsition.backend.elastic import utils as elastic +from bsition.backend.postgres import relations as postgres from bsition.backend.mongo.utils import get_database -def get_documents(): +def get_documents(user_id): dbname = get_database() docs_coll = dbname["docs"] + + docs = postgres.get_accesible_documents(user_id) + pipeline = [ + {"$match": {"_id": {"$in": [ObjectId(doc) for doc in docs]}}}, { "$project": { "id": {"$toString": "$_id"}, diff --git a/bsition/backend/postgres/relations.py b/bsition/backend/postgres/relations.py index 5270493..0e8d63a 100644 --- a/bsition/backend/postgres/relations.py +++ b/bsition/backend/postgres/relations.py @@ -30,6 +30,15 @@ def create_relations_tables(): """ ) ) + cur.execute( + sql.SQL( + """ + CREATE TABLE public_docs ( + doc_id INTEGER PRIMARY KEY + ) + """ + ) + ) conn.commit() @@ -92,6 +101,43 @@ def has_access(user_id, id, destination): return list(cur.fetchall()) +def is_public(doc_id): + conn = get_connection() + cur = conn.cursor() + cur.execute( + sql.SQL( + "SELECT doc_id FROM public_docs WHERE doc_id = {doc_id}" + ).format( + doc_id=sql.Literal(doc_id), + ) + ) + return cur.rowcount > 0 + + +def make_public(doc_id): + conn = get_connection() + cur = conn.cursor() + cur.execute( + sql.SQL( + "INSERT INTO public_docs VALUES {doc_id} ON CONFLICT DO NOTHING" + ).format( + doc_id=sql.Literal(doc_id), + ) + ) + + +def make_private(doc_id): + conn = get_connection() + cur = conn.cursor() + cur.execute( + sql.SQL( + "DELETE FROM public_docs WHERE doc_id = {doc_id} ON CONFLICT DO NOTHING" + ).format( + doc_id=sql.Literal(doc_id), + ) + ) + + def deny_access_table(user_id, table_id): return deny_access(user_id, table_id, "table") @@ -127,4 +173,8 @@ def get_accesible_documents(user_id): user_id=sql.Literal(user_id) ) ) - return list(cur.fetchall()) + + docs = [] + for doc in cur.fetchall(): + docs.append(doc[0]) + return docs diff --git a/bsition/backend/redis/tokens.py b/bsition/backend/redis/tokens.py index 8ccf169..bb50e69 100644 --- a/bsition/backend/redis/tokens.py +++ b/bsition/backend/redis/tokens.py @@ -18,10 +18,7 @@ def remove_tokens(username): def remove_token(username, token): client = get_client() - tokens = client.zrangebyscore(username, -inf, inf) - for aux in tokens: - if aux.decode("utf-8") == token: - client.zrem(username, token) + client.zrem(username, token) # Puede correr en un cron o, por ejemplo, cada vez que el usuario hace login (o logout)