MGLDZM commited on
Commit
002d869
1 Parent(s): 656f5ab
main.py CHANGED
@@ -122,7 +122,7 @@ async def get_token(response: Response, session: Session_find_from_data):
122
  ##########################################################
123
 
124
  @app.post("/chat")
125
- async def chat_async(response:Response, session: Session_find_from_data):
126
  body = model.Chat(messages = session.data["messages"])
127
  if(len(body.messages) < 1 or body.messages[-1].content==""):
128
  log_module.logger.warning(session.gid, "Mensaje RQ", "error, mensajes vacios")
@@ -132,7 +132,7 @@ async def chat_async(response:Response, session: Session_find_from_data):
132
  )
133
 
134
  response_async = llm.ejecutar(body, session)
135
- session.create_cookie(response)
136
  return StreamingResponse(llm.streamer(response_async, body, session), media_type="application/json")
137
 
138
 
 
122
  ##########################################################
123
 
124
  @app.post("/chat")
125
+ async def chat_async(session: Session_find_from_data):
126
  body = model.Chat(messages = session.data["messages"])
127
  if(len(body.messages) < 1 or body.messages[-1].content==""):
128
  log_module.logger.warning(session.gid, "Mensaje RQ", "error, mensajes vacios")
 
132
  )
133
 
134
  response_async = llm.ejecutar(body, session)
135
+
136
  return StreamingResponse(llm.streamer(response_async, body, session), media_type="application/json")
137
 
138
 
modules/llm.py CHANGED
@@ -95,4 +95,5 @@ async def streamer(response_async, body, session):
95
  session.update_usage(message)
96
 
97
  yield json.dumps({"comando":"challenge", "challenge": session.challenge} )
 
98
  yield json.dumps({"comando":"mensaje", "mensaje": message.model_dump()} )
 
95
  session.update_usage(message)
96
 
97
  yield json.dumps({"comando":"challenge", "challenge": session.challenge} )
98
+ yield json.dumps({"comando":"token", "token": session.create_cookie_token() } )
99
  yield json.dumps({"comando":"mensaje", "mensaje": message.model_dump()} )
modules/model.py CHANGED
@@ -78,7 +78,7 @@ class Session(BaseModel):
78
  def validate_signature(self: Self, signature:str):
79
  valid = security.validate_signature(self.public_key, signature, self.challenge)
80
  if not valid:
81
- security.raise_401()
82
  return True
83
 
84
  @classmethod
@@ -87,17 +87,17 @@ class Session(BaseModel):
87
 
88
  if "gid" not in cookie_data or "guid" not in cookie_data:
89
  log_module.logger.error("Cookie without session needed data")
90
- security.raise_401()
91
 
92
  if not (public_key := cookie_data.get("public_key", None)): # FIX Vuln Code
93
  if request.scope["path"] != "/getToken":
94
  log_module.logger.error(f"User without public key saved | {json.dumps(cookie_data)}")
95
- security.raise_401()
96
  else:
97
  log_module.logger.info(f"API public key set for user {cookie_data['gid']}")
98
  public_key = data["public_key"]
99
  else:
100
- security.check_challenge(data["fingerprint"], cookie_data["challenge"])
101
 
102
  session: Self = cls(
103
  gid = cookie_data["gid"],
@@ -107,18 +107,18 @@ class Session(BaseModel):
107
  data = data,
108
  configs = Configs(**cookie_data["configs"])
109
  )
 
110
 
111
  if session.hashed != cookie_data["fprint"]:
112
  log_module.logger.error(f"Fingerprint didnt match | {json.dumps(cookie_data)}")
113
- security.raise_401()
114
 
115
 
116
- security.add_challenge(session.fprint, session.challenge)
117
 
118
  return session
119
-
120
- def create_cookie(self:Self, response: Response):
121
- jwt = security.create_jwt_token({
122
  "gid":self.gid,
123
  "guid": self.guid,
124
  "fprint": self.hashed,
@@ -126,10 +126,26 @@ class Session(BaseModel):
126
  "challenge": self.challenge,
127
  "configs": self.configs.model_dump()
128
  })
 
 
 
