Skip to content

Commit

Permalink
🐛 fix(auth): fix double redirect
Browse files Browse the repository at this point in the history
  • Loading branch information
thrownullexception committed Jan 23, 2024
1 parent 6a2f2b3 commit 4fa4962
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 47 deletions.
171 changes: 171 additions & 0 deletions src/__tests__/api/integrations/storage/active.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import activeHandler from "pages/api/integrations/storage/active";
import credentialsHandler from "pages/api/integrations/storage/credentials";
import {
createAuthenticatedMocks,
setupAllTestData,
} from "__tests__/api/_test-utils";
import { createKeyValueDomainPersistenceService } from "backend/lib/key-value";

describe("/api/integrations/actions/[key]/index", () => {
beforeAll(async () => {
const _currentStorageKeyValueStoreApiService =
createKeyValueDomainPersistenceService<string>("current-storage");

_currentStorageKeyValueStoreApiService.clearItem();

await setupAllTestData(["users", "credentials"]);
});

describe("CREATE", () => {
describe("with invalid input", () => {
it("should throw error on request", async () => {
const { req, res } = createAuthenticatedMocks({
method: "POST",
body: {
storageKey: "s3",
configuration: {
region: "some invalid region",
},
},
});
await activeHandler(req, res);

expect(res._getStatusCode()).toBe(400);
expect(res._getJSONData()).toMatchInlineSnapshot(`
{
"message": "Invalid Request",
"method": "POST",
"name": "BadRequestError",
"path": "",
"statusCode": 400,
"validations": {
"accessKeyId": "Access Key Id is required",
"secretAccessKey": "Secret Access Key is required",
},
}
`);
});

it("should not save the credentials", async () => {
const { req: credentialsReq, res: credentialsRes } =
createAuthenticatedMocks({
method: "POST",
body: {
_password: "password",
},
});
await credentialsHandler(credentialsReq, credentialsRes);

expect(credentialsRes._getJSONData()).toMatchInlineSnapshot(`{}`);
});
});

describe("valid", () => {
it("should activate a storage key", async () => {
const { req, res } = createAuthenticatedMocks({
method: "POST",
body: {
storageKey: "s3",
configuration: {
key1: "should not be saved",
accessKeyId: "some-access-key-id",
secretAccessKey: "some-secret-access-key",
region: "some-region",
},
},
});
await activeHandler(req, res);

expect(res._getStatusCode()).toBe(201);
});

it("should show the activated storage", async () => {
const { req: activeReq, res: activeRes } = createAuthenticatedMocks({
method: "GET",
});

await activeHandler(activeReq, activeRes);

expect(activeRes._getJSONData()).toMatchInlineSnapshot(`
{
"data": "s3",
}
`);
});

it("should save the credentials", async () => {
const { req: credentialsReq, res: credentialsRes } =
createAuthenticatedMocks({
method: "POST",
body: {
_password: "password",
},
});
await credentialsHandler(credentialsReq, credentialsRes);

expect(credentialsRes._getStatusCode()).toBe(201);
expect(credentialsRes._getJSONData()).toMatchInlineSnapshot(`
{
"accessKeyId": "some-access-key-id",
"region": "some-region",
"secretAccessKey": "some-secret-access-key",
}
`);
});
});
});

describe("UPDATE", () => {
it("should update storage configuration", async () => {
const { req, res } = createAuthenticatedMocks({
method: "POST",
body: {
storageKey: "cloudinary",
configuration: {
invalid: "should not show up",
apiKey: "updated some-api-key",
apiSecret: "updated some-api-secret",
cloudName: "updated some-cloud-name",
},
},
});
await activeHandler(req, res);

expect(res._getStatusCode()).toBe(201);
});

it("should show the activated storage", async () => {
const { req: activeReq, res: activeRes } = createAuthenticatedMocks({
method: "GET",
});

await activeHandler(activeReq, activeRes);

expect(activeRes._getJSONData()).toMatchInlineSnapshot(`
{
"data": "cloudinary",
}
`);
});

it("should return updated storage config", async () => {
const { req: credentialsReq, res: credentialsRes } =
createAuthenticatedMocks({
method: "POST",
body: {
_password: "password",
},
});
await credentialsHandler(credentialsReq, credentialsRes);

expect(credentialsRes._getStatusCode()).toBe(201);
expect(credentialsRes._getJSONData()).toMatchInlineSnapshot(`
{
"apiKey": "updated some-api-key",
"apiSecret": "updated some-api-secret",
"cloudName": "updated some-cloud-name",
}
`);
});
});
});
4 changes: 0 additions & 4 deletions src/frontend/_layouts/app/IsSignedIn.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import React, { ReactNode, useEffect } from "react";
import { useRouter } from "next/router";
import { AUTHENTICATED_ACCOUNT_URL } from "frontend/hooks/auth/user.store";
import { useUserAuthenticatedState } from "frontend/hooks/auth/useAuthenticateUser";
import { useQueryClient } from "react-query";
import { removeAuthToken } from "frontend/hooks/auth/auth.store";
import { getQueryCachekey } from "frontend/lib/data/constants/getQueryCacheKey";
import { ComponentIsLoading } from "frontend/design-system/components/ComponentIsLoading";
import { NAVIGATION_LINKS } from "frontend/lib/routing/links";
import { useAppTheme } from "../useAppTheme";

