Skip to content
Snippets Groups Projects
Commit 612d1c14 authored by Mohammad Torkashvand's avatar Mohammad Torkashvand
Browse files

add OPA

parent b370f5a8
No related branches found
No related tags found
1 merge request!22Feature/nat 569 add opa to gui2
......@@ -17,6 +17,8 @@ NEXTAUTH_WELL_KNOWN_OVERRIDE="http://localhost:8085/auth/.well-known/openid-conf
NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE="openid profile"
NEXTAUTH_URL=http://localhost:3000/api/auth
OPA_PUBLIC_BUNDLE_URL=http://localhost:8181/v1/data/opa/public-bundle
# docker-compose variables
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
......
import React, {FC, ReactNode, useContext, useEffect, useState} from 'react';
import {join} from 'lodash';
import {LoadedPolicy, loadPolicy} from '@open-policy-agent/opa-wasm';
import {
OrchestratorConfigContext,
WfoAuth,
WfoLoading,
useWfoSession,
} from '@orchestrator-ui/orchestrator-ui-components';
import {useGsoConfig} from '@/contexts/GsoConfigContext';
export type WfoAuthWithPolicyProps = {
children: ReactNode;
};
export enum PolicyLoadingStatus {
LOADING = 'loading',
SUCCESS = 'success',
FAILED = 'failed',
NOT_LOADED = 'notLoaded',
}
type Policy =
| {
loadedPolicy: LoadedPolicy;
policyLoadingStatus: PolicyLoadingStatus.SUCCESS;
}
| {
loadedPolicy: null;
policyLoadingStatus:
| PolicyLoadingStatus.LOADING
| PolicyLoadingStatus.FAILED
| PolicyLoadingStatus.NOT_LOADED;
};
export const WfoAuthWithPolicy: FC<WfoAuthWithPolicyProps> = ({children}) => {
const {authActive} = useContext(OrchestratorConfigContext);
const {publicBundleUrl, clientId} = useGsoConfig();
const {status: sessionStatus, session} = useWfoSession({
required: authActive,
});
const [policy, setPolicy] = useState<Policy>({
loadedPolicy: null,
policyLoadingStatus: PolicyLoadingStatus.LOADING,
});
useEffect(() => {
const getPolicy = async () => {
try {
const policyResult = await fetch(publicBundleUrl);
const policyWasm = await policyResult.arrayBuffer();
const loadedPolicy = await loadPolicy(policyWasm);
setPolicy({
loadedPolicy,
policyLoadingStatus: PolicyLoadingStatus.SUCCESS,
});
} catch (e) {
console.error('Failed to load policy', e);
setPolicy({
loadedPolicy: null,
policyLoadingStatus: PolicyLoadingStatus.FAILED,
});
}
};
// When auth is disabled, the policy should not be loaded
if (!authActive) {
setPolicy({
loadedPolicy: null,
policyLoadingStatus: PolicyLoadingStatus.NOT_LOADED,
});
return;
}
getPolicy();
}, [authActive, publicBundleUrl, clientId]);
const isAllowedHandler = (routerPath: string, resource?: string) => {
if (
session &&
policy.policyLoadingStatus === PolicyLoadingStatus.SUCCESS
) {
const {profile} = session;
// Fix unexpected scope array
if (profile && Array.isArray(profile.scope)) {
profile.scope = join(profile.scope, ' ');
}
const policyInput = {
resource: resource ?? routerPath,
active: true,
client_id: clientId,
method: 'GET',
...profile,
};
const policyTestResult = policy.loadedPolicy.evaluate(policyInput);
return policyTestResult[0].result === true;
}
// By default the handler allows everything when session is null or when
// the policy is not available (failed or not loaded)
return true;
};
if (
policy.policyLoadingStatus === PolicyLoadingStatus.LOADING ||
sessionStatus === 'loading'
) {
return <WfoLoading/>;
}
return authActive ? (
<WfoAuth isAllowedHandler={isAllowedHandler}>{children}</WfoAuth>
) : (
<WfoAuth>{children}</WfoAuth>
);
};
\ No newline at end of file
import {
Environment,
OrchestratorConfig,
} from '@orchestrator-ui/orchestrator-ui-components';
import process from 'process';
export const DEFAULT_GRAPHQL_CORE_ENDPOINT =
'http://localhost:8080/api/graphql';
export const DEFAULT_ORCHESTRATOR_API_BASE_URL = 'http://localhost:8080/api';
export const DEFAULT_ORCHESTRATOR_WEBSOCKET_URL = 'ws://localhost:8080';
export const ENGINE_STATUS_ENDPOINT = '/settings/status';
export const PROCESS_STATUS_COUNTS_ENDPOINT = '/processes/status-counts';
export const PROCESSES_ENDPOINT = '/processes';
export const SUBSCRIPTION_ACTIONS_ENDPOINT = '/subscriptions/workflows';
export const SUBSCRIPTION_PROCESSES_ENDPOINT =
'/processes/process-subscriptions-by-subscription-id';
export const DEFAULT_WORKFLOW_INFORMATION_LINK_URL = 'http://localhost:8080';
import { Environment, OrchestratorConfig } from "@orchestrator-ui/orchestrator-ui-components";
import getConfig from 'next/config';
export type gsoConfig = {
publicBundleUrl: string;
clientId: string;
};
const { publicRuntimeConfig } = getConfig();
const DEFAULT_GRAPHQL_CORE_ENDPOINT = "http://localhost:8080/api/graphql";
const DEFAULT_ORCHESTRATOR_API_BASE_URL = "http://localhost:8080/api";
const DEFAULT_ORCHESTRATOR_WEBSOCKET_URL = "ws://localhost:8080";
const ENGINE_STATUS_ENDPOINT = "/settings/status";
const PROCESS_STATUS_COUNTS_ENDPOINT = "/processes/status-counts";
const PROCESSES_ENDPOINT = "/processes";
const SUBSCRIPTION_PROCESSES_ENDPOINT = "/processes/process-subscriptions-by-subscription-id";
const DEFAULT_WORKFLOW_INFORMATION_LINK_URL = "http://localhost:8080";
export const getInitialOrchestratorConfig = (): OrchestratorConfig => {
const orchestratorGraphqlBaseUrl =
process.env.ORCHESTRATOR_GRAPHQL_HOST &&
process.env.ORCHESTRATOR_GRAPHQL_PATH
? `${process.env.ORCHESTRATOR_GRAPHQL_HOST}${process.env.ORCHESTRATOR_GRAPHQL_PATH}`
: DEFAULT_GRAPHQL_CORE_ENDPOINT;
const orchestratorApiBaseUrl =
process.env.ORCHESTRATOR_API_HOST && process.env.ORCHESTRATOR_API_PATH
? `${process.env.ORCHESTRATOR_API_HOST}${process.env.ORCHESTRATOR_API_PATH}`
: DEFAULT_ORCHESTRATOR_API_BASE_URL;
return {
orchestratorApiBaseUrl,
engineStatusEndpoint: orchestratorApiBaseUrl + ENGINE_STATUS_ENDPOINT,
graphqlEndpointCore: orchestratorGraphqlBaseUrl,
processesEndpoint: orchestratorApiBaseUrl + PROCESSES_ENDPOINT,
environmentName: process.env.ENVIRONMENT_NAME ?? Environment.DEVELOPMENT,
// subscriptionActionsEndpoint: orchestratorApiBaseUrl + SUBSCRIPTION_ACTIONS_ENDPOINT,
subscriptionProcessesEndpoint:
orchestratorApiBaseUrl + SUBSCRIPTION_PROCESSES_ENDPOINT,
orchestratorWebsocketUrl:
process.env.ORCHESTRATOR_WEBSOCKET_URL ||
DEFAULT_ORCHESTRATOR_WEBSOCKET_URL,
authActive: process.env.AUTH_ACTIVE?.toLowerCase() != 'false',
useWebSockets: process.env.USE_WEB_SOCKETS?.toLowerCase() === 'true',
useThemeToggle: process.env.USE_THEME_TOGGLE?.toLowerCase() === 'true',
workflowInformationLinkUrl:
process.env.WORKFLOW_INFORMATION_LINK_URL ??
DEFAULT_WORKFLOW_INFORMATION_LINK_URL,
showWorkflowInformationLink:
process.env.SHOW_WORKFLOW_INFORMATION_LINK?.toLowerCase() === 'true',
};
const orchestratorGraphqlBaseUrl = process.env.ORCHESTRATOR_GRAPHQL_HOST && process.env.ORCHESTRATOR_GRAPHQL_PATH
? `${process.env.ORCHESTRATOR_GRAPHQL_HOST}${process.env.ORCHESTRATOR_GRAPHQL_PATH}`
: DEFAULT_GRAPHQL_CORE_ENDPOINT;
const orchestratorApiBaseUrl = process.env.ORCHESTRATOR_API_HOST && process.env.ORCHESTRATOR_API_PATH
? `${process.env.ORCHESTRATOR_API_HOST}${process.env.ORCHESTRATOR_API_PATH}`
: DEFAULT_ORCHESTRATOR_API_BASE_URL;
return {
orchestratorApiBaseUrl,
engineStatusEndpoint: `${orchestratorApiBaseUrl}${ENGINE_STATUS_ENDPOINT}`,
graphqlEndpointCore: orchestratorGraphqlBaseUrl,
processesEndpoint: `${orchestratorApiBaseUrl}${PROCESSES_ENDPOINT}`,
environmentName: process.env.ENVIRONMENT_NAME ?? Environment.DEVELOPMENT,
subscriptionProcessesEndpoint: `${orchestratorApiBaseUrl}${SUBSCRIPTION_PROCESSES_ENDPOINT}`,
orchestratorWebsocketUrl: process.env.ORCHESTRATOR_WEBSOCKET_URL ?? DEFAULT_ORCHESTRATOR_WEBSOCKET_URL,
authActive: process.env.AUTH_ACTIVE?.toLowerCase() !== "false",
useWebSockets: process.env.USE_WEB_SOCKETS?.toLowerCase() === "true",
useThemeToggle: process.env.USE_THEME_TOGGLE?.toLowerCase() === "true",
workflowInformationLinkUrl: process.env.WORKFLOW_INFORMATION_LINK_URL ?? DEFAULT_WORKFLOW_INFORMATION_LINK_URL,
showWorkflowInformationLink: process.env.SHOW_WORKFLOW_INFORMATION_LINK?.toLowerCase() === "true",
};
};
export const getInitialGsoConfig = (): gsoConfig => {
return {
publicBundleUrl: publicRuntimeConfig.OPA_PUBLIC_BUNDLE_URL ?? '',
clientId: publicRuntimeConfig.NEXTAUTH_CLIENT_ID ?? '',
};
};
import React, {createContext, useContext, ReactNode} from 'react';
// Define the shape of the GsoConfig
interface GsoConfig {
publicBundleUrl: string;
clientId: string;
}
// Create the default values for the context
const defaultGsoConfig: GsoConfig = {
publicBundleUrl: 'asdasdsad',
clientId: 'asdasdasd',
};
// Create the context
const GsoConfigContext = createContext<GsoConfig>(defaultGsoConfig);
// Define the provider's props, including children
interface GsoConfigProviderProps {
initialOrchestratorConfig: GsoConfig;
children: ReactNode;
}
// Create a provider component
export const GsoConfigProvider: React.FC<GsoConfigProviderProps> = ({initialOrchestratorConfig, children}) => {
return (
<GsoConfigContext.Provider value={initialOrchestratorConfig}>
{children}
</GsoConfigContext.Provider>
);
};
// Create a custom hook for using the context
export const useGsoConfig = () => {
return useContext(GsoConfigContext);
};
export default GsoConfigContext;
......@@ -8,4 +8,8 @@ module.exports = {
defaultLocale: 'en-GB',
},
transpilePackages: ['@orchestrator-ui/orchestrator-ui-components'],
publicRuntimeConfig: {
OPA_PUBLIC_BUNDLE_URL: process.env.OPA_PUBLIC_BUNDLE_URL,
NEXTAUTH_CLIENT_ID: process.env.NEXTAUTH_CLIENT_ID,
},
};
import '../font/inter.css';
import { getAppLogo } from '@/components/AppLogo/AppLogo';
import { getInitialOrchestratorConfig } from '@/configuration';
import { TranslationsProvider } from '@/translations/translationsProvider';
import { EuiProvider, EuiThemeColorMode } from '@elastic/eui';
import React, {useEffect, useState} from 'react';
import NoSSR from 'react-no-ssr';
import {QueryClient, QueryClientProvider} from 'react-query';
import {ReactQueryDevtools} from 'react-query/devtools';
import {QueryClientConfig} from 'react-query/types/core/types';
import {SessionProvider} from 'next-auth/react';
import {NextAdapter} from 'next-query-params';
import App, {AppContext, AppInitialProps, AppProps} from 'next/app';
import Head from 'next/head';
import {QueryParamProvider} from 'use-query-params';
import {EuiProvider, EuiThemeColorMode} from '@elastic/eui';
import '@elastic/eui/dist/eui_theme_dark.min.css';
import '@elastic/eui/dist/eui_theme_light.min.css';
import {
ApiClientContextProvider,
ColorModes,
ConfirmationDialogContextWrapper,
OrchestratorConfig,
OrchestratorConfigProvider,
StoreProvider,
WfoAuth,
WfoErrorBoundary,
WfoPageTemplate,
WfoToastsList,
defaultOrchestratorTheme,
ApiClientContextProvider,
ColorModes,
ConfirmationDialogContextWrapper,
OrchestratorConfig,
OrchestratorConfigProvider,
StoreProvider,
WfoErrorBoundary,
WfoPageTemplate,
WfoToastsList,
defaultOrchestratorTheme,
} from '@orchestrator-ui/orchestrator-ui-components';
import { SessionProvider } from 'next-auth/react';
import { NextAdapter } from 'next-query-params';
import App, { AppContext, AppInitialProps, AppProps } from 'next/app';
import Head from 'next/head';
import React, { useEffect, useState } from 'react';
import NoSSR from 'react-no-ssr';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import { QueryClientConfig } from 'react-query/types/core/types';
import { QueryParamProvider } from 'use-query-params';
type AppOwnProps = { orchestratorConfig: OrchestratorConfig };
import {getAppLogo} from '@/components/AppLogo/AppLogo';
import {getInitialGsoConfig, getInitialOrchestratorConfig} from '@/configuration';
import {TranslationsProvider} from '@/translations/translationsProvider';
import {WfoAuthWithPolicy} from '@/components/WfoAuthWithPolicy'; // Adjust the import path as necessary
import {GsoConfigProvider} from '@/contexts/GsoConfigContext'; // Adjust the import path as necessary
import '../font/inter.css';
import {gsoConfig} from "@/configuration/configuration";
type AppOwnProps = {
orchestratorConfig: OrchestratorConfig;
gsoConfig: gsoConfig;
};
const queryClientConfig: QueryClientConfig = {
defaultOptions: {
queries: {
cacheTime: 5 * 1000,
refetchOnWindowFocus: true,
defaultOptions: {
queries: {
cacheTime: 5 * 1000,
refetchOnWindowFocus: true,
},
},
},
};
function CustomApp({
Component,
pageProps,
orchestratorConfig,
}: AppProps & AppOwnProps) {
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
Component,
pageProps,
orchestratorConfig,
gsoConfig,
}: AppProps & AppOwnProps) {
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
const [themeMode, setThemeMode] = useState<EuiThemeColorMode>(
ColorModes.LIGHT,
);
const [themeMode, setThemeMode] = useState<EuiThemeColorMode>(
ColorModes.LIGHT,
);
const handleThemeSwitch = (newThemeMode: EuiThemeColorMode) => {
setThemeMode(newThemeMode);
localStorage.setItem('themeMode', newThemeMode);
};
const handleThemeSwitch = (newThemeMode: EuiThemeColorMode) => {
setThemeMode(newThemeMode);
localStorage.setItem('themeMode', newThemeMode);
};
useEffect(() => {
// Initialize theme mode from localStorage or set it to 'light' if not present
const storedTheme = localStorage.getItem('themeMode');
if (
!storedTheme ||
(storedTheme !== ColorModes.LIGHT && storedTheme !== ColorModes.DARK)
) {
handleThemeSwitch(ColorModes.LIGHT);
}
}, []);
useEffect(() => {
const storedTheme = localStorage.getItem('themeMode');
if (
!storedTheme ||
(storedTheme !== ColorModes.LIGHT &&
storedTheme !== ColorModes.DARK)
) {
handleThemeSwitch(ColorModes.LIGHT);
}
}, []);
return (
<WfoErrorBoundary>
<OrchestratorConfigProvider
initialOrchestratorConfig={orchestratorConfig}
>
<StoreProvider initialOrchestratorConfig={orchestratorConfig}>
<SessionProvider session={pageProps.session}>
<NoSSR>
<WfoAuth>
<EuiProvider
colorMode={themeMode}
modify={defaultOrchestratorTheme}
>
<ApiClientContextProvider>
<QueryClientProvider
client={queryClient}
contextSharing={true}
>
<TranslationsProvider>
<Head>
<link rel="icon" href="/favicon.png" />
<title>Welcome to example-orchestrator-ui!</title>
</Head>
<main className="app">
<ConfirmationDialogContextWrapper>
<WfoPageTemplate
getAppLogo={getAppLogo}
onThemeSwitch={handleThemeSwitch}
>
<QueryParamProvider
adapter={NextAdapter}
options={{
removeDefaultsFromUrl: false,
enableBatching: true,
}}
>
<Component {...pageProps} />
</QueryParamProvider>
</WfoPageTemplate>
<WfoToastsList />
</ConfirmationDialogContextWrapper>
<ReactQueryDevtools initialIsOpen={false} />
</main>
</TranslationsProvider>
</QueryClientProvider>
</ApiClientContextProvider>
</EuiProvider>
</WfoAuth>
</NoSSR>
</SessionProvider>
</StoreProvider>
</OrchestratorConfigProvider>
</WfoErrorBoundary>
);
return (
<WfoErrorBoundary>
<OrchestratorConfigProvider
initialOrchestratorConfig={orchestratorConfig}
>
<GsoConfigProvider initialOrchestratorConfig={gsoConfig}>
<StoreProvider initialOrchestratorConfig={orchestratorConfig}>
<SessionProvider session={pageProps.session}>
<NoSSR>
<WfoAuthWithPolicy>
<EuiProvider
colorMode={themeMode}
modify={defaultOrchestratorTheme}
>
<ApiClientContextProvider>
<QueryClientProvider
client={queryClient}
contextSharing={true}
>
<TranslationsProvider>
<Head>
<link
rel="icon"
href="/favicon.png"
/>
<title>
Welcome to
example-orchestrator-ui!
</title>
</Head>
<main className="app">
<ConfirmationDialogContextWrapper>
<WfoPageTemplate
getAppLogo={
getAppLogo
}
onThemeSwitch={
handleThemeSwitch
}
>
<QueryParamProvider
adapter={
NextAdapter
}
options={{
removeDefaultsFromUrl:
false,
enableBatching:
true,
}}
>
<Component
{...pageProps}
/>
</QueryParamProvider>
</WfoPageTemplate>
<WfoToastsList/>
</ConfirmationDialogContextWrapper>
<ReactQueryDevtools
initialIsOpen={false}
/>
</main>
</TranslationsProvider>
</QueryClientProvider>
</ApiClientContextProvider>
</EuiProvider>
</WfoAuthWithPolicy>
</NoSSR>
</SessionProvider>
</StoreProvider>
</GsoConfigProvider>
</OrchestratorConfigProvider>
</WfoErrorBoundary>
);
}
CustomApp.getInitialProps = async (
context: AppContext,
context: AppContext,
): Promise<AppOwnProps & AppInitialProps> => {
const ctx = await App.getInitialProps(context);
return {
...ctx,
orchestratorConfig: getInitialOrchestratorConfig(),
};
const ctx = await App.getInitialProps(context);
return {
...ctx,
orchestratorConfig: getInitialOrchestratorConfig(),
gsoConfig: getInitialGsoConfig(),
};
};
export default CustomApp;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment