MGLDZM commited on
Commit
4e433fa
·
1 Parent(s): d1ae1d5

dev step aouth....

Browse files
Files changed (5) hide show
  1. main.py +70 -89
  2. modules/db.py +0 -41
  3. modules/model.py +66 -0
  4. modules/security.py +20 -33
  5. modules/token_counter.py +39 -0
main.py CHANGED
@@ -5,16 +5,12 @@ import os, json
5
  import time
6
  from openai import OpenAI
7
  from hashlib import sha256
8
- from modules import oauth, security, log_module, llm, db
9
  from fastapi.staticfiles import StaticFiles
10
  from fastapi.templating import Jinja2Templates
11
 
12
 
13
-
14
-
15
-
16
-
17
- app = FastAPI()
18
  httpsecurity = HTTPBasic()
19
 
20
  app.mount("/static", StaticFiles(directory="static"), name="static")
@@ -26,68 +22,47 @@ fecha_unix = str(int(time.time()))
26
  log_module.log_write("master", "iniciado", "-")
27
 
28
 
29
-
30
-
31
- def manejar_funciones(chunk, response_async, data):
32
- f_cb = dict(chunk["choices"][0]["delta"]["function_call"])
33
- for chunk in response_async:
34
- if chunk["choices"][0]["finish_reason"]:
35
- break
36
- for k, v in chunk["choices"][0]["delta"]["function_call"].items():
37
- f_cb[k] += v
38
 
39
- f_cb["arguments"] = json.loads(f_cb["arguments"])
40
- log_module.log_write(data["token_data"]["user"], "Funcion ejecutada RQ", json.dumps(f_cb))
41
- resultado_funcion = chat_functions.function_callbacks[f_cb["name"]](**f_cb["arguments"])
42
- log_module.log_write(data["token_data"]["user"], "Funcion ejecutada RS", json.dumps(resultado_funcion))
43
- data["messages"].append({"role": "function",
44
- "name": f_cb["name"],
45
- "content": resultado_funcion,
46
- })
47
- return json.dumps(f_cb), llm.ejecutar(data)
48
 
49
- @app.get("/", response_class=HTMLResponse)
50
- @app.post("/", response_class=HTMLResponse)
51
- async def root(request: Request, data = Depends(security.validate_cookie)):
52
- data, redirect = data
53
- response = None
 
 
 
 
54
 
55
- if redirect:
56
- redirect.delete_cookie("token")
57
- return redirect
58
 
59
- user = db.check_user(data["token_data"], False)
60
-
61
- if security.user_scale(user["role"], "chat"):
62
- response = templates.TemplateResponse("main.html", {"request": request, "token": data["token"], "version": fecha_unix})
63
- response.set_cookie("token", data["token"])
64
-
65
- elif "on hold" in user["role"]:
66
- if request.method=="POST" and not user["description"]:
67
- form = await request.form()
68
- message = form["message"].strip()
69
-
70
- db.add_description(user["gid"], message)
71
- response = RedirectResponse(url='/')
72
- else:
73
- response = templates.TemplateResponse("no_access.html", {
74
- "request": request,
75
- "description": bool(user["description"])
76
- })
77
 
 
 
 
 
 
 
 
 
78
  return response
 
79
 
80
-
81
-
82
- @app.get("/test", response_class=HTMLResponse)
83
- async def test(request: Request):
84
- return templates.TemplateResponse("no_access.html", {"request": request})
85
-
86
  @app.post("/chat")
87
- async def chat(data = Depends(security.validate_token)):
88
- messages = data.get("messages", "")
89
 
90
- if not messages:
91
  print("Empty message")
92
  error = "What ??"
93
  log_module.log_write(data["token_data"]["user"], "Mensaje RQ", "error, mensajes vacios")
@@ -110,46 +85,52 @@ async def chat(data = Depends(security.validate_token)):
110
  message = completion.content
111
  role = completion.role
112
  yield {"comando": "mensaje", "mensaje": {"role": role, "content": message}}
113
-
114
 
115
 
116
 