const useUserAuthCheck = () => {
const userAuthenticatedState = useUserAuthenticatedState();
const router = useRouter();
const queryClient = useQueryClient();

useEffect(() => {
if (userAuthenticatedState === false) {
removeAuthToken();
router.replace(NAVIGATION_LINKS.AUTH_SIGNIN);
queryClient.invalidateQueries(
getQueryCachekey(AUTHENTICATED_ACCOUNT_URL)
);
Expand Down
29 changes: 15 additions & 14 deletions src/frontend/design-system/components/DropdownMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export interface IDropDownMenuItem extends IGroupActionButton {

export interface IProps {
menuItems: IDropDownMenuItem[];
isMakingActionRequest?: boolean;
ariaLabel: string;
disabled?: boolean;
ellipsis?: true;
Expand Down Expand Up @@ -125,7 +124,6 @@ const CurrentButton = styled(SoftButtonStyled)`
`;
export function DropDownMenu({
menuItems: menuItems$1,
isMakingActionRequest,
disabled,
ellipsis,
ariaLabel,
Expand All @@ -135,7 +133,7 @@ export function DropDownMenu({
const router = useRouter();

const toggleDropDown = () => {
if (!isMakingActionRequest && !disabled) {
if (!disabled) {
setDropDownOpen(togglePreviousState);
}
};
Expand Down Expand Up @@ -185,12 +183,11 @@ export function DropDownMenu({

const currentItem = (
<Stack spacing={4} align="center">
{/* eslint-disable-next-line no-nested-ternary */}
{isMakingActionRequest ? (
{currentMenuItem.isMakingRequest ? (
<Spin as={Loader} size={14} />
) : systemIcon ? (
) : (
<SystemIcon icon={systemIcon} size={14} />
) : null}
)}
<Label>{label}</Label>
</Stack>
);
Expand All @@ -199,7 +196,7 @@ export function DropDownMenu({
return (
<SoftButtonStyled
size="sm"
disabled={isMakingActionRequest || disabled}
disabled={currentMenuItem.isMakingRequest || disabled}
onClick={() => runAction(currentMenuItem)}
>
{currentItem}
Expand Down Expand Up @@ -228,7 +225,7 @@ export function DropDownMenu({
<>
<CurrentButton
size="sm"
disabled={isMakingActionRequest || disabled}
disabled={disabled || currentMenuItem.isMakingRequest}
onClick={() => runAction(currentMenuItem)}
type="button"
>
Expand All @@ -248,11 +245,15 @@ export function DropDownMenu({
type="button"
>
<Stack>
<SystemIcon
icon={menuItem.systemIcon}
size={14}
color={menuItem.disabled ? "muted-text" : "main-text"}
/>
{currentMenuItem.isMakingRequest ? (
<Spin as={Loader} size={14} />
) : (
<SystemIcon
icon={menuItem.systemIcon}
size={14}
color={menuItem.disabled ? "muted-text" : "main-text"}
/>
)}
<Typo.XS
as="span"
color={menuItem.disabled ? "muted" : undefined}
Expand Down
9 changes: 0 additions & 9 deletions src/frontend/hooks/auth/useAuthenticateUser.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { NAVIGATION_LINKS } from "frontend/lib/routing/links";
import { TemporayStorageService } from "frontend/lib/storage";
import { useRouter } from "next/router";
import { useEffect } from "react";
import { createStore } from "frontend/lib/store";
import { STORAGE_CONSTANTS } from "frontend/lib/storage/constants";
import { DataStates } from "frontend/lib/data/types";
import * as AuthStore from "./auth.store";

Expand Down Expand Up @@ -35,7 +31,6 @@ export const useUserAuthenticatedState = () => {
};

export function useAuthenticateUser() {
const router = useRouter();
const setIsAuthenticated = useIsAuthenticatedStore(
(store) => store.setIsAuthenticated
);
Expand All @@ -44,9 +39,5 @@ export function useAuthenticateUser() {
AuthStore.setAuthToken(authToken, rememberMe);

setIsAuthenticated(true);
router.push(
TemporayStorageService.getString(STORAGE_CONSTANTS.PREVIOUS_AUTH_URL) ||
NAVIGATION_LINKS.DASHBOARD.HOME
);
};
}
3 changes: 2 additions & 1 deletion src/frontend/lib/data/makeRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { REQUEST_ERROR_CODES } from "shared/constants/auth";
import { TemporayStorageService } from "frontend/lib/storage";
import { STORAGE_CONSTANTS } from "frontend/lib/storage/constants";
import { ApiRequestError } from "./_errors";
import { NAVIGATION_LINKS } from "../routing/links";

const pathWithBaseUrl = (path: string) => {
if (path.startsWith("http")) {
Expand Down Expand Up @@ -35,7 +36,7 @@ const handleRequestError = async (response: Response, errorMessage: string) => {
STORAGE_CONSTANTS.PREVIOUS_AUTH_URL,
window.location.href
);
window.location.replace("/auth");
window.location.replace(NAVIGATION_LINKS.AUTH_SIGNIN);
}
}
throw new ApiRequestError(response.status, error.message || errorMessage);
Expand Down
14 changes: 7 additions & 7 deletions src/frontend/views/entity/Actions/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,16 @@ export function ActionForm({
value: integration,
}));

const [formValues, setFormValues] = useState<Partial<IFormAction>>({});
const [integration, setIntegration] = useState("");
const [action, setAction] = useState("");

const implementations = useIntegrationImplementationsList(
formValues.integration
);
const implementations = useIntegrationImplementationsList(integration);

const currentActionTitle = integrationsListMap[formValues.integration]?.title;
const currentActionTitle = integrationsListMap[integration]?.title;

const selectedImplementation = Object.fromEntries(
Object.entries(
implementations.data.find(({ key }) => key === formValues.action)
implementations.data.find(({ key }) => key === action)
?.configurationSchema || {}
).map(([key, value]) => [
`${CONFIGURATION_FORM_PREFIX}${key}`,
Expand Down Expand Up @@ -89,6 +88,7 @@ export function ActionForm({
formState: ($) => ({
disabled: $.action === "update" || !$.formValues.trigger,
}),
onChange: setIntegration,
},
action: {
label: "Action",
Expand All @@ -101,6 +101,7 @@ export function ActionForm({
formState: ($) => ({
disabled: !$.formValues.trigger,
}),
onChange: setAction,
},
...selectedImplementation,
};
Expand All @@ -122,7 +123,6 @@ export function ActionForm({
initialValues={initialValues$1}
fields={fields}
systemIcon={formAction === "create" ? "Plus" : "Save"}
onChange={setFormValues}
action={formAction}
onSubmit={async (value) => {
const cleanedConfigurationForm = Object.entries(value).reduce(
Expand Down
4 changes: 1 addition & 3 deletions src/frontend/views/integrations/storage/Credentials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function StorageCredentialsSettings() {
label: datum.title,
value: datum.key,
})),
onChange: setCurrentStorage,
validations: [
{
validationType: "required",
Expand Down Expand Up @@ -89,9 +90,6 @@ export function StorageCredentialsSettings() {
</>
) : (
<SchemaForm<{ storageKey: string }>
onChange={(data) => {
setCurrentStorage(data.storageKey);
}}
fields={{
storageKey: storageFormConfig,
...(currentStorageDetails?.configurationSchema || {}),
Expand Down
Loading

0 comments on commit 4fa4962

Please sign in to comment.