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

Allow super user access to the coral UI #2455

Merged
merged 10 commits into from
Aug 29, 2024
120 changes: 46 additions & 74 deletions coral/src/app/context-provider/AuthProvider.test.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,53 @@
import { cleanup, screen, within } from "@testing-library/react";
import { customRender } from "src/services/test-utils/render-with-wrappers";
import {
cleanup,
render,
renderHook,
screen,
waitFor,
within,
} from "@testing-library/react";
import {
AuthProvider,
useAuthContext,
} from "src/app/context-provider/AuthProvider";
import { waitForElementToBeRemoved } from "@testing-library/react/pure";
import { testAuthUser } from "src/domain/auth-user/auth-user-test-helper";
import { setupFeatureFlagMock } from "src/services/feature-flags/test-helper";
import { FeatureFlag } from "src/services/feature-flags/types";
import { QueryClientProvider } from "@tanstack/react-query";
import { getQueryClientForTests } from "src/services/test-utils/query-client-tests";

const getAuthMock = jest.fn();
jest.mock("src/domain/auth-user", () => ({
...jest.requireActual("src/domain/auth-user"),
getAuth: () => getAuthMock(),
}));

const AllProviders = ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={getQueryClientForTests()}>
<AuthProvider>{children}</AuthProvider>
</QueryClientProvider>
);

const ChildComponent = () => {
const authUser = useAuthContext();

return <div data-testid={"auth-provider-child"}>{authUser?.username}</div>;
};

const mockAuthUser = { ...testAuthUser, username: "Jon Snow" };
const mockAuthSuperAdminUser = {
...testAuthUser,
username: "Arya Stark",
userrole: "SUPERADMIN",
};

