XThomasBU commited on
Commit
eefbb54
1 Parent(s): 66e15e6

updates: making tutor restricted to students

Browse files
.gitignore CHANGED
@@ -176,4 +176,6 @@ code/storage/models/
176
  **/translations/zh-CN.json
177
 
178
 
179
- **/vectorstores/*
 
 
 
176
  **/translations/zh-CN.json
177
 
178
 
179
+ **/vectorstores/*
180
+
181
+ **/private/students.json
apps/ai_tutor/app.py CHANGED
@@ -16,6 +16,7 @@ from modules.config.constants import (
16
  DOCS_WEBSITE,
17
  ALL_TIME_TOKENS_ALLOCATED,
18
  TOKENS_LEFT,
 
19
  )
20
  from fastapi.middleware.cors import CORSMiddleware
21
  from fastapi.staticfiles import StaticFiles
@@ -26,6 +27,7 @@ from modules.chat_processor.helpers import (
26
  check_user_cooldown,
27
  update_user_info,
28
  )
 
29
 
30
  GOOGLE_CLIENT_ID = OAUTH_GOOGLE_CLIENT_ID
31
  GOOGLE_CLIENT_SECRET = OAUTH_GOOGLE_CLIENT_SECRET
@@ -46,13 +48,8 @@ session_store = {}
46
  CHAINLIT_PATH = "/chainlit_tutor"
47
 
48
  # only admin is given any additional permissions for now -- no limits on tokens
49
- USER_ROLES = {
50
- "tgardos@bu.edu": ["instructor", "bu"],
51
- "xthomas@bu.edu": ["admin", "instructor", "bu"],
52
- "faridkar@bu.edu": ["instructor", "bu"],
53
- "xavierohan1@gmail.com": ["guest"],
54
- # Add more users and roles as needed
55
- }
56
 
57
  # Create a Google OAuth flow