117
- @app.get("/read_log", response_class=HTMLResponse)
118
- async def read_log(request: Request, credentials: HTTPBasicCredentials = Depends(httpsecurity)):
119
- if sha256(credentials.username.encode()).hexdigest()=="bc1c32d709aef061bbde4fc848421cdb933e8a9f391c3a089f2861ac0772c168" and security.authenticate_user(credentials):
120
- log_module.log_write(credentials.username, "Log Accesado", "")
121
- with open("logs/eventos.log", "r") as f:
122
- return HTMLResponse(f.read())
123
- log_module.log_write(credentials.username, "Intento acceder logs", f"{request.client.host}:{request.client.port} - {str(dict(request.headers))}")
124
- raise HTTPException(status_code=404)
125
 
 
126
  @app.get("/privacy", response_class=HTMLResponse)
127
- async def sesion_i(request: Request):
128
  return templates.TemplateResponse("PrivacyPolicy.html", {"request": request})
129
-
130
- @app.get("/login", response_class=HTMLResponse)
131
- async def login(request: Request):
132
- ret = templates.TemplateResponse("iniciar_sesion.html", {"request": request, "redirecturi": oauth.OAUTH_REDIRECT})
133
- ret.delete_cookie("token")
134
- return ret
135
 
136
- @app.get("/oauth", response_class=HTMLResponse)
137
- async def validate_oauth(request: Request):
138
- params = dict(request.query_params)
139
- google_userinfo = oauth.validate_redirect(params)
 
 
 
 
 
140
 
141
- user = db.check_user(google_userinfo)
142
- token = security.create_jwt_token({
143
- "user": user["name"],
144
- "role": user["role"],
145
- "gid": user["gid"],
 
 
 
 
 
 
 
146
  })
147
 
148
- response = RedirectResponse(url='/')
149
- response.set_cookie(key="token", value=token)
150
-
151
- return response
152
-
153
 
154
 
155
 
 
 
 
 
 
 
 
 
 
 
 
5
  import time
6
  from openai import OpenAI
7
  from hashlib import sha256
8
+ from modules import model, oauth, security, log_module, llm
9
  from fastapi.staticfiles import StaticFiles
10
  from fastapi.templating import Jinja2Templates
11
 
12
 
13
+ app = FastAPI(docs_url=None, redoc_url=None)
 
 
 
 
14
  httpsecurity = HTTPBasic()
15
 
16
  app.mount("/static", StaticFiles(directory="static"), name="static")
 
22
  log_module.log_write("master", "iniciado", "-")
23
 
24
 
25
+ @app.get("/", response_class=HTMLResponse)
26
+ async def main_page(request: Request):
 
 
 
 
 
 
 
27
 
28
+ if not (token := request.cookies.get('token', "")):
29
+ return RedirectResponse(url='/login')
 
 
 
 
 
 
 
30
 
31
+ user = model.User.find_from_token(token)
32
+
33
+ if not user.can_use("chat"):
34
+ return RedirectResponse(url='/hold')
35
+
36
+ token = user.create_token()
37
+ response = templates.TemplateResponse("main.html", {"request": request, "token": token, "version": fecha_unix})
38
+ response.set_cookie("token", token)
39
+ return response
40
 
 
 
 
41
 
42
+ ########################## SECURITY ##########################
43
+ @app.get("/login", response_class=HTMLResponse)
44
+ async def login(request: Request):
45
+ ret = templates.TemplateResponse("iniciar_sesion.html", {"request": request, "redirecturi": oauth.OAUTH_REDIRECT})
46
+ ret.delete_cookie("token")
47
+ return ret
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ @app.get("/oauth", response_class=HTMLResponse)
50
+ async def validate_oauth(request: Request):
51
+ params = dict(request.query_params)
52
+ google_userinfo = oauth.validate_redirect(params)
53
+ user = model.User.find_or_create(google_userinfo)
54
+ token = user.create_token()
55
+ response = RedirectResponse(url='/')
56
+ response.set_cookie(key="token", value=token)
57
  return response
58
+ ########################## SECURITY ##########################
59
 
60
+ ########################## API ##########################
 
 
 
 
 
61
  @app.post("/chat")