describe("AuthProvider.tsx", () => {
describe("gets the auth user", () => {
beforeEach(() => {
getAuthMock.mockReturnValue({});
customRender(
<AuthProvider>
render(
<AllProviders>
<ChildComponent />
</AuthProvider>,
{
queryClient: true,
}
</AllProviders>
);
});

Expand All @@ -54,13 +69,10 @@ describe("AuthProvider.tsx", () => {
describe("renders an auth provider with given children when auth user is available", () => {
beforeEach(async () => {
getAuthMock.mockReturnValue(mockAuthUser);
customRender(
<AuthProvider>
render(
<AllProviders>
<ChildComponent />
</AuthProvider>,
{
queryClient: true,
}
</AllProviders>
);

await waitForElementToBeRemoved(screen.getByText("Loading Klaw"));
Expand All @@ -84,75 +96,35 @@ describe("AuthProvider.tsx", () => {
});
});

describe("does not render the auth provider with given children when auth user is SUPERADMIN", () => {
beforeEach(async () => {
getAuthMock.mockReturnValue({ mockAuthUser, userrole: "SUPERADMIN" });
customRender(
<AuthProvider>
<ChildComponent />
</AuthProvider>,
{
queryClient: true,
}
);

await waitForElementToBeRemoved(screen.getByText("Loading Klaw"));
});

describe("useAuthContext hook", () => {
afterEach(() => {
jest.resetAllMocks();
cleanup();
});

it("does not returns context provider with given children", () => {
const childElement = screen.queryByTestId("auth-provider-child");
expect(childElement).not.toBeInTheDocument();
});
it("returns the correct user data and identifies a SUPERADMIN user", async () => {
getAuthMock.mockResolvedValue(mockAuthSuperAdminUser);

it("shows a dialog informing superadmin they can not access coral", async () => {
const dialog = await screen.findByRole("dialog", {
name: "You're currently logged in as superadmin.",
const { result } = renderHook(() => useAuthContext(), {
wrapper: AllProviders,
});

expect(dialog).toBeVisible();
});
});

describe("render the auth provider with given children when auth user is SUPERADMIN and feature flag is enabled", () => {
beforeEach(async () => {
setupFeatureFlagMock(
FeatureFlag.FEATURE_FLAG_SUPER_ADMIN_ACCESS_CORAL,
true
);
getAuthMock.mockReturnValue({ mockAuthUser, userrole: "SUPERADMIN" });
customRender(
<AuthProvider>
<ChildComponent />
</AuthProvider>,
{
queryClient: true,
}
);

await waitForElementToBeRemoved(screen.getByText("Loading Klaw"));
});

afterEach(() => {
jest.resetAllMocks();
cleanup();
});

it("returns context provider with given children", () => {
const childElement = screen.getByTestId("auth-provider-child");
expect(childElement).toBeVisible();
await waitFor(() => {
expect(result.current.username).toEqual("Arya Stark");
expect(result.current.isSuperAdminUser).toBe(true);
});
});

it("does not show dialog informing superadmin they can not access coral", () => {
const dialog = screen.queryByText(
"You're currently logged in as superadmin."
);
it("returns the correct user data and identifies a non-SUPERADMIN user", async () => {
getAuthMock.mockResolvedValue(mockAuthUser);
const { result } = renderHook(() => useAuthContext(), {
wrapper: AllProviders,
});

expect(dialog).not.toBeInTheDocument();
await waitFor(() => {
expect(result.current.username).toEqual("Jon Snow");
expect(result.current.isSuperAdminUser).toBe(false);
});
});
});
});
31 changes: 10 additions & 21 deletions coral/src/app/context-provider/AuthProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import { AuthUser, getAuth, isSuperAdmin } from "src/domain/auth-user";
import { BasePage } from "src/app/layout/page/BasePage";
import { Box, Icon } from "@aivenio/aquarium";
import loading from "@aivenio/aquarium/icons/loading";
import { NoCoralAccessSuperadmin } from "src/app/components/NoCoralAccessSuperadmin";
import useFeatureFlag from "src/services/feature-flags/hook/useFeatureFlag";
import { FeatureFlag } from "src/services/feature-flags/types";

/** We don't do Authentication on Corals side
* at the moment, so we only have a AuthUser
Expand Down Expand Up @@ -50,31 +47,22 @@ const AuthContext = createContext<AuthUser>({
},
});

const useAuthContext = () => useContext(AuthContext);
type UseAuthContext = AuthUser & { isSuperAdminUser: boolean };

const useAuthContext = (): UseAuthContext => {
const authUser = useContext(AuthContext);

const isSuperAdminUser = isSuperAdmin(authUser);

return { ...authUser, isSuperAdminUser };
};

const AuthProvider = ({ children }: { children: ReactNode }) => {
const { data: authUser, isLoading } = useQuery<AuthUser | undefined>(
["user-getAuth-data"],
getAuth
);

const superAdminAccessCoralEnabled = useFeatureFlag(
FeatureFlag.FEATURE_FLAG_SUPER_ADMIN_ACCESS_CORAL
);

// SUPERADMIN does not have access to coral, so we show a reduced page with
// information about that and nothing else.
if (
!isLoading &&
authUser &&
isSuperAdmin(authUser) &&
!superAdminAccessCoralEnabled
) {
return (
<BasePage headerContent={<></>} content={<NoCoralAccessSuperadmin />} />
);
}

if (!isLoading && authUser) {
return (
<AuthContext.Provider value={authUser}>{children}</AuthContext.Provider>
Expand All @@ -94,3 +82,4 @@ const AuthProvider = ({ children }: { children: ReactNode }) => {
};

export { useAuthContext, AuthProvider };
export type { UseAuthContext };
75 changes: 23 additions & 52 deletions coral/src/app/features/configuration/clusters/Clusters.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,16 @@ import { KlawApiError } from "src/services/api";
import { mockIntersectionObserver } from "src/services/test-utils/mock-intersection-observer";
import { userEvent } from "@testing-library/user-event";
import { clusterTypeToString } from "src/services/formatter/cluster-type-formatter";
import { useAuthContext } from "src/app/context-provider/AuthProvider";
import { AuthUser } from "src/domain/auth-user";

const INITIAL_AUTH_DATA: AuthUser = {
username: "",
userrole: "",
teamname: "",
teamId: "",
canSwitchTeams: "",
totalTeamTopics: 0,
totalOrgTopics: 0,
permissions: {
addDeleteEditClusters: false,
canShutdownKw: false,
canUpdatePermissions: false,
addEditRoles: false,
viewTopics: false,
requestItems: false,
viewKafkaConnect: false,
syncBackTopics: false,
syncBackSchemas: false,
syncBackAcls: false,
updateServerConfig: false,
showServerConfigEnvProperties: false,
addUser: false,
addTeams: false,
syncTopicsAcls: false,
syncConnectors: false,
manageConnectors: false,
syncSchemas: false,
approveAtleastOneRequest: false,
approveDeclineTopics: false,
approveDeclineOperationalReqs: false,
approveDeclineSubscriptions: false,
approveDeclineSchemas: false,
approveDeclineConnectors: false,
showAddDeleteTenants: false,
addDeleteEditEnvs: false,
},
import {
UseAuthContext,
useAuthContext,
} from "src/app/context-provider/AuthProvider";

import { testAuthUser } from "src/domain/auth-user/auth-user-test-helper";

const INITIAL_AUTH_USER_CONTEXT_DATA: UseAuthContext = {
...testAuthUser,
isSuperAdminUser: false,
};

jest.mock("src/domain/cluster/cluster-api.ts");
Expand Down Expand Up @@ -114,7 +85,7 @@ describe("Clusters.tsx", () => {
totalPages: 1,
entries: [],
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });
});
Expand All @@ -137,7 +108,7 @@ describe("Clusters.tsx", () => {
totalPages: 1,
entries: [],
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });
await waitForElementToBeRemoved(screen.getByTestId("skeleton-table"));
Expand Down Expand Up @@ -168,7 +139,7 @@ describe("Clusters.tsx", () => {
beforeAll(async () => {
jest.spyOn(console, "error").mockImplementation((error) => error);
mockGetClustersPaginated.mockRejectedValue(testError);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -201,7 +172,7 @@ describe("Clusters.tsx", () => {
totalPages: 1,
entries: testCluster,
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -271,7 +242,7 @@ describe("Clusters.tsx", () => {
totalPages: 3,
entries: testCluster,
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -307,7 +278,7 @@ describe("Clusters.tsx", () => {
totalPages: 5,
entries: testCluster,
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -350,7 +321,7 @@ describe("Clusters.tsx", () => {
totalPages: 5,
entries: testCluster,
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -390,7 +361,7 @@ describe("Clusters.tsx", () => {
totalPages: 5,
entries: testCluster,
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, { queryClient: true, memoryRouter: true });

Expand Down Expand Up @@ -432,7 +403,7 @@ describe("Clusters.tsx", () => {
totalPages: 1,
entries: [testCluster[0]],
});
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_DATA);
mockUseAuthContext.mockReturnValue(INITIAL_AUTH_USER_CONTEXT_DATA);

customRender(<Clusters />, {
queryClient: true,
Expand Down Expand Up @@ -475,9 +446,9 @@ describe("Clusters.tsx", () => {
});

mockUseAuthContext.mockReturnValue({
...INITIAL_AUTH_DATA,
...INITIAL_AUTH_USER_CONTEXT_DATA,
permissions: {
...INITIAL_AUTH_DATA.permissions,
...INITIAL_AUTH_USER_CONTEXT_DATA.permissions,
addDeleteEditClusters: true,
},
});
Expand Down Expand Up @@ -541,9 +512,9 @@ describe("Clusters.tsx", () => {
});

mockUseAuthContext.mockReturnValue({
...INITIAL_AUTH_DATA,
...INITIAL_AUTH_USER_CONTEXT_DATA,
permissions: {
...INITIAL_AUTH_DATA.permissions,
...INITIAL_AUTH_USER_CONTEXT_DATA.permissions,
addDeleteEditClusters: false,
},
});
Expand Down
Loading