From 88ebda42ffbd4dbdb5aefd22ee205b91b3491c3f Mon Sep 17 00:00:00 2001 From: Nikhil <118773738+pablohashescobar@users.noreply.github.com> Date: Fri, 10 May 2024 17:30:38 +0530 Subject: [PATCH] fix: authentication redirection and UI (#4432) * dev: update python version * dev: handle magic code attempt exhausted * dev: update app, space and god mode redirection paths * fix: handled signup and signin workflow * chore: auth input error indication and autofill styling improvement * dev: add app redirection urls * dev: update redirections * chore: onboarding improvement * chore: onboarding improvement * chore: redirection issue in space resolved * chore: instance empty state added * dev: fix app, space, admin redirection in docker setitngs --------- Co-authored-by: guru_sainath Co-authored-by: Anmol Singh Bhatia --- admin/Dockerfile.admin | 4 +- admin/components/common/empty-state.tsx | 46 ++ admin/components/common/index.ts | 1 + admin/lib/wrappers/instance-wrapper.tsx | 8 +- .../plane/authentication/adapter/error.py | 75 +-- .../provider/credentials/magic_code.py | 8 +- apiserver/plane/authentication/utils/host.py | 35 +- apiserver/plane/authentication/utils/login.py | 9 +- .../plane/authentication/views/app/email.py | 38 +- .../plane/authentication/views/app/github.py | 8 +- .../plane/authentication/views/app/google.py | 8 +- .../plane/authentication/views/app/magic.py | 26 +- .../views/app/password_management.py | 15 +- .../plane/authentication/views/app/signout.py | 9 +- .../plane/authentication/views/common.py | 4 +- .../plane/authentication/views/space/email.py | 28 +- .../authentication/views/space/github.py | 6 +- .../authentication/views/space/google.py | 2 +- .../plane/authentication/views/space/magic.py | 18 +- .../views/space/password_management.py | 6 +- .../authentication/views/space/signout.py | 8 +- apiserver/plane/license/api/views/admin.py | 43 +- apiserver/plane/settings/common.py | 2 +- apiserver/plane/settings/local.py | 8 +- apiserver/plane/settings/production.py | 2 - apiserver/runtime.txt | 2 +- space/components/accounts/onboarding-form.tsx | 14 +- space/hooks/store/user/use-user-profile.ts | 2 +- space/lib/wrappers/auth-wrapper.tsx | 8 +- space/pages/onboarding/index.tsx | 6 +- .../public/onboarding/profile-setup-dark.svg | 514 +++++++++++++----- .../public/onboarding/profile-setup-light.svg | 407 ++++++++++++++ space/store/root.store.ts | 6 +- web/Dockerfile.web | 2 +- .../account/auth-forms/auth-root.tsx | 72 +-- web/components/account/auth-forms/email.tsx | 25 +- .../account/auth-forms/password.tsx | 51 +- .../account/auth-forms/unique-code.tsx | 116 ++-- .../account/password-strength-meter.tsx | 4 +- .../onboarding/create-workspace.tsx | 15 +- web/components/onboarding/invite-members.tsx | 1 + web/components/onboarding/profile-setup.tsx | 20 +- web/helpers/authentication.helper.tsx | 168 ++++-- web/layouts/auth-layout/workspace-wrapper.tsx | 2 +- web/lib/wrappers/authentication-wrapper.tsx | 6 +- web/pages/accounts/forgot-password.tsx | 2 +- web/pages/index.tsx | 2 +- web/pages/{accounts => }/sign-in.tsx | 0 web/styles/globals.css | 11 +- 49 files changed, 1334 insertions(+), 539 deletions(-) create mode 100644 admin/components/common/empty-state.tsx create mode 100644 space/public/onboarding/profile-setup-light.svg rename web/pages/{accounts => }/sign-in.tsx (100%) diff --git a/admin/Dockerfile.admin b/admin/Dockerfile.admin index 901c39e27d7..b2908f356cb 100644 --- a/admin/Dockerfile.admin +++ b/admin/Dockerfile.admin @@ -32,7 +32,7 @@ ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL ARG NEXT_PUBLIC_WEB_BASE_URL="" ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL -ARG NEXT_PUBLIC_SPACE_BASE_URL="" +ARG NEXT_PUBLIC_SPACE_BASE_URL="/spaces" ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode" @@ -62,7 +62,7 @@ ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL ARG NEXT_PUBLIC_WEB_BASE_URL="" ENV NEXT_PUBLIC_WEB_BASE_URL=$NEXT_PUBLIC_WEB_BASE_URL -ARG NEXT_PUBLIC_SPACE_BASE_URL="" +ARG NEXT_PUBLIC_SPACE_BASE_URL="/spaces" ENV NEXT_PUBLIC_SPACE_BASE_URL=$NEXT_PUBLIC_SPACE_BASE_URL ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode" diff --git a/admin/components/common/empty-state.tsx b/admin/components/common/empty-state.tsx new file mode 100644 index 00000000000..fbbe0bc0f33 --- /dev/null +++ b/admin/components/common/empty-state.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import Image from "next/image"; +import { Button } from "@plane/ui"; + +type Props = { + title: string; + description?: React.ReactNode; + image?: any; + primaryButton?: { + icon?: any; + text: string; + onClick: () => void; + }; + secondaryButton?: React.ReactNode; + disabled?: boolean; +}; + +export const EmptyState: React.FC = ({ + title, + description, + image, + primaryButton, + secondaryButton, + disabled = false, +}) => ( +
+
+ {image && {primaryButton?.text} +
{title}
+ {description &&

{description}

} +
+ {primaryButton && ( + + )} + {secondaryButton} +
+
+
+); diff --git a/admin/components/common/index.ts b/admin/components/common/index.ts index 97248b999d9..77f0e9327f9 100644 --- a/admin/components/common/index.ts +++ b/admin/components/common/index.ts @@ -4,3 +4,4 @@ export * from "./controller-input"; export * from "./copy-field"; export * from "./password-strength-meter"; export * from "./banner"; +export * from "./empty-state"; diff --git a/admin/lib/wrappers/instance-wrapper.tsx b/admin/lib/wrappers/instance-wrapper.tsx index 6ee1dc247df..f86adfdce5d 100644 --- a/admin/lib/wrappers/instance-wrapper.tsx +++ b/admin/lib/wrappers/instance-wrapper.tsx @@ -13,6 +13,7 @@ import { InstanceNotReady } from "@/components/instance"; import { useInstance } from "@/hooks/store"; // helpers import { EInstancePageType } from "@/helpers"; +import { EmptyState } from "@/components/common"; type TInstanceWrapper = { children: ReactNode; @@ -41,7 +42,12 @@ export const InstanceWrapper: FC = observer((props) => { ); if (!instance) { - return <>Something went wrong; + return ( + + ); } if (instance?.instance?.is_setup_done === false && authEnabled === "1") diff --git a/apiserver/plane/authentication/adapter/error.py b/apiserver/plane/authentication/adapter/error.py index 4b975939d58..73809b9ad5c 100644 --- a/apiserver/plane/authentication/adapter/error.py +++ b/apiserver/plane/authentication/adapter/error.py @@ -1,51 +1,52 @@ AUTHENTICATION_ERROR_CODES = { # Global "INSTANCE_NOT_CONFIGURED": 5000, - "INVALID_EMAIL": 5012, - "EMAIL_REQUIRED": 5013, - "SIGNUP_DISABLED": 5001, + "INVALID_EMAIL": 5005, + "EMAIL_REQUIRED": 5010, + "SIGNUP_DISABLED": 5015, # Password strength - "INVALID_PASSWORD": 5002, - "SMTP_NOT_CONFIGURED": 5007, + "INVALID_PASSWORD": 5020, + "SMTP_NOT_CONFIGURED": 5025, # Sign Up - "USER_ALREADY_EXIST": 5003, - "AUTHENTICATION_FAILED_SIGN_UP": 5006, - "REQUIRED_EMAIL_PASSWORD_SIGN_UP": 5015, - "INVALID_EMAIL_SIGN_UP": 5017, - "INVALID_EMAIL_MAGIC_SIGN_UP": 5019, - "MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5023, + "USER_ALREADY_EXIST": 5030, + "AUTHENTICATION_FAILED_SIGN_UP": 5035, + "REQUIRED_EMAIL_PASSWORD_SIGN_UP": 5040, + "INVALID_EMAIL_SIGN_UP": 5045, + "INVALID_EMAIL_MAGIC_SIGN_UP": 5050, + "MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5055, # Sign In - "USER_DOES_NOT_EXIST": 5004, - "AUTHENTICATION_FAILED_SIGN_IN": 5005, - "REQUIRED_EMAIL_PASSWORD_SIGN_IN": 5014, - "INVALID_EMAIL_SIGN_IN": 5016, - "INVALID_EMAIL_MAGIC_SIGN_IN": 5018, - "MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED": 5022, - # Both Sign in and Sign up - "INVALID_MAGIC_CODE": 5008, - "EXPIRED_MAGIC_CODE": 5009, + "USER_DOES_NOT_EXIST": 5060, + "AUTHENTICATION_FAILED_SIGN_IN": 5065, + "REQUIRED_EMAIL_PASSWORD_SIGN_IN": 5070, + "INVALID_EMAIL_SIGN_IN": 5075, + "INVALID_EMAIL_MAGIC_SIGN_IN": 5080, + "MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED": 5085, + # Both Sign in and Sign up for magic + "INVALID_MAGIC_CODE": 5090, + "EXPIRED_MAGIC_CODE": 5095, + "EMAIL_CODE_ATTEMPT_EXHAUSTED": 5100, # Oauth - "GOOGLE_NOT_CONFIGURED": 5010, - "GITHUB_NOT_CONFIGURED": 5011, - "GOOGLE_OAUTH_PROVIDER_ERROR": 5021, - "GITHUB_OAUTH_PROVIDER_ERROR": 5020, + "GOOGLE_NOT_CONFIGURED": 5105, + "GITHUB_NOT_CONFIGURED": 5110, + "GOOGLE_OAUTH_PROVIDER_ERROR": 5115, + "GITHUB_OAUTH_PROVIDER_ERROR": 5120, # Reset Password - "INVALID_PASSWORD_TOKEN": 5024, - "EXPIRED_PASSWORD_TOKEN": 5025, + "INVALID_PASSWORD_TOKEN": 5125, + "EXPIRED_PASSWORD_TOKEN": 5130, # Change password - "INCORRECT_OLD_PASSWORD": 5026, - "INVALID_NEW_PASSWORD": 5027, + "INCORRECT_OLD_PASSWORD": 5135, + "INVALID_NEW_PASSWORD": 5140, # set passowrd - "PASSWORD_ALREADY_SET": 5028, + "PASSWORD_ALREADY_SET": 5145, # Admin - "ADMIN_ALREADY_EXIST": 5029, - "REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME": 5030, - "INVALID_ADMIN_EMAIL": 5031, - "INVALID_ADMIN_PASSWORD": 5032, - "REQUIRED_ADMIN_EMAIL_PASSWORD": 5033, - "ADMIN_AUTHENTICATION_FAILED": 5034, - "ADMIN_USER_ALREADY_EXIST": 5035, - "ADMIN_USER_DOES_NOT_EXIST": 5036, + "ADMIN_ALREADY_EXIST": 5150, + "REQUIRED_ADMIN_EMAIL_PASSWORD_FIRST_NAME": 5155, + "INVALID_ADMIN_EMAIL": 5160, + "INVALID_ADMIN_PASSWORD": 5165, + "REQUIRED_ADMIN_EMAIL_PASSWORD": 5170, + "ADMIN_AUTHENTICATION_FAILED": 5175, + "ADMIN_USER_ALREADY_EXIST": 5180, + "ADMIN_USER_DOES_NOT_EXIST": 5185, } diff --git a/apiserver/plane/authentication/provider/credentials/magic_code.py b/apiserver/plane/authentication/provider/credentials/magic_code.py index 71451ef0d65..c1207d14d50 100644 --- a/apiserver/plane/authentication/provider/credentials/magic_code.py +++ b/apiserver/plane/authentication/provider/credentials/magic_code.py @@ -77,7 +77,13 @@ def initiate(self): current_attempt = data["current_attempt"] + 1 if data["current_attempt"] > 2: - return key, "" + raise AuthenticationException( + error_code=AUTHENTICATION_ERROR_CODES[ + "EMAIL_CODE_ATTEMPT_EXHAUSTED" + ], + error_message="EMAIL_CODE_ATTEMPT_EXHAUSTED", + payload={"email": self.key}, + ) value = { "current_attempt": current_attempt, diff --git a/apiserver/plane/authentication/utils/host.py b/apiserver/plane/authentication/utils/host.py index b670eed41ca..4046c1e2079 100644 --- a/apiserver/plane/authentication/utils/host.py +++ b/apiserver/plane/authentication/utils/host.py @@ -5,21 +5,38 @@ from django.conf import settings -def base_host(request, is_admin=False, is_space=False): +def base_host(request, is_admin=False, is_space=False, is_app=False): """Utility function to return host / origin from the request""" - - if is_admin and settings.ADMIN_BASE_URL: - return settings.ADMIN_BASE_URL - - if is_space and settings.SPACE_BASE_URL: - return settings.SPACE_BASE_URL - - return ( + # Calculate the base origin from request + base_origin = str( request.META.get("HTTP_ORIGIN") or f"{urlsplit(request.META.get('HTTP_REFERER')).scheme}://{urlsplit(request.META.get('HTTP_REFERER')).netloc}" or f"""{"https" if request.is_secure() else "http"}://{request.get_host()}""" ) + # Admin redirections + if is_admin: + if settings.ADMIN_BASE_URL: + return settings.ADMIN_BASE_URL + else: + return base_origin + "/god-mode/" + + # Space redirections + if is_space: + if settings.SPACE_BASE_URL: + return settings.SPACE_BASE_URL + else: + return base_origin + "/spaces/" + + # App Redirection + if is_app: + if settings.APP_BASE_URL: + return settings.APP_BASE_URL + else: + return base_origin + + return base_origin + def user_ip(request): return str(request.META.get("REMOTE_ADDR")) diff --git a/apiserver/plane/authentication/utils/login.py b/apiserver/plane/authentication/utils/login.py index 88a988c8f58..45dbdc24943 100644 --- a/apiserver/plane/authentication/utils/login.py +++ b/apiserver/plane/authentication/utils/login.py @@ -5,12 +5,17 @@ from plane.authentication.utils.host import base_host -def user_login(request, user): +def user_login(request, user, is_app=False, is_admin=False, is_space=False): login(request=request, user=user) device_info = { "user_agent": request.META.get("HTTP_USER_AGENT", ""), "ip_address": request.META.get("REMOTE_ADDR", ""), - "domain": base_host(request=request), + "domain": base_host( + request=request, + is_app=is_app, + is_admin=is_admin, + is_space=is_space, + ), } request.session["device_info"] = device_info request.session.save() diff --git a/apiserver/plane/authentication/views/app/email.py b/apiserver/plane/authentication/views/app/email.py index 7ef6ac9f4ca..4093be1080c 100644 --- a/apiserver/plane/authentication/views/app/email.py +++ b/apiserver/plane/authentication/views/app/email.py @@ -42,8 +42,8 @@ def post(self, request): params["next_path"] = str(next_path) # Base URL join url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -66,8 +66,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -85,8 +85,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -100,8 +100,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -111,7 +111,7 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) # Get the redirection path @@ -121,15 +121,15 @@ def post(self, request): path = get_redirection_path(user=user) # redirect to referer path - url = urljoin(base_host(request=request), path) + url = urljoin(base_host(request=request, is_app=True), path) return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -152,7 +152,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -173,7 +173,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -192,7 +192,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -207,7 +207,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -218,7 +218,7 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) # Get the redirection path @@ -227,14 +227,14 @@ def post(self, request): else: path = get_redirection_path(user=user) # redirect to referer path - url = urljoin(base_host(request=request), path) + url = urljoin(base_host(request=request, is_app=True), path) return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/github.py b/apiserver/plane/authentication/views/app/github.py index 48b7e09d96e..73afa674b99 100644 --- a/apiserver/plane/authentication/views/app/github.py +++ b/apiserver/plane/authentication/views/app/github.py @@ -24,7 +24,7 @@ class GitHubOauthInitiateEndpoint(View): def get(self, request): # Get host and next path - request.session["host"] = base_host(request=request) + request.session["host"] = base_host(request=request, is_app=True) next_path = request.GET.get("next_path") if next_path: request.session["next_path"] = str(next_path) @@ -42,7 +42,7 @@ def get(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -57,7 +57,7 @@ def get(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -110,7 +110,7 @@ def get(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) # Get the redirection path diff --git a/apiserver/plane/authentication/views/app/google.py b/apiserver/plane/authentication/views/app/google.py index 690a9778bd7..ea3afed896c 100644 --- a/apiserver/plane/authentication/views/app/google.py +++ b/apiserver/plane/authentication/views/app/google.py @@ -24,7 +24,7 @@ class GoogleOauthInitiateEndpoint(View): def get(self, request): - request.session["host"] = base_host(request=request) + request.session["host"] = base_host(request=request, is_app=True) next_path = request.GET.get("next_path") if next_path: request.session["next_path"] = str(next_path) @@ -42,7 +42,7 @@ def get(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -58,7 +58,7 @@ def get(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -108,7 +108,7 @@ def get(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) # Get the redirection path diff --git a/apiserver/plane/authentication/views/app/magic.py b/apiserver/plane/authentication/views/app/magic.py index 8f9f3633b43..0fa5296740e 100644 --- a/apiserver/plane/authentication/views/app/magic.py +++ b/apiserver/plane/authentication/views/app/magic.py @@ -90,8 +90,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -104,8 +104,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -116,7 +116,7 @@ def post(self, request): user = provider.authenticate() profile = Profile.objects.get(user=user) # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) if user.is_password_autoset and profile.is_onboarded: @@ -129,7 +129,7 @@ def post(self, request): else str(process_workspace_project_invitations(user=user)) ) # redirect to referer path - url = urljoin(base_host(request=request), path) + url = urljoin(base_host(request=request, is_app=True), path) return HttpResponseRedirect(url) except AuthenticationException as e: @@ -137,8 +137,8 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode(params), + base_host(request=request, is_app=True), + "sign-in?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -163,7 +163,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -177,7 +177,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -188,7 +188,7 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_app=True) # Process workspace and project invitations process_workspace_project_invitations(user=user) # Get the redirection path @@ -197,7 +197,7 @@ def post(self, request): else: path = get_redirection_path(user=user) # redirect to referer path - url = urljoin(base_host(request=request), path) + url = urljoin(base_host(request=request, is_app=True), path) return HttpResponseRedirect(url) except AuthenticationException as e: @@ -205,7 +205,7 @@ def post(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/password_management.py b/apiserver/plane/authentication/views/app/password_management.py index 80803cd253c..b26b5776066 100644 --- a/apiserver/plane/authentication/views/app/password_management.py +++ b/apiserver/plane/authentication/views/app/password_management.py @@ -146,7 +146,7 @@ def post(self, request, uidb64, token): ) params = exc.get_error_dict() url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "accounts/reset-password?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -159,8 +159,9 @@ def post(self, request, uidb64, token): error_message="INVALID_PASSWORD", ) url = urljoin( - base_host(request=request), - "?" + urlencode(exc.get_error_dict()), + base_host(request=request, is_app=True), + "accounts/reset-password?" + + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -172,7 +173,7 @@ def post(self, request, uidb64, token): error_message="INVALID_PASSWORD", ) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "accounts/reset-password?" + urlencode(exc.get_error_dict()), ) @@ -184,8 +185,8 @@ def post(self, request, uidb64, token): user.save() url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode({"success": True}), + base_host(request=request, is_app=True), + "sign-in?" + urlencode({"success": True}), ) return HttpResponseRedirect(url) except DjangoUnicodeDecodeError: @@ -196,7 +197,7 @@ def post(self, request, uidb64, token): error_message="EXPIRED_PASSWORD_TOKEN", ) url = urljoin( - base_host(request=request), + base_host(request=request, is_app=True), "accounts/reset-password?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/app/signout.py b/apiserver/plane/authentication/views/app/signout.py index 967f398eb10..10461f240df 100644 --- a/apiserver/plane/authentication/views/app/signout.py +++ b/apiserver/plane/authentication/views/app/signout.py @@ -1,5 +1,5 @@ # Python imports -from urllib.parse import urlencode, urljoin +from urllib.parse import urljoin # Django imports from django.views import View @@ -23,12 +23,9 @@ def post(self, request): user.save() # Log the user out logout(request) - url = urljoin( - base_host(request=request), - "accounts/sign-in?" + urlencode({"success": "true"}), - ) + url = urljoin(base_host(request=request, is_app=True), "sign-in") return HttpResponseRedirect(url) except Exception: return HttpResponseRedirect( - base_host(request=request), "accounts/sign-in" + base_host(request=request, is_app=True), "sign-in" ) diff --git a/apiserver/plane/authentication/views/common.py b/apiserver/plane/authentication/views/common.py index 4b93010de58..16ac058b02b 100644 --- a/apiserver/plane/authentication/views/common.py +++ b/apiserver/plane/authentication/views/common.py @@ -70,7 +70,7 @@ def post(self, request): user.set_password(serializer.data.get("new_password")) user.is_password_autoset = False user.save() - user_login(user=user, request=request) + user_login(user=user, request=request, is_app=True) return Response( {"message": "Password updated successfully"}, status=status.HTTP_200_OK, @@ -131,7 +131,7 @@ def post(self, request): user.is_password_autoset = False user.save() # Login the user as the session is invalidated - user_login(user=user, request=request) + user_login(user=user, request=request, is_app=True) # Return the user serializer = UserSerializer(user) return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/apiserver/plane/authentication/views/space/email.py b/apiserver/plane/authentication/views/space/email.py index 4505332ebe6..e11ab29b510 100644 --- a/apiserver/plane/authentication/views/space/email.py +++ b/apiserver/plane/authentication/views/space/email.py @@ -38,7 +38,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -60,7 +60,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -79,7 +79,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -94,7 +94,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -104,11 +104,11 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # redirect to next path url = urljoin( base_host(request=request, is_space=True), - str(next_path) if next_path else "/", + str(next_path) if next_path else "", ) return HttpResponseRedirect(url) except AuthenticationException as e: @@ -117,7 +117,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -141,7 +141,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -162,7 +162,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) # Validate the email @@ -181,7 +181,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -196,7 +196,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -206,11 +206,11 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # redirect to referer path url = urljoin( base_host(request=request, is_space=True), - str(next_path) if next_path else "spaces", + str(next_path) if next_path else "", ) return HttpResponseRedirect(url) except AuthenticationException as e: @@ -219,6 +219,6 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/github.py b/apiserver/plane/authentication/views/space/github.py index 4a0f2309847..8430cbdfb03 100644 --- a/apiserver/plane/authentication/views/space/github.py +++ b/apiserver/plane/authentication/views/space/github.py @@ -55,7 +55,7 @@ def get(self, request): if next_path: params["next_path"] = str(next_path) url = urljoin( - base_host(request=request), + base_host(request=request, is_space=True), "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -108,10 +108,10 @@ def get(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # Process workspace and project invitations # redirect to referer path - url = urljoin(base_host, str(next_path) if next_path else "/") + url = urljoin(base_host, str(next_path) if next_path else "") return HttpResponseRedirect(url) except AuthenticationException as e: params = e.get_error_dict() diff --git a/apiserver/plane/authentication/views/space/google.py b/apiserver/plane/authentication/views/space/google.py index 2f6b576993f..502f146c31d 100644 --- a/apiserver/plane/authentication/views/space/google.py +++ b/apiserver/plane/authentication/views/space/google.py @@ -103,7 +103,7 @@ def get(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # redirect to referer path url = urljoin( base_host, str(next_path) if next_path else "/spaces" diff --git a/apiserver/plane/authentication/views/space/magic.py b/apiserver/plane/authentication/views/space/magic.py index 52771f71b99..45a8e375503 100644 --- a/apiserver/plane/authentication/views/space/magic.py +++ b/apiserver/plane/authentication/views/space/magic.py @@ -86,7 +86,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -99,7 +99,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -109,14 +109,14 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # redirect to referer path profile = Profile.objects.get(user=user) if user.is_password_autoset and profile.is_onboarded: - path = "spaces/accounts/set-password" + path = "accounts/set-password" else: # Get the redirection path - path = str(next_path) if next_path else "spaces" + path = str(next_path) if next_path else "" url = urljoin(base_host(request=request, is_space=True), path) return HttpResponseRedirect(url) @@ -126,7 +126,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -152,7 +152,7 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) @@ -176,7 +176,7 @@ def post(self, request): ) user = provider.authenticate() # Login the user and record his device info - user_login(request=request, user=user) + user_login(request=request, user=user, is_space=True) # redirect to referer path url = urljoin( base_host(request=request, is_space=True), @@ -190,6 +190,6 @@ def post(self, request): params["next_path"] = str(next_path) url = urljoin( base_host(request=request, is_space=True), - "spaces/accounts/sign-in?" + urlencode(params), + "?" + urlencode(params), ) return HttpResponseRedirect(url) diff --git a/apiserver/plane/authentication/views/space/password_management.py b/apiserver/plane/authentication/views/space/password_management.py index aeac9776d1e..5263c89566d 100644 --- a/apiserver/plane/authentication/views/space/password_management.py +++ b/apiserver/plane/authentication/views/space/password_management.py @@ -183,11 +183,9 @@ def post(self, request, uidb64, token): user.is_password_autoset = False user.save() - url = urljoin( - base_host(request=request, is_space=True), - "accounts/sign-in?" + urlencode({"success": True}), + return HttpResponseRedirect( + base_host(request=request, is_space=True) ) - return HttpResponseRedirect(url) except DjangoUnicodeDecodeError: exc = AuthenticationException( error_code=AUTHENTICATION_ERROR_CODES[ diff --git a/apiserver/plane/authentication/views/space/signout.py b/apiserver/plane/authentication/views/space/signout.py index 3cfd6d47114..655d8b1c8cc 100644 --- a/apiserver/plane/authentication/views/space/signout.py +++ b/apiserver/plane/authentication/views/space/signout.py @@ -23,12 +23,10 @@ def post(self, request): user.save() # Log the user out logout(request) - url = urljoin( - base_host(request=request, is_space=True), - "accounts/sign-in?" + urlencode({"success": "true"}), + return HttpResponseRedirect( + base_host(request=request, is_space=True) ) - return HttpResponseRedirect(url) except Exception: return HttpResponseRedirect( - base_host(request=request, is_space=True), "accounts/sign-in" + base_host(request=request, is_space=True) ) diff --git a/apiserver/plane/license/api/views/admin.py b/apiserver/plane/license/api/views/admin.py index ed3c00f17a4..945f4b1b172 100644 --- a/apiserver/plane/license/api/views/admin.py +++ b/apiserver/plane/license/api/views/admin.py @@ -107,7 +107,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -119,7 +119,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -148,7 +148,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -170,7 +170,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -192,7 +192,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) else: @@ -214,7 +214,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/setup?" + urlencode(exc.get_error_dict()), + "setup?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -247,10 +247,8 @@ def post(self, request): instance.save() # get tokens for user - user_login(request=request, user=user) - url = urljoin( - base_host(request=request, is_admin=True), "god-mode/general" - ) + user_login(request=request, user=user, is_admin=True) + url = urljoin(base_host(request=request, is_admin=True), "general") return HttpResponseRedirect(url) @@ -272,7 +270,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -293,7 +291,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -311,7 +309,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -331,7 +329,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -348,7 +346,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) @@ -365,7 +363,7 @@ def post(self, request): ) url = urljoin( base_host(request=request, is_admin=True), - "god-mode/login?" + urlencode(exc.get_error_dict()), + "?" + urlencode(exc.get_error_dict()), ) return HttpResponseRedirect(url) # settings last active for the user @@ -378,10 +376,8 @@ def post(self, request): user.save() # get tokens for user - user_login(request=request, user=user) - url = urljoin( - base_host(request=request, is_admin=True), "god-mode/general" - ) + user_login(request=request, user=user, is_admin=True) + url = urljoin(base_host(request=request, is_admin=True), "general") return HttpResponseRedirect(url) @@ -414,12 +410,9 @@ def post(self, request): user.save() # Log the user out logout(request) - url = urljoin( - base_host(request=request, is_admin=True), - "accounts/sign-in?" + urlencode({"success": "true"}), - ) + url = urljoin(base_host(request=request, is_admin=True)) return HttpResponseRedirect(url) except Exception: return HttpResponseRedirect( - base_host(request=request, is_admin=True), "accounts/sign-in" + base_host(request=request, is_admin=True) ) diff --git a/apiserver/plane/settings/common.py b/apiserver/plane/settings/common.py index 4f5e6d4eef1..f043340a297 100644 --- a/apiserver/plane/settings/common.py +++ b/apiserver/plane/settings/common.py @@ -346,4 +346,4 @@ # Base URLs ADMIN_BASE_URL = os.environ.get("ADMIN_BASE_URL", None) SPACE_BASE_URL = os.environ.get("SPACE_BASE_URL", None) -APP_BASE_URL = os.environ.get("ADMIN_BASE_URL", None) +APP_BASE_URL = os.environ.get("APP_BASE_URL") or os.environ.get("WEB_URL") diff --git a/apiserver/plane/settings/local.py b/apiserver/plane/settings/local.py index 2290262aefa..b96d1ca31b2 100644 --- a/apiserver/plane/settings/local.py +++ b/apiserver/plane/settings/local.py @@ -35,10 +35,10 @@ "http://127.0.0.1", "http://localhost:3000", "http://127.0.0.1:3000", - "http://localhost:4000", - "http://127.0.0.1:4000", - "http://localhost:3333", - "http://127.0.0.1:3333", + "http://localhost:3001", + "http://127.0.0.1:3001", + "http://localhost:3002", + "http://127.0.0.1:3002", ] CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS CORS_ALLOW_ALL_ORIGINS = True diff --git a/apiserver/plane/settings/production.py b/apiserver/plane/settings/production.py index c56222c67e1..806f83aca7e 100644 --- a/apiserver/plane/settings/production.py +++ b/apiserver/plane/settings/production.py @@ -12,8 +12,6 @@ INSTALLED_APPS += ("scout_apm.django",) # noqa -# Honor the 'X-Forwarded-Proto' header for request.is_secure() -SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # Scout Settings SCOUT_MONITOR = os.environ.get("SCOUT_MONITOR", False) diff --git a/apiserver/runtime.txt b/apiserver/runtime.txt index cd0aac54270..8cf46af5fce 100644 --- a/apiserver/runtime.txt +++ b/apiserver/runtime.txt @@ -1 +1 @@ -python-3.11.9 \ No newline at end of file +python-3.12.3 \ No newline at end of file diff --git a/space/components/accounts/onboarding-form.tsx b/space/components/accounts/onboarding-form.tsx index 7a719d93855..768d5160fc4 100644 --- a/space/components/accounts/onboarding-form.tsx +++ b/space/components/accounts/onboarding-form.tsx @@ -140,8 +140,11 @@ export const OnBoardingForm: React.FC = observer((props) => {
-
-