Skip to content
Snippets Groups Projects
Commit 8b2374b1 authored by geant-release-service's avatar geant-release-service
Browse files

Finished release 2.5.

parents 7c8e32fb 14264053
No related branches found
No related tags found
No related merge requests found
Pipeline #89139 passed
ENVIRONMENT_NAME=Development
PROCESS_DETAIL_REFETCH_INTERVAL=3000
ORCHESTRATOR_API_HOST=http://localhost:8080
ORCHESTRATOR_API_PATH=/api
ORCHESTRATOR_GRAPHQL_HOST=http://localhost:8080
ORCHESTRATOR_GRAPHQL_PATH=/api/graphql
ORCHESTRATOR_WEBSOCKET_URL=ws://localhost:8080
AUTH_ACTIVE=true
NEXTAUTH_ID="keycloak"
NEXTAUTH_CLIENT_ID="orchestrator-client"
NEXTAUTH_CLIENT_SECRET="KEYCLOAK_SECRET"
NEXTAUTH_SECRET="NEXTAUTH_SECRET"
NEXTAUTH_ISSUER="http://localhost:8085/realms/orchestrator"
NEXTAUTH_WELL_KNOWN_OVERRIDE="http://localhost:8085/auth/.well-known/openid-configuration"
NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE="openid profile"
NEXTAUTH_URL=http://localhost:3000/api/auth
# OPA Settings
OPA_PUBLIC_BUNDLE_URL=http://localhost:8181/v1/data/opa/public-bundle
#Maps Settings
NETWORK_TOPOLOGY_API_URL="https://orchestrator.uat.gap.geant.org/api/v1/networks/topology"
# docker-compose variables
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=admin
KEYCLOAK_PORT=8085
USE_WEBSOCKET=false
USE_THEME_TOGGLE=false
SHOW_WORKFLOW_INFORMATION_LINK=false
WORKFLOW_INFORMATION_LINK_URL=http://localhost:8080
ENVIRONMENT_NAME=Development ENVIRONMENT_NAME=Development
PROCESS_DETAIL_REFETCH_INTERVAL=3000
ORCHESTRATOR_API_HOST=http://localhost:8080 ORCHESTRATOR_API_HOST=http://localhost:8080
ORCHESTRATOR_API_PATH=/api ORCHESTRATOR_API_PATH=/api
ORCHESTRATOR_GRAPHQL_HOST=http://localhost:8080 ORCHESTRATOR_GRAPHQL_HOST=http://localhost:8080
ORCHESTRATOR_GRAPHQL_PATH=/api/graphql ORCHESTRATOR_GRAPHQL_PATH=/api/graphql
ORCHESTRATOR_WEBSOCKET_URL=ws://localhost:8080 ORCHESTRATOR_WEBSOCKET_URL=ws://localhost:8080
USE_WEB_SOCKETS=true
USE_THEME_TOGGLE=true
SHOW_WORKFLOW_INFORMATION_LINK=true
WORKFLOW_INFORMATION_LINK_URL="https://workfloworchestrator.org/"
# Auth variables
OAUTH2_ACTIVE=true
NEXTAUTH_PROVIDER_ID="oidc"
NEXTAUTH_PROVIDER_NAME="GÉANT Identity Provider"
NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE="openid profile email aarc offline_access"
OAUTH2_CLIENT_ID="APP-A43E6FB7-1EEF-49CB-95B9-9AFF6FA7EF66"
OAUTH2_CLIENT_SECRET=""
OIDC_CONF_FULL_WELL_KNOWN_URL="https://proxy.aai.geant.org/.well-known/openid-configuration"
OIDC_TOKEN_ENDPOINT="https://proxy.aai.geant.org/OIDC/token"
# OPA Settings
OPA_PUBLIC_BUNDLE_URL="http://localhost:8000/policy.wasm"
#Maps Settings
NETWORK_TOPOLOGY_API_URL="http://localhost:8080/api/v1/networks/topology"
AUTH_ACTIVE=true # Required by the Nextauth middleware
NEXTAUTH_URL=http://localhost:3000/api/auth NEXTAUTH_URL=http://localhost:3000/api/auth
NEXTAUTH_SECRET="LR1a4CU9UVFr1OcVeu4ULDX/VHMMiI+s3wNvOkf6fdE=" # openssl rand -base64 32
# OIDC Authentication Settings
NEXTAUTH_ID="oidc"
NEXTAUTH_CLIENT_ID="APP-A43E6FB7-1EEF-49CB-95B9-9AFF6FA7EF66"
NEXTAUTH_CLIENT_SECRET="YOUR_OIDC_CLIENT_SECRET"
NEXTAUTH_SECRET="SOhxHLn53mV7ML7y8L6rL5oOQxOVb0V4p2Ez0ZSIuOs=" # openssl rand -base64 32
NEXTAUTH_ISSUER="https://proxy.aai.geant.org"
NEXTAUTH_WELL_KNOWN_OVERRIDE="https://proxy.aai.geant.org/.well-known/openid-configuration"
# docker-compose variables
# KEYCLOAK_ADMIN=admin
# KEYCLOAK_ADMIN_PASSWORD=admin
# KEYCLOAK_PORT=8085
USE_WEBSOCKET=true
USE_THEME_TOGGLE=true
\ No newline at end of file
module.exports = { module.exports = {
root: true, root: true,
extends: [ extends: [
'@orchestrator-ui/eslint-config-custom', '@orchestrator-ui/eslint-config-custom',
// 'plugin:prettier/recommended' // 'plugin:prettier/recommended'
], ],
// plugins: ['prettier'], // plugins: ['prettier'],
// rules: { // rules: {
// 'prettier/prettier': 'error', // 'prettier/prettier': 'error',
// }, // },
}; };
# Add files here to ignore them from prettier formatting
/coverage
/mkdocs
.next
node_modules
...@@ -2,21 +2,32 @@ ...@@ -2,21 +2,32 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [2.5] - 2024-09-18
- Add refresh token logic
- update core-lib
## [2.4] - 2024-08-06 ## [2.4] - 2024-08-06
- Redesigned maps to utilize the leafletmap package, replacing graph maps with real-world maps for enhanced accuracy and user experience. - Redesigned maps to utilize the leafletmap package, replacing graph maps with real-world maps for enhanced accuracy and user experience.
## [2.3] - 2024-06-18 ## [2.3] - 2024-06-18
- Extended the timeout for authentication on GAP - Extended the timeout for authentication on GAP
## [2.2] - 2024-06-04 ## [2.2] - 2024-06-04
- Add a new page that shows the IP trunks in the network. - Add a new page that shows the IP trunks in the network.
## [2.1] - 2024-05-24 ## [2.1] - 2024-05-24
- Fix a bug that prevented operators from viewing workflows. - Fix a bug that prevented operators from viewing workflows.
## [2.0] - 2024-05-23 ## [2.0] - 2024-05-23
- Update GUI to version 2. - Update GUI to version 2.
- Rework project structure to be compatible with new UI library. - Rework project structure to be compatible with new UI library.
## [0.2] - 2023-12-04 ## [0.2] - 2023-12-04
- initial skeleton - initial skeleton
import { import {
Environment, Environment,
OrchestratorConfig, OrchestratorConfig,
getEnvironmentVariables,
} from '@orchestrator-ui/orchestrator-ui-components'; } from '@orchestrator-ui/orchestrator-ui-components';
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 => { export const getInitialOrchestratorConfig = (): OrchestratorConfig => {
const orchestratorGraphqlBaseUrl = const {
process.env.ORCHESTRATOR_GRAPHQL_HOST && USE_THEME_TOGGLE,
process.env.ORCHESTRATOR_GRAPHQL_PATH ENVIRONMENT_NAME,
? `${process.env.ORCHESTRATOR_GRAPHQL_HOST}${process.env.ORCHESTRATOR_GRAPHQL_PATH}` ORCHESTRATOR_API_HOST,
: DEFAULT_GRAPHQL_CORE_ENDPOINT; ORCHESTRATOR_API_PATH,
ORCHESTRATOR_GRAPHQL_HOST,
ORCHESTRATOR_GRAPHQL_PATH,
ORCHESTRATOR_WEBSOCKET_URL,
USE_WEB_SOCKETS,
WORKFLOW_INFORMATION_LINK_URL,
SHOW_WORKFLOW_INFORMATION_LINK,
OAUTH2_ACTIVE,
} = getEnvironmentVariables([
'USE_THEME_TOGGLE',
'ENVIRONMENT_NAME',
'ORCHESTRATOR_API_HOST',
'ORCHESTRATOR_API_PATH',
'ORCHESTRATOR_GRAPHQL_HOST',
'ORCHESTRATOR_GRAPHQL_PATH',
'ORCHESTRATOR_WEBSOCKET_URL',
'USE_WEB_SOCKETS',
'WORKFLOW_INFORMATION_LINK_URL',
'SHOW_WORKFLOW_INFORMATION_LINK',
'OAUTH2_ACTIVE',
]);
const orchestratorApiBaseUrl = const graphqlEndpointCore = `${ORCHESTRATOR_GRAPHQL_HOST}${ORCHESTRATOR_GRAPHQL_PATH}`;
process.env.ORCHESTRATOR_API_HOST && process.env.ORCHESTRATOR_API_PATH const orchestratorApiBaseUrl = `${ORCHESTRATOR_API_HOST}${ORCHESTRATOR_API_PATH}`;
? `${process.env.ORCHESTRATOR_API_HOST}${process.env.ORCHESTRATOR_API_PATH}`
: DEFAULT_ORCHESTRATOR_API_BASE_URL;
return { return {
orchestratorApiBaseUrl, orchestratorApiBaseUrl,
engineStatusEndpoint: `${orchestratorApiBaseUrl}${ENGINE_STATUS_ENDPOINT}`, graphqlEndpointCore,
graphqlEndpointCore: orchestratorGraphqlBaseUrl, environmentName: ENVIRONMENT_NAME ?? Environment.DEVELOPMENT,
processesEndpoint: `${orchestratorApiBaseUrl}${PROCESSES_ENDPOINT}`, orchestratorWebsocketUrl: ORCHESTRATOR_WEBSOCKET_URL,
environmentName: process.env.ENVIRONMENT_NAME ?? Environment.DEVELOPMENT, authActive: OAUTH2_ACTIVE?.toLowerCase() != 'false',
subscriptionProcessesEndpoint: `${orchestratorApiBaseUrl}${SUBSCRIPTION_PROCESSES_ENDPOINT}`, useWebSockets: USE_WEB_SOCKETS?.toLowerCase() === 'true',
orchestratorWebsocketUrl: useThemeToggle: USE_THEME_TOGGLE?.toLowerCase() === 'true',
process.env.ORCHESTRATOR_WEBSOCKET_URL ?? workflowInformationLinkUrl: WORKFLOW_INFORMATION_LINK_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: showWorkflowInformationLink:
process.env.SHOW_WORKFLOW_INFORMATION_LINK?.toLowerCase() === 'true', SHOW_WORKFLOW_INFORMATION_LINK?.toLowerCase() === 'true',
}; };
}; };
import { useEffect } from 'react';
import axios from 'axios';
import { signOut } from 'next-auth/react';
const useAxiosInterceptor = () => {
useEffect(() => {
const responseInterceptor = axios.interceptors.response.use(
response => response,
error => {
if (error.response && error.response.status === 401) {
const currentUrl = window.location.href;
signOut({ callbackUrl: `/api/auth/signin?error=SessionRequired&callbackUrl=${encodeURIComponent(currentUrl)}` });
}
return Promise.reject(error);
}
);
return () => {
axios.interceptors.response.eject(responseInterceptor);
};
}, []);
};
export default useAxiosInterceptor;
import { useEffect } from 'react';
import { signOut } from 'next-auth/react';
const useFetchInterceptor = () => {
useEffect(() => {
const handleResponse = (response: Response) => {
if (response.status === 401) {
const currentUrl = window.location.href;
signOut({ callbackUrl: `/api/auth/signin?error=SessionRequired&callbackUrl=${encodeURIComponent(currentUrl)}` });
}
return response;
};
const originalFetch = global.fetch.bind(global);
global.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
const response = await originalFetch(input, init);
return handleResponse(response);
};
return () => {
global.fetch = originalFetch;
};
}, []);
};
export default useFetchInterceptor;
module.exports = { module.exports = {
reactStrictMode: false, reactStrictMode: false,
output: 'standalone', output: 'standalone',
i18n: { i18n: {
// These are all the locales you want to support in // These are all the locales you want to support in
// your application // your application
locales: ['en-GB', 'nl-NL'], locales: ['en-GB'],
defaultLocale: 'en-GB', defaultLocale: 'en-GB',
}, },
transpilePackages: ['@orchestrator-ui/orchestrator-ui-components'], transpilePackages: ['@orchestrator-ui/orchestrator-ui-components'],
publicRuntimeConfig: { publicRuntimeConfig: {
OPA_PUBLIC_BUNDLE_URL: process.env.OPA_PUBLIC_BUNDLE_URL, OPA_PUBLIC_BUNDLE_URL: process.env.OPA_PUBLIC_BUNDLE_URL,
NEXTAUTH_CLIENT_ID: process.env.NEXTAUTH_CLIENT_ID, OAUTH2_CLIENT_ID: process.env.OAUTH2_CLIENT_ID,
}, },
}; };
This diff is collapsed.
...@@ -9,18 +9,19 @@ ...@@ -9,18 +9,19 @@
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"prettier": "prettier --write \"{components,configuration,contexts,pages,translations,public}/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"", "prettier": "prettier -c \"{**/*,*}.{ts,tsx,json,js,md}\"",
"prettier-fix": "prettier --write \"{**/*,*}.{ts,tsx,json,js,md}\"",
"prettier:check": "prettier --check \"{components,configuration,contexts,pages,translations,public}/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"", "prettier:check": "prettier --check \"{components,configuration,contexts,pages,translations,public}/**/*.{js,jsx,ts,tsx,json,css,scss,md}\"",
"prepare": "husky" "prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@elastic/datemath": "^5.0.3", "@elastic/datemath": "^5.0.3",
"@elastic/eui": "^93.1.1", "@elastic/eui": "^95.1.0",
"@elfalem/leaflet-curve": "^0.9.2", "@elfalem/leaflet-curve": "^0.9.2",
"@emotion/css": "^11.11.2", "@emotion/css": "^11.11.2",
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@open-policy-agent/opa-wasm": "^1.8.1", "@open-policy-agent/opa-wasm": "^1.8.1",
"@orchestrator-ui/orchestrator-ui-components": "1.20.0", "@orchestrator-ui/orchestrator-ui-components": "1.37.1",
"@reduxjs/toolkit": "^2.0.1", "@reduxjs/toolkit": "^2.0.1",
"axios": "^1.7.2", "axios": "^1.7.2",
"cytoscape": "^3.29.2", "cytoscape": "^3.29.2",
...@@ -42,7 +43,7 @@ ...@@ -42,7 +43,7 @@
"react-leaflet-markercluster": "^3.0.0-rc1", "react-leaflet-markercluster": "^3.0.0-rc1",
"react-no-ssr": "^1.1.0", "react-no-ssr": "^1.1.0",
"react-query": "3.39.3", "react-query": "3.39.3",
"react-redux": "^9.1.0", "react-redux": "^8.1.3",
"use-query-params": "2.2.1" "use-query-params": "2.2.1"
}, },
"devDependencies": { "devDependencies": {
...@@ -54,23 +55,25 @@ ...@@ -54,23 +55,25 @@
"@types/cytoscape-fcose": "^2.2.4", "@types/cytoscape-fcose": "^2.2.4",
"@types/leaflet": "^1.9.12", "@types/leaflet": "^1.9.12",
"@types/node": "^20.10.5", "@types/node": "^20.10.5",
"@types/react": "^18.3.3", "@types/node-fetch": "^2.6.11",
"@types/react": "^18.2.45",
"@types/react-dom": "^18.2.18", "@types/react-dom": "^18.2.18",
"@types/react-leaflet-markercluster": "^3.0.4", "@types/react-leaflet-markercluster": "^3.0.4",
"@types/react-no-ssr": "^1.1.7", "@types/react-no-ssr": "^1.1.7",
"babel-jest": "^29.7.0",
"esbuild-jest": "^0.4.0", "esbuild-jest": "^0.4.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"husky": "^9.0.11", "husky": "^9.0.11",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"typescript": "^5.3.2" "typescript": "^5.5.2"
}, },
"overrides": { "overrides": {
"@elastic/eui": { "@elastic/eui": {
"typescript": "^5.3.2" "typescript": "^5.5.2"
}, },
"react-no-ssr": { "react-no-ssr": {
"react": "^18.2.0" "react": "^18.3.1"
} }
} }
} }
...@@ -3,25 +3,21 @@ import { getAppLogo } from '@/components/AppLogo/AppLogo'; ...@@ -3,25 +3,21 @@ import { getAppLogo } from '@/components/AppLogo/AppLogo';
import { WfoAuthWithPolicy } from '@/components/WfoAuthWithPolicy'; import { WfoAuthWithPolicy } from '@/components/WfoAuthWithPolicy';
import { getInitialOrchestratorConfig } from '@/configuration'; import { getInitialOrchestratorConfig } from '@/configuration';
import { GsoConfigProvider, GsoConfig } from '@/contexts/GsoConfigContext'; import { GsoConfigProvider, GsoConfig } from '@/contexts/GsoConfigContext';
import useAxiosInterceptor from '@/hooks/useAxiosInterceptor';
import useFetchInterceptor from '@/hooks/useFetchInterceptor';
import { TranslationsProvider } from '@/translations/translationsProvider'; import { TranslationsProvider } from '@/translations/translationsProvider';
import type { EuiSideNavItemType } from '@elastic/eui';
import { EuiProvider, EuiThemeColorMode } from '@elastic/eui'; 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 '@elastic/eui/dist/eui_theme_light.min.css';
import { EuiSideNavItemType } from '@elastic/eui/src/components/side_nav/side_nav_types';
import { import {
ApiClientContextProvider,
ColorModes, ColorModes,
ConfirmationDialogContextWrapper, ConfirmationDialogContextWrapper,
OrchestratorConfig, OrchestratorConfig,
OrchestratorConfigProvider, OrchestratorConfigProvider,
StoreProvider, StoreProvider,
WfoErrorBoundary, WfoErrorBoundary,
WfoMenuItemLink,
WfoPageTemplate, WfoPageTemplate,
WfoToastsList, WfoToastsList,
defaultOrchestratorTheme, defaultOrchestratorTheme,
WfoMenuItemLink,
} from '@orchestrator-ui/orchestrator-ui-components'; } from '@orchestrator-ui/orchestrator-ui-components';
import { SessionProvider } from 'next-auth/react'; import { SessionProvider } from 'next-auth/react';
import { NextAdapter } from 'next-query-params'; import { NextAdapter } from 'next-query-params';
...@@ -30,36 +26,17 @@ import Head from 'next/head'; ...@@ -30,36 +26,17 @@ import Head from 'next/head';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import NoSSR from 'react-no-ssr'; 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'; import { QueryParamProvider } from 'use-query-params';
type AppOwnProps = { orchestratorConfig: OrchestratorConfig }; type AppOwnProps = { orchestratorConfig: OrchestratorConfig };
const queryClientConfig: QueryClientConfig = {
defaultOptions: {
queries: {
cacheTime: 5 * 1000,
refetchOnWindowFocus: true,
},
},
};
const useAuthInterceptor = () => {
useFetchInterceptor();
useAxiosInterceptor();
};
function CustomApp({ function CustomApp({
Component, Component,
pageProps, pageProps,
orchestratorConfig, orchestratorConfig,
}: AppProps & AppOwnProps) { }: AppProps & AppOwnProps) {
const router = useRouter(); const router = useRouter();
useAuthInterceptor();
const [queryClient] = useState(() => new QueryClient(queryClientConfig));
const [themeMode, setThemeMode] = useState<EuiThemeColorMode>( const [themeMode, setThemeMode] = useState<EuiThemeColorMode>(
ColorModes.LIGHT, ColorModes.LIGHT,
); );
...@@ -128,40 +105,32 @@ function CustomApp({ ...@@ -128,40 +105,32 @@ function CustomApp({
colorMode={themeMode} colorMode={themeMode}
modify={defaultOrchestratorTheme} modify={defaultOrchestratorTheme}
> >
<ApiClientContextProvider> <TranslationsProvider>
<QueryClientProvider <Head>
client={queryClient} <link rel="icon" href="/favicon.png" />
contextSharing={true} <title>GÉANT Service Orchestrator</title>
> </Head>
<TranslationsProvider> <main className="app">
<Head> <ConfirmationDialogContextWrapper>
<link rel="icon" href="/favicon.png" /> <WfoPageTemplate
<title>GÉANT Service Orchestrator</title> getAppLogo={getAppLogo}
</Head> onThemeSwitch={handleThemeSwitch}
<main className="app"> overrideMenuItems={addMenuItems}
<ConfirmationDialogContextWrapper> >
<WfoPageTemplate <QueryParamProvider
getAppLogo={getAppLogo} adapter={NextAdapter}
onThemeSwitch={handleThemeSwitch} options={{
overrideMenuItems={addMenuItems} removeDefaultsFromUrl: false,
> enableBatching: true,
<QueryParamProvider }}
adapter={NextAdapter} >
options={{ <Component {...pageProps} />
removeDefaultsFromUrl: false, </QueryParamProvider>
enableBatching: true, </WfoPageTemplate>
}} <WfoToastsList />
> </ConfirmationDialogContextWrapper>
<Component {...pageProps} /> </main>
</QueryParamProvider> </TranslationsProvider>
</WfoPageTemplate>
<WfoToastsList />
</ConfirmationDialogContextWrapper>
<ReactQueryDevtools initialIsOpen={false} />
</main>
</TranslationsProvider>
</QueryClientProvider>
</ApiClientContextProvider>
</EuiProvider> </EuiProvider>
</WfoAuthWithPolicy> </WfoAuthWithPolicy>
</GsoConfigProvider> </GsoConfigProvider>
......
import { import {
WfoSession, WfoSession,
WfoUserProfile, WfoUserProfile,
getEnvironmentVariables,
} from '@orchestrator-ui/orchestrator-ui-components'; } from '@orchestrator-ui/orchestrator-ui-components';
import NextAuth, { AuthOptions } from 'next-auth'; import NextAuth, { AuthOptions } from 'next-auth';
import { JWT } from 'next-auth/jwt'; import { JWT } from 'next-auth/jwt';
import { OAuthConfig } from 'next-auth/providers'; import { OAuthConfig } from 'next-auth/providers';
const token_endpoint_auth_method = process.env.NEXTAUTH_CLIENT_SECRET const {
OAUTH2_ACTIVE,
OAUTH2_CLIENT_ID,
OAUTH2_CLIENT_SECRET,
NEXTAUTH_PROVIDER_ID,
NEXTAUTH_PROVIDER_NAME,
NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE,
OIDC_CONF_FULL_WELL_KNOWN_URL,
OIDC_TOKEN_ENDPOINT,
} = getEnvironmentVariables([
'OAUTH2_ACTIVE',
'OAUTH2_CLIENT_ID',
'OAUTH2_CLIENT_SECRET',
'NEXTAUTH_PROVIDER_ID',
'NEXTAUTH_PROVIDER_NAME',
'NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE',
'OIDC_CONF_FULL_WELL_KNOWN_URL',
'OIDC_TOKEN_ENDPOINT',
]);
const isOauth2Enabled = OAUTH2_ACTIVE?.toLowerCase() != 'false';
const token_endpoint_auth_method = OAUTH2_CLIENT_SECRET
? 'client_secret_basic' ? 'client_secret_basic'
: 'none'; : 'none';
const authActive = process.env.AUTH_ACTIVE?.toLowerCase() != 'false'; async function refreshAccessToken(token: JWT): Promise<JWT> {
try {
const raw = JSON.stringify({
client_id: OAUTH2_CLIENT_ID,
grant_type: 'refresh_token',
refresh_token: token.refreshToken as string,
});
const response = await fetch(OIDC_TOKEN_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: raw,
redirect: 'follow',
});
if (response.ok) {
const data: {
access_token: string;
expires_in: number;
refresh_token: string;
} = await response.json();
return {
...token,
accessToken: data.access_token,
accessTokenExpires:
Math.floor(Date.now() / 1000) + (data.expires_in - 300), // Store expiry as a Unix timestamp (in seconds)
refreshToken: data.refresh_token,
};
} else {
return {
...token,
error: `${response.statusText}`,
};
}
} catch (error) {
return {
...token,
error: `${error}`,
};
}
}
const wfoProvider: OAuthConfig<WfoUserProfile> = { const wfoProvider: OAuthConfig<WfoUserProfile> = {
id: process.env.NEXTAUTH_ID || '', id: NEXTAUTH_PROVIDER_ID,
name: process.env.NEXTAUTH_ID || '', name: NEXTAUTH_PROVIDER_NAME,
type: 'oauth', type: 'oauth',
clientId: process.env.NEXTAUTH_CLIENT_ID || '', clientId: OAUTH2_CLIENT_ID,
clientSecret: process.env.NEXTAUTH_CLIENT_SECRET || undefined, wellKnown: OIDC_CONF_FULL_WELL_KNOWN_URL,
wellKnown:
process.env.NEXTAUTH_WELL_KNOWN_OVERRIDE ??
`${process.env.NEXTAUTH_ISSUER || ''}/.well-known/openid-configuration`,
authorization: { authorization: {
params: { params: {
scope: scope: NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE,
process.env.NEXTAUTH_AUTHORIZATION_SCOPE_OVERRIDE ?? 'openid profile', prompt: 'consent',
}, },
}, },
idToken: true, idToken: true,
...@@ -35,7 +99,6 @@ const wfoProvider: OAuthConfig<WfoUserProfile> = { ...@@ -35,7 +99,6 @@ const wfoProvider: OAuthConfig<WfoUserProfile> = {
if (!context.provider.wellKnown || !tokens.access_token) { if (!context.provider.wellKnown || !tokens.access_token) {
return {}; return {};
} }
return await client.userinfo(tokens.access_token); return await client.userinfo(tokens.access_token);
}, },
}, },
...@@ -53,15 +116,29 @@ const wfoProvider: OAuthConfig<WfoUserProfile> = { ...@@ -53,15 +116,29 @@ const wfoProvider: OAuthConfig<WfoUserProfile> = {
}; };
export const authOptions: AuthOptions = { export const authOptions: AuthOptions = {
providers: authActive ? [wfoProvider] : [], providers: isOauth2Enabled ? [wfoProvider] : [],
callbacks: { callbacks: {
async jwt({ token, account, profile }) { async jwt({ token, account, profile }): Promise<JWT> {
// The "account" is only available right after signing in -- adding useful data to the token // The "account" is only available right after signing in -- adding useful data to the token
if (account) { if (account) {
token.accessToken = account.access_token; return {
token.profile = profile; ...token,
accessToken: account.access_token,
refreshToken: account.refresh_token,
accessTokenExpires: account.expires_at as number,
profile: profile as WfoUserProfile,
};
}
const now = Math.floor(Date.now() / 1000);
if (
typeof token.accessTokenExpires === 'number' &&
now < token.accessTokenExpires
) {
return token;
} }
return token;
return await refreshAccessToken(token);
}, },
async session({ session, token }: { session: WfoSession; token: JWT }) { async session({ session, token }: { session: WfoSession; token: JWT }) {
// Assign data to the session to be available in the client through the useSession hook // Assign data to the session to be available in the client through the useSession hook
...@@ -72,4 +149,5 @@ export const authOptions: AuthOptions = { ...@@ -72,4 +149,5 @@ export const authOptions: AuthOptions = {
}, },
}, },
}; };
export default NextAuth(authOptions); export default NextAuth(authOptions);
import { getEnvironmentVariables } from '@orchestrator-ui/orchestrator-ui-components';
import { NextApiRequest, NextApiResponse } from 'next'; import { NextApiRequest, NextApiResponse } from 'next';
interface RuntimeConfig { interface RuntimeConfig {
...@@ -6,14 +7,21 @@ interface RuntimeConfig { ...@@ -6,14 +7,21 @@ interface RuntimeConfig {
networkTopologyApiUrl: string; networkTopologyApiUrl: string;
} }
const { OPA_PUBLIC_BUNDLE_URL, OAUTH2_CLIENT_ID, NETWORK_TOPOLOGY_API_URL } =
getEnvironmentVariables([
'OPA_PUBLIC_BUNDLE_URL',
'OAUTH2_CLIENT_ID',
'NETWORK_TOPOLOGY_API_URL',
]);
export default async function handler( export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse<RuntimeConfig | { error: string }>, res: NextApiResponse<RuntimeConfig | { error: string }>,
) { ) {
const config: RuntimeConfig = { const config: RuntimeConfig = {
opaPublicBundleUrl: process.env.OPA_PUBLIC_BUNDLE_URL || '', opaPublicBundleUrl: OPA_PUBLIC_BUNDLE_URL || '',
oidcClientId: process.env.NEXTAUTH_CLIENT_ID || '', oidcClientId: OAUTH2_CLIENT_ID || '',
networkTopologyApiUrl: process.env.NETWORK_TOPOLOGY_API_URL || '', networkTopologyApiUrl: NETWORK_TOPOLOGY_API_URL || '',
}; };
res.status(200).json(config); res.status(200).json(config);
......
...@@ -3,7 +3,7 @@ from setuptools import setup ...@@ -3,7 +3,7 @@ from setuptools import setup
setup( setup(
name="geant-service-orchestrator-gui", name="geant-service-orchestrator-gui",
py_modules=[], py_modules=[],
version="2.4", version="2.5",
author="GÉANT Orchestration and Automation Team", author="GÉANT Orchestration and Automation Team",
author_email="goat@geant.org", author_email="goat@geant.org",
description="GÉANT Service Orchestrator GUI dummy package", description="GÉANT Service Orchestrator GUI dummy package",
......
{ {}
"workflow": { "activate_iptrunk": "Activate IP trunk" },
"forms": { "fields": { "site_ts_address": "Hello Chris!" } }
}
{ {
"extends": "@orchestrator-ui/tsconfig/nextjs.json", "extends": "@orchestrator-ui/tsconfig/nextjs.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
},
"plugins": [
{
"name": "next"
}
],
"strictNullChecks": true,
"jsx": "preserve"
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "plugins": [
"exclude": ["node_modules"] {
"name": "next"
}
],
"strictNullChecks": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
} }
...@@ -15,7 +15,7 @@ export interface IptrunkSideNode { ...@@ -15,7 +15,7 @@ export interface IptrunkSideNode {
site_latitude: number; site_latitude: number;
site_longitude: number; site_longitude: number;
}; };
owner_subscription_id: string owner_subscription_id: string;
} }
export interface Iptrunk { export interface Iptrunk {
...@@ -25,10 +25,14 @@ export interface Iptrunk { ...@@ -25,10 +25,14 @@ export interface Iptrunk {
iptrunk_type: string; iptrunk_type: string;
iptrunk_ipv4_network: string; iptrunk_ipv4_network: string;
iptrunk_ipv6_network: string; iptrunk_ipv6_network: string;
geant_s_sid: string, geant_s_sid: string;
iptrunk_sides: Array<{ iptrunk_side_node: IptrunkSideNode }>; iptrunk_sides: Array<{ iptrunk_side_node: IptrunkSideNode }>;
} }
export interface NetworkTopologyData { export interface NetworkTopologyData {
iptrunks: Array<{ iptrunk: Iptrunk; insync: boolean; subscription_id: string }>; iptrunks: Array<{
iptrunk: Iptrunk;
insync: boolean;
subscription_id: string;
}>;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment