feat(api): testing oauth

This commit is contained in:
Alexander 2024-06-11 16:12:50 +02:00
parent a788fadb1c
commit a78c97bb09
2 changed files with 137 additions and 1 deletions

View file

@ -98,3 +98,72 @@ async def create_tenant(data: schemas.UserSignupSchema):
"user": r
}
}
async def create_oauth_tenant(fullname: str, email: str):
logger.info(f"==== Signup oauth started at {TimeUTC.to_human_readable(TimeUTC.now())} UTC")
errors = []
if not config("MULTI_TENANTS", cast=bool, default=False) and await tenants.tenants_exists():
return {"errors": ["tenants already registered"]}
logger.debug(f"email: {email}")
if email is None or len(email) < 5:
errors.append("Invalid email address.")
else:
if users.email_exists(email):
errors.append("Email address already in use.")
if users.get_deleted_user_by_email(email) is not None:
errors.append("Email address previously deleted.")
if fullname is None or len(fullname) < 1 or not helper.is_alphabet_space_dash(fullname):
errors.append("Invalid full name.")
if len(errors) > 0:
logger.warning(
f"==> signup error for:\n email:{email}, fullname:{fullname}")
logger.warning(errors)
return {"errors": errors}
project_name = "my first project"
params = {
"email": email, "fullname": fullname, "projectName": project_name,
"data": json.dumps({"lastAnnouncementView": TimeUTC.now()}),
"permissions": [p.value for p in schemas.Permissions]
}
query = """WITH t AS (
INSERT INTO public.tenants (name, plan)
VALUES (%(organizationName)s, %(plan)s::jsonb)
RETURNING tenant_id, api_key
),
r AS (
INSERT INTO public.roles(tenant_id, name, description, permissions, protected)
VALUES ((SELECT tenant_id FROM t), 'Owner', 'Owner', %(permissions)s::text[], TRUE),
((SELECT tenant_id FROM t), 'Member', 'Member', %(permissions)s::text[], FALSE)
RETURNING *
),
u AS (
INSERT INTO public.users (tenant_id, email, role, name, verified_email, data, role_id)
VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s, TRUE,%(data)s, (SELECT role_id FROM r WHERE name ='Owner'))
RETURNING user_id,email,role,name,role_id
)
INSERT INTO public.projects (tenant_id, name, active)
VALUES ((SELECT t.tenant_id FROM t), %(projectName)s, TRUE)
RETURNING tenant_id,project_id, (SELECT api_key FROM t) AS api_key;"""
with pg_client.PostgresClient() as cur:
cur.execute(cur.mogrify(query, params))
t = cur.fetchone()
telemetry.new_client(tenant_id=t["tenant_id"])
r = users.authenticate(email, "password")
r["smtp"] = smtp.has_smtp()
return {
'jwt': r.pop('jwt'),
'refreshToken': r.pop('refreshToken'),
'refreshTokenMaxAge': r.pop('refreshTokenMaxAge'),
'data': {
"user": r
}
}

View file

@ -2,9 +2,11 @@ import logging
from typing import Optional, Union
from decouple import config
from fastapi import Body, Depends, BackgroundTasks, Request
from fastapi import Body, Depends, BackgroundTasks, Request, RedirectResponse
from fastapi import HTTPException, status
from starlette.responses import RedirectResponse, FileResponse, JSONResponse, Response
import httpx
import os
import schemas
from chalicelib.core import scope
@ -55,6 +57,71 @@ if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exis
return content
# Environment variables (ensure these are set)
GOOGLE_CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET")
GOOGLE_REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI")
if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exists_sync(use_pool=False):
@public_app.get('/signup-oauth', tags=['signup'])
async def signup_oauth_handler():
google_authorization_url = (
"https://accounts.google.com/o/oauth2/auth"
f"?client_id={GOOGLE_CLIENT_ID}"
f"&response_type=code"
f"&redirect_uri={GOOGLE_REDIRECT_URI}"
f"&scope=email%20profile"
)
return RedirectResponse(url=google_authorization_url)
if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exists_sync(use_pool=False):
@public_app.get('/signup-oauth-callback', tags=['signup'])
async def signup_oauth_callback_handler(code: str):
# Exchange code for token
token_response = httpx.post(
url="https://oauth2.googleapis.com/token",
data={
"client_id": GOOGLE_CLIENT_ID,
"client_secret": GOOGLE_CLIENT_SECRET,
"code": code,
"grant_type": "authorization_code",
"redirect_uri": GOOGLE_REDIRECT_URI,
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
)
token_response_data = token_response.json()
access_token = token_response_data.get("access_token")
if not access_token:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not retrieve access token")
# Retrieve user info
user_info_response = httpx.get(
url="https://www.googleapis.com/oauth2/v1/userinfo",
headers={"Authorization": f"Bearer {access_token}"},
)
user_info = user_info_response.json()
name = user_info.get("name")
email = user_info.get("email")
if not email:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Could not retrieve user email")
if not name:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Could not retrieve user name")
content = await signup.create_oauth_tenant(name, email)
if "errors" in content:
return content
refresh_token = content.pop("refreshToken")
refresh_token_max_age = content.pop("refreshTokenMaxAge")
response = JSONResponse(content=content)
response.set_cookie(key="refreshToken", value=refresh_token, path="/api/refresh",
max_age=refresh_token_max_age, secure=True, httponly=True)
return response
@public_app.post('/login', tags=["authentication"])
def login_user(response: JSONResponse, spot: Optional[bool] = False, data: schemas.UserLoginSchema = Body(...)):
if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response):