129
  security.set_cookie(response, "token", jwt, {"hours": 24})
130
 
131
  def update_usage(self: Self, message:Message):
132
  User.update_usage(self.gid, message)
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  class User(BaseModel):
135
  name: str
@@ -166,13 +182,13 @@ class User(BaseModel):
166
 
167
  if "gid" not in cookie_data or "guid" not in cookie_data:
168
  log_module.logger.error("Cookie without needed data")
169
- security.raise_307()
170
 
171
  found:dict = DB.user.find_one({"gid":cookie_data["gid"]})
172
 
173
  if not found:
174
  log_module.logger.error("User not found on DB")
175
- security.raise_307()
176
 
177
  user: Self = cls(**found)
178
  user._session = Session(
@@ -193,7 +209,7 @@ class User(BaseModel):
193
 
194
  if not found:
195
  log_module.logger.error("User not found on DB")
196
- security.raise_307()
197
 
198
  user: Self = cls(**found)
199
  user._session = session
 
78
  def validate_signature(self: Self, signature:str):
79
  valid = security.validate_signature(self.public_key, signature, self.challenge)
80
  if not valid:
81
+ security.raise_401("Cannot validate Session signature")
82
  return True
83
 
84
  @classmethod
 
87
 
88
  if "gid" not in cookie_data or "guid" not in cookie_data:
89
  log_module.logger.error("Cookie without session needed data")
90
+ security.raise_401("gid or guid not in cookie")
91
 
92
  if not (public_key := cookie_data.get("public_key", None)): # FIX Vuln Code
93
  if request.scope["path"] != "/getToken":
94
  log_module.logger.error(f"User without public key saved | {json.dumps(cookie_data)}")
95
+ security.raise_401("the user must have a public key saved in token")
96
  else:
97
  log_module.logger.info(f"API public key set for user {cookie_data['gid']}")
98
  public_key = data["public_key"]
99
  else:
100
+ cls.check_challenge(data["fingerprint"], cookie_data["challenge"])
101
 
102
  session: Self = cls(
103
  gid = cookie_data["gid"],
 
107
  data = data,
108
  configs = Configs(**cookie_data["configs"])
109
  )
110
+
111
 
112
  if session.hashed != cookie_data["fprint"]:
113
  log_module.logger.error(f"Fingerprint didnt match | {json.dumps(cookie_data)}")
114
+ security.raise_401("Fingerprint didnt match")
115
 
116
 
117
+ session.add_challenge()
118
 
119
  return session
120
+ def create_cookie_token(self:Self):
121
+ return security.create_jwt_token({
 
122
  "gid":self.gid,
123
  "guid": self.guid,
124
  "fprint": self.hashed,
 
126
  "challenge": self.challenge,
127
  "configs": self.configs.model_dump()
128
  })
129
+
130
+ def create_cookie(self:Self, response: Response):
131
+ jwt = self.create_cookie_token()
132
  security.set_cookie(response, "token", jwt, {"hours": 24})
133
 
134
  def update_usage(self: Self, message:Message):
135
  User.update_usage(self.gid, message)
136
+
137
+ def add_challenge(self: Self):
138
+ return True
139
+ self.challenge = str(uuid.uuid4())
140
+ DB.sess.insert_one(self.model_dump(include={"fprint", "challenge"}) )
141
+
142
+ @staticmethod
143
+ def check_challenge(fprint:str, challenge: str):
144
+ return True
145
+ found = DB.sess.find_one_and_delete({"fprint":fprint})
146
+ if not found or found["challenge"] != challenge:
147
+ security.raise_401("Check challenge failed")
148
+
149
 
150
  class User(BaseModel):
151
  name: str
 
182
 
183
  if "gid" not in cookie_data or "guid" not in cookie_data:
184
  log_module.logger.error("Cookie without needed data")
185
+ security.raise_307("gid or guid not in cookie")
186
 
187
  found:dict = DB.user.find_one({"gid":cookie_data["gid"]})
188
 
189
  if not found:
190
  log_module.logger.error("User not found on DB")
191
+ security.raise_307("User not found on DB")
192
 
193
  user: Self = cls(**found)
194
  user._session = Session(
 
209
 
210
  if not found:
211
  log_module.logger.error("User not found on DB")
212
+ security.raise_307("User not found on DB")
213
 
214
  user: Self = cls(**found)
215
  user._session = session
modules/security.py CHANGED
@@ -9,8 +9,6 @@ from Crypto.PublicKey import RSA
9
 
10
  import base64
11
 
12
- challenges = {}
13
-
14
 
15
  for key in settings.USERS:
16
  if key == "master": continue
@@ -18,14 +16,6 @@ for key in settings.USERS:
18
  settings.USERS[key] = sha256(password.encode('UTF-8')).hexdigest()
19
 
20
 
21
- def add_challenge(fprint:str, challenge:str):
22
- challenges[fprint] = challenge
23
-
24
- def check_challenge(fprint:str, challenge:str):
25
- if challenges.pop(fprint, None) == challenge:
26
- return True
27
- raise_401()
28
-
29
  def create_jwt_token(data, maxlife=settings.JWT_EXPIRATION_TIME_MINUTES_API):
30
  expire = datetime.utcnow() + timedelta(minutes=maxlife)
31
  to_encode = data | {"exp": expire}
@@ -39,18 +29,18 @@ def validate_jwt_token(token: str, usage: str = "api"):
39
  except Exception as e:
40
  log_module.logger.error(repr(e) + " - Invalid token: " + str(token))
41
  if usage == "view":
42
- return raise_307()
43
- return raise_401()
44
 
45
  def token_from_cookie(request:Request):
46
  if token := request.cookies.get('token', ""):
47
  return validate_jwt_token(token, "view")
48
- return raise_307()
49
 
50
  def token_from_headers(request:Request):
51
  if (bearer := request.headers.get("Autorization", " ").split(" ",1)) and bearer[0] == "Bearer":
52
  return validate_jwt_token(bearer[1])
53
- return raise_401()
54
 
55
 
56
  def can_use(role, activity):
@@ -59,21 +49,21 @@ def can_use(role, activity):
59
  }
60
  return role in can[activity]
61
 
62
- def raise_401():
63
  headers = {"set-cookie": "token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
64
  "Location": "/login"}
65
  raise HTTPException(
66
  status_code=status.HTTP_401_UNAUTHORIZED,
67
- detail="Could not validate credentials",
68
  headers=headers
69
  )
70
 
71
- def raise_307():
72
  headers = {"set-cookie": "token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
73
  "Location": "/login"}
74
  raise HTTPException(
75
  status_code=status.HTTP_307_TEMPORARY_REDIRECT,
76
- detail="Could not validate access",
77
  headers=headers
78
  )
79
 
@@ -85,7 +75,7 @@ def validate_signature(public_key: str, signature: str, data:str) -> bool:
85
  pss.new(public_key).verify(data_, signature)
86
  return True
87
  except ValueError:
88
- raise_401()
89
 
90
  def sha256(data:str|bytes) -> str:
91
  data_ = data if isinstance(data, bytes) else data.encode()
 
9
 
10
  import base64
11
 
 
 
12
 
13
  for key in settings.USERS:
14
  if key == "master": continue
 
16
  settings.USERS[key] = sha256(password.encode('UTF-8')).hexdigest()
17
 
18
 
 
 
 
 
 
 
 
 
19
  def create_jwt_token(data, maxlife=settings.JWT_EXPIRATION_TIME_MINUTES_API):
20
  expire = datetime.utcnow() + timedelta(minutes=maxlife)
21
  to_encode = data | {"exp": expire}
 
29
  except Exception as e:
30
  log_module.logger.error(repr(e) + " - Invalid token: " + str(token))
31
  if usage == "view":
32
+ return raise_307("Failed validating jwt token")
33
+ return raise_401("Failed validating jwt token")
34
 
35
  def token_from_cookie(request:Request):
36
  if token := request.cookies.get('token', ""):
37
  return validate_jwt_token(token, "view")
38
+ return raise_307("Token not found")
39
 
40
  def token_from_headers(request:Request):
41
  if (bearer := request.headers.get("Autorization", " ").split(" ",1)) and bearer[0] == "Bearer":
42
  return validate_jwt_token(bearer[1])
43
+ return raise_401("Bearer malformed")
44
 
45
 
46
  def can_use(role, activity):
 
49
  }