58
  flow = Flow.from_client_config(
@@ -80,7 +77,20 @@ flow = Flow.from_client_config(
80
 
81
 
82
  def get_user_role(username: str):
83
- return USER_ROLES.get(username, ["guest"]) # Default to "guest" role
 
 
 
 
 
 
 
 
 
 
 
 
 
84
 
85
 
86
  async def get_user_info_from_cookie(request: Request):
@@ -146,6 +156,11 @@ async def login_page(request: Request):
146
  # return response
147
 
148
 
 
 
 
 
 
149
  @app.get("/login/google")
150
  async def login_google(request: Request):
151
  # Clear any existing session cookies to avoid conflicts with guest sessions
@@ -176,6 +191,9 @@ async def auth_google(request: Request):
176
  profile_image = user_info.get("picture", "")
177
  role = get_user_role(email)
178
 
 
 
 
179
  session_token = secrets.token_hex(16)
180
  session_store[session_token] = {
181
  "email": email,
 
16
  DOCS_WEBSITE,
17
  ALL_TIME_TOKENS_ALLOCATED,
18
  TOKENS_LEFT,
19
+ EMAIL_ENCRYPTION_KEY,
20
  )
21
  from fastapi.middleware.cors import CORSMiddleware
22
  from fastapi.staticfiles import StaticFiles
 
27
  check_user_cooldown,
28
  update_user_info,
29
  )
30
+ import hashlib
31
 
32
  GOOGLE_CLIENT_ID = OAUTH_GOOGLE_CLIENT_ID
33
  GOOGLE_CLIENT_SECRET = OAUTH_GOOGLE_CLIENT_SECRET
 
48
  CHAINLIT_PATH = "/chainlit_tutor"
49
 
50
  # only admin is given any additional permissions for now -- no limits on tokens
51
+ with open("private/students_encrypted.json", "r") as file:
52
+ USER_ROLES = json.load(file)
 
 
 
 
 
53
 
54
  # Create a Google OAuth flow
55
  flow = Flow.from_client_config(
 
77
 
78
 
79
  def get_user_role(username: str):
80
+
81
+ # Function to deterministically hash emails
82
+ def deterministic_hash(email, salt):
83
+ return hashlib.pbkdf2_hmac("sha256", email.encode(), salt, 100000).hex()
84
+
85
+ # encrypt email (#FIXME: this is not the best way to do this, not really encryption, more like a hash)
86
+ encryption_salt = EMAIL_ENCRYPTION_KEY.encode()
87
+ encrypted_email = deterministic_hash(username, encryption_salt)
88
+ role = USER_ROLES.get(encrypted_email, ["guest"])
89
+
90
+ if "guest" in role:
91
+ return "unauthorized"
92
+
93
+ return role
94
 
95
 
96
  async def get_user_info_from_cookie(request: Request):
 
156
  # return response
157
 
158
 
159
+ @app.get("/unauthorized", response_class=HTMLResponse)
160
+ async def unauthorized(request: Request):
161
+ return templates.TemplateResponse("unauthorized.html", {"request": request})
162
+
163
+
164
  @app.get("/login/google")
165
  async def login_google(request: Request):
166
  # Clear any existing session cookies to avoid conflicts with guest sessions
 
191
  profile_image = user_info.get("picture", "")
192
  role = get_user_role(email)
193
 
194
+ if role == "unauthorized":
195
+ return RedirectResponse("/unauthorized")
196
+
197
  session_token = secrets.token_hex(16)
198
  session_store[session_token] = {
199
  "email": email,
apps/ai_tutor/encrypt_students.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ import hashlib
4
+ import json
5
+
6
+ # Load the .env file
7
+ load_dotenv()
8
+
9
+ # Get the encryption key (salt)
10
+ encryption_salt = os.getenv("EMAIL_ENCRYPTION_KEY").encode()
11
+
12
+
13
+ # Function to deterministically hash emails
14
+ def deterministic_hash(email, salt):
15
+ return hashlib.pbkdf2_hmac("sha256", email.encode(), salt, 100000).hex()
16
+
17
+
18
+ # Load emails from private/students.json
19
+ with open("private/students.json", "r") as file:
20
+ emails = json.load(file)
21
+
22
+ # Replace emails with deterministic hashed emails, {hashed_email: [roles]}
23
+ hashed_emails = {
24
+ deterministic_hash(email, encryption_salt): roles for email, roles in emails.items()
25
+ }
26
+
27
+ # Save hashed emails to private/students_encrypted.json
28
+ with open("private/students_encrypted.json", "w") as file:
29
+ json.dump(hashed_emails, file)
apps/ai_tutor/private/students_encrypted.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"7f1cacca66ee914ddde2ee20e0f2c96651d60cd8aabd310ef25a9e6d88f42df0": ["instructor", "bu"], "f74d264b6b5b2b4c10ce69e4ec16e869e01cb5eb668ed846aa8f6dae5c96cda0": ["admin", "instructor", "bu"], "53401356a874b1539775c73a8564d5e5f4f840441630c9cf649e16d201454f20": ["instructor", "bu"]}
apps/ai_tutor/public/avatars/ai_tutor.png ADDED
apps/ai_tutor/templates/unauthorized.html ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Access Restricted</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
9
+
10
+ body, html {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: 'Inter', sans-serif;
14
+ background-color: #f7f7f7; /* Light gray background */
15
+ background-image: url('https://www.transparenttextures.com/patterns/cubes.png'); /* Subtle geometric pattern */
16
+ background-repeat: repeat;
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ height: 100vh;
21
+ color: #333;
22
+ }
23
+
24
+ .container {
25
+ background: rgba(255, 255, 255, 0.9);
26
+ border: 1px solid #ddd;
27
+ border-radius: 8px;
28
+ width: 100%;
29
+ max-width: 400px;
30
+ padding: 50px;
31
+ box-sizing: border-box;
32
+ text-align: center;
33
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
34
+ backdrop-filter: blur(10px);
35
+ -webkit-backdrop-filter: blur(10px);
36
+ }
37
+
38
+ .avatar {
39
+ width: 90px;
40
+ height: 90px;
41
+ border-radius: 50%;
42
+ margin-bottom: 25px;
43
+ border: 2px solid #ddd;
44
+ }
45
+
46
+ .container h1 {
47
+ margin-bottom: 20px;
48
+ font-size: 26px;
49
+ font-weight: 600;
50
+ color: #1a1a1a;
51
+ }
52
+
53
+ .container p {
54
+ font-size: 18px;
55
+ color: #4a4a4a;
56
+ margin-bottom: 35px;
57
+ line-height: 1.5;
58
+ }
59
+
60
+ .button {
61
+ padding: 14px 0;
62
+ margin: 12px 0;
63
+ font-size: 16px;
64
+ border-radius: 6px;
65
+ cursor: pointer;
66
+ width: 100%;
67
+ border: 1px solid #ccc;
68
+ background-color: #007BFF;
69
+ color: #fff;
70
+ transition: background-color 0.3s ease, border-color 0.3s ease;
71
+ }
72
+
73
+ .button:hover {
74
+ background-color: #0056b3;
75
+ border-color: #0056b3;
76
+ }
77
+ </style>
78
+ </head>
79
+ <body>
80
+ <div class="container">
81
+ <img src="/public/avatars/ai-tutor.png" alt="AI Tutor Avatar" class="avatar">
82
+ <h1>Access Restricted</h1>
83
+ <p>
84
+ We're currently testing things out for the <strong>DS701</strong> course.
85
+ Access is restricted to students of the course. If you're enrolled in <strong>DS701</strong> and seeing this message,
86
+ please reach out to us, and we'll help you get access.<br><br>
87
+ <em>P.S. Don't forget to use your BU email when logging in!</em>
88
+ </p>
89
+ <form action="/" method="get">
90
+ <button type="submit" class="button">Return to Home</button>
91
+ </form>
92
+ </div>
93
+ </body>
94
+ </html>
modules/config/constants.py CHANGED
@@ -20,6 +20,7 @@ HUGGINGFACE_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
20
  LITERAL_API_KEY_LOGGING = os.getenv("LITERAL_API_KEY_LOGGING")
21
  LITERAL_API_URL = os.getenv("LITERAL_API_URL")
22
  CHAINLIT_URL = os.getenv("CHAINLIT_URL")
 
23
 
24
  OAUTH_GOOGLE_CLIENT_ID = os.getenv("OAUTH_GOOGLE_CLIENT_ID")
25
  OAUTH_GOOGLE_CLIENT_SECRET = os.getenv("OAUTH_GOOGLE_CLIENT_SECRET")
 
20
  LITERAL_API_KEY_LOGGING = os.getenv("LITERAL_API_KEY_LOGGING")
21
  LITERAL_API_URL = os.getenv("LITERAL_API_URL")
22
  CHAINLIT_URL = os.getenv("CHAINLIT_URL")
23
+ EMAIL_ENCRYPTION_KEY = os.getenv("EMAIL_ENCRYPTION_KEY")
24
 
25
  OAUTH_GOOGLE_CLIENT_ID = os.getenv("OAUTH_GOOGLE_CLIENT_ID")
26
  OAUTH_GOOGLE_CLIENT_SECRET = os.getenv("OAUTH_GOOGLE_CLIENT_SECRET")
modules/dataloader/helpers.py CHANGED
@@ -2,7 +2,9 @@ import requests
2
  from bs4 import BeautifulSoup
3
  from urllib.parse import urlparse
4
  import tempfile
5
- from modules.config.constants import TIMEOUT # TODO: MOVE THIS TO APP SPECIFIC DIRECTORY
 
 
6
 
7
 
8
  def get_urls_from_file(file_path: str):
 
2
  from bs4 import BeautifulSoup
3
  from urllib.parse import urlparse
4
  import tempfile
5
+ from modules.config.constants import (
6
+ TIMEOUT,
7
+ ) # TODO: MOVE THIS TO APP SPECIFIC DIRECTORY
8
 
9
 
10
  def get_urls_from_file(file_path: str):
modules/vectorstore/store_manager.py CHANGED
@@ -172,7 +172,10 @@ if __name__ == "__main__":
172
  "--config_file", type=str, help="Path to the main config file", required=True
173
  )
174
  parser.add_argument(
175
- "--project_config_file", type=str, help="Path to the project config file", required=True
 
 
 
176
  )
177
  args = parser.parse_args()
178
 
 
172
  "--config_file", type=str, help="Path to the main config file", required=True
173
  )
174
  parser.add_argument(
175
+ "--project_config_file",
176
+ type=str,
177
+ help="Path to the project config file",
178
+ required=True,
179
  )
180
  args = parser.parse_args()
181
 
requirements.txt CHANGED
@@ -34,3 +34,4 @@ fastapi
34
  google-auth
35
  google-auth-oauthlib
36
  Jinja2
 
 
34
  google-auth
35
  google-auth-oauthlib
36
  Jinja2
37
+ cryptography
setup.py CHANGED
@@ -6,4 +6,4 @@ setup(
6
  packages=find_packages(),
7
  python_requires=">=3.7",
8
  description="A Deep Learning for Data Science Tutor application",
9
- )
 
6
  packages=find_packages(),
7
  python_requires=">=3.7",
8
  description="A Deep Learning for Data Science Tutor application",
9
+ )