Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: authentication redirection and UI #4432

Merged
merged 21 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
43ea577
dev: update python version
pablohashescobar May 10, 2024
d0ccd8c
dev: handle magic code attempt exhausted
pablohashescobar May 10, 2024
bf33f1b
dev: update app, space and god mode redirection paths
pablohashescobar May 10, 2024
84eedf5
fix: handled signup and signin workflow
gurusainath May 10, 2024
15334b0
chore: auth input error indication and autofill styling improvement
anmolsinghbhatia May 10, 2024
330812a
dev: add app redirection urls
pablohashescobar May 10, 2024
a5ca4e7
Merge branch 'preview' of gurusainath:makeplane/plane into fix-authen…
gurusainath May 10, 2024
a36db46
Merge branch 'preview' of gurusainath:makeplane/plane into fix-authen…
gurusainath May 10, 2024
5b4d169
Merge branch 'preview' of gurusainath:makeplane/plane into fix-authen…
gurusainath May 10, 2024
2826172
Merge branch 'fix-authentication-ui' of gurusainath:makeplane/plane i…
gurusainath May 10, 2024
aaf58aa
Merge branch 'preview' of github.com:makeplane/plane into fix-authent…
pablohashescobar May 10, 2024
7c6a31f
Merge branch 'fix-authentication-ui' of github.com:makeplane/plane in…
pablohashescobar May 10, 2024
4c865ef
dev: update redirections
pablohashescobar May 10, 2024
f716fea
Merge branch 'fix-authentication-ui' of github.com:makeplane/plane in…
pablohashescobar May 10, 2024
94917b8
chore: onboarding improvement
anmolsinghbhatia May 10, 2024
48e8e3d
Merge branch 'fix-authentication-ui' of github.com:makeplane/plane in…
anmolsinghbhatia May 10, 2024
39569c6
chore: onboarding improvement
anmolsinghbhatia May 10, 2024
7f52ef4
chore: redirection issue in space resolved
gurusainath May 10, 2024
e8e8efb
chore: instance empty state added
anmolsinghbhatia May 10, 2024
0ed7178
dev: fix app, space, admin redirection in docker setitngs
pablohashescobar May 10, 2024
c50dc59
Merge branch 'fix-authentication-ui' of github.com:makeplane/plane in…
pablohashescobar May 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions admin/Dockerfile.admin
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down
46 changes: 46 additions & 0 deletions admin/components/common/empty-state.tsx
Original file line number Diff line number Diff line change
@@ -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<Props> = ({
title,
description,
image,
primaryButton,
secondaryButton,
disabled = false,
}) => (
<div className={`flex h-full w-full items-center justify-center`}>
<div className="flex w-full flex-col items-center text-center">
{image && <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
<h6 className="mb-3 mt-6 text-xl font-semibold sm:mt-8">{title}</h6>
{description && <p className="mb-7 px-5 text-custom-text-300 sm:mb-8">{description}</p>}
<div className="flex items-center gap-4">
{primaryButton && (
<Button
variant="primary"
prependIcon={primaryButton.icon}
onClick={primaryButton.onClick}
disabled={disabled}
>
{primaryButton.text}
</Button>
)}
{secondaryButton}
</div>
</div>
</div>
);
1 change: 1 addition & 0 deletions admin/components/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from "./controller-input";
export * from "./copy-field";
export * from "./password-strength-meter";
export * from "./banner";
export * from "./empty-state";
8 changes: 7 additions & 1 deletion admin/lib/wrappers/instance-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -41,7 +42,12 @@ export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
);

if (!instance) {
return <>Something went wrong</>;
return (
<EmptyState
title="Your instance wasn't configured successfully."
description="Please try re-installing Plane to fix the problem. If the issue still persists please reach out to support@plane.so."
/>
);
}

if (instance?.instance?.is_setup_done === false && authEnabled === "1")
Expand Down
75 changes: 38 additions & 37 deletions apiserver/plane/authentication/adapter/error.py
Original file line number Diff line number Diff line change
@@ -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,
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
35 changes: 26 additions & 9 deletions apiserver/plane/authentication/utils/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/"

Check warning on line 22 in apiserver/plane/authentication/utils/host.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

apiserver/plane/authentication/utils/host.py#L22

Detected Flask route directly returning a formatted string.

# Space redirections
if is_space:
if settings.SPACE_BASE_URL:
return settings.SPACE_BASE_URL
else:
return base_origin + "/spaces/"

Check warning on line 29 in apiserver/plane/authentication/utils/host.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

apiserver/plane/authentication/utils/host.py#L29

Detected Flask route directly returning a formatted string.

# 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"))
9 changes: 7 additions & 2 deletions apiserver/plane/authentication/utils/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
38 changes: 19 additions & 19 deletions apiserver/plane/authentication/views/app/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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)

Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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)
Loading
Loading