62
+ async def chat(request: Request):
63
+
64
 
65
+ if not (messages := data.get("messages", "")):
66
  print("Empty message")
67
  error = "What ??"
68
  log_module.log_write(data["token_data"]["user"], "Mensaje RQ", "error, mensajes vacios")
 
85
  message = completion.content
86
  role = completion.role
87
  yield {"comando": "mensaje", "mensaje": {"role": role, "content": message}}
88
+ ########################## API ##########################
89
 
90
 
91
 
 
 
 
 
 
 
 
 
92
 
93
+ ########################## Static Pages ##########################
94
  @app.get("/privacy", response_class=HTMLResponse)
95
+ async def privacy_policy(request: Request):
96
  return templates.TemplateResponse("PrivacyPolicy.html", {"request": request})
 
 
 
 
 
 
97
 
98
+ @app.route("/hold", ["GET", "POST"], response_class=HTMLResponse)
99
+ async def on_hold(request: Request):
100
+ token = request.cookies.get('token', "")
101
+ redirect_to_login = RedirectResponse(url='/login')
102
+ if not token:
103
+ return redirect_to_login
104
+
105
+
106
+ user = model.User.find_from_token(token)
107
 
108
+ if user.can_use("chat"):
109
+ return redirect_to_login
110
+
111
+ if request.method=="POST" and not user.description:
112
+ form = await request.form()
113
+ if message := form["message"].strip():
114
+ user.update_description(message)
115
+
116
+ return templates.TemplateResponse(
117
+ "no_access.html", {
118
+ "request": request,
119
+ "description": bool(user.description)
120
  })
121
 
122
+
123
+ ########################## Static Pages ##########################
 
 
 
124
 
125
 
126
 
