@orchestrator-ui/orchestrator-ui-components 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchestrator-ui/orchestrator-ui-components",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "license": "MIT",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -21,7 +21,7 @@
21
21
  "invariant": "^2.2.4",
22
22
  "moment": "^2.29.4",
23
23
  "moment-timezone": "^0.5.43",
24
- "next-auth": "^4.23.1",
24
+ "next-auth": "^4.24.5",
25
25
  "next-intl": "^3.4.1",
26
26
  "next-query-params": "^5.0.0",
27
27
  "object-hash": "^3.0.0",
@@ -1,22 +1,50 @@
1
- import React, { JSX, useContext } from 'react';
2
-
3
- import { useSession } from 'next-auth/react';
1
+ import React, { ReactNode, useContext } from 'react';
4
2
 
5
3
  import { WfoLoading } from '@/components';
6
4
  import { OrchestratorConfigContext } from '@/contexts';
5
+ import { PolicyContextProvider } from '@/contexts/PolicyContext';
6
+ import { useWfoSession } from '@/hooks';
7
7
 
8
8
  interface AuthProps {
9
- children: JSX.Element;
9
+ children: ReactNode;
10
+ isAllowedHandler?: (routerPath: string, resource?: string) => boolean;
10
11
  }
11
12
 
12
- export const WfoAuth = ({ children }: AuthProps): JSX.Element => {
13
+ /**
14
+ * The WfoAuth component exposes a function isAllowedHandler to apply policy checks.
15
+ * In components in this library a usePolicy hook is available that exposes a function isAllowed
16
+ * with 1 optional parameter "resource".
17
+ *
18
+ * For convenience there is also a WfoIsAllowedToRender component available. In both hook
19
+ * and component the current route is determined in the usePolicy hook and passed as
20
+ * first parameter in the isAllowedHandler function in this WfoAuth component.
21
+ *
22
+ * Example for usage in any component:
23
+ * const { isAllowed } = usePolicy();
24
+ * const isAllowedToDoOrSeeSomething: boolean = isAllowed('something')
25
+ *
26
+ * Example for implementing the isAllowed function:
27
+ * const isAllowed = (routerPath: string, resource?: string) => {
28
+ * // Your own rules to determine if something is allowed or not
29
+ * // The useWfoSession hook can be used to get the current user profile
30
+ * }
31
+ */
32
+ export const WfoAuth = ({
33
+ children,
34
+ isAllowedHandler = () => true,
35
+ }: AuthProps) => {
13
36
  const { authActive } = useContext(OrchestratorConfigContext);
14
- const { status } = useSession({
37
+ const { status } = useWfoSession({
15
38
  required: authActive,
16
39
  });
17
40
 
18
41
  if (status === 'loading') {
19
42
  return <WfoLoading />;
20
43
  }
21
- return children;
44
+
45
+ return (
46
+ <PolicyContextProvider isAllowedHandler={isAllowedHandler}>
47
+ {children}
48
+ </PolicyContextProvider>
49
+ );
22
50
  };
@@ -0,0 +1,17 @@
1
+ import React, { FC, ReactNode } from 'react';
2
+
3
+ import { usePolicy } from '@/hooks';
4
+
5
+ export type WfoIsAllowedToRenderProps = {
6
+ resource?: string;
7
+ children: ReactNode;
8
+ };
9
+
10
+ export const WfoIsAllowedToRender: FC<WfoIsAllowedToRenderProps> = ({
11
+ resource,
12
+ children,
13
+ }) => {
14
+ const { isAllowed } = usePolicy();
15
+
16
+ return isAllowed(resource) ? <>{children}</> : null;
17
+ };
@@ -1 +1,2 @@
1
1
  export * from './WfoAuth';
2
+ export * from './WfoIsAllowedToRender';
@@ -2,7 +2,7 @@ import React, { createContext, useContext } from 'react';
2
2
  import type { ReactNode } from 'react';
3
3
 
4
4
  import { ApiClient, getApiClient } from '@/api';
5
- import { useSessionWithToken } from '@/hooks';
5
+ import { useWfoSession } from '@/hooks';
6
6
 
7
7
  import { OrchestratorConfigContext } from './OrchestratorConfigContext';
8
8
 
@@ -22,7 +22,7 @@ export const ApiClientContextProvider = ({
22
22
  children,
23
23
  }: ApiClientContextProviderProps) => {
24
24
  const { orchestratorApiBaseUrl } = useContext(OrchestratorConfigContext);
25
- const { session } = useSessionWithToken();
25
+ const { session } = useWfoSession();
26
26
  const accessToken = session?.accessToken;
27
27
  const apiClient = getApiClient(orchestratorApiBaseUrl, accessToken);
28
28
 
@@ -0,0 +1,23 @@
1
+ import React, { FC, ReactNode, createContext } from 'react';
2
+
3
+ export type Policy = {
4
+ isAllowedHandler: (routerPath: string, resource?: string) => boolean;
5
+ };
6
+
7
+ export const PolicyContext = createContext<Policy>({
8
+ isAllowedHandler: () => true,
9
+ });
10
+
11
+ export type PolicyProviderProps = {
12
+ isAllowedHandler: (routerPath: string, resource?: string) => boolean;
13
+ children: ReactNode;
14
+ };
15
+
16
+ export const PolicyContextProvider: FC<PolicyProviderProps> = ({
17
+ isAllowedHandler,
18
+ children,
19
+ }) => (
20
+ <PolicyContext.Provider value={{ isAllowedHandler }}>
21
+ {children}
22
+ </PolicyContext.Provider>
23
+ );
@@ -1,4 +1,5 @@
1
+ export * from './ApiClientContext';
1
2
  export * from './ConfirmationDialogProvider';
2
3
  export * from './OrchestratorConfigContext';
4
+ export * from './PolicyContext';
3
5
  export * from './TreeContext';
4
- export * from './ApiClientContext';
@@ -5,7 +5,7 @@ import { OrchestratorConfigContext } from '@/contexts';
5
5
  import { GraphqlFilter, ProcessDetailResultRaw } from '@/types';
6
6
 
7
7
  import { useQueryWithFetch } from './useQueryWithFetch';
8
- import { useSessionWithToken } from './useSessionWithToken';
8
+ import { useWfoSession } from './useWfoSession';
9
9
 
10
10
  export type CacheNames = { [key: string]: string };
11
11
 
@@ -48,7 +48,7 @@ export const useFilterQueryWithRest = <Type>(
48
48
  filters?: GraphqlFilter<Type>[],
49
49
  refetchInterval?: number,
50
50
  ) => {
51
- const { session } = useSessionWithToken();
51
+ const { session } = useWfoSession();
52
52
 
53
53
  const fetchFromApi = async () => {
54
54
  const response = await fetch(url, {
@@ -3,6 +3,7 @@ export * from './useQueryWithGraphql';
3
3
  export * from './useMutateProcess';
4
4
  export * from './useOrchestratorConfig';
5
5
  export * from './useOrchestratorTheme';
6
+ export * from './usePolicy';
6
7
  export * from './useProcessStatusCountsQuery';
7
8
  export * from './DataFetchHooks';
8
9
  export * from './useSubscriptionActions';
@@ -10,5 +11,5 @@ export * from './useDataDisplayParams';
10
11
  export * from './useShowToastMessage';
11
12
  export * from './useStoredTableConfig';
12
13
  export * from './useWithOrchestratorTheme';
13
- export * from './useSessionWithToken';
14
+ export * from './useWfoSession';
14
15
  export * from './useQueryWithFetch';
@@ -4,11 +4,11 @@ import { useMutation, useQueryClient } from 'react-query';
4
4
  import { signOut } from 'next-auth/react';
5
5
 
6
6
  import { OrchestratorConfigContext } from '@/contexts';
7
- import { useSessionWithToken } from '@/hooks/index';
7
+ import { useWfoSession } from '@/hooks/index';
8
8
 
9
9
  export const useMutateProcess = () => {
10
10
  const { processesEndpoint } = useContext(OrchestratorConfigContext);
11
- const { session } = useSessionWithToken();
11
+ const { session } = useWfoSession();
12
12
  const queryClient = useQueryClient();
13
13
 
14
14
  const genericRequestHeaders = {
@@ -0,0 +1,15 @@
1
+ import { useContext } from 'react';
2
+
3
+ import { useRouter } from 'next/router';
4
+
5
+ import { PolicyContext } from '@/contexts';
6
+
7
+ export const usePolicy = () => {
8
+ const { isAllowedHandler } = useContext(PolicyContext);
9
+ const router = useRouter();
10
+
11
+ return {
12
+ isAllowed: (resource?: string) =>
13
+ isAllowedHandler(router.asPath, resource),
14
+ };
15
+ };
@@ -3,14 +3,14 @@ import { useQuery } from 'react-query';
3
3
  import { Variables } from 'graphql-request/build/cjs/types';
4
4
  import { signOut } from 'next-auth/react';
5
5
 
6
- import { useSessionWithToken } from './useSessionWithToken';
6
+ import { useWfoSession } from './useWfoSession';
7
7
 
8
8
  export const useQueryWithFetch = <T, V extends Variables>(
9
9
  url: string,
10
10
  queryVars: V,
11
11
  queryKey: string,
12
12
  ) => {
13
- const { session } = useSessionWithToken();
13
+ const { session } = useWfoSession();
14
14
  const requestHeaders = {
15
15
  authorization: session ? `Bearer ${session.accessToken}` : '',
16
16
  };
@@ -8,7 +8,7 @@ import { TypedDocumentNode } from '@graphql-typed-document-node/core';
8
8
 
9
9
  import { OrchestratorConfigContext } from '@/contexts';
10
10
 
11
- import { useSessionWithToken } from './useSessionWithToken';
11
+ import { useWfoSession } from './useWfoSession';
12
12
 
13
13
  export const useQueryWithGraphql = <U, V extends Variables>(
14
14
  query: TypedDocumentNode<U, V>,
@@ -17,7 +17,7 @@ export const useQueryWithGraphql = <U, V extends Variables>(
17
17
  options: UseQueryOptions<U> = {},
18
18
  ) => {
19
19
  const { graphqlEndpointCore } = useContext(OrchestratorConfigContext);
20
- const { session } = useSessionWithToken();
20
+ const { session } = useWfoSession();
21
21
 
22
22
  const graphQLClient = new GraphQLClient(graphqlEndpointCore);
23
23
 
@@ -2,7 +2,7 @@ import { useContext } from 'react';
2
2
  import { useQuery } from 'react-query';
3
3
 
4
4
  import { OrchestratorConfigContext } from '@/contexts';
5
- import { useSessionWithToken } from '@/hooks';
5
+ import { useWfoSession } from '@/hooks';
6
6
 
7
7
  export interface SubscriptionAction {
8
8
  name: string;
@@ -28,7 +28,7 @@ export const useSubscriptionActions = (subscriptionId: string) => {
28
28
  const { subscriptionActionsEndpoint } = useContext(
29
29
  OrchestratorConfigContext,
30
30
  );
31
- const { session } = useSessionWithToken();
31
+ const { session } = useWfoSession();
32
32
  const fetchSubscriptionActions = async () => {
33
33
  const response = await fetch(
34
34
  `${subscriptionActionsEndpoint}/${subscriptionId}`,
@@ -0,0 +1,28 @@
1
+ import type { Session } from 'next-auth';
2
+ import { useSession } from 'next-auth/react';
3
+ import type { UseSessionOptions } from 'next-auth/react';
4
+
5
+ export type WfoSession = Session & {
6
+ accessToken?: string;
7
+ profile?: WfoUserProfile;
8
+ };
9
+
10
+ export type WfoUserProfile = {
11
+ sub: string;
12
+ name: string;
13
+ preferred_username: string;
14
+ email: string;
15
+ [key: string]: unknown;
16
+ };
17
+
18
+ export const useWfoSession = <R extends boolean>(
19
+ options?: UseSessionOptions<R>,
20
+ ) => {
21
+ const sessionData = useSession(options);
22
+
23
+ // Data prop of type Session from NextAuth endpoint is not properly typed
24
+ const { data, ...updatedSessionData } = sessionData;
25
+ const dataWithToken = data as WfoSession | null;
26
+
27
+ return { ...updatedSessionData, session: dataWithToken };
28
+ };
package/src/rtk/api.ts CHANGED
@@ -3,7 +3,7 @@ import { getSession } from 'next-auth/react';
3
3
  import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
4
4
  import { graphqlRequestBaseQuery } from '@rtk-query/graphql-request-base-query';
5
5
 
6
- import type { SessionWithToken } from '@/hooks';
6
+ import type { WfoSession } from '@/hooks';
7
7
 
8
8
  import type { RootState } from './store';
9
9
 
@@ -18,7 +18,7 @@ type ExtraOptions = {
18
18
  };
19
19
 
20
20
  const prepareHeaders = async (headers: Headers) => {
21
- const session = (await getSession()) as SessionWithToken;
21
+ const session = (await getSession()) as WfoSession;
22
22
  if (session?.accessToken) {
23
23
  headers.set('Authorization', `Bearer ${session.accessToken}`);
24
24
  }
@@ -1,12 +0,0 @@
1
- import type { Session } from 'next-auth';
2
- import { useSession } from 'next-auth/react';
3
-
4
- export type SessionWithToken = Session & {
5
- accessToken?: string;
6
- };
7
-
8
- export const useSessionWithToken = () => {
9
- const sessionData = useSession();
10
- const dataWithToken = sessionData.data as SessionWithToken;
11
- return { ...sessionData, session: dataWithToken };
12
- };