50
  return role in can[activity]
51
 
52
+ def raise_401(detail:str):
53
  headers = {"set-cookie": "token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
54
  "Location": "/login"}
55
  raise HTTPException(
56
  status_code=status.HTTP_401_UNAUTHORIZED,
57
+ detail=detail,
58
  headers=headers
59
  )
60
 
61
+ def raise_307(detail:str):
62
  headers = {"set-cookie": "token=deleted; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT",
63
  "Location": "/login"}
64
  raise HTTPException(
65
  status_code=status.HTTP_307_TEMPORARY_REDIRECT,
66
+ detail=detail,
67
  headers=headers
68
  )
69
 
 
75
  pss.new(public_key).verify(data_, signature)
76
  return True
77
  except ValueError:
78
+ raise_401("Signature failed")
79
 
80
  def sha256(data:str|bytes) -> str:
81
  data_ = data if isinstance(data, bytes) else data.encode()
modules/settings.py CHANGED
@@ -16,7 +16,7 @@ OPENAI_API_KEY=os.environ.get('OPENAI_API_KEY')
16
 
17
  USERS = json.loads(str(os.getenv("USER_KEYS")).replace("\n", ""))
18
 
19
- JWT_SECRET = os.environ.get('OPENAI_API_KEY')
20
 
21
  JWT_ALGORITHM = "HS256"
