mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-27 20:38:32 +02:00
DAN-58 Email validation (#39)
This commit is contained in:
@@ -1,10 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
DISABLE_AUTH = os.environ.get("DISABLE_AUTH", "").lower() == "true"
|
|
||||||
|
|
||||||
SECRET = os.environ.get("SECRET", "")
|
|
||||||
SESSION_EXPIRE_TIME_SECONDS = int(os.environ.get("SESSION_EXPIRE_TIME_SECONDS", 3600))
|
|
||||||
|
|
||||||
ENABLE_OAUTH = os.environ.get("ENABLE_OAUTH", "False").lower() != "false"
|
|
||||||
GOOGLE_OAUTH_CLIENT_ID = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "")
|
|
||||||
GOOGLE_OAUTH_CLIENT_SECRET = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "")
|
|
@@ -1,19 +1,30 @@
|
|||||||
|
import smtplib
|
||||||
import uuid
|
import uuid
|
||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from danswer.auth.configs import DISABLE_AUTH
|
|
||||||
from danswer.auth.configs import GOOGLE_OAUTH_CLIENT_ID
|
|
||||||
from danswer.auth.configs import GOOGLE_OAUTH_CLIENT_SECRET
|
|
||||||
from danswer.auth.configs import SECRET
|
|
||||||
from danswer.auth.configs import SESSION_EXPIRE_TIME_SECONDS
|
|
||||||
from danswer.auth.schemas import UserCreate
|
from danswer.auth.schemas import UserCreate
|
||||||
from danswer.auth.schemas import UserRole
|
from danswer.auth.schemas import UserRole
|
||||||
|
from danswer.configs.app_configs import DISABLE_AUTH
|
||||||
|
from danswer.configs.app_configs import GOOGLE_OAUTH_CLIENT_ID
|
||||||
|
from danswer.configs.app_configs import GOOGLE_OAUTH_CLIENT_SECRET
|
||||||
|
from danswer.configs.app_configs import REQUIRE_EMAIL_VERIFICATION
|
||||||
|
from danswer.configs.app_configs import SECRET
|
||||||
|
from danswer.configs.app_configs import SESSION_EXPIRE_TIME_SECONDS
|
||||||
|
from danswer.configs.app_configs import SMTP_PASS
|
||||||
|
from danswer.configs.app_configs import SMTP_PORT
|
||||||
|
from danswer.configs.app_configs import SMTP_SERVER
|
||||||
|
from danswer.configs.app_configs import SMTP_USER
|
||||||
|
from danswer.configs.app_configs import VALID_EMAIL_DOMAIN
|
||||||
|
from danswer.configs.app_configs import WEB_DOMAIN
|
||||||
from danswer.db.auth import get_access_token_db
|
from danswer.db.auth import get_access_token_db
|
||||||
from danswer.db.auth import get_user_count
|
from danswer.db.auth import get_user_count
|
||||||
from danswer.db.auth import get_user_db
|
from danswer.db.auth import get_user_db
|
||||||
from danswer.db.models import AccessToken
|
from danswer.db.models import AccessToken
|
||||||
from danswer.db.models import User
|
from danswer.db.models import User
|
||||||
|
from danswer.utils.logging import setup_logger
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
@@ -30,6 +41,27 @@ from fastapi_users.authentication.strategy.db import DatabaseStrategy
|
|||||||
from fastapi_users.db import SQLAlchemyUserDatabase
|
from fastapi_users.db import SQLAlchemyUserDatabase
|
||||||
from httpx_oauth.clients.google import GoogleOAuth2
|
from httpx_oauth.clients.google import GoogleOAuth2
|
||||||
|
|
||||||
|
logger = setup_logger()
|
||||||
|
|
||||||
|
|
||||||
|
def send_user_verification_email(user_email: str, token: str) -> None:
|
||||||
|
msg = MIMEMultipart()
|
||||||
|
msg["Subject"] = "Danswer Email Verification"
|
||||||
|
msg["From"] = "no-reply@danswer.dev"
|
||||||
|
msg["To"] = user_email
|
||||||
|
|
||||||
|
link = f"{WEB_DOMAIN}/verify-email?token={token}"
|
||||||
|
|
||||||
|
body = MIMEText(f"Click the following link to verify your email address: {link}")
|
||||||
|
msg.attach(body)
|
||||||
|
|
||||||
|
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as s:
|
||||||
|
s.starttls()
|
||||||
|
# If credentials fails with gmail, check (You need an app password, not just the basic email password)
|
||||||
|
# https://support.google.com/accounts/answer/185833?sjid=8512343437447396151-NA
|
||||||
|
s.login(SMTP_USER, SMTP_PASS)
|
||||||
|
s.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
|
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
|
||||||
reset_password_token_secret = SECRET
|
reset_password_token_secret = SECRET
|
||||||
@@ -52,17 +84,34 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
|
|||||||
async def on_after_register(
|
async def on_after_register(
|
||||||
self, user: User, request: Optional[Request] = None
|
self, user: User, request: Optional[Request] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
print(f"User {user.id} has registered.")
|
logger.info(f"User {user.id} has registered.")
|
||||||
|
|
||||||
async def on_after_forgot_password(
|
async def on_after_forgot_password(
|
||||||
self, user: User, token: str, request: Optional[Request] = None
|
self, user: User, token: str, request: Optional[Request] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
print(f"User {user.id} has forgot their password. Reset token: {token}")
|
logger.info(f"User {user.id} has forgot their password. Reset token: {token}")
|
||||||
|
|
||||||
async def on_after_request_verify(
|
async def on_after_request_verify(
|
||||||
self, user: User, token: str, request: Optional[Request] = None
|
self, user: User, token: str, request: Optional[Request] = None
|
||||||
) -> None:
|
) -> None:
|
||||||
print(f"Verification requested for user {user.id}. Verification token: {token}")
|
if VALID_EMAIL_DOMAIN:
|
||||||
|
if user.email.count("@") != 1:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Email is not valid",
|
||||||
|
)
|
||||||
|
domain = user.email.split("@")[-1]
|
||||||
|
if domain != VALID_EMAIL_DOMAIN:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="Email domain is not valid",
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Verification requested for user {user.id}. Verification token: {token}"
|
||||||
|
)
|
||||||
|
|
||||||
|
send_user_verification_email(user.email, token)
|
||||||
|
|
||||||
|
|
||||||
async def get_user_manager(
|
async def get_user_manager(
|
||||||
@@ -92,7 +141,9 @@ google_oauth_client = GoogleOAuth2(GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_S
|
|||||||
|
|
||||||
fastapi_users = FastAPIUsers[User, uuid.UUID](get_user_manager, [auth_backend])
|
fastapi_users = FastAPIUsers[User, uuid.UUID](get_user_manager, [auth_backend])
|
||||||
|
|
||||||
current_active_user = fastapi_users.current_user(active=True, optional=DISABLE_AUTH)
|
current_active_user = fastapi_users.current_user(
|
||||||
|
active=True, verified=REQUIRE_EMAIL_VERIFICATION, optional=DISABLE_AUTH
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def current_admin_user(user: User = Depends(current_active_user)) -> User | None:
|
def current_admin_user(user: User = Depends(current_active_user)) -> User | None:
|
||||||
|
@@ -23,6 +23,27 @@ BLURB_LENGTH = 200 # Characters. Blurbs will be truncated at the first punctuat
|
|||||||
WEB_DOMAIN = os.environ.get("WEB_DOMAIN", "http://localhost:3000")
|
WEB_DOMAIN = os.environ.get("WEB_DOMAIN", "http://localhost:3000")
|
||||||
|
|
||||||
|
|
||||||
|
#####
|
||||||
|
# Auth Configs
|
||||||
|
#####
|
||||||
|
DISABLE_AUTH = os.environ.get("DISABLE_AUTH", "").lower() == "true"
|
||||||
|
REQUIRE_EMAIL_VERIFICATION = (
|
||||||
|
os.environ.get("REQUIRE_EMAIL_VERIFICATION", "").lower() == "true"
|
||||||
|
)
|
||||||
|
SMTP_SERVER = os.environ.get("SMTP_SERVER", "smtp.gmail.com")
|
||||||
|
SMTP_PORT = int(os.environ.get("SMTP_PORT", "587"))
|
||||||
|
SMTP_USER = os.environ.get("SMTP_USER", "your-email@gmail.com")
|
||||||
|
SMTP_PASS = os.environ.get("SMTP_PASS", "your-gmail-password")
|
||||||
|
|
||||||
|
SECRET = os.environ.get("SECRET", "")
|
||||||
|
SESSION_EXPIRE_TIME_SECONDS = int(os.environ.get("SESSION_EXPIRE_TIME_SECONDS", 3600))
|
||||||
|
VALID_EMAIL_DOMAIN = os.environ.get("VALID_EMAIL_DOMAIN", "")
|
||||||
|
# OAuth Login Flow
|
||||||
|
ENABLE_OAUTH = os.environ.get("ENABLE_OAUTH", "").lower() != "false"
|
||||||
|
GOOGLE_OAUTH_CLIENT_ID = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "")
|
||||||
|
GOOGLE_OAUTH_CLIENT_SECRET = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "")
|
||||||
|
|
||||||
|
|
||||||
#####
|
#####
|
||||||
# Vector DB Configs
|
# Vector DB Configs
|
||||||
#####
|
#####
|
||||||
@@ -47,6 +68,7 @@ GOOGLE_DRIVE_INCLUDE_SHARED = False
|
|||||||
|
|
||||||
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", "")
|
GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN", "")
|
||||||
|
|
||||||
|
|
||||||
#####
|
#####
|
||||||
# Query Configs
|
# Query Configs
|
||||||
#####
|
#####
|
||||||
|
@@ -1,6 +1,4 @@
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from danswer.auth.configs import ENABLE_OAUTH
|
|
||||||
from danswer.auth.configs import SECRET
|
|
||||||
from danswer.auth.schemas import UserCreate
|
from danswer.auth.schemas import UserCreate
|
||||||
from danswer.auth.schemas import UserRead
|
from danswer.auth.schemas import UserRead
|
||||||
from danswer.auth.schemas import UserUpdate
|
from danswer.auth.schemas import UserUpdate
|
||||||
@@ -9,6 +7,8 @@ from danswer.auth.users import fastapi_users
|
|||||||
from danswer.auth.users import google_oauth_client
|
from danswer.auth.users import google_oauth_client
|
||||||
from danswer.configs.app_configs import APP_HOST
|
from danswer.configs.app_configs import APP_HOST
|
||||||
from danswer.configs.app_configs import APP_PORT
|
from danswer.configs.app_configs import APP_PORT
|
||||||
|
from danswer.configs.app_configs import ENABLE_OAUTH
|
||||||
|
from danswer.configs.app_configs import SECRET
|
||||||
from danswer.configs.app_configs import WEB_DOMAIN
|
from danswer.configs.app_configs import WEB_DOMAIN
|
||||||
from danswer.server.admin import router as admin_router
|
from danswer.server.admin import router as admin_router
|
||||||
from danswer.server.event_loading import router as event_processing_router
|
from danswer.server.event_loading import router as event_processing_router
|
||||||
|
@@ -116,7 +116,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--slack-export-dir",
|
"--slack-export-dir",
|
||||||
default="/Users/chrisweaver/Downloads/test-slack-export",
|
default="~/Downloads/test-slack-export",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--qdrant-collection",
|
"--qdrant-collection",
|
||||||
@@ -133,6 +133,6 @@ if __name__ == "__main__":
|
|||||||
recreate_collection(args.qdrant_collection)
|
recreate_collection(args.qdrant_collection)
|
||||||
|
|
||||||
# load_slack_batch(args.slack_export_dir, args.qdrant_collection)
|
# load_slack_batch(args.slack_export_dir, args.qdrant_collection)
|
||||||
# load_web_batch(args.website_url, args.qdrant_collection)
|
load_web_batch(args.website_url, args.qdrant_collection)
|
||||||
# load_google_drive_batch(args.qdrant_collection)
|
# load_google_drive_batch(args.qdrant_collection)
|
||||||
load_github_batch(args.github_owner, args.github_repo, args.qdrant_collection)
|
# load_github_batch(args.github_owner, args.github_repo, args.qdrant_collection)
|
||||||
|
Reference in New Issue
Block a user