127
+ ########################## Other ##########################
128
+ @app.get("/read_log", response_class=HTMLResponse)
129
+ async def read_log(request: Request, credentials: HTTPBasicCredentials = Depends(httpsecurity)):
130
+ if sha256(credentials.username.encode()).hexdigest()=="bc1c32d709aef061bbde4fc848421cdb933e8a9f391c3a089f2861ac0772c168" and security.authenticate_user(credentials):
131
+ log_module.log_write(credentials.username, "Log Accesado", "")
132
+ with open("logs/eventos.log", "r") as f:
133
+ return HTMLResponse(f.read())
134
+ log_module.log_write(credentials.username, "Intento acceder logs", f"{request.client.host}:{request.client.port} - {str(dict(request.headers))}")
135
+ raise HTTPException(status_code=404)
136
+ ########################## Other ##########################
modules/db.py DELETED
@@ -1,41 +0,0 @@
1
- import os
2
- from pymongo.mongo_client import MongoClient
3
- from pymongo.server_api import ServerApi
4
- from . import log_module
5
- import datetime
6
-
7
- MONGO_URL = os.environ.get("MONGO_URL", None)
8
- MONGO_PWD = os.environ.get("MONGO_PWD", None)
9
- MONGO_USR = os.environ.get("MONGO_USR", None)
10
- uri = f"mongodb+srv://{MONGO_USR}:{MONGO_PWD}@{MONGO_URL}/?retryWrites=true&w=majority"
11
- uri = f"mongodb://localhost:27017"
12
-
13
- tz = datetime.timezone(datetime.timedelta(hours=-4))
14
-
15
- def check_user(data, create=True):
16
- client = MongoClient(uri, server_api=ServerApi('1'))
17
- user_table = client.ChatDB.users
18
- user = user_table.find_one({"gid":data["gid"]})
19
- if not user and create:
20
- user = {
21
- "name": data["name"],
22
- "tokens": {},
23
- "approved": False,
24
- "created": datetime.datetime.now(tz),
25
- "description": "",
26
- "email": data["email"],
27
- "gid": data["gid"],
28
- "role": "on hold"
29
- }
30
- user_table.insert_one(user)
31
- user.pop("_id", "")
32
- return user
33
-
34
- def add_description(gid, message):
35
- client = MongoClient(uri, server_api=ServerApi('1'))
36
- user_table = client.ChatDB.users
37
- sel = {"gid":gid}
38
- upd = {"$set": { "description": message}}
39
- user_table.update_one(sel, upd)
40
-
41
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modules/model.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pymongo.mongo_client import MongoClient
3
+ from pymongo.server_api import ServerApi
4
+ from . import log_module, security
5
+ from datetime import timezone, datetime, timedelta
6
+ from pydantic import BaseModel
7
+
8
+
9
+ MONGO_URL = os.environ.get("MONGO_URL", None)
10
+ MONGO_PWD = os.environ.get("MONGO_PWD", None)
11
+ MONGO_USR = os.environ.get("MONGO_USR", None)
12
+ uri = f"mongodb+srv://{MONGO_USR}:{MONGO_PWD}@{MONGO_URL}/?retryWrites=true&w=majority"
13
+ #uri = f"mongodb://localhost:27017"
14
+ client = MongoClient(uri, server_api=ServerApi('1'))
15
+ user_table = client.ChatDB.users
16
+
17
+ tz = timezone(timedelta(hours=-4))
18
+
19
+ class User(BaseModel):
20
+ name: str
21
+ tokens: dict = {}
22
+ created: datetime = datetime.now(tz)
23
+ approved: datetime | None = None
24
+ description: str = ""
25
+ email: str
26
+ gid: str
27
+ role: str = "on hold"
28
+
29
+ @classmethod
30
+ def find(cls, gid: str):
31
+ found = user_table.find_one({"gid":gid})
32
+ if found:
33
+ return cls(**found)
34
+ return None
35
+
36
+ @classmethod
37
+ def find_from_token(cls, token: str):
38
+ data = security.validate_token(token)
39
+ user = user_table.find_one({"gid":data["gid"]})
40
+ return cls(**user)
41
+
42
+
43
+ @classmethod
44
+ def find_or_create(cls, data: dict):
45
+ found = cls.find(data["gid"])
46
+ if found:
47
+ return found
48
+ user = User(**data)
49
+ user_table.insert_one(user.model_dump())
50
+ log_module.logger.info(f"User created: {user.gid}")
51
+ return user
52
+
53
+ def create_token(self):
54
+ return security.create_jwt_token({"gid": self.gid})
55
+
56
+ def update_description(self, message):
57
+ log_module.logger.info(f"Description Updated: {self.gid}")
58
+ user_table.update_one(
59
+ {"gid":self.gid},
60
+ {"$set": { "description": message}}
61
+ )
62
+ self.description = message
63
+
64
+ def can_use(self, activity):
65
+ return security.can_use(self.role, activity)
66
+
modules/security.py CHANGED
@@ -7,8 +7,6 @@ from . import log_module
7
  from datetime import datetime, timedelta
8
 
9
 
10
-
11
-
12
  users = json.loads(str(os.getenv("USER_KEYS")).replace("\n", ""))
13
  for key in users:
14
  if key == "master": continue
@@ -19,45 +17,34 @@ JWT_SECRET = users["master"]
19
  JWT_ALGORITHM = "HS256"
20
  JWT_EXPIRATION_TIME_MINUTES = 30
21
 
22
- async def authenticate_user(credentials: HTTPBasicCredentials) -> bool:
23
- RedirectResponse(url='/login')
24
- return True
 
 
 
25
 
26
  def create_jwt_token(data):
27
- to_encode = {"data": data}
28
  expire = datetime.utcnow() + timedelta(minutes=JWT_EXPIRATION_TIME_MINUTES)
29
  to_encode.update({"exp": expire})
30
  encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
31
  return encoded_jwt
32
 
33
- async def validate_cookie(request: Request):
34
  try:
35
- token = request.cookies.get('token')
36
  payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
37
- data = {
38
- "token": create_jwt_token(payload["data"]),
39
- "token_data": payload["data"]
40
- }
41
- return (data, False)
42
  except Exception as e:
43
- log_module.logger.error(repr(e) + " - " + str(data))
44
- return ({}, RedirectResponse(url='/login'))
45
-
46
- async def validate_token(request: Request):
47
- data = {}
48
- try:
49
- data = await request.json()
50
- token = data.pop("token")
51
- payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
52
- data["token_data"] = payload["data"]
53
- except Exception as e:
54
- log_module.logger.error(repr(e) + " - " + str(data))
55
  raise HTTPException(status_code=404, detail="Token inválido")
