Compare commits
16 commits
main
...
test_oauth
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d87d9cb90 | ||
|
|
f206006e65 | ||
|
|
a9c2356f79 | ||
|
|
e081fdc446 | ||
|
|
09daaa2666 | ||
|
|
4f20d8dde0 | ||
|
|
07ee332b4c | ||
|
|
2b76b00e9a | ||
|
|
a6c811847a | ||
|
|
eab18dd9b4 | ||
|
|
caf5d76050 | ||
|
|
25baa6825e | ||
|
|
a6c4847ae2 | ||
|
|
a5fe0e246f | ||
|
|
771dd8bc49 | ||
|
|
a78c97bb09 |
5 changed files with 176 additions and 1 deletions
|
|
@ -176,6 +176,11 @@ def is_alphabet_space_dash(word):
|
|||
return r.match(word) is not None
|
||||
|
||||
|
||||
def remove_non_alphabet_space_dash(word):
|
||||
r = re.compile("[^a-zA-Z -]")
|
||||
return r.sub('', word)
|
||||
|
||||
|
||||
def merge_lists_by_key(l1, l2, key):
|
||||
merged = {}
|
||||
for item in l1 + l2:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import logging
|
|||
from decouple import config
|
||||
|
||||
import schemas
|
||||
from chalicelib.core import users, telemetry, tenants
|
||||
from chalicelib.core import users, telemetry, tenants, authorizers
|
||||
from chalicelib.utils import captcha, smtp
|
||||
from chalicelib.utils import helper
|
||||
from chalicelib.utils import pg_client
|
||||
|
|
@ -98,3 +98,88 @@ async def create_tenant(data: schemas.UserSignupSchema):
|
|||
"user": r
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def __plan_generator(type_name, trial_period, users_limit, data_retention, projects_limit, sessions_limits):
|
||||
return {
|
||||
"type": type_name,
|
||||
"trial": trial_period,
|
||||
"users": users_limit,
|
||||
"dataRetention": data_retention,
|
||||
"projects": projects_limit,
|
||||
"sessions": sessions_limits,
|
||||
# The following params can be overwritten in each plan change
|
||||
"stripeCustomerId": None,
|
||||
"stripeSubscriptionId": None,
|
||||
"endAt": None,
|
||||
"billingStartsAt": None
|
||||
}
|
||||
|
||||
|
||||
FREE_PLAN = __plan_generator(type_name="free",
|
||||
trial_period=0,
|
||||
users_limit=2,
|
||||
data_retention=7,
|
||||
projects_limit=1,
|
||||
sessions_limits=1000)
|
||||
|
||||
|
||||
async def create_oauth_tenant(fullname: str, email: str):
|
||||
logger.info(f"==== Signup oauth started at {TimeUTC.to_human_readable(TimeUTC.now())} UTC")
|
||||
errors = []
|
||||
|
||||
logger.debug(f"email: {email}")
|
||||
|
||||
if email is None or len(email) < 5:
|
||||
errors.append("Invalid email address.")
|
||||
else:
|
||||
if users.email_exists(email):
|
||||
users.update_user_internal_id(email, email)
|
||||
return users.authenticate_sso(email, email)
|
||||
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):
|
||||
edited_fullname = helper.remove_non_alphabet_space_dash(fullname)
|
||||
if len(edited_fullname) < 1:
|
||||
errors.append("Invalid full name.")
|
||||
fullname = edited_fullname
|
||||
|
||||
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],
|
||||
"plan": json.dumps(FREE_PLAN)
|
||||
}
|
||||
query = """WITH t AS (
|
||||
INSERT INTO public.tenants (name, plan)
|
||||
VALUES ('my organisation', %(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, origin, internal_id)
|
||||
VALUES ((SELECT tenant_id FROM t), %(email)s, 'owner', %(fullname)s, TRUE,%(data)s, (SELECT role_id FROM r WHERE name ='Owner'), 'google', %(email)s)
|
||||
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))
|
||||
r = cur.fetchone()
|
||||
|
||||
return users.authenticate_sso(email, email)
|
||||
|
|
|
|||
|
|
@ -1020,3 +1020,15 @@ def update_user_settings(user_id, settings):
|
|||
{"user_id": user_id, "settings": json.dumps(settings)})
|
||||
)
|
||||
return helper.dict_to_camel_case(cur.fetchone())
|
||||
|
||||
|
||||
def update_user_internal_id(email, internal_id):
|
||||
with pg_client.PostgresClient() as cur:
|
||||
cur.execute(
|
||||
cur.mogrify(
|
||||
f"""UPDATE public.users
|
||||
SET internal_id = %(internal_id)s
|
||||
WHERE email = %(email)s
|
||||
AND deleted_at IS NULL;""",
|
||||
{"email": email, "internal_id": internal_id})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ python3-saml==1.16.0 --no-binary=lxml
|
|||
redis==5.1.0b6
|
||||
#confluent-kafka==2.1.0
|
||||
azure-storage-blob==12.22.0
|
||||
httpx==0.23.0
|
||||
|
|
@ -5,6 +5,8 @@ from decouple import config
|
|||
from fastapi import Body, Depends, BackgroundTasks, Request
|
||||
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,76 @@ if config("MULTI_TENANTS", cast=bool, default=False) or not tenants.tenants_exis
|
|||
return content
|
||||
|
||||
|
||||
# Environment variables (ensure these are set)
|
||||
GOOGLE_CLIENT_ID = config("GOOGLE_CLIENT_ID")
|
||||
GOOGLE_CLIENT_SECRET = config("GOOGLE_CLIENT_SECRET")
|
||||
GOOGLE_REDIRECT_URI = config("GOOGLE_REDIRECT_URI")
|
||||
|
||||
|
||||
if True or 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 True or 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 content is None:
|
||||
return {"errors": ["null JWT"]}
|
||||
if "errors" in content:
|
||||
return content
|
||||
|
||||
refresh_token = content.pop("refreshToken")
|
||||
refresh_token_max_age = content.pop("refreshTokenMaxAge")
|
||||
response = Response(
|
||||
status_code=status.HTTP_302_FOUND,
|
||||
headers={'Location': config("SITE_URL") + "/login?jwt=" + content.pop("jwt")})
|
||||
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):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue