mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 14:49:07 +00:00
refactor: Update import statements for TimezoneUtil in multiple files
This commit is contained in:
parent
786ec6ce7a
commit
48b095f548
@ -22,7 +22,6 @@ const LoginPage: () => JSX.Element = () => {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
|
||||
const [initialValues, setInitialValues] = React.useState<JSONObject>({});
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
@ -105,9 +104,7 @@ const LoginPage: () => JSX.Element = () => {
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/sso")}>
|
||||
<div
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm"
|
||||
>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use single sign-on (SSO) instead
|
||||
</div>
|
||||
</Link>
|
||||
|
@ -9,7 +9,7 @@ import OneUptimeLogo from "CommonUI/src/Images/logos/OneUptimeSVG/3-transparent.
|
||||
import Navigation from "CommonUI/src/Utils/Navigation";
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import User from "Model/Models/User";
|
||||
import React, { useState } from "react";
|
||||
import React, { ReactElement, useState } from "react";
|
||||
import ProjectSSO from "Model/Models/ProjectSSO";
|
||||
import PageLoader from "CommonUI/src/Components/Loader/PageLoader";
|
||||
import API from "CommonUI/src/Utils/API/API";
|
||||
@ -20,188 +20,216 @@ import HTTPResponse from "Common/Types/API/HTTPResponse";
|
||||
import StaticModelList from "CommonUI/src/Components/ModelList/StaticModelList";
|
||||
|
||||
const LoginPage: () => JSX.Element = () => {
|
||||
const apiUrl: URL = SERVICE_PROVIDER_LOGIN_URL;
|
||||
const apiUrl: URL = SERVICE_PROVIDER_LOGIN_URL;
|
||||
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
if (UserUtil.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [projectSsoConfigList, setProjectSsoConfigList] = useState<Array<ProjectSSO>>([]);
|
||||
const [error, setError] = useState<string | undefined>(undefined);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||
const [projectSsoConfigList, setProjectSsoConfigList] = useState<
|
||||
Array<ProjectSSO>
|
||||
>([]);
|
||||
|
||||
const fetchSsoConfigs = async (email: Email) => {
|
||||
if (email) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
type FetchSSOConfigsFunction = (email: Email) => Promise<void>;
|
||||
|
||||
// get sso config by email.
|
||||
const listResult: HTTPErrorResponse | HTTPResponse<JSONArray> = await API.get(URL.fromString(apiUrl.toString()).addQueryParam("email", email.toString()));
|
||||
const fetchSsoConfigs: FetchSSOConfigsFunction = async (
|
||||
email: Email,
|
||||
): Promise<void> => {
|
||||
if (email) {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
// get sso config by email.
|
||||
const listResult: HTTPErrorResponse | HTTPResponse<JSONArray> =
|
||||
await API.get(
|
||||
URL.fromString(apiUrl.toString()).addQueryParam(
|
||||
"email",
|
||||
email.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
if (listResult instanceof HTTPErrorResponse) {
|
||||
throw listResult;
|
||||
}
|
||||
|
||||
if (!listResult.data || (listResult.data as JSONArray).length === 0) {
|
||||
setError("No SSO configuration found for the email: " + email.toString());
|
||||
} else {
|
||||
setProjectSsoConfigList(ProjectSSO.fromJSONArray(listResult['data'], ProjectSSO));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
setError(API.getFriendlyErrorMessage(error as Error));
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
setError("Email is required to perform this action");
|
||||
if (listResult instanceof HTTPErrorResponse) {
|
||||
throw listResult;
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
};
|
||||
|
||||
const getSsoConfigModelList = (configs: Array<ProjectSSO>) => {
|
||||
return (
|
||||
|
||||
<StaticModelList<ProjectSSO>
|
||||
list={configs}
|
||||
titleField="name"
|
||||
selectedItems={[]}
|
||||
descriptionField="description"
|
||||
onClick={(item: ProjectSSO) => {
|
||||
setIsLoading(true);
|
||||
Navigation.navigate(
|
||||
URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route(
|
||||
`/sso/${item.projectId?.toString()}/${item.id?.toString()
|
||||
}`,
|
||||
),
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>);
|
||||
if (!listResult.data || (listResult.data as JSONArray).length === 0) {
|
||||
setError(
|
||||
"No SSO configuration found for the email: " + email.toString(),
|
||||
);
|
||||
} else {
|
||||
setProjectSsoConfigList(
|
||||
ProjectSSO.fromJSONArray(listResult["data"], ProjectSSO),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
setError(API.getFriendlyErrorMessage(error as Error));
|
||||
}
|
||||
} else {
|
||||
setError("Email is required to perform this action");
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const getProjectName = (projectId: string): string => {
|
||||
const projectNames = projectSsoConfigList.filter((config: ProjectSSO) => config.projectId?.toString() === projectId.toString()).map((config: ProjectSSO) => config.project?.name);
|
||||
return projectNames[0] || 'Project';
|
||||
}
|
||||
type GetSsoConfigModelListFunction = (
|
||||
configs: Array<ProjectSSO>,
|
||||
) => ReactElement;
|
||||
|
||||
if (projectSsoConfigList.length > 0 && !error && !isLoading) {
|
||||
const getSsoConfigModelList: GetSsoConfigModelListFunction = (
|
||||
configs: Array<ProjectSSO>,
|
||||
): ReactElement => {
|
||||
return (
|
||||
<StaticModelList<ProjectSSO>
|
||||
list={configs}
|
||||
titleField="name"
|
||||
selectedItems={[]}
|
||||
descriptionField="description"
|
||||
onClick={(item: ProjectSSO) => {
|
||||
setIsLoading(true);
|
||||
Navigation.navigate(
|
||||
URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route(
|
||||
`/sso/${item.projectId?.toString()}/${item.id?.toString()}`,
|
||||
),
|
||||
),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const projectIds: Array<string> = projectSsoConfigList.map((config: ProjectSSO) => config.projectId?.toString() as string);
|
||||
if (isLoading) {
|
||||
return <PageLoader isVisible={true} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-10 text-center text-xl tracking-tight text-gray-900">
|
||||
Select Project
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Select the project you want to login to.
|
||||
</p>
|
||||
</div>
|
||||
type GetProjectNameFunction = (projectId: string) => string;
|
||||
|
||||
{projectIds.map((projectId: string) => {
|
||||
return (
|
||||
<div key={projectId}>
|
||||
<h3 className="mt-6 font-medium tracking-tight">
|
||||
{getProjectName(projectId)}
|
||||
</h3>
|
||||
{getSsoConfigModelList(projectSsoConfigList.filter((config: ProjectSSO) => config.projectId?.toString() === projectId.toString()))}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
const getProjectName: GetProjectNameFunction = (
|
||||
projectId: string,
|
||||
): string => {
|
||||
const projectNames: Array<string | undefined> = projectSsoConfigList
|
||||
.filter((config: ProjectSSO) => {
|
||||
return config.projectId?.toString() === projectId.toString();
|
||||
})
|
||||
.map((config: ProjectSSO) => {
|
||||
return config.project?.name;
|
||||
});
|
||||
return projectNames[0] || "Project";
|
||||
};
|
||||
|
||||
if (projectSsoConfigList.length > 0 && !error && !isLoading) {
|
||||
const projectIds: Array<string> = projectSsoConfigList.map(
|
||||
(config: ProjectSSO) => {
|
||||
return config.projectId?.toString() as string;
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Login with SSO
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Login with your SSO provider to access your account.
|
||||
</p>
|
||||
</div>
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-10 text-center text-xl tracking-tight text-gray-900">
|
||||
Select Project
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Select the project you want to login to.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<BasicForm
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
error={error}
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
}
|
||||
]}
|
||||
maxPrimaryButtonWidth={true}
|
||||
submitButtonText="Login with SSO"
|
||||
onSubmit={async (data: JSONObject) => {
|
||||
await fetchSsoConfigs(data['email'] as Email);
|
||||
}}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/login")}>
|
||||
<div
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm"
|
||||
>
|
||||
Use username and password insead.
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{projectIds.map((projectId: string) => {
|
||||
return (
|
||||
<div key={projectId}>
|
||||
<h3 className="mt-6 font-medium tracking-tight">
|
||||
{getProjectName(projectId)}
|
||||
</h3>
|
||||
{getSsoConfigModelList(
|
||||
projectSsoConfigList.filter((config: ProjectSSO) => {
|
||||
return (
|
||||
config.projectId?.toString() === projectId.toString()
|
||||
);
|
||||
}),
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||
<div className="">
|
||||
<img
|
||||
className="mx-auto h-12 w-auto"
|
||||
src={OneUptimeLogo}
|
||||
alt="OneUptime"
|
||||
/>
|
||||
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
|
||||
Login with SSO
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm text-gray-600">
|
||||
Login with your SSO provider to access your account.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||
<BasicForm
|
||||
modelType={User}
|
||||
id="login-form"
|
||||
error={error}
|
||||
name="Login"
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
email: true,
|
||||
},
|
||||
fieldType: FormFieldSchemaType.Email,
|
||||
placeholder: "jeff@example.com",
|
||||
required: true,
|
||||
title: "Email",
|
||||
dataTestId: "email",
|
||||
},
|
||||
]}
|
||||
maxPrimaryButtonWidth={true}
|
||||
submitButtonText="Login with SSO"
|
||||
onSubmit={async (data: JSONObject) => {
|
||||
await fetchSsoConfigs(data["email"] as Email);
|
||||
}}
|
||||
footer={
|
||||
<div className="actions text-center mt-4 hover:underline fw-semibold">
|
||||
<div>
|
||||
<Link to={new Route("/accounts/login")}>
|
||||
<div className="text-indigo-500 hover:text-indigo-900 cursor-pointer text-sm">
|
||||
Use username and password insead.
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-10 text-center">
|
||||
<div className="text-muted mb-0 text-gray-500">
|
||||
Don't have an account?{" "}
|
||||
<Link
|
||||
to={new Route("/accounts/register")}
|
||||
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
|
||||
>
|
||||
Register.
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
|
@ -9,9 +9,9 @@ export const LOGIN_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/login"),
|
||||
);
|
||||
|
||||
export const SERVICE_PROVIDER_LOGIN_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/service-provider-login"),
|
||||
);
|
||||
export const SERVICE_PROVIDER_LOGIN_URL: URL = URL.fromURL(
|
||||
IDENTITY_URL,
|
||||
).addRoute(new Route("/service-provider-login"));
|
||||
|
||||
export const FORGOT_PASSWORD_API_URL: URL = URL.fromURL(IDENTITY_URL).addRoute(
|
||||
new Route("/forgot-password"),
|
||||
|
@ -387,8 +387,6 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
|
||||
const APP_NAME: string = "api";
|
||||
|
||||
|
||||
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
new BaseAnalyticsAPI<Log, LogServiceType>(Log, LogService).getRouter(),
|
||||
@ -1208,7 +1206,6 @@ const BaseAPIFeatureSet: FeatureSet = {
|
||||
|
||||
app.use(`/${APP_NAME.toLocaleLowerCase()}`, NotificationAPI);
|
||||
|
||||
|
||||
//attach api's
|
||||
app.use(
|
||||
`/${APP_NAME.toLocaleLowerCase()}`,
|
||||
|
@ -30,7 +30,6 @@ import Express, {
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from "CommonServer/Utils/Express";
|
||||
import JSONWebToken from "CommonServer/Utils/JsonWebToken";
|
||||
import logger from "CommonServer/Utils/Logger";
|
||||
import Response from "CommonServer/Utils/Response";
|
||||
import EmailVerificationToken from "Model/Models/EmailVerificationToken";
|
||||
@ -181,24 +180,10 @@ router.post(
|
||||
// Refresh Permissions for this user here.
|
||||
await AccessTokenService.refreshUserAllPermissions(savedUser.id!);
|
||||
|
||||
const token: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: savedUser.id!,
|
||||
email: savedUser.email!,
|
||||
name: savedUser.name!,
|
||||
timezone: savedUser.timezone || null,
|
||||
isMasterAdmin: savedUser.isMasterAdmin!,
|
||||
isGlobalLogin: true, // This is a general login without SSO. So, we will set this to true. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30),
|
||||
),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserTokenKey(), token, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: savedUser,
|
||||
isGlobalLogin: true,
|
||||
});
|
||||
|
||||
logger.info("User signed up: " + savedUser.email?.toString());
|
||||
@ -577,24 +562,10 @@ router.post(
|
||||
) {
|
||||
logger.info("User logged in: " + alreadySavedUser.email?.toString());
|
||||
|
||||
const token: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: alreadySavedUser.id!,
|
||||
email: alreadySavedUser.email!,
|
||||
name: alreadySavedUser.name!,
|
||||
timezone: alreadySavedUser.timezone || null,
|
||||
isMasterAdmin: alreadySavedUser.isMasterAdmin!,
|
||||
isGlobalLogin: true, // This is a general login without SSO. So, we will set this to true. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(
|
||||
new PositiveNumber(30),
|
||||
),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserTokenKey(), token, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: alreadySavedUser,
|
||||
isGlobalLogin: true,
|
||||
});
|
||||
|
||||
return Response.sendEntityResponse(req, res, alreadySavedUser, User);
|
||||
|
@ -28,7 +28,6 @@ import Express, {
|
||||
ExpressRouter,
|
||||
NextFunction,
|
||||
} from "CommonServer/Utils/Express";
|
||||
import JSONWebToken from "CommonServer/Utils/JsonWebToken";
|
||||
import logger from "CommonServer/Utils/Logger";
|
||||
import Response from "CommonServer/Utils/Response";
|
||||
import ProjectSSO from "Model/Models/ProjectSso";
|
||||
@ -41,79 +40,111 @@ const router: ExpressRouter = Express.getRouter();
|
||||
// This route is used to get the SSO config for the user.
|
||||
// when the user logs in from OneUptime and not from the IDP.
|
||||
|
||||
router.get("/service-provider-login", async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
|
||||
router.get(
|
||||
"/service-provider-login",
|
||||
async (req: ExpressRequest, res: ExpressResponse): Promise<void> => {
|
||||
if (!req.query["email"]) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException("Email is required"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!req.query['email']) {
|
||||
return Response.sendErrorResponse(req, res, new BadRequestException("Email is required"));
|
||||
}
|
||||
const email: Email = new Email(req.query["email"] as string);
|
||||
|
||||
const email: Email = new Email(req.query['email'] as string);
|
||||
if (!email) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException("Email is required"),
|
||||
);
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
return Response.sendErrorResponse(req, res, new BadRequestException("Email is required"));
|
||||
}
|
||||
// get sso config for this user.
|
||||
|
||||
// get sso config for this user.
|
||||
|
||||
const user: User | null = await UserService.findOneBy({
|
||||
query: { email: email },
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
if (!user) {
|
||||
return Response.sendErrorResponse(req, res, new BadRequestException("No SSO config found for this user"));
|
||||
}
|
||||
|
||||
|
||||
const userId: ObjectID = user.id!;
|
||||
|
||||
if (!userId) {
|
||||
return Response.sendErrorResponse(req, res, new BadRequestException("No SSO config found for this user"));
|
||||
}
|
||||
|
||||
const projectUserBelongsTo: Array<ObjectID> = (await TeamMemberService.findBy({
|
||||
query: { userId: userId },
|
||||
select: {
|
||||
projectId: true,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
})).map((teamMember: TeamMember) => teamMember.projectId!);
|
||||
|
||||
if (projectUserBelongsTo.length === 0) {
|
||||
return Response.sendErrorResponse(req, res, new BadRequestException("No SSO config found for this user"));
|
||||
}
|
||||
|
||||
const projectSSOList: Array<ProjectSSO> = await ProjectSSOService.findBy({
|
||||
query: { projectId: QueryHelper.any(projectUserBelongsTo), isEnabled: true },
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
_id: true,
|
||||
projectId: true,
|
||||
project: {
|
||||
name: true,
|
||||
const user: User | null = await UserService.findOneBy({
|
||||
query: { email: email },
|
||||
select: {
|
||||
_id: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEntityArrayResponse(req, res, projectSSOList, projectSSOList.length, ProjectSSO);
|
||||
if (!user) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException("No SSO config found for this user"),
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
const userId: ObjectID = user.id!;
|
||||
|
||||
if (!userId) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException("No SSO config found for this user"),
|
||||
);
|
||||
}
|
||||
|
||||
const projectUserBelongsTo: Array<ObjectID> = (
|
||||
await TeamMemberService.findBy({
|
||||
query: { userId: userId },
|
||||
select: {
|
||||
projectId: true,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
})
|
||||
).map((teamMember: TeamMember) => {
|
||||
return teamMember.projectId!;
|
||||
});
|
||||
|
||||
if (projectUserBelongsTo.length === 0) {
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
res,
|
||||
new BadRequestException("No SSO config found for this user"),
|
||||
);
|
||||
}
|
||||
|
||||
const projectSSOList: Array<ProjectSSO> = await ProjectSSOService.findBy({
|
||||
query: {
|
||||
projectId: QueryHelper.any(projectUserBelongsTo),
|
||||
isEnabled: true,
|
||||
},
|
||||
limit: LIMIT_PER_PROJECT,
|
||||
skip: 0,
|
||||
select: {
|
||||
name: true,
|
||||
description: true,
|
||||
_id: true,
|
||||
projectId: true,
|
||||
project: {
|
||||
name: true,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isRoot: true,
|
||||
},
|
||||
});
|
||||
|
||||
return Response.sendEntityArrayResponse(
|
||||
req,
|
||||
res,
|
||||
projectSSOList,
|
||||
projectSSOList.length,
|
||||
ProjectSSO,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/sso/:projectId/:projectSsoId",
|
||||
@ -341,9 +372,9 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
|
||||
if (projectSSO.issuerURL.toString() !== issuerUrl) {
|
||||
logger.error(
|
||||
"Issuer URL does not match. It should be " +
|
||||
projectSSO.issuerURL.toString() +
|
||||
" but it is " +
|
||||
issuerUrl.toString(),
|
||||
projectSSO.issuerURL.toString() +
|
||||
" but it is " +
|
||||
issuerUrl.toString(),
|
||||
);
|
||||
return Response.sendErrorResponse(
|
||||
req,
|
||||
@ -454,39 +485,18 @@ const loginUserWithSso: LoginUserWithSsoFunction = async (
|
||||
|
||||
const projectId: ObjectID = new ObjectID(req.params["projectId"] as string);
|
||||
|
||||
const ssoToken: string = JSONWebToken.sign({
|
||||
data: {
|
||||
userId: alreadySavedUser.id!,
|
||||
projectId: projectId,
|
||||
name: alreadySavedUser.name!,
|
||||
email: email,
|
||||
isMasterAdmin: false,
|
||||
isGeneralLogin: false,
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(new PositiveNumber(30)),
|
||||
alreadySavedUser.email = email;
|
||||
|
||||
CookieUtil.setSSOCookie({
|
||||
user: alreadySavedUser,
|
||||
projectId: projectId,
|
||||
expressResponse: res,
|
||||
});
|
||||
|
||||
const oneUptimeToken: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: alreadySavedUser.id!,
|
||||
email: alreadySavedUser.email!,
|
||||
name: alreadySavedUser.name!,
|
||||
isMasterAdmin: alreadySavedUser.isMasterAdmin!,
|
||||
timezone: alreadySavedUser.timezone || null,
|
||||
isGlobalLogin: false, // This is a general login without SSO. So, we will set this to false. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(new PositiveNumber(30)),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserTokenKey(), oneUptimeToken, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
});
|
||||
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserSSOKey(projectId), ssoToken, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
CookieUtil.setUserCookie({
|
||||
expressResponse: res,
|
||||
user: alreadySavedUser,
|
||||
isGlobalLogin: false,
|
||||
});
|
||||
|
||||
// Refresh Permissions for this user here.
|
||||
|
11
Common/Types/CookieName.ts
Normal file
11
Common/Types/CookieName.ts
Normal file
@ -0,0 +1,11 @@
|
||||
enum CookieName {
|
||||
UserID = "user-id",
|
||||
Email = "user-email",
|
||||
Token = "user-token",
|
||||
Name = "user-name",
|
||||
Timezone = "user-timezone",
|
||||
IsMasterAdmin = "user-is-master-admin",
|
||||
ProfilePicID = "user-profile-pic-id",
|
||||
}
|
||||
|
||||
export default CookieName;
|
@ -2,13 +2,137 @@ import { ExpressRequest, ExpressResponse } from "./Express";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import { CookieOptions } from "express";
|
||||
import JSONWebToken from "./JsonWebToken";
|
||||
import User from "Model/Models/User";
|
||||
import OneUptimeDate from "Common/Types/Date";
|
||||
import PositiveNumber from "Common/Types/PositiveNumber";
|
||||
import CookieName from "Common/Types/CookieName";
|
||||
|
||||
export default class CookieUtil {
|
||||
// set cookie with express response
|
||||
|
||||
public static setSSOCookie(data: {
|
||||
user: User;
|
||||
projectId: ObjectID;
|
||||
expressResponse: ExpressResponse;
|
||||
}): void {
|
||||
const { user, projectId, expressResponse: res } = data;
|
||||
|
||||
const ssoToken: string = JSONWebToken.sign({
|
||||
data: {
|
||||
userId: user.id!,
|
||||
projectId: projectId,
|
||||
name: user.name!,
|
||||
email: user.email,
|
||||
isMasterAdmin: false,
|
||||
isGeneralLogin: false,
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(new PositiveNumber(30)),
|
||||
});
|
||||
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserSSOKey(projectId), ssoToken, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
});
|
||||
}
|
||||
|
||||
public static setUserCookie(data: {
|
||||
expressResponse: ExpressResponse;
|
||||
user: User;
|
||||
isGlobalLogin: boolean;
|
||||
}): void {
|
||||
const { expressResponse: res, user, isGlobalLogin } = data;
|
||||
|
||||
const token: string = JSONWebToken.signUserLoginToken({
|
||||
tokenData: {
|
||||
userId: user.id!,
|
||||
email: user.email!,
|
||||
name: user.name!,
|
||||
timezone: user.timezone || null,
|
||||
isMasterAdmin: user.isMasterAdmin!,
|
||||
isGlobalLogin: isGlobalLogin, // This is a general login without SSO. So, we will set this to true. This will give access to all the projects that dont require SSO.
|
||||
},
|
||||
expiresInSeconds: OneUptimeDate.getSecondsInDays(new PositiveNumber(30)),
|
||||
});
|
||||
|
||||
// Set a cookie with token.
|
||||
CookieUtil.setCookie(res, CookieUtil.getUserTokenKey(), token, {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: true,
|
||||
});
|
||||
|
||||
if (user.id) {
|
||||
// set user id cookie
|
||||
CookieUtil.setCookie(res, CookieName.UserID, user.id!.toString(), {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (user.email) {
|
||||
// set user email cookie
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieName.Email,
|
||||
user.email?.toString() || "",
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (user.name) {
|
||||
// set user name cookie
|
||||
CookieUtil.setCookie(res, CookieName.Name, user.name?.toString() || "", {
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
});
|
||||
}
|
||||
|
||||
if (user.timezone) {
|
||||
// set user timezone cookie
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieName.Timezone,
|
||||
user.timezone?.toString() || "",
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (user.isMasterAdmin) {
|
||||
// set user isMasterAdmin cookie
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieName.IsMasterAdmin,
|
||||
user.isMasterAdmin?.toString() || "",
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if (user.profilePictureId) {
|
||||
// set user profile picture id cookie
|
||||
CookieUtil.setCookie(
|
||||
res,
|
||||
CookieName.ProfilePicID,
|
||||
user.profilePictureId?.toString() || "",
|
||||
{
|
||||
maxAge: OneUptimeDate.getMillisecondsInDays(new PositiveNumber(30)),
|
||||
httpOnly: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static setCookie(
|
||||
res: ExpressResponse,
|
||||
name: string,
|
||||
name: string | CookieName,
|
||||
value: string,
|
||||
options: CookieOptions,
|
||||
): void {
|
||||
@ -37,10 +161,10 @@ export default class CookieUtil {
|
||||
|
||||
public static getUserTokenKey(id?: ObjectID): string {
|
||||
if (!id) {
|
||||
return `user-token`;
|
||||
return CookieName.Token;
|
||||
}
|
||||
|
||||
return `user-token-${id.toString()}`;
|
||||
return `${CookieName.Token}-${id.toString()}`;
|
||||
}
|
||||
|
||||
public static getUserSSOKey(id: ObjectID): string {
|
||||
|
@ -121,7 +121,6 @@ const BasicForm: ForwardRefExoticComponent<any> = forwardRef(
|
||||
null,
|
||||
);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (formSteps && formSteps.length > 0 && formSteps[0]) {
|
||||
setCurrentFormStepId(formSteps[0].id);
|
||||
|
@ -97,5 +97,4 @@ export default interface Field<TEntity> {
|
||||
// set this to true if you want to show this field in the form even when the form is in edit mode.
|
||||
doNotShowWhenEditing?: boolean | undefined;
|
||||
doNotShowWhenCreating?: boolean | undefined;
|
||||
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ import { JSONObject, JSONValue } from "Common/Types/JSON";
|
||||
import JSONFunctions from "Common/Types/JSONFunctions";
|
||||
import Typeof from "Common/Types/Typeof";
|
||||
import UniversalCookies, { CookieSetOptions } from "universal-cookie";
|
||||
import CookieName from "Common/Types/CookieName";
|
||||
|
||||
export default class Cookie {
|
||||
public static setItem(
|
||||
key: string,
|
||||
key: CookieName,
|
||||
value: JSONValue | Email | URL,
|
||||
options?:
|
||||
| {
|
||||
@ -42,9 +43,9 @@ export default class Cookie {
|
||||
cookies.set(key, value as string, cookieOptions);
|
||||
}
|
||||
|
||||
public static getItem(key: string): JSONValue {
|
||||
public static getItem(cookieName: CookieName): JSONValue {
|
||||
const cookies: UniversalCookies = new UniversalCookies();
|
||||
const value: JSONValue = cookies.get(key) as JSONValue;
|
||||
const value: JSONValue = cookies.get(cookieName) as JSONValue;
|
||||
|
||||
try {
|
||||
if (value) {
|
||||
@ -58,13 +59,13 @@ export default class Cookie {
|
||||
}
|
||||
}
|
||||
|
||||
public static removeItem(key: string): void {
|
||||
public static removeItem(key: CookieName): void {
|
||||
const cookies: UniversalCookies = new UniversalCookies();
|
||||
cookies.remove(key);
|
||||
}
|
||||
|
||||
// check if cookie exists
|
||||
public static exists(key: string): boolean {
|
||||
public static exists(key: CookieName): boolean {
|
||||
const cookies: UniversalCookies = new UniversalCookies();
|
||||
return Boolean(cookies.get(key));
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import { jwtDecode } from "jwt-decode";
|
||||
|
||||
export default class JWTToken {
|
||||
public static decodeToken(token: string): JSONObject {
|
||||
return jwtDecode(token);
|
||||
}
|
||||
}
|
||||
export default class JWTToken {
|
||||
public static decodeToken(token: string): JSONObject {
|
||||
return jwtDecode(token);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,13 @@ import URL from "Common/Types/API/URL";
|
||||
import Dictionary from "Common/Types/Dictionary";
|
||||
import Email from "Common/Types/Email";
|
||||
import BadDataException from "Common/Types/Exception/BadDataException";
|
||||
import { JSONObject } from "Common/Types/JSON";
|
||||
import { JSONObject, JSONValue } from "Common/Types/JSON";
|
||||
import Name from "Common/Types/Name";
|
||||
import ObjectID from "Common/Types/ObjectID";
|
||||
import Timezone from "Common/Types/Timezone";
|
||||
import API from "Common/Utils/API";
|
||||
import JWTToken from "./JWT";
|
||||
import Cookie from "./Cookie";
|
||||
import CookieName from "Common/Types/CookieName";
|
||||
|
||||
export default class User {
|
||||
public static setProfilePicId(id: ObjectID | null): void {
|
||||
@ -22,6 +23,16 @@ export default class User {
|
||||
}
|
||||
|
||||
public static getProfilePicId(): ObjectID | null {
|
||||
// check cookie first.
|
||||
|
||||
const profilePicIdCookie: JSONValue | string = Cookie.getItem(
|
||||
CookieName.ProfilePicID,
|
||||
);
|
||||
|
||||
if (profilePicIdCookie) {
|
||||
return new ObjectID(profilePicIdCookie as string);
|
||||
}
|
||||
|
||||
if (!LocalStorage.getItem("profile_pic_id")) {
|
||||
return null;
|
||||
}
|
||||
@ -36,6 +47,16 @@ export default class User {
|
||||
}
|
||||
|
||||
public static getSavedUserTimezone(): Timezone {
|
||||
// check cookie first.
|
||||
|
||||
const userTimezoneCookie: JSONValue | string = Cookie.getItem(
|
||||
CookieName.Timezone,
|
||||
);
|
||||
|
||||
if (userTimezoneCookie) {
|
||||
return userTimezoneCookie as Timezone;
|
||||
}
|
||||
|
||||
return LocalStorage.getItem("user_timezone") as Timezone;
|
||||
}
|
||||
|
||||
@ -52,10 +73,24 @@ export default class User {
|
||||
}
|
||||
|
||||
public static getUserId(): ObjectID {
|
||||
// check cookie first.
|
||||
const userIdCookie: JSONValue | string = Cookie.getItem(CookieName.UserID);
|
||||
|
||||
if (userIdCookie) {
|
||||
return new ObjectID(userIdCookie as string);
|
||||
}
|
||||
|
||||
return new ObjectID((LocalStorage.getItem("user_id") as string) || "");
|
||||
}
|
||||
|
||||
public static getName(): Name {
|
||||
// check cookie first.
|
||||
const userNameCookie: JSONValue | string = Cookie.getItem(CookieName.Name);
|
||||
|
||||
if (userNameCookie) {
|
||||
return new Name(userNameCookie as string);
|
||||
}
|
||||
|
||||
return new Name((LocalStorage.getItem("user_name") as string) || "");
|
||||
}
|
||||
|
||||
@ -63,32 +98,17 @@ export default class User {
|
||||
LocalStorage.setItem("user_name", name.toString());
|
||||
}
|
||||
|
||||
public static refreshUserDataFromToken(token: string): void {
|
||||
const decodedToken: JSONObject = JWTToken.decodeToken(token);
|
||||
|
||||
if(decodedToken["userId"]) {
|
||||
this.setUserId(new ObjectID(decodedToken["userId"] as string));
|
||||
}
|
||||
|
||||
if(decodedToken["email"]) {
|
||||
this.setEmail(new Email(decodedToken["email"] as string));
|
||||
}
|
||||
|
||||
if(decodedToken["name"]) {
|
||||
this.setName(new Name(decodedToken["name"] as string));
|
||||
}
|
||||
|
||||
if(decodedToken["isMasterAdmin"]) {
|
||||
this.setIsMasterAdmin(decodedToken["isMasterAdmin"] as boolean);
|
||||
}
|
||||
|
||||
if(decodedToken["timezone"]) {
|
||||
this.setSavedUserTimezone(decodedToken["timezone"] as Timezone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static getEmail(): Email | null {
|
||||
// check cookie first.
|
||||
|
||||
const userEmailCookie: JSONValue | string = Cookie.getItem(
|
||||
CookieName.Email,
|
||||
);
|
||||
|
||||
if (userEmailCookie) {
|
||||
return new Email(userEmailCookie as string);
|
||||
}
|
||||
|
||||
if (!LocalStorage.getItem("user_email")) {
|
||||
return null;
|
||||
}
|
||||
@ -138,6 +158,16 @@ export default class User {
|
||||
}
|
||||
|
||||
public static isMasterAdmin(): boolean {
|
||||
// check cookie first.
|
||||
|
||||
const isMasterAdminCookie: JSONValue | string = Cookie.getItem(
|
||||
CookieName.IsMasterAdmin,
|
||||
);
|
||||
|
||||
if (isMasterAdminCookie) {
|
||||
return isMasterAdminCookie === "true" || isMasterAdminCookie === true;
|
||||
}
|
||||
|
||||
return LocalStorage.getItem("is_master_admin") as boolean;
|
||||
}
|
||||
|
||||
|
@ -54,9 +54,6 @@ import {
|
||||
useParams,
|
||||
} from "react-router-dom";
|
||||
import useAsyncEffect from "use-async-effect";
|
||||
import User from "CommonUI/src/Utils/User";
|
||||
import Cookie from "CommonUI/src/Utils/Cookie";
|
||||
import { JSONValue } from "Common/Types/JSON";
|
||||
|
||||
const App: () => JSX.Element = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
@ -78,18 +75,6 @@ const App: () => JSX.Element = () => {
|
||||
|
||||
const [hasPaymentMethod, setHasPaymentMethod] = useState<boolean>(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const token: JSONValue = Cookie.getItem("user_token");
|
||||
|
||||
if(!token) {
|
||||
return;
|
||||
}
|
||||
|
||||
User.refreshUserDataFromToken(token.toString());
|
||||
}, []);
|
||||
|
||||
|
||||
useAsyncEffect(async () => {
|
||||
try {
|
||||
if (selectedProject && selectedProject._id) {
|
||||
|
@ -18,9 +18,8 @@ export interface ComponentProps {
|
||||
const MonitorCriteriaIncidentForm: FunctionComponent<ComponentProps> = (
|
||||
props: ComponentProps,
|
||||
): ReactElement => {
|
||||
|
||||
|
||||
const [showAdvancedFields, setShowAdvancedFields] = React.useState<boolean>(false);
|
||||
const [showAdvancedFields, setShowAdvancedFields] =
|
||||
React.useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (props.initialValue && props.initialValue.remediationNotes) {
|
||||
@ -111,12 +110,20 @@ const MonitorCriteriaIncidentForm: FunctionComponent<ComponentProps> = (
|
||||
required: false,
|
||||
showIf: () => {
|
||||
return showAdvancedFields;
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{!showAdvancedFields && <Button title="Show Advanced Fields" onClick={() => setShowAdvancedFields(true)} buttonStyle={ButtonStyleType.SECONDARY_LINK} />}
|
||||
{!showAdvancedFields && (
|
||||
<Button
|
||||
title="Show Advanced Fields"
|
||||
onClick={() => {
|
||||
return setShowAdvancedFields(true);
|
||||
}}
|
||||
buttonStyle={ButtonStyleType.SECONDARY_LINK}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* <div className='mt-4'>
|
||||
<Button
|
||||
|
@ -590,7 +590,7 @@ const IncidentView: FunctionComponent<
|
||||
}}
|
||||
/>
|
||||
|
||||
<CardModelDetail
|
||||
<CardModelDetail
|
||||
name="Remediation Notes"
|
||||
cardProps={{
|
||||
title: "Remediation Notes",
|
||||
@ -628,7 +628,6 @@ const IncidentView: FunctionComponent<
|
||||
modelId: modelId,
|
||||
}}
|
||||
/>
|
||||
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
@ -1016,8 +1016,6 @@ export default class Incident extends BaseModel {
|
||||
})
|
||||
public isCreatedAutomatically?: boolean = undefined;
|
||||
|
||||
|
||||
|
||||
@ColumnAccessControl({
|
||||
create: [
|
||||
Permission.ProjectOwner,
|
||||
@ -1050,6 +1048,4 @@ export default class Incident extends BaseModel {
|
||||
type: ColumnType.Markdown,
|
||||
})
|
||||
public remediationNotes?: string = undefined;
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user