56
- return data
57
-
58
- def user_scale(role, action):
59
- actions = {
60
- "chat": ["user", "admin"],
61
- "read_log": ["admin"]
62
- }
63
- return role in actions[action]
 
 
 
7
  from datetime import datetime, timedelta
8
 
9
 
 
 
10
  users = json.loads(str(os.getenv("USER_KEYS")).replace("\n", ""))
11
  for key in users:
12
  if key == "master": continue
 
17
  JWT_ALGORITHM = "HS256"
18
  JWT_EXPIRATION_TIME_MINUTES = 30
19
 
20
+ credentials_exception = HTTPException(
21
+ status_code=status.HTTP_401_UNAUTHORIZED,
22
+ detail="Could not validate credentials",
23
+ headers={"WWW-Authenticate": "Bearer"},
24
+ )
25
+
26
 
27
  def create_jwt_token(data):
28
+ to_encode = data
29
  expire = datetime.utcnow() + timedelta(minutes=JWT_EXPIRATION_TIME_MINUTES)
30
  to_encode.update({"exp": expire})
31
  encoded_jwt = jwt.encode(to_encode, JWT_SECRET, algorithm=JWT_ALGORITHM)
32
  return encoded_jwt
33
 
34
+ def validate_jwt_token(token):
35
  try:
 
36
  payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
37
+ return payload
 
 
 
 
38
  except Exception as e:
39
+ log_module.logger.error(repr(e) + " - Invalid token: " + str(token))
 
 
 
 
 
 
 
 
 
 
 
40
  raise HTTPException(status_code=404, detail="Token inválido")
41
+
42
+ def token_from_cookie(request:Request):
43
+ if token := request.cookies.get('token', None):
44
+ return validate_jwt_token(token)
45
+ return None
46
+
47
+ def token_from_json(request:Request):
48
+ if token := request.headers.get("Autorization") :
49
+ return validate_jwt_token(token)
50
+ return None
modules/token_counter.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
2
+ """Return the number of tokens used by a list of messages."""
3
+ try:
4
+ encoding = tiktoken.encoding_for_model(model)
5
+ except KeyError:
6
+ print("Warning: model not found. Using cl100k_base encoding.")
7
+ encoding = tiktoken.get_encoding("cl100k_base")
8
+ if model in {
9
+ "gpt-3.5-turbo-0613",
10
+ "gpt-3.5-turbo-16k-0613",
11
+ "gpt-4-0314",
12
+ "gpt-4-32k-0314",
13
+ "gpt-4-0613",
14
+ "gpt-4-32k-0613",
15
+ }:
16
+ tokens_per_message = 3
17
+ tokens_per_name = 1
18
+ elif model == "gpt-3.5-turbo-0301":
19
+ tokens_per_message = 4 # every message follows <|start|>{role/name}\n{content}<|end|>\n
20
+ tokens_per_name = -1 # if there's a name, the role is omitted
21
+ elif "gpt-3.5-turbo" in model:
22
+ print("Warning: gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613.")
23
+ return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613")
24
+ elif "gpt-4" in model:
25
+ print("Warning: gpt-4 may update over time. Returning num tokens assuming gpt-4-0613.")
26
+ return num_tokens_from_messages(messages, model="gpt-4-0613")
27
+ else:
28
+ raise NotImplementedError(
29
+ f"""num_tokens_from_messages() is not implemented for model {model}. See https://github.com/openai/openai-python/blob/main/chatml.md for information on how messages are converted to tokens."""
30
+ )
31
+ num_tokens = 0
32
+ for message in messages:
33
+ num_tokens += tokens_per_message
34
+ for key, value in message.items():
35
+ num_tokens += len(encoding.encode(value))
36
+ if key == "name":
37
+ num_tokens += tokens_per_name
38
+ num_tokens += 3 # every reply is primed with <|start|>assistant<|message|>
39
+ return num_tokens