From 72972ab5c22da4f23daf64607a9c047293c2067a Mon Sep 17 00:00:00 2001 From: Laura Date: Thu, 12 Dec 2024 10:15:40 +0100 Subject: [PATCH] Implement OpenApi TypeScript Client for Project Overview (#3703) --- .../security/SecHubSecurityConfiguration.java | 1 + .../docker/Web-UI-Debian.dockerfile | 1 + .../docker/nginx/config.json | 5 ++ .../docker/nginx/nginx.conf | 5 ++ sechub-web-ui-solution/env | 2 + sechub-web-ui/.env | 10 +++ sechub-web-ui/README.md | 40 +++++++--- sechub-web-ui/src/components/Projects.vue | 76 +++++++++++-------- sechub-web-ui/src/config.ts | 27 ++++++- sechub-web-ui/src/i18n/locales/en.json | 4 +- sechub-web-ui/src/main.ts | 9 ++- sechub-web-ui/src/plugins/vuetify.ts | 2 +- sechub-web-ui/src/services/configuration.ts | 28 +++++-- .../src/services/configurationService.ts | 7 ++ sechub-web-ui/src/services/defaultClient.ts | 13 ++++ .../services/productAdministrationService.ts | 7 ++ .../src/services/systemApiService.ts | 7 ++ sechub-web-ui/src/types/global.d.ts | 5 +- sechub-web-ui/tsconfig.json | 5 +- sechub-web-ui/vite.config.mts | 9 +++ 20 files changed, 208 insertions(+), 55 deletions(-) create mode 100644 sechub-web-ui-solution/docker/nginx/config.json create mode 100644 sechub-web-ui/.env create mode 100644 sechub-web-ui/src/services/configurationService.ts create mode 100644 sechub-web-ui/src/services/defaultClient.ts create mode 100644 sechub-web-ui/src/services/productAdministrationService.ts create mode 100644 sechub-web-ui/src/services/systemApiService.ts diff --git a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java index 0d73e11c6..52ed67b53 100644 --- a/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java +++ b/sechub-shared-kernel/src/main/java/com/mercedesbenz/sechub/sharedkernel/security/SecHubSecurityConfiguration.java @@ -36,6 +36,7 @@ protected Customizer.Authorization .requestMatchers(APIConstants.API_USER + "**").hasAnyRole(RoleConstants.ROLE_USER, RoleConstants.ROLE_SUPERADMIN) .requestMatchers(APIConstants.API_PROJECT + "**").hasAnyRole(RoleConstants.ROLE_USER, RoleConstants.ROLE_SUPERADMIN) .requestMatchers(APIConstants.API_OWNER + "**").hasAnyRole(RoleConstants.ROLE_OWNER, RoleConstants.ROLE_SUPERADMIN) + .requestMatchers(APIConstants.API_PROJECTS).hasAnyRole(RoleConstants.ROLE_USER, RoleConstants.ROLE_SUPERADMIN, RoleConstants.ROLE_OWNER) .requestMatchers(APIConstants.API_ANONYMOUS + "**").permitAll() .requestMatchers(APIConstants.ERROR_PAGE).permitAll() .requestMatchers(APIConstants.ACTUATOR + "**").permitAll() diff --git a/sechub-web-ui-solution/docker/Web-UI-Debian.dockerfile b/sechub-web-ui-solution/docker/Web-UI-Debian.dockerfile index 9c3cfca78..98ec1bd32 100644 --- a/sechub-web-ui-solution/docker/Web-UI-Debian.dockerfile +++ b/sechub-web-ui-solution/docker/Web-UI-Debian.dockerfile @@ -125,6 +125,7 @@ COPY nginx/ /etc/nginx/ # Copy content to web server's document root COPY --from=builder "${WEB_UI_ARTIFACTS}/dist" "${HTDOCS_FOLDER}" COPY htdocs/ "${HTDOCS_FOLDER}/" +COPY nginx/config.json "${HTDOCS_FOLDER}/" # Create self-signed certificate RUN cd /tmp && \ diff --git a/sechub-web-ui-solution/docker/nginx/config.json b/sechub-web-ui-solution/docker/nginx/config.json new file mode 100644 index 000000000..a94a3a9c0 --- /dev/null +++ b/sechub-web-ui-solution/docker/nginx/config.json @@ -0,0 +1,5 @@ +{ + // HOST represents the URL of the API client. In this case, it's the URL of the SPA because we proxy API requests to the backend to sidestep any CORS issues. + "HOST": "https://localhost", + "LOCAL_DEV": false +} \ No newline at end of file diff --git a/sechub-web-ui-solution/docker/nginx/nginx.conf b/sechub-web-ui-solution/docker/nginx/nginx.conf index be47f6a5e..60f830061 100644 --- a/sechub-web-ui-solution/docker/nginx/nginx.conf +++ b/sechub-web-ui-solution/docker/nginx/nginx.conf @@ -52,6 +52,11 @@ http { add_header Content-Type text/plain; } + # loading environment variables from config.json during runtime + location /config.json { + root /var/www/html; + } + ssl_certificate /etc/nginx/certificates/sechub-web-ui.cert; ssl_certificate_key /etc/nginx/certificates/sechub-web-ui.key; ssl_protocols TLSv1.2 TLSv1.3; diff --git a/sechub-web-ui-solution/env b/sechub-web-ui-solution/env index bde262d72..42345bd5a 100644 --- a/sechub-web-ui-solution/env +++ b/sechub-web-ui-solution/env @@ -15,3 +15,5 @@ WEB_UI_VERSION="0.2.0" GIT_TAG="" GIT_BRANCH="" WEB_UI_SSL_KEYSTORE_ALIAS="undefined" + +# Please see docker/nignx/config.json for ENV variables for the SPA itself \ No newline at end of file diff --git a/sechub-web-ui/.env b/sechub-web-ui/.env new file mode 100644 index 000000000..6ca560f87 --- /dev/null +++ b/sechub-web-ui/.env @@ -0,0 +1,10 @@ +# This .env file contains the default environment variables +# For local development, create an .env.local file with the desired variable values +# Note: .env.local will be ignored by git +# Local development variables must start with VITE_API +VITE_API_HOST=http://localhost:3000 + +# Optional variables needed for testing with SecHub Server Basic Auth +VITE_API_LOCAL_DEV=false +VITE_API_USER='example-test-user' +VITE_API_PASSWORD='example-api-token' \ No newline at end of file diff --git a/sechub-web-ui/README.md b/sechub-web-ui/README.md index 53d669c33..9d4872cec 100644 --- a/sechub-web-ui/README.md +++ b/sechub-web-ui/README.md @@ -7,7 +7,12 @@ This project is a web application that provides a user interface for the SecHub ### Installation -Install the node version manager (nvm) and use it to install the correct version of Node.js: +Install the node version manager (nvm) and use it to install and use the correct version of Node.js: +You can skip the installation processes if you already have nvm installed and the node version in nvm. + +```bash +nvm install +``` ```bash nvm use @@ -19,6 +24,14 @@ Install the project dependencies: npm install ``` +### Building openAPI SecHub Client + +To generate the SecHub openAPI Client use: + +```bash + npm run generate-api-client + ``` + ### Starting the Development Server To start the development server with hot-reload, run the following command. The server will be accessible at [http://localhost:3000](http://localhost:3000): @@ -29,19 +42,26 @@ npm run dev > Add NODE_OPTIONS='--no-warnings' to suppress the JSON import warnings that happen as part of the Vuetify import mapping. If you are on Node [v21.3.0](https://nodejs.org/en/blog/release/v21.3.0) or higher, you can change this to NODE_OPTIONS='--disable-warning=5401'. If you don't mind the warning, you can remove this from your package.json dev script. +#### Running in Development mode with sechub for testing + +1. Start SecHub Integration Test Server (or Docker Server) +2. Configure your `.env.local` file by copying `.env` to `.env.local` and adjusting the variables as needed. +Set `VITE_API_LOCAL_DEV=true` +Set `VITE_API_USER` to your SecHub user +Set `VITE_API_PASSWORD` to your SecHub Api Token +3. Set `VITE_API_HOST` to the URL of your application http://localhost:3000 - this is because of the proxy defined in the Vite dev server to avoid CORS Issues +4. Start the SPA in Development mode (npm run dev) + +Happy Testing! + ### Building for Production +Set Environment Variables: +Be aware that `npm run build` sets the environment variables at build time. +For deploying runtime ENV please se sechub-web-ui-solution/docker/nginx/conf.json it will override the VITE variables. Be aware that the config.json will be served by nginx. + To build your project for production, use: ```bash npm run build ``` - -### Building openAPI SecHub Client - -To generate the SecHub openAPI Client use: - -```bash - npm run codegen - ``` - diff --git a/sechub-web-ui/src/components/Projects.vue b/sechub-web-ui/src/components/Projects.vue index 78df97800..3fc9a1479 100644 --- a/sechub-web-ui/src/components/Projects.vue +++ b/sechub-web-ui/src/components/Projects.vue @@ -1,37 +1,36 @@ diff --git a/sechub-web-ui/src/config.ts b/sechub-web-ui/src/config.ts index 5554b2de5..f7a3bf435 100644 --- a/sechub-web-ui/src/config.ts +++ b/sechub-web-ui/src/config.ts @@ -1,4 +1,27 @@ // SPDX-License-Identifier: MIT -export const CONFIG = { - HOST: String(import.meta.env.SECHUB_HOST) || '', + +import { ref } from 'vue' + +const config = ref({ + // New ENV must be defined in global.d.ts + HOST: String(import.meta.env.VITE_API_HOST) || '', + USERNAME: String(import.meta.env.VITE_API_USER) || '', + PASSWORD: String(import.meta.env.VITE_API_PASSWORD) || '', + LOCAL_DEV: String(import.meta.env.VITE_API_LOCAL_DEV) || '', +}) + +// Overrides local environment variables after project compilation +// Utilizes variables from config.json if available +export async function loadConfig () { + try { + const response = await fetch('/config.json') + const runtimeConfig = await response.json() + config.value = { ...config.value, ...runtimeConfig } + } catch (error) { + console.error('Failed to load configuration, using fallback in .env:', error) + } +} + +export function useConfig () { + return config } diff --git a/sechub-web-ui/src/i18n/locales/en.json b/sechub-web-ui/src/i18n/locales/en.json index 0c3f8b575..7c924f137 100644 --- a/sechub-web-ui/src/i18n/locales/en.json +++ b/sechub-web-ui/src/i18n/locales/en.json @@ -2,5 +2,7 @@ "GREETING": "Welcome", "PROJECTS": "Projects", "OWNED": "owned", - "MEMBER": "member" + "MEMBER": "member", + "NO_PROJECTS_ASSIGNED": "You are not assigned to any projects.", + "ERROR_FETCHING_DATA": "Could not load data from Server." } \ No newline at end of file diff --git a/sechub-web-ui/src/main.ts b/sechub-web-ui/src/main.ts index c128f5046..fe563677f 100644 --- a/sechub-web-ui/src/main.ts +++ b/sechub-web-ui/src/main.ts @@ -7,6 +7,7 @@ // Plugins import { registerPlugins } from '@/plugins' +import { loadConfig } from './config' // Components import App from './App.vue' @@ -14,8 +15,10 @@ import App from './App.vue' // Composables import { createApp } from 'vue' -const app = createApp(App) +loadConfig().then(() => { + const app = createApp(App) -registerPlugins(app) + registerPlugins(app) -app.mount('#app') + app.mount('#app') +}) diff --git a/sechub-web-ui/src/plugins/vuetify.ts b/sechub-web-ui/src/plugins/vuetify.ts index e6b501f98..726e3420d 100644 --- a/sechub-web-ui/src/plugins/vuetify.ts +++ b/sechub-web-ui/src/plugins/vuetify.ts @@ -18,7 +18,7 @@ import { createVuetify } from 'vuetify' // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides export default createVuetify({ theme: { - defaultTheme: 'lightTheme', + defaultTheme: 'darkTheme', themes: { lightTheme, darkTheme, diff --git a/sechub-web-ui/src/services/configuration.ts b/sechub-web-ui/src/services/configuration.ts index 69e8b237f..93f5e06ae 100644 --- a/sechub-web-ui/src/services/configuration.ts +++ b/sechub-web-ui/src/services/configuration.ts @@ -1,10 +1,28 @@ // SPDX-License-Identifier: MIT -import { CONFIG } from 'src/config' +import { useConfig } from '@/config' import { Configuration } from '@/generated-sources/openapi' -const apiConfig = new Configuration({ - basePath: CONFIG.HOST, - // todo: check if cookie set by server is sent back (for auth reasons) -}) +let apiConfig: Configuration +const config = useConfig() + +if (config.value.LOCAL_DEV) { + // api configuration for local development with basic auth + apiConfig = new Configuration({ + basePath: config.value.HOST, + username: config.value.USERNAME, + password: config.value.PASSWORD, + headers: { + 'Content-Type': 'application/json', + }, + }) +} else { + apiConfig = new Configuration({ + basePath: config.value.HOST, + headers: { + // todo: check if cookie set by server is sent back (for auth reasons) + 'Content-Type': 'application/json', + }, + }) +} export default apiConfig diff --git a/sechub-web-ui/src/services/configurationService.ts b/sechub-web-ui/src/services/configurationService.ts new file mode 100644 index 000000000..382b95156 --- /dev/null +++ b/sechub-web-ui/src/services/configurationService.ts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +import { ConfigurationApi } from '@/generated-sources/openapi' +import apiConfig from './configuration' + +const configurationApi = new ConfigurationApi(apiConfig) + +export default configurationApi diff --git a/sechub-web-ui/src/services/defaultClient.ts b/sechub-web-ui/src/services/defaultClient.ts new file mode 100644 index 000000000..1fe706c8e --- /dev/null +++ b/sechub-web-ui/src/services/defaultClient.ts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +import configurationApi from './configurationService' +import projectApi from './productAdministrationService' +import systemApi from './systemApiService' + +const defaultClient = { + withProjectApi: projectApi, + withSystemApi: systemApi, + withConfigurationApi: configurationApi, + +} + +export default defaultClient diff --git a/sechub-web-ui/src/services/productAdministrationService.ts b/sechub-web-ui/src/services/productAdministrationService.ts new file mode 100644 index 000000000..39c7b2e77 --- /dev/null +++ b/sechub-web-ui/src/services/productAdministrationService.ts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +import { ProjectAdministrationApi } from '@/generated-sources/openapi' +import apiConfig from './configuration' + +const projectApi = new ProjectAdministrationApi(apiConfig) + +export default projectApi diff --git a/sechub-web-ui/src/services/systemApiService.ts b/sechub-web-ui/src/services/systemApiService.ts new file mode 100644 index 000000000..346856f05 --- /dev/null +++ b/sechub-web-ui/src/services/systemApiService.ts @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +import { SystemApi } from '@/generated-sources/openapi' +import apiConfig from './configuration' + +const systemApi = new SystemApi(apiConfig) + +export default systemApi diff --git a/sechub-web-ui/src/types/global.d.ts b/sechub-web-ui/src/types/global.d.ts index 705eacd03..18a58518e 100644 --- a/sechub-web-ui/src/types/global.d.ts +++ b/sechub-web-ui/src/types/global.d.ts @@ -3,6 +3,9 @@ declare module 'insane'; interface ImportMeta { env: { - SECHUB_HOST: string + VITE_API_HOST: string, + VITE_API_USER: string, + VITE_API_PASSWORD: string, + VITE_API_LOCAL_DEV: boolean, } } diff --git a/sechub-web-ui/tsconfig.json b/sechub-web-ui/tsconfig.json index e84fcc889..b33d61f5d 100644 --- a/sechub-web-ui/tsconfig.json +++ b/sechub-web-ui/tsconfig.json @@ -13,12 +13,13 @@ "types": [ "vite/client", "vite-plugin-vue-layouts/client", - "unplugin-vue-router/client" + "unplugin-vue-router/client", ], "allowJs": true, "strict": true, "strictNullChecks": true, - "noUnusedLocals": true, + // Set to false to enable build with unused imports from the generated OpenAPI client + "noUnusedLocals": false, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "isolatedModules": true, diff --git a/sechub-web-ui/vite.config.mts b/sechub-web-ui/vite.config.mts index d4604c146..d77a11358 100644 --- a/sechub-web-ui/vite.config.mts +++ b/sechub-web-ui/vite.config.mts @@ -76,6 +76,15 @@ export default defineConfig({ ], }, server: { + // configuration for local dev server port: 3000, + // configuration to avoid CORS errors (as long as not impl. in server backend) + proxy: { + '/api': { + target: 'https://localhost:8443', + changeOrigin: true, + secure: false, + }, + }, }, })