22
  JWT_EXPIRATION_TIME_MINUTES_API = 7*24*60
 
16
 
17
  USERS = json.loads(str(os.getenv("USER_KEYS")).replace("\n", ""))
18
 
19
+ JWT_SECRET = os.environ.get('JWT_SECRET')
20
 
21
  JWT_ALGORITHM = "HS256"
22
  JWT_EXPIRATION_TIME_MINUTES_API = 7*24*60
requirements.txt CHANGED
@@ -12,6 +12,6 @@ tiktoken>=0.5.*
12
  pycryptodome==3.19.*
13
  pydantic==2.5.*
14
  PyJWT==2.8.*
15
- pymongo==4.6.*
16
  python-multipart>=0.0.6
17
  tiktoken==0.5.*
 
12
  pycryptodome==3.19.*
13
  pydantic==2.5.*
14
  PyJWT==2.8.*
15
+ pymongo[srv]==4.6.*
16
  python-multipart>=0.0.6
17
  tiktoken==0.5.*
static/js/chatHandler.js CHANGED
@@ -248,6 +248,10 @@ class ChatGPT{
248
  case "token":
249
  self.token = response.token;
250
  break;
 
 
 
 
251
  // Status del comportamiento
252
  case "status":
253
  ctx.respuestaStatus(response.status.mensaje, response.status.modo)
@@ -263,7 +267,7 @@ class ChatGPT{
263
  // break;
264
  // Algo fall贸
265
  default:
266
- console.log("???")
267
  }
268
  }
269
 
@@ -296,10 +300,7 @@ class ChatGPT{
296
  return
297
  }
298
  return consume(response.body.getReader());
299
- }).then(data => {
300
- console.log("esto", data)
301
- })
302
- .catch(err =>{
303
  // Error
304
  console.log('Solicitud fallida', err)
305
  ctx.respuestaError(response)
 
248
  case "token":
249
  self.token = response.token;
250
  break;
251
+ // Se carga el nuevo challenge
252
+ case "challenge":
253
+ this.secHand.sign(response.challenge).then((result) => {self.challenge = result})
254
+ break;
255
  // Status del comportamiento
256
  case "status":
257
  ctx.respuestaStatus(response.status.mensaje, response.status.modo)
 
267
  // break;
268
  // Algo fall贸
269
  default:
270
+ console.log("???", response)
271
  }
272
  }
273
 
 
300
  return
301
  }
302
  return consume(response.body.getReader());
303
+ }).catch(err =>{
 
 
 
304
  // Error
305
  console.log('Solicitud fallida', err)
306
  ctx.respuestaError(response)