├── src ├── app │ ├── app.component.scss │ ├── configs │ │ ├── configs.component.scss │ │ └── config-detail │ │ │ └── config-detail.component.scss │ ├── devices │ │ ├── tls │ │ │ ├── tls.component.scss │ │ │ ├── tls.component.ts │ │ │ ├── tls.component.spec.ts │ │ │ └── tls.component.html │ │ ├── alarms │ │ │ ├── alarms.component.scss │ │ │ └── alarms.component.spec.ts │ │ ├── explorer │ │ │ ├── explorer.component.scss │ │ │ └── explorer.component.html │ │ ├── general │ │ │ ├── general.component.scss │ │ │ └── general.component.spec.ts │ │ ├── edit-tags │ │ │ ├── edit-tags.component.css │ │ │ └── edit-tags.component.html │ │ ├── certificates │ │ │ ├── certificates.component.scss │ │ │ ├── add-cert-dialog │ │ │ │ ├── add-cert-dialog.component.scss │ │ │ │ ├── add-cert-dialog.component.html │ │ │ │ └── add-cert-dialog.component.ts │ │ │ └── certificates.component.html │ │ ├── device-cert-dialog │ │ │ ├── device-cert-dialog.component.scss │ │ │ └── device-cert-dialog.component.ts │ │ ├── device-enable-kvm │ │ │ ├── device-enable-kvm.component.scss │ │ │ ├── device-enable-kvm.component.html │ │ │ ├── device-enable-kvm.component.ts │ │ │ └── device-enable-kvm.component.spec.ts │ │ ├── device-enable-sol │ │ │ ├── device-enable-sol.component.scss │ │ │ ├── device-enable-sol.component.html │ │ │ ├── device-enable-sol.component.ts │ │ │ └── device-enable-sol.component.spec.ts │ │ ├── network-settings │ │ │ ├── network-settings.component.scss │ │ │ ├── network-settings.component.spec.ts │ │ │ └── network-settings.component.ts │ │ ├── hardware-information │ │ │ └── hardware-information.component.scss │ │ ├── device-toolbar │ │ │ ├── pba-boot-dialog │ │ │ │ ├── pba-boot-dialog.component.scss │ │ │ │ └── pba-boot-dialog.component.html │ │ │ ├── http-boot-dialog │ │ │ │ ├── http-boot-dialog.component.scss │ │ │ │ └── http-boot-dialog.component.html │ │ │ └── device-toolbar.component.scss │ │ ├── device-user-consent-dialog │ │ │ ├── device-user-consent-dialog.component.scss │ │ │ └── device-user-consent-dialog.component.html │ │ ├── event-log │ │ │ └── event-log.component.scss │ │ ├── audit-log │ │ │ ├── audit-log.component.scss │ │ │ ├── audit-log.component.spec.ts │ │ │ └── audit-log.component.html │ │ ├── sol │ │ │ ├── sol.component.scss │ │ │ └── sol.component.html │ │ ├── kvm │ │ │ └── kvm.component.scss │ │ ├── device-detail │ │ │ └── device-detail.component.scss │ │ ├── devices.component.scss │ │ ├── device-log.service.spec.ts │ │ └── device-log.service.ts │ ├── domains │ │ ├── domains.component.scss │ │ └── domain-detail │ │ │ └── domain-detail.component.scss │ ├── login │ │ └── login.component.scss │ ├── ieee8021x │ │ ├── ieee8021x.component.scss │ │ ├── ieee8021x-detail │ │ │ └── ieee8021x-detail.component.scss │ │ └── ieee8021x.constants.ts │ ├── profiles │ │ ├── profiles.component.scss │ │ ├── export-dialog │ │ │ └── export-dialog.component.scss │ │ ├── key-display-dialog │ │ │ ├── key-display-dialog.component.scss │ │ │ ├── key-display-dialog.component.html │ │ │ ├── key-display-dialog.component.spec.ts │ │ │ └── key-display-dialog.component.ts │ │ ├── profile-detail │ │ │ └── profile-detail.component.scss │ │ └── profiles.constants.ts │ ├── wireless │ │ ├── wireless.component.scss │ │ ├── wireless-detail │ │ │ └── wireless-detail.component.scss │ │ └── wireless.constants.ts │ ├── event-channel │ │ ├── event-channel.component.scss │ │ └── event-channel.component.spec.ts │ ├── proxy-configs │ │ ├── proxy-configs.component.scss │ │ └── proxy-config-detail │ │ │ └── proxy-config-detail.component.scss │ ├── shared │ │ ├── are-you-sure │ │ │ ├── are-you-sure.component.scss │ │ │ ├── are-you-sure.component.html │ │ │ ├── are-you-sure.component.ts │ │ │ └── are-you-sure.component.spec.ts │ │ ├── dialog-content │ │ │ ├── dialog-content.component.scss │ │ │ ├── dialog-content.component.html │ │ │ ├── dialog-content.component.ts │ │ │ └── dialog-content.component.spec.ts │ │ ├── power-up-alert │ │ │ ├── power-up-alert.component.scss │ │ │ ├── power-up-alert.component.html │ │ │ ├── power-up-alert.component.ts │ │ │ └── power-up-alert.component.spec.ts │ │ ├── add-device-enterprise │ │ │ └── add-device-enterprise.component.scss │ │ ├── static-cira-warning │ │ │ ├── static-cira-warning.component.scss │ │ │ ├── static-cira-warning.component.html │ │ │ ├── static-cira-warning.component.ts │ │ │ └── static-cira-warning.component.spec.ts │ │ ├── random-pass-alert │ │ │ ├── random-pass-alert.component.scss │ │ │ ├── random-pass-alert.component.html │ │ │ ├── random-pass-alert.component.ts │ │ │ └── random-pass-alert.component.spec.ts │ │ ├── add-device │ │ │ ├── add-device.component.scss │ │ │ └── add-device.component.html │ │ ├── pipes │ │ │ ├── date-formatter.pipe.ts.pipe.ts │ │ │ ├── time-ago-formatter.pipe.ts.pipe.ts │ │ │ ├── toolkit.pipe.ts │ │ │ ├── date-formatter.pipe.ts.pipe.spec.ts │ │ │ ├── time-ago-formatter.pipe.ts.pipe.spec.ts │ │ │ └── toolkit.pipe.spec.ts │ │ ├── auth-guard.service.ts │ │ ├── config │ │ │ └── snackBarDefault.ts │ │ └── auth-guard.service.spec.ts │ ├── core │ │ ├── about │ │ │ ├── about.component.scss │ │ │ ├── about.component.spec.ts │ │ │ ├── about.component.html │ │ │ └── about.component.ts │ │ ├── navbar │ │ │ ├── navbar.component.scss │ │ │ ├── navbar.component.ts │ │ │ ├── navbar.component.spec.ts │ │ │ └── navbar.component.html │ │ └── toolbar │ │ │ ├── toolbar.component.scss │ │ │ └── toolbar.component.html │ ├── app.component.html │ ├── dashboard │ │ ├── dashboard.component.scss │ │ ├── dashboard.component.spec.ts │ │ └── dashboard.component.ts │ ├── authorize.interceptor.ts │ ├── error-handling.interceptor.ts │ └── app.component.ts ├── favicon.ico ├── assets │ ├── logo.png │ ├── kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2 │ └── i18n │ │ └── translate-paginator-intl.ts ├── index.html ├── environments │ ├── environment.prod.ts │ ├── environment.enterprise.dev.ts │ ├── environment.enterprise.ts │ └── environment.ts ├── constants.ts ├── utils.ts ├── styles │ ├── css2.css │ ├── _variables.scss │ └── styles.scss └── test.ts ├── .gitattributes ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── tsconfig.eslint.json ├── init.sh ├── license-header.js ├── .prettierrc.js ├── docker-compose.yml ├── cypress ├── support │ └── e2e.ts ├── tsconfig.json ├── e2e │ ├── fixtures │ │ ├── formEntry │ │ │ ├── paging.ts │ │ │ ├── device.ts │ │ │ ├── domain.ts │ │ │ ├── wireless.ts │ │ │ ├── urls.ts │ │ │ ├── eventlogs.ts │ │ │ ├── auditLogs.ts │ │ │ ├── ieee8021x.ts │ │ │ └── cira.ts │ │ └── api │ │ │ ├── stats.ts │ │ │ ├── httpCodes.ts │ │ │ ├── tags.ts │ │ │ ├── general.ts │ │ │ ├── networkConfig.ts │ │ │ └── ieee8021x.ts │ └── integration │ │ ├── eventchannellogs │ │ └── get.spec.ts │ │ ├── dashboard │ │ └── devicecount.spec.ts │ │ ├── domain │ │ ├── exipiration.spec.ts │ │ ├── vault-read-certificate.spec.ts │ │ ├── delete.spec.ts │ │ └── create.spec.ts │ │ ├── login │ │ └── ensureEmpty.spec.ts │ │ ├── profile │ │ └── delete.spec.ts │ │ ├── wireless │ │ ├── delete.spec.ts │ │ ├── create-error.spec.ts │ │ └── create.spec.ts │ │ ├── cira │ │ ├── delete.spec.ts │ │ └── create-error.spec.ts │ │ ├── ieee8021x │ │ └── delete.spec.ts │ │ └── device │ │ └── device.spec.ts └── README.md ├── docker-compose.debug.yml ├── tsconfig.app.json ├── .dockerignore ├── .editorconfig ├── SECURITY.md ├── tsconfig.spec.json ├── .github ├── copilot-instructions.md ├── commitlint.config.cjs ├── PULL_REQUEST_TEMPLATE.md ├── workflows │ ├── projectsSync.yaml │ ├── semantic.yml │ ├── dependency-review.yml │ ├── docker.yaml │ ├── trivy-scan.yaml │ ├── nodejs.yaml │ ├── cypress.yaml │ └── release.yml ├── ISSUE_TEMPLATE.md └── dependabot.yml ├── nginx.conf ├── .pre-commit-config.yaml ├── Dockerfile ├── Makefile ├── .releaserc.json ├── .devcontainer └── devcontainer.json ├── tsconfig.json ├── .gitignore └── karma.conf.js /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | init.sh text eol=lf -------------------------------------------------------------------------------- /src/app/configs/configs.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/tls/tls.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/domains/domains.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/login/login.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/ieee8021x/ieee8021x.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/profiles/profiles.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/wireless/wireless.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/alarms/alarms.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/explorer/explorer.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/general/general.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/edit-tags/edit-tags.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/event-channel/event-channel.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/proxy-configs/proxy-configs.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/configs/config-detail/config-detail.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/certificates/certificates.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/domains/domain-detail/domain-detail.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/are-you-sure/are-you-sure.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/profiles/export-dialog/export-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/dialog-content/dialog-content.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/power-up-alert/power-up-alert.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/wireless/wireless-detail/wireless-detail.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/device-cert-dialog/device-cert-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-kvm/device-enable-kvm.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-sol/device-enable-sol.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/network-settings/network-settings.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/ieee8021x/ieee8021x-detail/ieee8021x-detail.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/hardware-information/hardware-information.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/profiles/key-display-dialog/key-display-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/add-device-enterprise/add-device-enterprise.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/static-cira-warning/static-cira-warning.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/certificates/add-cert-dialog/add-cert-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/device-toolbar/pba-boot-dialog/pba-boot-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/proxy-configs/proxy-config-detail/proxy-config-detail.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "CIRA" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/app/devices/device-toolbar/http-boot-dialog/http-boot-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/devices/device-user-consent-dialog/device-user-consent-dialog.component.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/shared/random-pass-alert/random-pass-alert.component.scss: -------------------------------------------------------------------------------- 1 | a { 2 | color: #fff; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/shared/add-device/add-device.component.scss: -------------------------------------------------------------------------------- 1 | .mat-mdc-form-field { 2 | margin-top: 12px; 3 | } 4 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/device-management-toolkit/sample-web-ui/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/device-management-toolkit/sample-web-ui/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | "**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /src/app/devices/event-log/event-log.component.scss: -------------------------------------------------------------------------------- 1 | ::ng-deep .mat-mdc-paginator-range-label { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/devices/audit-log/audit-log.component.scss: -------------------------------------------------------------------------------- 1 | .mat-column-Description { 2 | white-space: pre-line; 3 | padding: 6px 0; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/devices/device-toolbar/device-toolbar.component.scss: -------------------------------------------------------------------------------- 1 | .pad-15 { 2 | margin-left: 15px; 3 | //margin-inline-start: 15px; 4 | } 5 | -------------------------------------------------------------------------------- /src/app/core/about/about.component.scss: -------------------------------------------------------------------------------- 1 | label { 2 | padding-right: 10px; 3 | } 4 | 5 | p { 6 | color: black; 7 | } 8 | 9 | h3 { 10 | color: black; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/device-management-toolkit/sample-web-ui/HEAD/src/assets/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2 -------------------------------------------------------------------------------- /src/app/devices/sol/sol.component.scss: -------------------------------------------------------------------------------- 1 | @import '@xterm/xterm/css/xterm.css'; 2 | 3 | .spinner { 4 | position: absolute; 5 | top: 50%; 6 | left: 50%; 7 | } 8 | .xterm { 9 | text-align: left; 10 | } 11 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | sed -i \ 2 | -e "s|##RPS_SERVER##|$RPS_SERVER|g" \ 3 | -e "s|##MPS_SERVER##|$MPS_SERVER|g" \ 4 | -e "s|##VAULT_SERVER##|$VAULT_SERVER|g" \ 5 | -e "s|##AUTH_MODE_ENABLED##|$AUTH_MODE_ENABLED|g" \ 6 | /usr/share/nginx/html/*.js -------------------------------------------------------------------------------- /src/app/devices/kvm/kvm.component.scss: -------------------------------------------------------------------------------- 1 | .canvas { 2 | background-color: black; 3 | } 4 | .spinner { 5 | position: absolute; 6 | top: 50%; 7 | left: 50%; 8 | } 9 | .stop-ider-button { 10 | margin-inline-start: 8px; 11 | } 12 | -------------------------------------------------------------------------------- /license-header.js: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.scss: -------------------------------------------------------------------------------- 1 | .mdc-list-item--with-leading-icon .mdc-list-item__start { 2 | margin-right: 12px; 3 | } 4 | .mat-mdc-list-item.active { 5 | color: #fff; 6 | background-color: rgba(255, 255, 255, 0.1); 7 | width: calc(100%); 8 | } 9 | -------------------------------------------------------------------------------- /src/app/shared/dialog-content/dialog-content.component.html: -------------------------------------------------------------------------------- 1 | {{ data.name }} 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Docker Node.js Launch", 5 | "type": "docker", 6 | "request": "launch", 7 | "preLaunchTask": "docker-run: debug", 8 | "platform": "node" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/app/devices/device-detail/device-detail.component.scss: -------------------------------------------------------------------------------- 1 | .mdc-list-item--with-leading-icon .mdc-list-item__start { 2 | margin-right: 12px; 3 | } 4 | 5 | a.mat-mdc-list-item.active { 6 | color: #fff; 7 | background-color: rgba(0, 0, 0, 0.1); 8 | width: calc(100%); 9 | } 10 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ...require('prettier-config-standard'), 3 | plugins: ['prettier-plugin-multiline-arrays'], 4 | printWidth: 120, 5 | singleAttributePerLine: false, 6 | bracketSameLine: true, 7 | trailingComma: 'none', 8 | multilineArraysWrapThreshold: 2 9 | } 10 | -------------------------------------------------------------------------------- /src/app/core/toolbar/toolbar.component.scss: -------------------------------------------------------------------------------- 1 | .version { 2 | margin-right: 16px; 3 | } 4 | mat-divider { 5 | height: 30px; 6 | margin-right: 16px; 7 | } 8 | 9 | .lang-scroll { 10 | max-height: 200px; 11 | overflow-y: auto; 12 | } 13 | 14 | .lang-menu { 15 | min-width: 180px; 16 | } 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | sample-web-ui: 3 | image: sample-web-ui 4 | build: 5 | context: . 6 | dockerfile: ./Dockerfile 7 | environment: 8 | NODE_ENV: production 9 | ports: 10 | - 8089:80 11 | volumes: 12 | - ./nginx.conf:/etc/nginx/conf.d/default.conf 13 | -------------------------------------------------------------------------------- /cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Import commands.js using ES2015 syntax: 7 | import './commands' 8 | -------------------------------------------------------------------------------- /cypress/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | // be explicit about types included 6 | // to avoid clashing with Jest types 7 | "types": ["cypress"] 8 | }, 9 | "include": [ 10 | "../node_modules/cypress", 11 | "**/*.ts" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/paging.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const paging = { 7 | totalCount: 100 8 | } 9 | export { paging } 10 | -------------------------------------------------------------------------------- /docker-compose.debug.yml: -------------------------------------------------------------------------------- 1 | version: '3.4' 2 | 3 | services: 4 | sample-web-ui: 5 | image: sample-web-ui 6 | build: 7 | context: . 8 | dockerfile: ./Dockerfile 9 | environment: 10 | NODE_ENV: development 11 | ports: 12 | - 80:80 13 | - 9229:9229 14 | command: ["node", "--inspect=0.0.0.0:9229", "index.js"] 15 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/device.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const deviceFixtures = { 7 | totalCount: 100 8 | } 9 | 10 | export { deviceFixtures } 11 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [ 7 | "@angular/localize" 8 | ] 9 | }, 10 | "files": [ 11 | "src/main.ts" 12 | ], 13 | "include": [ 14 | "src/**/*.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/app/devices/devices.component.scss: -------------------------------------------------------------------------------- 1 | .mat-column-actions { 2 | width: 275px; 3 | flex: 0 0 275px; 4 | } 5 | .mat-column-notification { 6 | width: 75px; 7 | flex: 0 0 75px; 8 | } 9 | 10 | mat-icon.addTag { 11 | visibility: hidden; 12 | } 13 | 14 | mat-icon.alwaysHidden { 15 | visibility: hidden; 16 | } 17 | 18 | mat-cell:hover mat-icon.addTag { 19 | visibility: visible; 20 | } 21 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 | 2 | @if (isLoggedIn) { 3 | 4 | 5 | 6 | } 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/domain.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const domainFixtures = { 7 | default: { 8 | profileName: 'happyPath' 9 | } 10 | } 11 | 12 | export { domainFixtures } 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/.classpath 2 | **/.dockerignore 3 | **/.env 4 | **/.git 5 | **/.gitignore 6 | **/.project 7 | **/.settings 8 | **/.toolstarget 9 | **/.vs 10 | **/.vscode 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/charts 16 | **/docker-compose* 17 | **/Dockerfile* 18 | **/node_modules 19 | **/npm-debug.log 20 | **/obj 21 | **/secrets.dev.yaml 22 | **/values.dev.yaml 23 | README.md 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.html] 12 | indent_size = 2 13 | max_line_length = 240 14 | 15 | [*.ts] 16 | quote_type = single 17 | 18 | [*.md] 19 | max_line_length = off 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. 4 | 5 | ## Reporting a Vulnerability 6 | 7 | Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). 8 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Device Management Toolkit 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/spec", 6 | "types": [ 7 | "jasmine", 8 | "@angular/localize" 9 | ] 10 | }, 11 | "files": [ 12 | "src/test.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/stats.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const stats = { 7 | get: { 8 | success: { 9 | response: { totalCount: 10, connectedCount: 5, disconnectedCount: 5 } 10 | } 11 | } 12 | } 13 | 14 | export default stats 15 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-sol/device-enable-sol.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'sol.title.value' | translate }}

2 | {{ 'sol.enable.value' | translate }} 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/httpCodes.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const httpCodes = { 7 | SUCCESS: 200, 8 | CREATED: 201, 9 | NO_CONTENT: 204, 10 | BAD_REQUEST: 400, 11 | UNAUTHORIZED: 401, 12 | INTERNAL_SERVER_ERROR: 500 13 | } 14 | export { httpCodes } 15 | -------------------------------------------------------------------------------- /src/app/shared/power-up-alert/power-up-alert.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'powerUp.title.value' | translate }}

2 | {{ 'powerUp.message.value' | translate }} 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-kvm/device-enable-kvm.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'kvm.title.value' | translate }}

2 |
{{ 'kvm.enable.value' | translate }}
3 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | use Angular 20 2 | Do not use semicolons 3 | use single quotes for typescript 4 | use control flow syntax 5 | use signals if applicable 6 | use reactive forms 7 | don't use deprecated APIs like HttpTestingModule or RouterTestingModule in unit tests 8 | prefer zoneless change detection 9 | use angular material components 10 | use standalone components 11 | Always ensure components and its associated HTML are working 12 | Always ensure unit tests pass 100% 13 | run `npm run lint` when complete 14 | -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | export const environment = { 7 | production: true, 8 | cloud: true, 9 | useOAuth: false, // for use with console 10 | mpsServer: '##MPS_SERVER##', 11 | rpsServer: '##RPS_SERVER##', 12 | vault: '##VAULT_SERVER##', 13 | auth: {} 14 | } 15 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | server_name _; 5 | 6 | 7 | location / { 8 | root /usr/share/nginx/html; 9 | index index.html index.htm; 10 | } 11 | 12 | error_page 404 /index.html; 13 | 14 | # redirect server error pages to the static page /50x.html 15 | # 16 | error_page 500 502 503 504 /50x.html; 17 | location = /50x.html { 18 | root /usr/share/nginx/html; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/app/shared/are-you-sure/are-you-sure.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'delete.question.value' | translate }}

2 | {{ 'delete.questionRecord.value' | translate }} 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/shared/pipes/date-formatter.pipe.ts.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core' 2 | import { format, isValid } from 'date-fns' 3 | 4 | @Pipe({ 5 | name: 'amDateFormatter' 6 | }) 7 | export class AmDateFormatterPipe implements PipeTransform { 8 | transform(date: Date | string | number, useFormat = 'MMMM d, yyyy h:mma'): string { 9 | if (!date) return '' 10 | const parsedDate = new Date(date) 11 | if (!isValid(parsedDate)) return 'Invalid Date' 12 | return format(parsedDate, useFormat) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/jumanjihouse/pre-commit-hooks 7 | rev: 3.0.0 8 | hooks: 9 | - id: shellcheck 10 | - repo: https://github.com/pre-commit/mirrors-eslint 11 | rev: v8.38.0 12 | hooks: 13 | - id: eslint 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v4.4.0 16 | hooks: 17 | - id: end-of-file-fixer 18 | - id: trailing-whitespace 19 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/tags.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const tags = { 7 | getAll: { 8 | success: { 9 | response: [ 10 | 'Windows', 11 | 'Linux', 12 | 'NUC', 13 | 'Dell', 14 | 'Store #123', 15 | 'Store #456' 16 | ] 17 | } 18 | } 19 | } 20 | 21 | export { tags } 22 | -------------------------------------------------------------------------------- /src/app/shared/static-cira-warning/static-cira-warning.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'staticCira.title.value' | translate }}

2 | {{ 'staticCira.message.value' | translate }} 3 | 4 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /src/app/shared/pipes/time-ago-formatter.pipe.ts.pipe.ts: -------------------------------------------------------------------------------- 1 | import { Pipe, PipeTransform } from '@angular/core' 2 | import { formatDistanceToNow, isValid } from 'date-fns' 3 | 4 | @Pipe({ 5 | name: 'amTimeAgoFormatter' 6 | }) 7 | export class AmTimeAgoFormatterPipe implements PipeTransform { 8 | transform(date: Date | string | number, addSuffix = true): string { 9 | if (!date) return '' 10 | const parsedDate = new Date(date) 11 | if (!isValid(parsedDate)) return 'Invalid Date' 12 | return formatDistanceToNow(parsedDate, { addSuffix: addSuffix }) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/wireless.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const wirelessFixtures = { 7 | happyPath: { 8 | profileName: 'happyPath', 9 | authenticationMethod: 'WPA2 PSK', 10 | encryptionMethod: 'CCMP', 11 | linkPolicy: [14, 16] 12 | }, 13 | wrong: { 14 | profileName: 'wireless config' 15 | } 16 | } 17 | 18 | export { wirelessFixtures } 19 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | export const availableLangs = [ 2 | { code: 'en', label: 'English' }, 3 | { code: 'fr', label: 'Français' }, 4 | { code: 'de', label: 'Deutsch' }, 5 | { code: 'es', label: 'Español' }, 6 | { code: 'nl', label: 'Nederlands' }, 7 | { code: 'it', label: 'Italiano' }, 8 | { code: 'ja', label: '日本語' }, 9 | { code: 'ar', label: 'العربية' }, 10 | { code: 'fi', label: 'Suomi' }, 11 | { code: 'he', label: 'עברית' }, 12 | { code: 'ru', label: 'Русский' }, 13 | { code: 'sv', label: 'Svenska' } 14 | ] 15 | 16 | export const rtlLangs = [ 17 | 'ar', 18 | 'he' 19 | ] 20 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Direction } from '@angular/cdk/bidi' 7 | import { rtlLangs } from './constants' 8 | 9 | export const caseInsensitiveCompare = (s1: string, s2: string): number => { 10 | return s1.localeCompare(s2) 11 | } 12 | 13 | export const getDirection = (langCode: string): Direction => { 14 | return rtlLangs.includes(langCode) ? 'rtl' : 'ltr' 15 | } 16 | -------------------------------------------------------------------------------- /.github/commitlint.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | rules: { 4 | 'body-max-line-length': [2, 'always', 200], 5 | 'subject-case': [ 6 | 1, 7 | 'never', 8 | ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], 9 | ], 10 | 'scope-enum': [ 11 | 2, 12 | 'always', 13 | ['core', 'dashboard', 'devices', 'domains', 'profiles', '8021x', 'login', 'wireless', 'docker', 'deps', 'deps-dev', 'gh-actions', 'config', 'e2e', 'i18n'] 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/general.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const badRequest = { 7 | response: { 8 | error: 'string', 9 | message: 'string' 10 | } 11 | } 12 | const empty = { 13 | response: { 14 | data: [], 15 | totalCount: 0 16 | } 17 | } 18 | const error = { 19 | response: { 20 | error: 'string', 21 | message: 'string' 22 | } 23 | } 24 | export { badRequest, error, empty } 25 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/urls.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const urlFixtures = { 7 | base: 'http://localhost:4200/', 8 | 9 | page: { 10 | login: 'login', 11 | domain: 'domains', 12 | cira: 'ciraconfigs', 13 | profile: 'profiles', 14 | device: 'devices', 15 | wireless: 'wireless', 16 | ieee8021x: 'ieee8021x' 17 | }, 18 | 19 | extensions: { 20 | creation: 'new' 21 | } 22 | } 23 | export { urlFixtures } 24 | -------------------------------------------------------------------------------- /src/styles/css2.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 100 700; 6 | src: url(../assets/kJEhBvYX7BgnkSrUwT8OhrdQw4oELdPIeeII9v6oFsI.woff2) format('woff2'); 7 | } 8 | 9 | .material-icons { 10 | font-family: 'Material Icons'; 11 | font-weight: normal; 12 | font-style: normal; 13 | font-size: 24px; 14 | line-height: 1; 15 | letter-spacing: normal; 16 | text-transform: none; 17 | display: inline-block; 18 | white-space: nowrap; 19 | word-wrap: normal; 20 | direction: ltr; 21 | -webkit-font-feature-settings: 'liga'; 22 | -webkit-font-smoothing: antialiased; 23 | } 24 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | // Import material theming functions 2 | @use '@angular/material' as mat; 3 | 4 | // Define the palettes for your theme using the Material Design palettes available in palette.scss 5 | // (imported above). For each palette, you can optionally specify a default, lighter, and darker 6 | // hue. Available color palettes: https://material.io/design/color/ 7 | $samplewebui-primary: mat.m2-define-palette(mat.$m2-light-blue-palette); 8 | $samplewebui-accent: mat.m2-define-palette(mat.$m2-blue-grey-palette, A200, A100, A400); 9 | 10 | // The warn palette is optional (defaults to red). 11 | $samplewebui-warn: mat.m2-define-palette(mat.$m2-red-palette); 12 | -------------------------------------------------------------------------------- /src/environments/environment.enterprise.dev.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | export const environment = { 7 | production: false, 8 | cloud: false, 9 | useOAuth: false, // for use with console 10 | mpsServer: 'http://localhost:8181', 11 | rpsServer: 'http://localhost:8181', 12 | vault: '', 13 | auth: { 14 | clientId: '##CLIENTID##', 15 | issuer: '##ISSUER##', 16 | redirectUri: '##REDIRECTURI##', 17 | scope: '##SCOPE##', 18 | responseType: 'code', 19 | requireHttps: false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## PR Checklist 2 | 3 | 4 | 5 | - [ ] Unit Tests have been added for new changes 6 | - [ ] API tests have been updated if applicable 7 | - [ ] All commented code has been removed 8 | - [ ] If you've added a dependency, you've ensured license is compatible with Apache 2.0 and clearly outlined the added dependency. 9 | 10 | ## What are you changing? 11 | 12 | 13 | 14 | ## Anything the reviewer should know when reviewing this PR? 15 | 16 | ### If the there are associated PRs in other repositories, please link them here (i.e. device-management-toolkit/repo#365 ) 17 | -------------------------------------------------------------------------------- /.github/workflows/projectsSync.yaml: -------------------------------------------------------------------------------- 1 | name: Adds all issues to project board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | add-to-project: 13 | name: Add issue to project 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Harden Runner 17 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 18 | with: 19 | egress-policy: audit 20 | 21 | - uses: actions/add-to-project@244f685bbc3b7adfa8466e08b698b5577571133e # v1.0.2 22 | with: 23 | project-url: https://github.com/orgs/device-management-toolkit/projects/10 24 | github-token: ${{ secrets.PROJECTS_PAT }} 25 | -------------------------------------------------------------------------------- /src/app/shared/random-pass-alert/random-pass-alert.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'randomPassword.title.value' | translate }}

2 | 3 | {{ 'randomPassword.message.value' | translate }} 4 | 5 | {{ 'randomPassword.learnMore.value' | translate }}. 7 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /src/app/shared/pipes/toolkit.pipe.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Pipe, PipeTransform } from '@angular/core' 7 | import { FormOption } from 'src/models/models' 8 | 9 | @Pipe({ 10 | name: 'toolkit', 11 | standalone: true 12 | }) 13 | export class ToolkitPipe implements PipeTransform { 14 | transform(value: number, ...args: unknown[]): string | null { 15 | const values = args[0] as FormOption[] 16 | const method = values.find((method) => method.value === value) 17 | return method ? method.label : null 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/styles/styles.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | mat-drawer-container, 4 | mat-sidenav-container { 5 | margin: 0; 6 | width: 100%; 7 | height: 100%; 8 | overflow: auto; //font-size: 14px; 9 | } 10 | // adjust mat-checkbox to align 11 | // at the top of the label text 12 | // (needed for multiple line labels) 13 | /*TODO(mdc-migration): The following rule targets internal classes of checkbox that may no longer apply for the MDC version.*/ 14 | .mat-checkbox-inner-container { 15 | margin-top: 5px !important; 16 | } 17 | 18 | .error-messages-container.mat-mdc-list-item.mdc-list-item--with-leading-icon.mdc-list-item--with-one-line { 19 | height: auto; 20 | padding: 10px 0; 21 | } 22 | 23 | .error-messages.mdc-list-item__primary-text { 24 | white-space: break-spaces; 25 | } 26 | -------------------------------------------------------------------------------- /src/app/shared/auth-guard.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, inject } from '@angular/core' 2 | import { AuthService } from '../auth.service' 3 | import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router' 4 | import { Observable, tap } from 'rxjs' 5 | 6 | @Injectable() 7 | export class AuthGuard { 8 | private authService = inject(AuthService) 9 | private router = inject(Router) 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 12 | canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { 13 | return this.authService.canActivateProtectedRoutes$.pipe( 14 | tap((x) => { 15 | if (!x) { 16 | this.router.navigate(['/login']) 17 | } 18 | }) 19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/app/profiles/key-display-dialog/key-display-dialog.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'keyDisplayDialog.title.value' | translate }}

2 | 3 | 4 |

{{ 'keyDisplayDialog.description.value' | translate }}

5 | 6 | {{ 'keyDisplayDialog.keyLabel.value' | translate }} 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | -------------------------------------------------------------------------------- /src/environments/environment.enterprise.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | export const environment = { 7 | production: true, 8 | cloud: false, 9 | useOAuth: false, // for use with console 10 | mpsServer: '##CONSOLE_SERVER_API##', 11 | rpsServer: '##CONSOLE_SERVER_API##', 12 | vault: '##VAULT_SERVER##', 13 | auth: { 14 | clientId: '##CLIENTID##', 15 | issuer: '##ISSUER##', 16 | redirectUri: '##REDIRECTURI##', 17 | scope: '##SCOPE##', 18 | responseType: 'code', 19 | requireHttps: true, // set to false when local 20 | strictDiscoveryDocumentValidation: true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cypress/e2e/integration/eventchannellogs/get.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | describe('Test eventchannellogs', () => { 7 | beforeEach('', () => { 8 | cy.setup() 9 | }) 10 | 11 | // it('check default values', () => { 12 | // cy.goToPage('MQTT Events') 13 | // cy.get('input[name="hostname"]').invoke('val').should('eq', 'localhost') 14 | // cy.get('input[name="path"]').invoke('val').should('eq', '/mosquitto/mqtt') 15 | // }) 16 | 17 | // it('load all the eventchannelogs', () => { 18 | // cy.goToPage('MQTT Events') 19 | // cy.get('h3').should('have.text', 'No Events') 20 | // }) 21 | }) 22 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/eventlogs.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const eventLogFixtures = { 7 | happyPath: { 8 | DeviceAddress: 255, 9 | EventSensorType: 15, 10 | EventType: 'Sensor specific event', 11 | EventOffset: 2, 12 | EventSourceType: 104, 13 | EventSeverity: 8, 14 | SensorNumber: 255, 15 | Entity: 34, 16 | EntityInstance: 0, 17 | EventData: [ 18 | 64, 19 | 19, 20 | 0, 21 | 0, 22 | 0, 23 | 0, 24 | 0, 25 | 0 26 | ], 27 | Time: '2021-09-08T16:31:02.000Z', 28 | EntityStr: 'BIOS', 29 | Desc: 'Starting operating system boot process' 30 | } 31 | } 32 | export { eventLogFixtures } 33 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/auditLogs.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const auditLogFixtures = { 7 | happyPath: { 8 | totalCnt: 1, 9 | records: [ 10 | { 11 | AuditAppID: 16, 12 | EventID: 11, 13 | InitiatorType: 0, 14 | AuditApp: 'Security Admin', 15 | Event: 'KVM enabled', 16 | Initiator: 'admin', 17 | Time: '2021-09-08T16:31:02.000Z', 18 | MCLocationType: 0, 19 | NetAddress: '127.0.0.1', 20 | Ex: '\u0004`g\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000', 21 | ExStr: null 22 | } 23 | ] 24 | } 25 | } 26 | export { auditLogFixtures as eventLogFixtures } 27 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "docker-build", 6 | "label": "docker-build", 7 | "platform": "node", 8 | "dockerBuild": { 9 | "dockerfile": "${workspaceFolder}/Dockerfile", 10 | "context": "${workspaceFolder}", 11 | "pull": true 12 | } 13 | }, 14 | { 15 | "type": "docker-run", 16 | "label": "docker-run: release", 17 | "dependsOn": [ 18 | "docker-build" 19 | ], 20 | "platform": "node" 21 | }, 22 | { 23 | "type": "docker-run", 24 | "label": "docker-run: debug", 25 | "dependsOn": [ 26 | "docker-build" 27 | ], 28 | "dockerRun": { 29 | "env": { 30 | "DEBUG": "*", 31 | "NODE_ENV": "development" 32 | } 33 | }, 34 | "node": { 35 | "enableDebugging": true 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/app/wireless/wireless.constants.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { FormOption } from '../../models/models' 7 | 8 | // https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/WS-Management_Class_Reference/CIM_WiFiEndpointSettings.htm 9 | 10 | export const AuthenticationMethods: FormOption[] = [ 11 | { value: 4, mode: 'PSK', label: 'WPA PSK' }, 12 | { value: 5, mode: '802.1x', label: 'WPA IEEE 802.1x' }, 13 | { value: 6, mode: 'PSK', label: 'WPA2 PSK' }, 14 | { value: 7, mode: '802.1x', label: 'WPA2 IEEE 802.1x' } 15 | ] 16 | 17 | export const EncryptionMethods: FormOption[] = [ 18 | { value: 3, label: 'TKIP' }, 19 | { value: 4, label: 'CCMP' } 20 | ] 21 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.scss: -------------------------------------------------------------------------------- 1 | @use '@angular/material' as mat; 2 | @use 'src/styles/_variables.scss' as vars; 3 | 4 | h1, 5 | .mat-headline-4, 6 | .mat-headline-3 { 7 | margin-bottom: 12px; 8 | padding-bottom: 0; 9 | } 10 | 11 | .mat-headline-2 { 12 | margin: 12px; 13 | } 14 | 15 | .mat-icon { 16 | font-size: 48px; 17 | height: 48px; 18 | line-height: 1; 19 | width: 48px; 20 | color: mat.m2-get-color-from-palette(vars.$samplewebui-primary, A200); 21 | } 22 | 23 | .mat-mdc-icon-button { 24 | width: 100px; 25 | height: 100px; 26 | border: 2px solid mat.m2-get-color-from-palette(vars.$samplewebui-accent, A100); 27 | } 28 | 29 | .mat-mdc-card { 30 | background-color: mat.m2-get-color-from-palette(vars.$samplewebui-accent, A100); 31 | } 32 | 33 | mat-divider { 34 | margin: 24px 0; 35 | } 36 | 37 | h3 { 38 | margin-top: 24px; 39 | text-align: center; 40 | } 41 | 42 | .pointer { 43 | cursor: pointer; 44 | } 45 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 7 | 8 | import { NgModule, provideZonelessChangeDetection } from '@angular/core' 9 | import { getTestBed } from '@angular/core/testing' 10 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing' 11 | @NgModule({ 12 | providers: [provideZonelessChangeDetection()] 13 | }) 14 | export class ZonelessTestModule {} 15 | // First, initialize the Angular testing environment. 16 | getTestBed().initTestEnvironment([BrowserDynamicTestingModule, ZonelessTestModule], platformBrowserDynamicTesting(), { 17 | teardown: { destroyAfterEach: false } 18 | }) 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | #********************************************************************* 2 | # Copyright (c) Intel Corporation 2021 3 | # SPDX-License-Identifier: Apache-2.0 4 | #*********************************************************************/ 5 | ### STAGE 1: Build ### 6 | FROM node:25-bullseye-slim@sha256:7e6104f4086bd04c44bd62f6b7469422865c19342ab1073ddf9de0936d43f207 AS build 7 | WORKDIR /usr/src/app 8 | COPY package.json package-lock.json ./ 9 | RUN npm ci 10 | COPY . . 11 | RUN npm run build -- --configuration=production 12 | 13 | ### STAGE 2: Run ### 14 | FROM nginx:mainline-alpine-slim@sha256:a5459dbb9ed17c9f1eff5448a5dfb22ea3eb386a356e26fc16871dc426ac5383 15 | 16 | LABEL license='SPDX-License-Identifier: Apache-2.0' \ 17 | copyright='Copyright (c) 2021: Intel' 18 | 19 | RUN apk update && apk upgrade --no-cache 20 | 21 | COPY --from=build /usr/src/app/dist/samplewebui/browser /usr/share/nginx/html 22 | COPY --from=build /usr/src/app/init.sh /docker-entrypoint.d/init.sh 23 | EXPOSE 80 -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/networkConfig.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const networkConfigs = { 7 | getAll: { 8 | success: { 9 | response: [ 10 | { 11 | profileName: 'happyPath', 12 | dhcpEnabled: true 13 | } 14 | ] 15 | } 16 | }, 17 | create: { 18 | success: { 19 | response: { 20 | profileName: 'profile6', 21 | dhcpEnabled: true 22 | } 23 | } 24 | }, 25 | update: { 26 | success: { 27 | response: { 28 | profileName: 'profile6', 29 | dhcpEnabled: true 30 | } 31 | } 32 | }, 33 | get: { 34 | success: { 35 | response: { 36 | profileName: 'profile6', 37 | dhcpEnabled: true 38 | } 39 | } 40 | } 41 | } 42 | 43 | export { networkConfigs } 44 | -------------------------------------------------------------------------------- /src/app/shared/are-you-sure/are-you-sure.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component } from '@angular/core' 7 | import { MatButton } from '@angular/material/button' 8 | import { CdkScrollable } from '@angular/cdk/scrolling' 9 | import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | @Component({ 12 | selector: 'app-are-you-sure', 13 | templateUrl: './are-you-sure.component.html', 14 | styleUrls: ['./are-you-sure.component.scss'], 15 | imports: [ 16 | MatDialogTitle, 17 | CdkScrollable, 18 | MatDialogContent, 19 | MatDialogActions, 20 | MatButton, 21 | MatDialogClose, 22 | TranslateModule 23 | ] 24 | }) 25 | export class AreYouSureDialogComponent {} 26 | -------------------------------------------------------------------------------- /src/app/shared/power-up-alert/power-up-alert.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component } from '@angular/core' 7 | import { MatButton } from '@angular/material/button' 8 | import { CdkScrollable } from '@angular/cdk/scrolling' 9 | import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | @Component({ 13 | selector: 'app-power-up-alert', 14 | templateUrl: './power-up-alert.component.html', 15 | styleUrls: ['./power-up-alert.component.scss'], 16 | imports: [ 17 | MatDialogTitle, 18 | CdkScrollable, 19 | MatDialogContent, 20 | MatDialogActions, 21 | MatButton, 22 | TranslateModule, 23 | MatDialogClose 24 | ] 25 | }) 26 | export class PowerUpAlertComponent {} 27 | -------------------------------------------------------------------------------- /src/app/shared/random-pass-alert/random-pass-alert.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component } from '@angular/core' 7 | import { MatButton } from '@angular/material/button' 8 | import { CdkScrollable } from '@angular/cdk/scrolling' 9 | import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | @Component({ 13 | selector: 'app-random-pass-alert', 14 | templateUrl: './random-pass-alert.component.html', 15 | styleUrls: ['./random-pass-alert.component.scss'], 16 | imports: [ 17 | MatDialogTitle, 18 | CdkScrollable, 19 | MatDialogContent, 20 | MatDialogActions, 21 | MatButton, 22 | TranslateModule, 23 | MatDialogClose 24 | ] 25 | }) 26 | export class RandomPassAlertComponent {} 27 | -------------------------------------------------------------------------------- /src/app/shared/dialog-content/dialog-content.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, inject } from '@angular/core' 7 | import { MAT_DIALOG_DATA, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 8 | import { MatButton } from '@angular/material/button' 9 | import { CdkScrollable } from '@angular/cdk/scrolling' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | @Component({ 13 | selector: 'app-dialog-content', 14 | templateUrl: './dialog-content.component.html', 15 | styleUrls: ['./dialog-content.component.scss'], 16 | imports: [ 17 | CdkScrollable, 18 | MatDialogContent, 19 | MatDialogActions, 20 | MatButton, 21 | TranslateModule, 22 | MatDialogClose 23 | ] 24 | }) 25 | export class DialogContentComponent { 26 | data = inject(MAT_DIALOG_DATA) 27 | } 28 | -------------------------------------------------------------------------------- /src/app/shared/static-cira-warning/static-cira-warning.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component } from '@angular/core' 7 | import { MatButton } from '@angular/material/button' 8 | import { CdkScrollable } from '@angular/cdk/scrolling' 9 | import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | @Component({ 13 | selector: 'app-static-cira-warning', 14 | templateUrl: './static-cira-warning.component.html', 15 | styleUrls: ['./static-cira-warning.component.scss'], 16 | imports: [ 17 | MatDialogTitle, 18 | CdkScrollable, 19 | MatDialogContent, 20 | MatDialogActions, 21 | MatButton, 22 | TranslateModule, 23 | MatDialogClose 24 | ] 25 | }) 26 | export class StaticCIRAWarningComponent {} 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | cy: 2 | @read -r -p "Isolate the front end? (y/n): " isolate;\ 3 | read -r -p "Enter the target url: " targetUrl;\ 4 | npm run cypress -- --env ISOLATED=$$isolate,BASEURL=$$targetUrl 5 | 6 | runner: 7 | @read -r -p "Isolate the front end? (y/n): " isolate;\ 8 | read -r -p "Enter the target url: " targetUrl;\ 9 | npm run cy-runner -- --env ISOLATED=$$isolate,BASEURL=$$targetUrl 10 | 11 | # container: 12 | # docker run -d -p 4200:80 vprodemo.azurecr.ui/samplewebui:latest 13 | 14 | # test-container: 15 | # docker run -d -p 4201:80 vprodemo.azurecr.ui/samplewebui:latest 16 | 17 | #npm run start 18 | 19 | e2e-runner: 20 | #prep 21 | npx cypress run --spec "cypress/integration/login/*" 22 | #create 23 | npx cypress run --spec "cypress/integration/cira/create.*" 24 | npx cypress run --spec "cypress/integration/profile/create.*" 25 | #test 26 | npx cypress run --spec "cypress/integration/**/create-error.*,cypress/integration/domain/* " 27 | #delete 28 | npx cypress run --spec "cypress/integration/profile/delete.*" 29 | npx cypress run --spec "cypress/integration/cira/delete.*" 30 | -------------------------------------------------------------------------------- /.releaserc.json: -------------------------------------------------------------------------------- 1 | { 2 | "branches": [ 3 | "main", 4 | "next" 5 | ], 6 | "plugins": [ 7 | [ 8 | "@semantic-release/commit-analyzer", 9 | { 10 | "releaseRules": [ 11 | { 12 | "type": "chore", 13 | "release": "patch" 14 | } 15 | ] 16 | } 17 | ], 18 | "@semantic-release/changelog", 19 | [ 20 | "@semantic-release/npm", 21 | { 22 | "npmPublish": false 23 | } 24 | ], 25 | "@semantic-release/release-notes-generator", 26 | "@semantic-release/github", 27 | [ 28 | "@semantic-release/exec", 29 | { 30 | "publishCmd": "docker buildx build --platform linux/amd64,linux/arm64 --push -t vprodemo.azurecr.io/webui:v${nextRelease.version} -t vprodemo.azurecr.io/webui:latest -t docker.io/intel/oact-webui:v${nextRelease.version} -t docker.io/intel/oact-webui:latest -t docker.io/intel/device-mgmt-toolkit-web-ui:v${nextRelease.version} -t docker.io/intel/device-mgmt-toolkit-web-ui:latest ." 31 | } 32 | ], 33 | "@semantic-release/git" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/semantic.yml: -------------------------------------------------------------------------------- 1 | name: "Semantic Pull Request" 2 | 3 | on: 4 | pull_request: 5 | types: ["opened", "edited", "reopened", "synchronize"] 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | main: 12 | name: Validate PR and Commits 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Harden Runner 16 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 17 | with: 18 | egress-policy: audit 19 | 20 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 21 | with: 22 | fetch-depth: 0 23 | - uses: wagoid/commitlint-github-action@b948419dd99f3fd78a6548d48f94e3df7f6bf3ed # v6.2.1 24 | with: 25 | configFile: .github/commitlint.config.cjs 26 | - name: Install Dependencies 27 | run: npm install @commitlint/config-conventional@18.5 28 | - uses: JulienKode/pull-request-name-linter-action@4fb4c2773193ad7ae5fe105c98ab30778abb1536 # v20.1.0 29 | with: 30 | configuration-path: ./.github/commitlint.config.cjs 31 | -------------------------------------------------------------------------------- /cypress/e2e/integration/dashboard/devicecount.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 7 | 8 | describe('Dashboard Test', () => { 9 | beforeEach(() => { 10 | cy.setup() 11 | }) 12 | 13 | it('shows stats on dashboard', () => { 14 | cy.wait('@stats-request').then((response) => { 15 | if (response.response) { 16 | expect(response.response.statusCode).to.eq(httpCodes.SUCCESS) 17 | const totalCount = response.response.body.totalCount 18 | const connectedCount = response.response.body.connectedCount 19 | cy.get('[data-cy="totalCount"]').should('have.text', totalCount) 20 | cy.get('[data-cy="connectedCount"]').should('have.text', connectedCount) 21 | cy.get('[data-cy="disconnectedCount"]').should('have.text', totalCount - connectedCount) 22 | } 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 28 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-kvm/device-enable-kvm.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, inject } from '@angular/core' 7 | import { 8 | MatDialogRef, 9 | MatDialogTitle, 10 | MatDialogActions, 11 | MatDialogClose, 12 | MatDialogContent 13 | } from '@angular/material/dialog' 14 | import { MatButton } from '@angular/material/button' 15 | import { TranslateModule } from '@ngx-translate/core' 16 | 17 | @Component({ 18 | selector: 'app-device-enable-kvm', 19 | templateUrl: './device-enable-kvm.component.html', 20 | styleUrls: ['./device-enable-kvm.component.scss'], 21 | imports: [ 22 | MatDialogTitle, 23 | MatDialogContent, 24 | MatDialogActions, 25 | MatButton, 26 | TranslateModule, 27 | MatDialogClose 28 | ] 29 | }) 30 | export class DeviceEnableKvmComponent { 31 | dialogRef = inject>(MatDialogRef) 32 | } 33 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-sol/device-enable-sol.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, inject } from '@angular/core' 7 | import { MatDialogRef, MatDialogTitle, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 8 | import { MatButton } from '@angular/material/button' 9 | import { MatCardContent } from '@angular/material/card' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | @Component({ 13 | selector: 'app-device-enable-sol', 14 | templateUrl: './device-enable-sol.component.html', 15 | styleUrls: ['./device-enable-sol.component.scss'], 16 | imports: [ 17 | MatDialogTitle, 18 | MatCardContent, 19 | MatDialogActions, 20 | MatButton, 21 | TranslateModule, 22 | MatDialogClose 23 | ] 24 | }) 25 | export class DeviceEnableSolComponent { 26 | dialogRef = inject>(MatDialogRef) 27 | } 28 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/go 3 | { 4 | "name": "Node", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/typescript-node:22-bullseye", 7 | // Features to add to the dev container. More info: https://containers.dev/features. 8 | // "features": {}, 9 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 10 | "forwardPorts": [ 11 | 4200 12 | ], 13 | "portsAttributes": { 14 | "4200": { 15 | "label": "Angular Dev Server", 16 | "onAutoForward": "notify" 17 | } 18 | }, 19 | // Use 'postCreateCommand' to run commands after the container is created. 20 | "postCreateCommand": "npm install" 21 | // Configure tool-specific properties. 22 | // "customizations": {}, 23 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 24 | // "remoteUser": "root" 25 | } 26 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "forceConsistentCasingInFileNames": true, 8 | "esModuleInterop": true, 9 | "strict": true, 10 | "strictBindCallApply": true, 11 | "noImplicitReturns": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "noImplicitAny": true, 14 | "sourceMap": false, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "bundler", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "es2020", 21 | "lib": [ 22 | "es2022", 23 | "dom" 24 | ], 25 | "useDefineForClassFields": false, 26 | "types": [ 27 | "jasmine", 28 | "@angular/localize" 29 | ] 30 | }, 31 | "angularCompilerOptions": { 32 | "strictInjectionParameters": true, 33 | "strictInputAccessModifiers": true, 34 | "strictTemplates": true, 35 | "strictMetadataEmit": true 36 | }, 37 | "include": [ 38 | "src" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /ui 6 | /tmp 7 | /out-tsc 8 | *.exe 9 | # Only exists if Bazel was run 10 | /bazel-out 11 | 12 | # dependencies 13 | /node_modules 14 | 15 | # profiling files 16 | chrome-profiler-events*.json 17 | speed-measure-plugin*.json 18 | 19 | # IDEs and editors 20 | /.idea 21 | .project 22 | .classpath 23 | .c9/ 24 | *.launch 25 | .settings/ 26 | *.sublime-workspace 27 | 28 | # IDE - VSCode 29 | .vscode/* 30 | !.vscode/settings.json 31 | !.vscode/tasks.json 32 | !.vscode/launch.json 33 | !.vscode/extensions.json 34 | .history/* 35 | 36 | # misc 37 | /.angular/cache 38 | /.sass-cache 39 | /connect.lock 40 | /coverage 41 | /libpeerconnection.log 42 | npm-debug.log 43 | yarn-error.log 44 | testem.log 45 | /typings 46 | *.mp4 47 | *.png 48 | *.log 49 | !src/assets/* 50 | 51 | # env file to override CI/CD environment variables 52 | # for cypress tests in dev/local environment 53 | cypress.env.* 54 | cypress.config.dev.ts 55 | cypress-ui-test-output* 56 | 57 | TESTS-* 58 | # System Files 59 | .DS_Store 60 | Thumbs.db 61 | update-angular.sh 62 | -------------------------------------------------------------------------------- /src/app/authorize.interceptor.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { inject } from '@angular/core' 7 | import { HttpInterceptorFn } from '@angular/common/http' 8 | import { AuthService } from './auth.service' 9 | 10 | export const authorizationInterceptor: HttpInterceptorFn = (request, next) => { 11 | const authService = inject(AuthService) 12 | 13 | if (request.url.toString().includes('/authorize') && !request.url.toString().includes('/authorize/redirection')) { 14 | // Skip adding authorization headers for specific routes 15 | return next(request) 16 | } 17 | 18 | const headers: any = { 19 | Authorization: `Bearer ${authService.getLoggedUserToken()}` 20 | } 21 | 22 | if ((request.body as any)?.version != null && (request.body as any)?.version !== '') { 23 | headers['if-match'] = (request.body as any).version 24 | } 25 | 26 | request = request.clone({ 27 | setHeaders: headers 28 | }) 29 | 30 | return next(request) 31 | } 32 | -------------------------------------------------------------------------------- /src/app/devices/device-user-consent-dialog/device-user-consent-dialog.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'deviceUserConsent.title.value' | translate }}

2 |
3 | 4 | {{ 'deviceUserConsent.description.value' | translate }} 5 |

6 | 7 | 15 | {{ 'deviceUserConsent.error.value' | translate }} 16 | {{ 'deviceUserConsent.hint.value' | translate }} 17 | 18 |
19 | 20 | 21 | 22 | 25 | 26 |
27 | -------------------------------------------------------------------------------- /src/app/devices/sol/sol.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | @if (deviceState() === 0 && isLoading() !== false) { 5 | 8 | } @else if (deviceState() !== 2 && isLoading()) { 9 | 12 | } @else { 13 | 16 | } 17 |
18 |
19 | @if (readyToLoadSol) { 20 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/api/ieee8021x.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { wiredConfigs, wirelessConfigs } from '../formEntry/ieee8021x' 7 | 8 | export const wiredConfigsResponse = { 9 | data: wiredConfigs, 10 | totalCount: wiredConfigs.length 11 | } 12 | 13 | export const wirelessConfigsResponse = { 14 | data: wirelessConfigs, 15 | totalCount: wirelessConfigs.length 16 | } 17 | 18 | export const noConfigsResponse = { 19 | data: [], 20 | totalCount: 0 21 | } 22 | 23 | export function interceptGetAll(statusCode: number, body: any): Cypress.Chainable { 24 | return cy.myIntercept('GET', 'ieee8021xconfigs?*$count=true', { statusCode, body }) 25 | } 26 | 27 | export function interceptPost(statusCode: number, body: any): Cypress.Chainable { 28 | return cy.myIntercept('POST', 'ieee8021xconfigs', { statusCode, body }) 29 | } 30 | 31 | export function interceptDelete(statusCode: number, body: any): Cypress.Chainable { 32 | return cy.myIntercept('DELETE', /.*ieee8021x.*/, { statusCode, body }) 33 | } 34 | -------------------------------------------------------------------------------- /cypress/e2e/integration/domain/exipiration.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { domains } from 'cypress/e2e/fixtures/api/domain' 7 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 8 | 9 | // ---------------------------- Test section ---------------------------- 10 | 11 | describe('Test Domain Page', () => { 12 | beforeEach('before', () => { 13 | cy.setup() 14 | }) 15 | 16 | it('checks the expiration date ui functionality', () => { 17 | cy.myIntercept('GET', 'domains?$top=25&$skip=0&$count=true', { 18 | statusCode: httpCodes.SUCCESS, 19 | body: domains.getThree.success.response 20 | }).as('get-domains') 21 | 22 | cy.goToPage('Domains') 23 | cy.wait('@get-domains') 24 | 25 | for (let i = 0; i < 3; i++) { 26 | cy.get('mat-cell').contains(domains.getThree.success.response.data[i].profileName) 27 | cy.get('mat-cell').contains(domains.getThree.success.response.data[i].domainSuffix) 28 | } 29 | 30 | cy.get('simple-snack-bar').contains('expired').should('exist') 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /src/app/profiles/key-display-dialog/key-display-dialog.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing' 2 | 3 | import { KeyDisplayDialogComponent } from './key-display-dialog.component' 4 | import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog' 5 | import { provideNoopAnimations } from '@angular/platform-browser/animations' 6 | import { TranslateModule } from '@ngx-translate/core' 7 | 8 | describe('KeyDisplayDialogComponent', () => { 9 | let component: KeyDisplayDialogComponent 10 | let fixture: ComponentFixture 11 | 12 | beforeEach(() => { 13 | TestBed.configureTestingModule({ 14 | providers: [ 15 | provideNoopAnimations(), 16 | { 17 | provide: MAT_DIALOG_DATA, 18 | useValue: { key: 'test' } 19 | } 20 | ], 21 | imports: [ 22 | KeyDisplayDialogComponent, 23 | MatDialogModule, 24 | TranslateModule.forRoot() 25 | ] 26 | }) 27 | 28 | fixture = TestBed.createComponent(KeyDisplayDialogComponent) 29 | component = fixture.componentInstance 30 | fixture.detectChanges() 31 | }) 32 | 33 | it('should create', () => { 34 | expect(component).toBeTruthy() 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/app/profiles/key-display-dialog/key-display-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, inject } from '@angular/core' 2 | import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog' 3 | import { MatFormFieldModule } from '@angular/material/form-field' 4 | import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard' 5 | import { MatInputModule } from '@angular/material/input' 6 | import { MatButtonModule } from '@angular/material/button' 7 | import { MatIconModule } from '@angular/material/icon' 8 | import { TranslateModule } from '@ngx-translate/core' 9 | 10 | @Component({ 11 | selector: 'app-key-display-dialog', 12 | imports: [ 13 | MatDialogModule, 14 | MatFormFieldModule, 15 | MatInputModule, 16 | MatButtonModule, 17 | ClipboardModule, 18 | MatIconModule, 19 | TranslateModule 20 | ], 21 | templateUrl: './key-display-dialog.component.html', 22 | styleUrl: './key-display-dialog.component.scss' 23 | }) 24 | export class KeyDisplayDialogComponent { 25 | private readonly data = inject(MAT_DIALOG_DATA) 26 | private readonly clipboard = inject(Clipboard) 27 | 28 | public key = '' 29 | 30 | constructor() { 31 | const data = this.data 32 | this.key = data.key 33 | } 34 | 35 | copyKey() { 36 | this.clipboard.copy(this.key) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | **Describe the bug** 🪲 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 🪜 11 | Steps to reproduce the behavior: 12 | 13 | 1. Go to '...' 14 | 2. Click on '....' 15 | 3. Scroll down to '....' 16 | 4. See error 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 🖼️ 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Browser Information (please complete the following information):** 🖥️ 25 | 26 | - OS: [e.g. Windows 10 build 21H1, Ubuntu 20.04 LTS] 27 | - Browser and Version: [e.g. IE Edge 95.0.1020.40 ] 28 | 29 | **Server Side Deployment (please complete the following information):** ⛈️ 30 | 31 | - Deployment Type: [e.g. Azure, Docker, K8s] 32 | - Node Version: [e.g. LTS 14] 33 | - Component & Version: [e.g. RPS 2.0.0] 34 | 35 | **Additional context** 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /src/app/shared/config/snackBarDefault.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { MatSnackBarConfig } from '@angular/material/snack-bar' 7 | 8 | interface SnackbarConfigs { 9 | defaultWarn: MatSnackBarConfig 10 | defaultError: MatSnackBarConfig 11 | longError: MatSnackBarConfig 12 | quickError: MatSnackBarConfig 13 | defaultSuccess: MatSnackBarConfig 14 | longSuccess: MatSnackBarConfig 15 | quickSuccess: MatSnackBarConfig 16 | } 17 | const SnackbarDefaults: SnackbarConfigs = { 18 | defaultWarn: { duration: 3000, panelClass: ['warn', 'mat-elevation-z12'] }, 19 | defaultError: { duration: 3000, panelClass: ['error', 'mat-elevation-z12'] }, 20 | longError: { duration: 10000, panelClass: ['error', 'mat-elevation-z12'] }, 21 | quickError: { duration: 1000, panelClass: ['error', 'mat-elevation-z12'] }, 22 | defaultSuccess: { duration: 3000, panelClass: ['success', 'mat-elevation-z12'] }, 23 | longSuccess: { duration: 10000, panelClass: ['success', 'mat-elevation-z12'] }, 24 | quickSuccess: { duration: 1000, panelClass: ['success', 'mat-elevation-z12'] } 25 | } 26 | 27 | export default SnackbarDefaults 28 | -------------------------------------------------------------------------------- /cypress/e2e/integration/login/ensureEmpty.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Tests the login page with a multitude of fake accounts in 7 | // Checks to make sure that there are no domains, cira configs, or profiles present 8 | // This ensures that the e2e flow is in the proper state before beginning 9 | 10 | describe('Ensure the server is empty', () => { 11 | beforeEach('before', () => { 12 | cy.setup() 13 | }) 14 | 15 | it('checks for domains', () => { 16 | if (Cypress.env('ISOLATE').charAt(0).toLowerCase() === 'n') { 17 | cy.goToPage('Domains') 18 | cy.contains('No Domains').should('be.visible') 19 | } 20 | }) 21 | 22 | it('checks for cira configs', () => { 23 | if (Cypress.env('ISOLATE').charAt(0).toLowerCase() === 'n') { 24 | cy.goToPage('CIRA Configs') 25 | cy.contains('No CIRA Configs').should('be.visible') 26 | } 27 | }) 28 | 29 | it('checks for profiles', () => { 30 | if (Cypress.env('ISOLATE').charAt(0).toLowerCase() === 'n') { 31 | cy.goToPage('Profiles') 32 | cy.contains('No Profiles').should('be.visible') 33 | } 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /src/app/shared/random-pass-alert/random-pass-alert.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogModule } from '@angular/material/dialog' 8 | 9 | import { RandomPassAlertComponent } from './random-pass-alert.component' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | describe('RandomPassAlertComponent', () => { 13 | let component: RandomPassAlertComponent 14 | let fixture: ComponentFixture 15 | 16 | beforeEach(() => { 17 | TestBed.configureTestingModule({ 18 | imports: [ 19 | MatDialogModule, 20 | RandomPassAlertComponent, 21 | TranslateModule.forRoot() 22 | ] 23 | }) 24 | }) 25 | 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(RandomPassAlertComponent) 28 | component = fixture.componentInstance 29 | fixture.detectChanges() 30 | }) 31 | 32 | afterEach(() => { 33 | TestBed.resetTestingModule() 34 | }) 35 | 36 | it('should create', () => { 37 | expect(component).toBeTruthy() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/app/devices/edit-tags/edit-tags.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'editTags.title.value' | translate }}

2 | 3 |

{{ 'editTags.header.value' | translate }}

4 |

{{ 'editTags.description.value' | translate }}

5 |
6 | 7 | {{ 'common.tags.value' | translate }} 8 | 9 | @for (tag of tags; track tag) { 10 | 11 | {{ tag }} 12 | cancel 13 | 14 | } 15 | 21 | 22 | 23 |
24 |
25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/app/shared/static-cira-warning/static-cira-warning.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogModule } from '@angular/material/dialog' 8 | 9 | import { StaticCIRAWarningComponent } from './static-cira-warning.component' 10 | import { TranslateModule } from '@ngx-translate/core' 11 | 12 | describe('StaticCIRAWarningComponent', () => { 13 | let component: StaticCIRAWarningComponent 14 | let fixture: ComponentFixture 15 | 16 | beforeEach(() => { 17 | TestBed.configureTestingModule({ 18 | imports: [ 19 | MatDialogModule, 20 | StaticCIRAWarningComponent, 21 | TranslateModule.forRoot() 22 | ] 23 | }) 24 | }) 25 | 26 | beforeEach(() => { 27 | fixture = TestBed.createComponent(StaticCIRAWarningComponent) 28 | component = fixture.componentInstance 29 | fixture.detectChanges() 30 | }) 31 | 32 | afterEach(() => { 33 | TestBed.resetTestingModule() 34 | }) 35 | 36 | it('should create', () => { 37 | expect(component).toBeTruthy() 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /src/app/shared/power-up-alert/power-up-alert.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatButtonModule } from '@angular/material/button' 8 | import { MatDialogModule } from '@angular/material/dialog' 9 | 10 | import { PowerUpAlertComponent } from './power-up-alert.component' 11 | import { TranslateModule } from '@ngx-translate/core' 12 | 13 | describe('PowerUpAlertComponent', () => { 14 | let component: PowerUpAlertComponent 15 | let fixture: ComponentFixture 16 | 17 | beforeEach(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | MatDialogModule, 21 | MatButtonModule, 22 | PowerUpAlertComponent, 23 | TranslateModule.forRoot() 24 | ] 25 | }) 26 | }) 27 | 28 | beforeEach(() => { 29 | fixture = TestBed.createComponent(PowerUpAlertComponent) 30 | component = fixture.componentInstance 31 | fixture.detectChanges() 32 | }) 33 | 34 | afterEach(() => { 35 | TestBed.resetTestingModule() 36 | }) 37 | 38 | it('should create', () => { 39 | expect(component).toBeTruthy() 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /src/app/shared/pipes/date-formatter.pipe.ts.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { AmDateFormatterPipe } from './date-formatter.pipe.ts.pipe' 2 | 3 | describe('AmDateFormatterPipe', () => { 4 | let pipe: AmDateFormatterPipe 5 | 6 | beforeEach(() => { 7 | pipe = new AmDateFormatterPipe() 8 | }) 9 | 10 | it('create an instance', () => { 11 | expect(pipe).toBeTruthy() 12 | }) 13 | 14 | it('should format date correctly', () => { 15 | const date = new Date('2025-03-05T12:00:00Z') 16 | const result = pipe.transform(date, 'MMMM d, yyyy') 17 | expect(result).toBe('March 5, 2025') 18 | }) 19 | 20 | it('should format date string correctly', () => { 21 | const date = '2025-03-05T12:00:00Z' 22 | const result = pipe.transform(date, 'MMMM d, yyyy') 23 | expect(result).toBe('March 5, 2025') 24 | }) 25 | 26 | it('should format timestamp correctly', () => { 27 | const timestamp = Date.parse('2025-03-05T12:00:00Z') 28 | const result = pipe.transform(timestamp, 'MMMM d, yyyy') 29 | expect(result).toBe('March 5, 2025') 30 | }) 31 | 32 | it('should return empty string if no date is provided', () => { 33 | const result = pipe.transform('') 34 | expect(result).toBe('') 35 | }) 36 | 37 | it('should handle invalid date input gracefully', () => { 38 | const result = pipe.transform('invalid-date') 39 | expect(result).toBe('Invalid Date') 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /src/app/shared/are-you-sure/are-you-sure.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatButtonModule } from '@angular/material/button' 8 | import { MatDialogModule } from '@angular/material/dialog' 9 | 10 | import { AreYouSureDialogComponent } from './are-you-sure.component' 11 | import { TranslateModule } from '@ngx-translate/core' 12 | 13 | describe('AreYouSureComponent', () => { 14 | let component: AreYouSureDialogComponent 15 | let fixture: ComponentFixture 16 | 17 | beforeEach(() => { 18 | TestBed.configureTestingModule({ 19 | imports: [ 20 | MatDialogModule, 21 | MatButtonModule, 22 | AreYouSureDialogComponent, 23 | TranslateModule.forRoot() 24 | ] 25 | }) 26 | }) 27 | 28 | beforeEach(() => { 29 | fixture = TestBed.createComponent(AreYouSureDialogComponent) 30 | component = fixture.componentInstance 31 | fixture.detectChanges() 32 | }) 33 | 34 | afterEach(() => { 35 | TestBed.resetTestingModule() 36 | }) 37 | 38 | it('should create', () => { 39 | expect(component).toBeTruthy() 40 | }) 41 | }) 42 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // This file can be replaced during build by using the `fileReplacements` array. 7 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 8 | // The list of file replacements can be found in `angular.json`. 9 | 10 | export const environment = { 11 | production: false, 12 | cloud: true, 13 | useOAuth: false, // for use with console 14 | mpsServer: 'http://localhost:3000', 15 | rpsServer: 'http://localhost:8081', 16 | vault: 'http://localhost/vault', 17 | auth: { 18 | clientId: '', 19 | issuer: '', 20 | redirectUri: 'http://localhost:4200/dashboard', 21 | scope: '', 22 | responseType: 'code', 23 | requireHttps: true // set to false when local 24 | } 25 | } 26 | 27 | /* 28 | * For easier debugging in development mode, you can import the following file 29 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 30 | * 31 | * This import should be commented out in production mode because it will have a negative impact 32 | * on performance if an error is thrown. 33 | */ 34 | // import 'zone.js/plugins/zone-error'; // Included with Angular CLI. 35 | -------------------------------------------------------------------------------- /src/app/profiles/profile-detail/profile-detail.component.scss: -------------------------------------------------------------------------------- 1 | fieldset { 2 | border: 1px solid #ccc; 3 | border-radius: 4px; 4 | padding: 10px; 5 | margin: 12px 0; 6 | } 7 | .mat-mdc-list { 8 | width: 100%; 9 | margin-bottom: 24px; 10 | } 11 | .drag-box { 12 | padding: 10px 10px; 13 | border-bottom: solid 1px #ccc; 14 | color: rgba(0, 0, 0, 0.87); 15 | line-height: 1.3; 16 | flex-direction: row; 17 | align-items: center; 18 | justify-content: space-between; 19 | box-sizing: border-box; 20 | cursor: move; 21 | background: white; 22 | font-size: 14px; 23 | } 24 | .cdk-drag-preview { 25 | button, 26 | .mat-icon { 27 | display: none; 28 | } 29 | box-sizing: border-box; 30 | border-radius: 4px; 31 | box-shadow: 32 | 0 5px 5px -3px rgba(0, 0, 0, 0.2), 33 | 0 8px 10px 1px rgba(0, 0, 0, 0.14), 34 | 0 3px 14px 2px rgba(0, 0, 0, 0.12); 35 | } 36 | 37 | .cdk-drag-placeholder { 38 | opacity: 0; 39 | } 40 | 41 | .cdk-drag-animating { 42 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 43 | } 44 | 45 | .drag-box:last-child { 46 | border: none; 47 | } 48 | 49 | .mat-mdc-list.cdk-drop-list-dragging .drag-box:not(.cdk-drag-placeholder) { 50 | transition: transform 250ms cubic-bezier(0, 0, 0.2, 1); 51 | width: 100%; 52 | } 53 | 54 | .no-results { 55 | pointer-events: none; 56 | } 57 | 58 | .like-mat-hint { 59 | font-size: 75%; 60 | color: rgba(0, 0, 0, 0.54); 61 | } 62 | -------------------------------------------------------------------------------- /src/app/devices/network-settings/network-settings.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing' 2 | 3 | import { NetworkSettingsComponent } from './network-settings.component' 4 | import { DevicesService } from '../devices.service' 5 | import { of } from 'rxjs' 6 | import { TranslateModule } from '@ngx-translate/core' 7 | 8 | describe('NetworkSettingsComponent', () => { 9 | let component: NetworkSettingsComponent 10 | let fixture: ComponentFixture 11 | let devicesServiceSpy: jasmine.SpyObj 12 | 13 | beforeEach(async () => { 14 | devicesServiceSpy = jasmine.createSpyObj('DevicesService', [ 15 | 'getNetworkSettings' 16 | ]) 17 | devicesServiceSpy.getNetworkSettings.and.returnValue( 18 | of({ 19 | wired: { ieee8021x: {} }, 20 | wireless: { 21 | wifiNetworks: [], 22 | ieee8021xSettings: [] 23 | } 24 | } as any) 25 | ) 26 | TestBed.configureTestingModule({ 27 | imports: [NetworkSettingsComponent, TranslateModule.forRoot()], 28 | providers: [ 29 | { provide: DevicesService, useValue: devicesServiceSpy }] 30 | }) 31 | 32 | fixture = TestBed.createComponent(NetworkSettingsComponent) 33 | component = fixture.componentInstance 34 | fixture.detectChanges() 35 | }) 36 | 37 | it('should create', () => { 38 | expect(component).toBeTruthy() 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /cypress/e2e/integration/domain/vault-read-certificate.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 7 | import { domainFixtures } from 'cypress/e2e/fixtures/formEntry/domain' 8 | 9 | // Test REST call to Vault to verify Provisioning Certificate and Password are present 10 | 11 | describe('REST API - Vault Test Suite', () => { 12 | it('REST API - Read Provisioning Certificate and Password from Vault test case', () => { 13 | if (Cypress.env('ISOLATE') === 'N') { 14 | const vaultAddress: string = Cypress.env('VAULT_ADDRESS') 15 | const vaultToken = Cypress.env('VAULT_TOKEN') 16 | const vaultURL = `${vaultAddress}/v1/secret/data/certs/${domainFixtures.default.profileName}` 17 | cy.request({ 18 | auth: { bearer: vaultToken }, 19 | method: 'GET', 20 | url: vaultURL 21 | }).should((response) => { 22 | expect(response.status).to.equal(httpCodes.SUCCESS) 23 | expect(JSON.stringify(response.body.data.data.CERT)).contains(Cypress.env('PROVISIONING_CERT')) 24 | expect(JSON.stringify(response.body.data.data.CERT_PASSWORD)).contains( 25 | Cypress.env('PROVISIONING_CERT_PASSWORD') 26 | ) 27 | }) 28 | } 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /src/app/shared/dialog-content/dialog-content.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog' 8 | import { DialogContentComponent } from './dialog-content.component' 9 | import { TranslateModule } from '@ngx-translate/core' 10 | 11 | describe('DialogContentComponent', () => { 12 | let component: DialogContentComponent 13 | let fixture: ComponentFixture 14 | 15 | beforeEach(() => { 16 | TestBed.configureTestingModule({ 17 | imports: [ 18 | MatDialogModule, 19 | DialogContentComponent, 20 | TranslateModule.forRoot() 21 | ], 22 | providers: [ 23 | { provide: MAT_DIALOG_DATA, useValue: {} }, 24 | { provide: MatDialogRef, useValue: {} } 25 | ] 26 | }) 27 | }) 28 | 29 | beforeEach(() => { 30 | fixture = TestBed.createComponent(DialogContentComponent) 31 | component = fixture.componentInstance 32 | fixture.detectChanges() 33 | }) 34 | 35 | afterEach(() => { 36 | TestBed.resetTestingModule() 37 | }) 38 | 39 | it('should create', () => { 40 | expect(component).toBeTruthy() 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /src/app/devices/device-log.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http' 2 | import { inject, Injectable } from '@angular/core' 3 | import { catchError, Observable } from 'rxjs' 4 | import { environment } from 'src/environments/environment' 5 | import { AuditLogResponse, EventLog } from 'src/models/models' 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class DeviceLogService { 11 | private readonly http = inject(HttpClient) 12 | 13 | downloadAuditLog(deviceId: string): Observable { 14 | return this.http.get(`${environment.mpsServer}/api/v1/amt/log/audit/${deviceId}/download`, { 15 | responseType: 'blob' 16 | }) 17 | } 18 | 19 | getAuditLog(deviceId: string, startIndex = 0): Observable { 20 | return this.http 21 | .get(`${environment.mpsServer}/api/v1/amt/log/audit/${deviceId}?startIndex=${startIndex}`) 22 | .pipe( 23 | catchError((err) => { 24 | throw err 25 | }) 26 | ) 27 | } 28 | 29 | downloadEventLog(deviceId: string): Observable { 30 | return this.http.get(`${environment.mpsServer}/api/v1/amt/log/event/${deviceId}/download`, { 31 | responseType: 'blob' 32 | }) 33 | } 34 | 35 | getEventLog(deviceId: string): Observable { 36 | return this.http.get(`${environment.mpsServer}/api/v1/amt/log/event/${deviceId}`).pipe( 37 | catchError((err) => { 38 | throw err 39 | }) 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/ieee8021x.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { AuthenticationProtocols } from 'src/app/ieee8021x/ieee8021x.constants' 7 | import { IEEE8021xConfig } from 'src/models/models' 8 | 9 | export const wiredConfigs: IEEE8021xConfig[] = [] 10 | AuthenticationProtocols.filter((x) => x.mode === 'wired').forEach((authProtocol) => { 11 | ;[0, 60 * 60 * 24].forEach((pxeTimeout) => { 12 | wiredConfigs.push({ 13 | profileName: `8021xWiredProto${authProtocol.value}Pxe${pxeTimeout}`, 14 | wiredInterface: true, 15 | authenticationProtocol: authProtocol.value, 16 | pxeTimeout 17 | }) 18 | }) 19 | }) 20 | 21 | export const wirelessConfigs: IEEE8021xConfig[] = [] 22 | AuthenticationProtocols.filter((x) => x.mode === 'both').forEach((authProtocol) => { 23 | // at this release, only one protocol is supported, but would like to ensure 24 | // multiple wireless configurations can be created, so use a couple of names 25 | ;[ 26 | 'cfg01', 27 | 'cfg02', 28 | 'cfg03' 29 | ].forEach((nameSuffix) => { 30 | wirelessConfigs.push({ 31 | profileName: `8021xWirelessProto${authProtocol.value}${nameSuffix}`, 32 | wiredInterface: false, 33 | authenticationProtocol: authProtocol.value, 34 | pxeTimeout: 0 35 | }) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /.github/workflows/docker.yaml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | docker_registry: 7 | description: 'Registry URL' 8 | required: true 9 | default: 'docker.io/username' 10 | docker_tag_name: 11 | description: 'Tag you wish to use on the docker image' 12 | required: true 13 | default: 'webui:latest' 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | build: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Harden Runner 23 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 24 | with: 25 | egress-policy: audit 26 | 27 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 28 | - name: Build the Docker image 29 | run: docker buildx build --platform linux/amd64,linux/arm64 . --file Dockerfile --tag ${{ github.event.inputs.docker_registry }}/${{ github.event.inputs.docker_tag_name }} 30 | - name: Docker Login 31 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 32 | with: 33 | registry: ${{ github.event.inputs.docker_registry }} 34 | username: ${{ secrets.DOCKER_USERNAME }} 35 | password: ${{ secrets.DOCKER_PASSWORD }} 36 | logout: true 37 | - name: Push the Docker image to ${{ github.event.inputs.docker_registry }} 38 | run: docker push ${{ github.event.inputs.docker_registry }}/${{ github.event.inputs.docker_tag_name }} 39 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, inject } from '@angular/core' 7 | import { environment } from 'src/environments/environment' 8 | import { MatIcon } from '@angular/material/icon' 9 | import { RouterLink, RouterLinkActive } from '@angular/router' 10 | import { MatDivider } from '@angular/material/divider' 11 | import { MatNavList, MatListItem, MatListItemIcon } from '@angular/material/list' 12 | import { MatTooltip } from '@angular/material/tooltip' 13 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 14 | 15 | @Component({ 16 | selector: 'app-navbar', 17 | templateUrl: './navbar.component.html', 18 | styleUrls: ['./navbar.component.scss'], 19 | imports: [ 20 | MatNavList, 21 | MatDivider, 22 | MatListItem, 23 | RouterLink, 24 | RouterLinkActive, 25 | MatIcon, 26 | MatListItemIcon, 27 | MatTooltip, 28 | TranslateModule 29 | ] 30 | }) 31 | export class NavbarComponent { 32 | cloudMode = environment.cloud 33 | showSubNav = false 34 | private readonly translate = inject(TranslateService) 35 | 36 | get ciraTitle(): string { 37 | return this.cloudMode === false 38 | ? this.translate.instant('configs.header.noCiraTitle.value') 39 | : this.translate.instant('configs.header.ciraTitle.value') 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/devices/device-toolbar/pba-boot-dialog/pba-boot-dialog.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{ 'devices.header.pbaBootTitle.value' | translate }} 3 |

4 | 5 |

{{ 'devices.header.pbaBootDescription.value' | translate }}

6 |
7 |
8 | 9 | {{ 'pba.title.value' | translate }} 10 | 11 | @for (filePath of pbaBootFilePaths; track filePath) { 12 | {{ filePath.biosBootString }} ({{ filePath.bootString }}) 13 | } 14 | 15 | @if (bootForm.get('selectedBootSource')?.hasError('required')) { 16 | 17 | {{ 'pba.required.value' | translate }} 18 | 19 | } 20 | 21 |
22 | {{ 23 | 'pba.label.value' | translate 24 | }} 25 |
26 |
27 | 28 | 29 | 32 | 33 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for npm modules 4 | - package-ecosystem: "npm" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | # Group Angular ecosystem deps so they update together (reduces PR noise) 9 | groups: 10 | angular-stack: 11 | patterns: 12 | - '@angular/*' # covers core, cli, cdk, material, etc. 13 | - '@angular-devkit/*' 14 | - 'rxjs' 15 | - 'zone.js' 16 | - 'typescript' 17 | # Only create grouped PRs for safe patch & minor updates; majors handled manually with ng update 18 | update-types: 19 | - patch 20 | - minor 21 | # Ignore major version bumps so they don't auto-open without schematics migrations 22 | ignore: 23 | - dependency-name: '@angular/*' 24 | update-types: 25 | - version-update:semver-major 26 | - dependency-name: '@angular-devkit/*' 27 | update-types: 28 | - version-update:semver-major 29 | - dependency-name: 'rxjs' 30 | update-types: 31 | - version-update:semver-major 32 | - dependency-name: 'zone.js' 33 | update-types: 34 | - version-update:semver-major 35 | - dependency-name: 'typescript' 36 | update-types: 37 | - version-update:semver-major 38 | 39 | - package-ecosystem: github-actions 40 | directory: / 41 | schedule: 42 | interval: daily 43 | 44 | - package-ecosystem: docker 45 | directory: / 46 | schedule: 47 | interval: daily 48 | -------------------------------------------------------------------------------- /src/app/core/about/about.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogModule } from '@angular/material/dialog' 8 | import { MatIconModule } from '@angular/material/icon' 9 | import { MatListModule } from '@angular/material/list' 10 | 11 | import { AboutComponent } from './about.component' 12 | import { TranslateModule } from '@ngx-translate/core' 13 | 14 | describe('AboutComponent', () => { 15 | let component: AboutComponent 16 | let fixture: ComponentFixture 17 | 18 | beforeEach(() => { 19 | TestBed.configureTestingModule({ 20 | imports: [ 21 | MatListModule, 22 | MatIconModule, 23 | MatDialogModule, 24 | AboutComponent, 25 | TranslateModule.forRoot() 26 | ] 27 | }) 28 | }) 29 | 30 | beforeEach(() => { 31 | fixture = TestBed.createComponent(AboutComponent) 32 | component = fixture.componentInstance 33 | fixture.detectChanges() 34 | }) 35 | 36 | afterEach(() => { 37 | TestBed.resetTestingModule() 38 | }) 39 | 40 | it('should create', () => { 41 | expect(component).toBeTruthy() 42 | }) 43 | 44 | it('should initialize doNotShowAgain from localStorage', () => { 45 | spyOn(localStorage, 'getItem').and.returnValue('true') 46 | component.ngOnInit() 47 | expect(localStorage.getItem).toHaveBeenCalledWith('doNotShowAgain') 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-kvm/device-enable-kvm.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogRef } from '@angular/material/dialog' 8 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations' 9 | import { DeviceEnableKvmComponent } from './device-enable-kvm.component' 10 | import { RouterModule } from '@angular/router' 11 | import { TranslateModule } from '@ngx-translate/core' 12 | 13 | describe('DeviceEnableKvmComponent', () => { 14 | let component: DeviceEnableKvmComponent 15 | let fixture: ComponentFixture 16 | const dialogMock = { 17 | close: jasmine.createSpy('close') 18 | } 19 | 20 | beforeEach(() => { 21 | TestBed.configureTestingModule({ 22 | imports: [ 23 | BrowserAnimationsModule, 24 | RouterModule, 25 | DeviceEnableKvmComponent, 26 | TranslateModule.forRoot() 27 | ], 28 | providers: [ 29 | { provide: MatDialogRef, useValue: dialogMock }] 30 | }) 31 | }) 32 | 33 | beforeEach(() => { 34 | fixture = TestBed.createComponent(DeviceEnableKvmComponent) 35 | component = fixture.componentInstance 36 | fixture.detectChanges() 37 | dialogMock.close = jasmine.createSpy('close') 38 | }) 39 | 40 | afterEach(() => { 41 | TestBed.resetTestingModule() 42 | }) 43 | 44 | it('should create', () => { 45 | expect(component).toBeTruthy() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /src/app/devices/device-enable-sol/device-enable-sol.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDialogRef } from '@angular/material/dialog' 8 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations' 9 | import { DeviceEnableSolComponent } from './device-enable-sol.component' 10 | import { RouterModule } from '@angular/router' 11 | import { TranslateModule } from '@ngx-translate/core' 12 | 13 | describe('DeviceEnableSolComponent', () => { 14 | let component: DeviceEnableSolComponent 15 | let fixture: ComponentFixture 16 | const dialogMock = { 17 | close: jasmine.createSpy('close') 18 | } 19 | 20 | beforeEach(() => { 21 | TestBed.configureTestingModule({ 22 | imports: [ 23 | BrowserAnimationsModule, 24 | RouterModule, 25 | DeviceEnableSolComponent, 26 | TranslateModule.forRoot() 27 | ], 28 | providers: [ 29 | { provide: MatDialogRef, useValue: dialogMock }] 30 | }) 31 | }) 32 | 33 | beforeEach(() => { 34 | fixture = TestBed.createComponent(DeviceEnableSolComponent) 35 | component = fixture.componentInstance 36 | fixture.detectChanges() 37 | dialogMock.close = jasmine.createSpy('close') 38 | }) 39 | 40 | afterEach(() => { 41 | TestBed.resetTestingModule() 42 | }) 43 | 44 | it('should create', () => { 45 | expect(component).toBeTruthy() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /src/app/core/about/about.component.html: -------------------------------------------------------------------------------- 1 |

2 | @if (cloudMode === true) { 3 | warning 4 | {{ 'about.cloudMode.value' | translate }} 5 | } @else { 6 | {{ 'about.nonCloudMode.value' | translate }} 7 | } 8 |

9 | 10 | @if (cloudMode === true) { 11 |
12 |

13 | {{ 'about.referenceImplementation.value' | translate }} 14 |

15 | 16 |
17 |

{{ 'about.sampleUI.value' | translate }}

18 |
    19 |
  • {{ 'about.learn.value' | translate }}
  • 20 |
  • {{ 'about.setUp.value' | translate }}
  • 21 |
  • 22 | {{ 'about.understand.value' | translate }} 23 |
  • 24 |
  • {{ 'about.howTo.value' | translate }}
  • 25 |
26 | } @else { 27 |

28 | {{ 'about.description.value' | translate }} 29 |

30 | 31 |
32 |

{{ 'about.consoleProvides.value' | translate }}

33 |
    34 |
  • {{ 'about.remotePower.value' | translate }}
  • 35 |
  • {{ 'about.redirection.value' | translate }}
  • 36 |
  • {{ 'about.device.value' | translate }}
  • 37 |
38 | } 39 |
40 | 41 | @if (cloudMode === true) { 42 | 45 | } 46 | 49 | 50 | -------------------------------------------------------------------------------- /src/app/core/about/about.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, OnDestroy, OnInit } from '@angular/core' 7 | import { environment } from 'src/environments/environment' 8 | import { MatButton } from '@angular/material/button' 9 | import { ReactiveFormsModule, FormsModule } from '@angular/forms' 10 | import { MatDivider } from '@angular/material/divider' 11 | import { CdkScrollable } from '@angular/cdk/scrolling' 12 | import { MatIcon } from '@angular/material/icon' 13 | import { MatDialogTitle, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog' 14 | import { TranslateModule } from '@ngx-translate/core' 15 | 16 | @Component({ 17 | selector: 'app-about', 18 | templateUrl: './about.component.html', 19 | styleUrls: ['./about.component.scss'], 20 | imports: [ 21 | MatDialogTitle, 22 | MatIcon, 23 | CdkScrollable, 24 | MatDialogContent, 25 | MatDivider, 26 | MatDialogActions, 27 | ReactiveFormsModule, 28 | FormsModule, 29 | MatButton, 30 | MatDialogClose, 31 | TranslateModule 32 | ] 33 | }) 34 | export class AboutComponent implements OnDestroy, OnInit { 35 | doNotShowAgain = false 36 | cloudMode: boolean = environment.cloud 37 | ngOnInit(): void { 38 | const storedValue = localStorage.getItem('doNotShowAgain') 39 | this.doNotShowAgain = storedValue ? JSON.parse(storedValue) : false 40 | } 41 | 42 | ngOnDestroy(): void { 43 | localStorage.setItem('doNotShowAgain', JSON.stringify(this.doNotShowAgain)) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/app/shared/pipes/time-ago-formatter.pipe.ts.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { AmTimeAgoFormatterPipe } from './time-ago-formatter.pipe.ts.pipe' 2 | 3 | describe('AmTimeAgoFormatterPipe', () => { 4 | let pipe: AmTimeAgoFormatterPipe 5 | 6 | beforeEach(() => { 7 | pipe = new AmTimeAgoFormatterPipe() 8 | }) 9 | 10 | it('create an instance', () => { 11 | expect(pipe).toBeTruthy() 12 | }) 13 | 14 | it('should transform date to time ago format', () => { 15 | const date = new Date() 16 | const result = pipe.transform(date) 17 | expect(result).toContain('less than a minute ago') 18 | }) 19 | 20 | it('should transform date string to time ago format', () => { 21 | const date = new Date().toISOString() 22 | const result = pipe.transform(date) 23 | expect(result).toContain('less than a minute ago') 24 | }) 25 | 26 | it('should transform timestamp to time ago format', () => { 27 | const timestamp = Date.now() 28 | const result = pipe.transform(timestamp) 29 | expect(result).toContain('less than a minute ago') 30 | }) 31 | 32 | it('should return empty string if no date is provided', () => { 33 | const result = pipe.transform('') 34 | expect(result).toBe('') 35 | }) 36 | 37 | it('should add suffix if addSuffix is true', () => { 38 | const date = new Date(Date.now() - 60000) // 1 minute ago 39 | const result = pipe.transform(date, true) 40 | expect(result).toContain('minute ago') 41 | }) 42 | 43 | it('should not add suffix if addSuffix is false', () => { 44 | const date = new Date(Date.now() - 60000) // 1 minute ago 45 | const result = pipe.transform(date, false) 46 | expect(result).toContain('minute') 47 | expect(result).not.toContain('ago') 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /src/app/shared/pipes/toolkit.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing' 2 | import { FormOption } from 'src/models/models' 3 | import { ToolkitPipe } from './toolkit.pipe' 4 | import { TranslateModule } from '@ngx-translate/core' 5 | 6 | describe('ToolkitPipe', () => { 7 | let pipe: ToolkitPipe 8 | 9 | beforeEach(() => { 10 | TestBed.configureTestingModule({ 11 | imports: [TranslateModule.forRoot()], 12 | providers: [ToolkitPipe] 13 | }) 14 | pipe = TestBed.inject(ToolkitPipe) 15 | }) 16 | 17 | it('create an instance', () => { 18 | expect(pipe).toBeTruthy() 19 | }) 20 | 21 | it('should return the correct label when value matches', () => { 22 | const options: FormOption[] = [ 23 | { value: 1, label: 'Option 1' }, 24 | { value: 2, label: 'Option 2' } 25 | ] 26 | expect(pipe.transform(1, options)).toBe('Option 1') 27 | }) 28 | 29 | it('should return null when no value matches', () => { 30 | const options: FormOption[] = [ 31 | { value: 1, label: 'Option 1' }, 32 | { value: 2, label: 'Option 2' } 33 | ] 34 | expect(pipe.transform(3, options)).toBeNull() 35 | }) 36 | 37 | it('should handle empty options array', () => { 38 | const options: FormOption[] = [] 39 | expect(pipe.transform(1, options)).toBeNull() 40 | }) 41 | 42 | it('should handle non-number values in options', () => { 43 | const options: FormOption[] = [ 44 | { value: 'test', label: 'Option Test' }, 45 | { value: true, label: 'Option True' } 46 | ] 47 | expect(pipe.transform('test' as unknown as number, options)).toBe('Option Test') 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /src/app/devices/certificates/add-cert-dialog/add-cert-dialog.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{ 'devices.header.addCertificateTitle.value' | translate }} 3 |

4 | 5 |

{{ 'devices.header.addCertificateDescription.value' | translate }}

6 | 7 |
8 |
9 | 12 | 13 |
14 |
15 | @if (certInfo.cert === '') { 16 |
17 | remove_circle 18 | {{ 'common.cetificateNotUploaded.value' | translate }} 19 |
20 | } @else { 21 |
22 | check_circle 23 | {{ 'common.cetificateUploaded.value' | translate }} 24 |
25 | } 26 |
27 |
28 | 29 | {{ 30 | 'common.trustedRoot.value' | translate 31 | }} 32 |
33 | 34 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/i18n/translate-paginator-intl.ts: -------------------------------------------------------------------------------- 1 | import { inject, Injectable } from '@angular/core' 2 | import { MatPaginatorIntl } from '@angular/material/paginator' 3 | import { TranslateService } from '@ngx-translate/core' 4 | 5 | @Injectable() 6 | export class TranslatePaginatorIntl extends MatPaginatorIntl { 7 | private readonly translate = inject(TranslateService) 8 | constructor() { 9 | super() 10 | this.translateLabels() 11 | 12 | this.translate.onLangChange.subscribe(() => { 13 | this.translateLabels() 14 | this.changes.next() 15 | }) 16 | } 17 | 18 | private translateLabels(): void { 19 | this.itemsPerPageLabel = this.translate.instant('configs.paginator.itemsPerPageLabel.value') 20 | this.nextPageLabel = this.translate.instant('configs.paginator.nextPageLabel.value') 21 | this.previousPageLabel = this.translate.instant('configs.paginator.previousPageLabel.value') 22 | this.firstPageLabel = this.translate.instant('configs.paginator.firstPageLabel.value') 23 | this.lastPageLabel = this.translate.instant('configs.paginator.lastPageLabel.value') 24 | 25 | this.getRangeLabel = (page: number, pageSize: number, length: number): string => { 26 | if (length === 0 || pageSize === 0) { 27 | return this.translate.instant('configs.paginator.range.value', { 28 | startIndex: 0, 29 | endIndex: 0, 30 | length: length 31 | }) 32 | } 33 | const startIndex = page * pageSize 34 | const endIndex = startIndex < length ? Math.min(startIndex + pageSize, length) : startIndex + pageSize 35 | 36 | return this.translate.instant('configs.paginator.range.value', { 37 | startIndex: startIndex + 1, 38 | endIndex: endIndex, 39 | length: length 40 | }) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/app/error-handling.interceptor.ts: -------------------------------------------------------------------------------- 1 | import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http' 2 | import { catchError, of, throwError } from 'rxjs' 3 | import { DialogContentComponent } from './shared/dialog-content/dialog-content.component' 4 | import { MatSnackBar } from '@angular/material/snack-bar' 5 | import { MatDialog } from '@angular/material/dialog' 6 | import { AuthService } from './auth.service' 7 | import { inject } from '@angular/core' 8 | import { TranslateService } from '@ngx-translate/core' 9 | 10 | export const errorHandlingInterceptor: HttpInterceptorFn = (request, next) => { 11 | const authService = inject(AuthService) 12 | const dialog = inject(MatDialog) 13 | const snackbar = inject(MatSnackBar) 14 | const translate = inject(TranslateService) 15 | 16 | return next(request).pipe( 17 | catchError((error: any) => { 18 | if (error instanceof HttpErrorResponse) { 19 | if (error.status === 401) { 20 | if (error.error.exp === 'token expired') { 21 | dialog.open(DialogContentComponent, { data: { name: translate.instant('error.sessionTimedOut.value') } }) 22 | } 23 | authService.logout() 24 | } else if (error.status === 412 || error.status === 409) { 25 | dialog.open(DialogContentComponent, { 26 | data: { name: translate.instant('error.itemModified.value') } 27 | }) 28 | } else if (error.status === 504) { 29 | snackbar.open( 30 | translate.instant('error.deviceNotResponding.value'), 31 | translate.instant('common.dismiss.value'), 32 | { 33 | duration: 5000 34 | } 35 | ) 36 | return of() 37 | } 38 | } 39 | return throwError(() => error) 40 | }) 41 | ) 42 | } 43 | -------------------------------------------------------------------------------- /src/app/devices/tls/tls.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, inject, signal, input } from '@angular/core' 2 | import { catchError, finalize, throwError } from 'rxjs' 3 | import SnackbarDefaults from 'src/app/shared/config/snackBarDefault' 4 | import { DevicesService } from '../devices.service' 5 | import { MatSnackBar } from '@angular/material/snack-bar' 6 | import { MatCardModule } from '@angular/material/card' 7 | import { MatDividerModule } from '@angular/material/divider' 8 | import { MatProgressBarModule } from '@angular/material/progress-bar' 9 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 10 | 11 | @Component({ 12 | selector: 'app-tls', 13 | imports: [ 14 | MatCardModule, 15 | MatDividerModule, 16 | MatProgressBarModule, 17 | TranslateModule 18 | ], 19 | templateUrl: './tls.component.html', 20 | styleUrl: './tls.component.scss' 21 | }) 22 | export class TLSComponent implements OnInit { 23 | // Dependency Injection 24 | private readonly snackBar = inject(MatSnackBar) 25 | private readonly devicesService = inject(DevicesService) 26 | private readonly translate = inject(TranslateService) 27 | public readonly deviceId = input('') 28 | 29 | public isLoading = signal(true) 30 | public tlsData?: any[] = [] 31 | 32 | ngOnInit(): void { 33 | this.devicesService 34 | .getTLSSettings(this.deviceId()) 35 | .pipe( 36 | catchError((err) => { 37 | const msg: string = this.translate.instant('tls.errorSettings.value') 38 | this.snackBar.open(msg, undefined, SnackbarDefaults.defaultError) 39 | return throwError(err) 40 | }), 41 | finalize(() => { 42 | this.isLoading.set(false) 43 | }) 44 | ) 45 | .subscribe((results) => { 46 | this.tlsData = results 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.github/workflows/trivy-scan.yaml: -------------------------------------------------------------------------------- 1 | name: Trivy Container Scan 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [main] 9 | permissions: 10 | contents: read 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | security-events: write 16 | steps: 17 | - name: Harden Runner 18 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 19 | with: 20 | egress-policy: audit 21 | 22 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 23 | - name: Build the Docker image 24 | 25 | run: docker build . --file Dockerfile --tag vprodemo.azurecr.io/webui:${{ github.sha }} --tag vprodemo.azurecr.io/webui:latest 26 | - name: Run Trivy vulnerability scanner 27 | uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # master 28 | with: 29 | image-ref: 'vprodemo.azurecr.io/webui:${{ github.sha }}' 30 | format: 'sarif' 31 | output: 'webui-trivy-results.sarif' 32 | exit-code: '1' 33 | ignore-unfixed: true 34 | vuln-type: 'os,library' 35 | severity: 'UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL' 36 | - name: Upload Trivy scan results to GitHub Security tab 37 | uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5 38 | if: always() 39 | with: 40 | sarif_file: 'webui-trivy-results.sarif' 41 | - name: Upload Trivy Artifacts 42 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 43 | if: always() 44 | with: 45 | name: webui-trivy-results.sarif 46 | path: webui-trivy-results.sarif 47 | -------------------------------------------------------------------------------- /src/app/core/toolbar/toolbar.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | @if (cloudMode === true) { 4 | {{ 'toolbar.header.cloudModeTitle.value' | translate }} 5 | } @else { 6 | {{ 'toolbar.header.consoleModeTitle.value' | translate }} 7 | @if (consoleVersion().current) { 8 | {{ consoleVersion().current }} 9 | } 10 | } 11 |
12 |
13 | @if (cloudMode === true) { 14 | @if (isLoggedIn === true) { 15 |
MPS: v{{ mpsVersions().serviceVersion }}
16 |
RPS: v{{ rpsVersions().serviceVersion }}
17 | } 18 | 19 | } 20 | 21 | {{ 'toolbar.header.hello.value' | translate }} 22 | 23 | 26 | 27 |
28 | @for (lang of availableLangs; track lang.code) { 29 | 32 | } 33 |
34 |
35 | 36 | @if (isLoggedIn === true) { 37 | 40 | } 41 | 42 | 45 | 46 | 49 |
50 |
51 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | workflow_dispatch: 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | node-version: [22.x] 22 | 23 | steps: 24 | - name: Harden Runner 25 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 26 | with: 27 | egress-policy: audit 28 | 29 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 30 | - name: Use Node.js ${{ matrix.node-version }} 31 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | - run: npm install 35 | - run: npm run lint 36 | if: ${{ matrix.node-version == '22.x' }} 37 | - run: npm run lint:cypress 38 | if: ${{ matrix.node-version == '22.x' }} 39 | - run: npm run ci-prettify 40 | - run: npm run test-ci 41 | - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 42 | name: Upload Coverage Results 43 | if: ${{ matrix.node-version == '22.x' }} 44 | with: 45 | directory: ./coverage/samplewebui 46 | - name: Upload Karma Results 47 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 48 | with: 49 | name: sample-web-ui-unit-${{ matrix.node-version }} 50 | path: TESTS-Chrome-${{ matrix.node-version }}_*.xml 51 | -------------------------------------------------------------------------------- /src/app/shared/auth-guard.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing' 2 | import { AuthGuard } from './auth-guard.service' 3 | import { AuthService } from '../auth.service' 4 | import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router' 5 | import { of } from 'rxjs' 6 | 7 | describe('AuthGuard', () => { 8 | let authGuard: AuthGuard 9 | let authService: jasmine.SpyObj 10 | let router: jasmine.SpyObj 11 | 12 | beforeEach(() => { 13 | const authServiceSpy = jasmine.createSpyObj('AuthService', ['canActivateProtectedRoutes$']) 14 | const routerSpy = jasmine.createSpyObj('Router', ['navigate']) 15 | 16 | TestBed.configureTestingModule({ 17 | providers: [ 18 | AuthGuard, 19 | { provide: AuthService, useValue: authServiceSpy }, 20 | { provide: Router, useValue: routerSpy }] 21 | }) 22 | 23 | authGuard = TestBed.inject(AuthGuard) 24 | authService = TestBed.inject(AuthService) as jasmine.SpyObj 25 | router = TestBed.inject(Router) as jasmine.SpyObj 26 | }) 27 | 28 | it('should redirect to login if canActivateProtectedRoutes$ emits false', (done) => { 29 | authService.canActivateProtectedRoutes$ = of(false) 30 | 31 | authGuard.canActivate({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe((result) => { 32 | expect(result).toBe(false) 33 | expect(router.navigate).toHaveBeenCalledWith(['/login']) 34 | done() 35 | }) 36 | }) 37 | 38 | it('should allow activation if canActivateProtectedRoutes$ emits true', (done) => { 39 | authService.canActivateProtectedRoutes$ = of(true) 40 | 41 | authGuard.canActivate({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot).subscribe((result) => { 42 | expect(result).toBe(true) 43 | expect(router.navigate).not.toHaveBeenCalled() 44 | done() 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, OnInit, inject } from '@angular/core' 7 | import { Router, RouterModule } from '@angular/router' 8 | import { AuthService } from './auth.service' 9 | import { ToolbarComponent } from './core/toolbar/toolbar.component' 10 | import { NavbarComponent } from './core/navbar/navbar.component' 11 | import { MatSidenavModule } from '@angular/material/sidenav' 12 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 13 | import { BidiModule, Direction } from '@angular/cdk/bidi' 14 | import { getDirection } from 'src/utils' 15 | 16 | @Component({ 17 | selector: 'app-root', 18 | templateUrl: './app.component.html', 19 | styleUrls: ['./app.component.scss'], 20 | imports: [ 21 | RouterModule, 22 | ToolbarComponent, 23 | NavbarComponent, 24 | MatSidenavModule, 25 | BidiModule, 26 | TranslateModule 27 | ] 28 | }) 29 | export class AppComponent implements OnInit { 30 | // Dependency Injection 31 | private readonly router = inject(Router) 32 | private readonly authService = inject(AuthService) 33 | public readonly translate = inject(TranslateService) 34 | public direction: Direction = 'ltr' 35 | public isLoggedIn = false 36 | 37 | ngOnInit(): void { 38 | this.authService.loggedInSubject$.subscribe((value: any) => { 39 | this.isLoggedIn = value 40 | }) 41 | 42 | this.translate.setFallbackLang('en') 43 | this.setDirection(this.translate.getCurrentLang()) 44 | 45 | this.translate.onLangChange.subscribe((event) => { 46 | this.setDirection(event.lang) 47 | }) 48 | } 49 | 50 | private setDirection(lang: string): void { 51 | this.direction = getDirection(lang) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cypress/e2e/integration/profile/delete.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Tests the creation of a profile 7 | import { empty } from 'cypress/e2e/fixtures/api/general' 8 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 9 | import { profiles } from 'cypress/e2e/fixtures/api/profile' 10 | import { amtProfiles } from '../../fixtures/formEntry/profile' 11 | 12 | // ---------------------------- Test section ---------------------------- 13 | 14 | describe('Test Profile Page', () => { 15 | beforeEach(() => { 16 | cy.setup() 17 | // Stub the requests 18 | cy.myIntercept('DELETE', /.*profiles.*/, { 19 | statusCode: httpCodes.NO_CONTENT 20 | }).as('delete-profile') 21 | cy.myIntercept('GET', 'profiles?$top=25&$skip=0&$count=true', { 22 | statusCode: httpCodes.SUCCESS, 23 | body: profiles.getAll.success.response 24 | }).as('get-profiles') 25 | cy.goToPage('Profiles') 26 | cy.wait('@get-profiles') 27 | }) 28 | 29 | it('should not delete when cancelled', () => { 30 | // Delete profile (but cancel) 31 | cy.get('mat-cell').contains('delete').click() 32 | cy.get('button').contains('No').click() 33 | }) 34 | 35 | amtProfiles.forEach((profile: any) => { 36 | it(`should delete ${profile.profileName as string}`, () => { 37 | cy.myIntercept('GET', 'profiles?$top=25&$skip=0&$count=true', { 38 | statusCode: httpCodes.SUCCESS, 39 | body: empty.response 40 | }).as('get-profiles4') 41 | // Delete profile 42 | cy.get('mat-row').contains(profile.profileName).parent().contains('delete').click() 43 | cy.get('button').contains('Yes').click() 44 | cy.wait('@delete-profile') 45 | cy.wait('@get-profiles4') 46 | }) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /cypress/e2e/integration/wireless/delete.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { empty } from 'cypress/e2e/fixtures/api/general' 7 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 8 | import { wirelessConfigs } from 'cypress/e2e/fixtures/api/wireless' 9 | import { wirelessFixtures } from 'cypress/e2e/fixtures/formEntry/wireless' 10 | 11 | describe('test wireless profiles page', () => { 12 | beforeEach('clear cache and login', () => { 13 | cy.setup() 14 | }) 15 | 16 | it('deletes the default profile', () => { 17 | cy.myIntercept('DELETE', /.*wirelessconfigs.*/, { 18 | statusCode: httpCodes.NO_CONTENT 19 | }).as('delete-profile') 20 | cy.myIntercept('GET', 'wirelessconfigs?$top=25&$skip=0&$count=true', { 21 | statusCode: httpCodes.SUCCESS, 22 | body: wirelessConfigs.getAll.success.response 23 | }).as('get-wireless') 24 | 25 | cy.goToPage('Wireless') 26 | cy.wait('@get-wireless') 27 | 28 | cy.get('mat-cell').contains('delete').click() 29 | cy.get('button').contains('No').click() 30 | 31 | cy.get('mat-cell').contains(wirelessFixtures.happyPath.profileName) 32 | cy.get('mat-cell').contains(wirelessFixtures.happyPath.authenticationMethod) 33 | cy.get('mat-cell').contains(wirelessFixtures.happyPath.encryptionMethod) 34 | 35 | cy.myIntercept('GET', 'wirelessconfigs?$top=25&$skip=0&$count=true', { 36 | statusCode: httpCodes.SUCCESS, 37 | body: empty.response 38 | }).as('get-wireless2') 39 | 40 | cy.get('mat-cell').contains('delete').click() 41 | cy.get('button').contains('Yes').click() 42 | cy.wait('@delete-profile') 43 | cy.wait('@get-wireless2') 44 | 45 | cy.contains(wirelessFixtures.happyPath.profileName).should('not.exist') 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /src/app/devices/tls/tls.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing' 2 | import { TLSComponent } from './tls.component' 3 | import { DevicesService } from '../devices.service' 4 | import { MatSnackBar } from '@angular/material/snack-bar' 5 | import { of } from 'rxjs' 6 | import { MatCardModule } from '@angular/material/card' 7 | import { MatDividerModule } from '@angular/material/divider' 8 | import { TranslateModule } from '@ngx-translate/core' 9 | 10 | describe('TLSComponent', () => { 11 | let component: TLSComponent 12 | let fixture: ComponentFixture 13 | let mockDevicesService: any 14 | let mockSnackBar: any 15 | const mockTLSData = [{}, {}] 16 | 17 | beforeEach(() => { 18 | mockDevicesService = jasmine.createSpyObj('DevicesService', ['getTLSSettings']) 19 | mockSnackBar = jasmine.createSpyObj('MatSnackBar', ['open']) 20 | 21 | TestBed.configureTestingModule({ 22 | imports: [ 23 | MatCardModule, 24 | MatDividerModule, 25 | TLSComponent, 26 | TranslateModule.forRoot() 27 | ], 28 | providers: [ 29 | { provide: DevicesService, useValue: mockDevicesService }, 30 | { provide: MatSnackBar, useValue: mockSnackBar } 31 | ] 32 | }) 33 | }) 34 | 35 | beforeEach(() => { 36 | fixture = TestBed.createComponent(TLSComponent) 37 | component = fixture.componentInstance 38 | 39 | fixture.componentRef.setInput('deviceId', 'test-device-id') 40 | mockDevicesService.getTLSSettings.and.returnValue(of(mockTLSData)) 41 | }) 42 | 43 | it('should create the component', () => { 44 | expect(component).toBeTruthy() 45 | }) 46 | 47 | it('should call getTLSSettings on ngOnInit and set tlsData', () => { 48 | component.ngOnInit() 49 | 50 | expect(mockDevicesService.getTLSSettings).toHaveBeenCalledWith('test-device-id') 51 | expect(component.tlsData).toEqual(mockTLSData) 52 | expect(component.isLoading()).toBeFalse() 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /src/app/devices/device-cert-dialog/device-cert-dialog.component.ts: -------------------------------------------------------------------------------- 1 | import { DatePipe } from '@angular/common' 2 | import { Component, inject } from '@angular/core' 3 | import { MatButtonModule } from '@angular/material/button' 4 | import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog' 5 | import { MatListModule } from '@angular/material/list' 6 | import { DevicesService } from '../devices.service' 7 | import { MatSnackBar } from '@angular/material/snack-bar' 8 | import SnackbarDefaults from 'src/app/shared/config/snackBarDefault' 9 | import { TranslateModule } from '@ngx-translate/core' 10 | 11 | @Component({ 12 | selector: 'app-device-cert-dialog', 13 | imports: [ 14 | MatDialogModule, 15 | MatButtonModule, 16 | MatListModule, 17 | DatePipe, 18 | TranslateModule 19 | ], 20 | templateUrl: './device-cert-dialog.component.html', 21 | styleUrl: './device-cert-dialog.component.scss' 22 | }) 23 | export class DeviceCertDialogComponent { 24 | // Dependency Injection 25 | private readonly deviceService = inject(DevicesService) 26 | private readonly snackBar = inject(MatSnackBar) 27 | private readonly dialogData = inject(MAT_DIALOG_DATA) 28 | private readonly ref = inject(MatDialogRef) 29 | public data: any = {} 30 | public isPinned = false 31 | 32 | constructor() { 33 | const dialogData = this.dialogData 34 | 35 | this.data = dialogData.certData 36 | this.isPinned = dialogData.isPinned 37 | } 38 | 39 | pin(): void { 40 | this.deviceService.pinDeviceCertificate(this.data.guid, this.data.sha256Fingerprint).subscribe(() => { 41 | this.snackBar.open('Certificate pinned', 'Close', SnackbarDefaults.defaultSuccess) 42 | this.ref.close(true) 43 | }) 44 | } 45 | 46 | remove(): void { 47 | this.deviceService.deleteDeviceCertificate(this.data.guid).subscribe(() => { 48 | this.snackBar.open('Certificate removed', 'Close', SnackbarDefaults.defaultSuccess) 49 | this.ref.close(false) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/devices/device-log.service.ts: -------------------------------------------------------------------------------- 1 | import { HttpClient } from '@angular/common/http' 2 | import { inject, Injectable } from '@angular/core' 3 | import { catchError, Observable, tap, of } from 'rxjs' 4 | import { environment } from 'src/environments/environment' 5 | import { AuditLogResponse, EventLog, EventLogResponse } from 'src/models/models' 6 | 7 | const DEFAULT_TOP = 0 8 | const DEFAULT_SKIP = 120 9 | 10 | @Injectable({ 11 | providedIn: 'root' 12 | }) 13 | export class DeviceLogService { 14 | private readonly http = inject(HttpClient) 15 | 16 | downloadAuditLog(deviceId: string): Observable { 17 | return this.http.get(`${environment.mpsServer}/api/v1/amt/log/audit/${deviceId}/download`, { 18 | responseType: 'blob' 19 | }) 20 | } 21 | 22 | getAuditLog(deviceId: string, startIndex = 0): Observable { 23 | return this.http 24 | .get(`${environment.mpsServer}/api/v1/amt/log/audit/${deviceId}?startIndex=${startIndex}`) 25 | .pipe( 26 | catchError((err) => { 27 | throw err 28 | }) 29 | ) 30 | } 31 | 32 | downloadEventLog(deviceId: string): Observable { 33 | return this.http.get(`${environment.mpsServer}/api/v1/amt/log/event/${deviceId}/download`, { 34 | responseType: 'blob' 35 | }) 36 | } 37 | 38 | getEventLog( 39 | deviceId: string, 40 | startIndex: number = DEFAULT_TOP, 41 | maxReadRecords: number = DEFAULT_SKIP 42 | ): Observable { 43 | const url = `${environment.mpsServer}/api/v1/amt/log/event/${deviceId}?$skip=${startIndex}&$top=${maxReadRecords}` 44 | 45 | return this.http.get(url).pipe( 46 | tap((response) => { 47 | if (environment.cloud) { 48 | response = { hasMoreRecords: false, records: response as unknown as EventLog[] } as EventLogResponse 49 | } 50 | return of(response) 51 | }), 52 | catchError((err) => { 53 | throw err 54 | }) 55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/app/devices/audit-log/audit-log.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { NoopAnimationsModule } from '@angular/platform-browser/animations' 8 | import { ActivatedRoute, RouterModule } from '@angular/router' 9 | import { of } from 'rxjs' 10 | import { AuditLogComponent } from './audit-log.component' 11 | import { DeviceLogService } from '../device-log.service' 12 | import { TranslateModule } from '@ngx-translate/core' 13 | 14 | describe('AuditLogComponent', () => { 15 | let component: AuditLogComponent 16 | let fixture: ComponentFixture 17 | let getAuditLogSpy: jasmine.Spy 18 | 19 | beforeEach(() => { 20 | const devicesService = jasmine.createSpyObj('DeviceLogService', ['getAuditLog']) 21 | getAuditLogSpy = devicesService.getAuditLog.and.returnValue(of({ totalCnt: 0, records: [] })) 22 | 23 | TestBed.configureTestingModule({ 24 | imports: [ 25 | NoopAnimationsModule, 26 | RouterModule, 27 | AuditLogComponent, 28 | TranslateModule.forRoot() 29 | ], 30 | providers: [ 31 | { provide: DeviceLogService, useValue: devicesService }, 32 | { 33 | provide: ActivatedRoute, 34 | useValue: { 35 | params: of({ id: 'guid' }) 36 | } 37 | } 38 | ] 39 | }) 40 | }) 41 | 42 | beforeEach(() => { 43 | fixture = TestBed.createComponent(AuditLogComponent) 44 | component = fixture.componentInstance 45 | fixture.detectChanges() 46 | }) 47 | 48 | afterEach(() => { 49 | TestBed.resetTestingModule() 50 | }) 51 | 52 | it('should create', () => { 53 | expect(component).toBeTruthy() 54 | expect(getAuditLogSpy.calls.any()).toBe(true, 'getAuditLog called') 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /src/app/devices/alarms/alarms.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | 8 | import { AlarmsComponent } from './alarms.component' 9 | import { DevicesService } from '../devices.service' 10 | import { provideNativeDateAdapter } from '@angular/material/core' 11 | import { of } from 'rxjs' 12 | import { NoopAnimationsModule } from '@angular/platform-browser/animations' 13 | import { TranslateModule } from '@ngx-translate/core' 14 | 15 | describe('AlarmsComponent', () => { 16 | let component: AlarmsComponent 17 | let fixture: ComponentFixture 18 | let devicesServiceSpy: jasmine.SpyObj 19 | 20 | beforeEach(() => { 21 | devicesServiceSpy = jasmine.createSpyObj('DevicesService', [ 22 | 'getDevices', 23 | 'updateDevice', 24 | 'getAlarmOccurrences', 25 | 'getTags', 26 | 'getPowerState', 27 | 'getAMTVersion', 28 | 'getAMTFeatures', 29 | 'getGeneralSettings', 30 | 'PowerStates', 31 | 'sendPowerAction', 32 | 'bulkPowerAction', 33 | 'sendDeactivate', 34 | 'sendBulkDeactivate', 35 | 'getWsmanOperations' 36 | ]) 37 | 38 | devicesServiceSpy.getAlarmOccurrences.and.returnValue(of([{ StartTime: {} } as any])) 39 | TestBed.configureTestingModule({ 40 | imports: [ 41 | NoopAnimationsModule, 42 | AlarmsComponent, 43 | TranslateModule.forRoot() 44 | ], 45 | providers: [provideNativeDateAdapter(), { provide: DevicesService, useValue: devicesServiceSpy }] 46 | }) 47 | 48 | fixture = TestBed.createComponent(AlarmsComponent) 49 | component = fixture.componentInstance 50 | fixture.detectChanges() 51 | }) 52 | 53 | it('should create', () => { 54 | expect(component).toBeTruthy() 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /src/app/devices/device-toolbar/http-boot-dialog/http-boot-dialog.component.html: -------------------------------------------------------------------------------- 1 |

2 | {{ 'devices.header.httpsBootTitle.value' | translate }} 3 |

4 | 5 |

{{ 'devices.header.httpsBootDescription.value' | translate }}

6 |
7 |
8 | 9 | {{ 'httpBoot.label.value' | translate }} 10 | 11 | @if (bootForm.get('url')?.hasError('required')) { 12 | {{ 'httpBoot.error.value' | translate }} 13 | } 14 | 15 | 27 |
28 | {{ 29 | 'httpBoot.enforce.value' | translate 30 | }} 31 |
32 |
33 | 34 | 35 | 38 | 39 | -------------------------------------------------------------------------------- /src/app/devices/network-settings/network-settings.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit, inject, signal, input } from '@angular/core' 2 | import { DevicesService } from '../devices.service' 3 | import { MatCardModule } from '@angular/material/card' 4 | import { catchError, finalize, throwError } from 'rxjs' 5 | import { MatSnackBar } from '@angular/material/snack-bar' 6 | import SnackbarDefaults from 'src/app/shared/config/snackBarDefault' 7 | import { MatListModule } from '@angular/material/list' 8 | import { MatIcon } from '@angular/material/icon' 9 | import { MatDivider } from '@angular/material/divider' 10 | import { MatProgressBarModule } from '@angular/material/progress-bar' 11 | import { NetworkConfig } from 'src/models/models' 12 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 13 | 14 | @Component({ 15 | selector: 'app-network-settings', 16 | imports: [ 17 | MatCardModule, 18 | MatListModule, 19 | MatDivider, 20 | MatIcon, 21 | MatProgressBarModule, 22 | TranslateModule 23 | ], 24 | templateUrl: './network-settings.component.html', 25 | styleUrl: './network-settings.component.scss' 26 | }) 27 | export class NetworkSettingsComponent implements OnInit { 28 | // Dependency Injection 29 | private readonly snackBar = inject(MatSnackBar) 30 | private readonly devicesService = inject(DevicesService) 31 | private readonly translate = inject(TranslateService) 32 | public readonly deviceId = input('') 33 | 34 | public isLoading = signal(true) 35 | public networkResults?: NetworkConfig 36 | 37 | ngOnInit(): void { 38 | this.devicesService 39 | .getNetworkSettings(this.deviceId()) 40 | .pipe( 41 | catchError((err) => { 42 | const msg: string = this.translate.instant('network.errorNetworkSetting.value') 43 | this.snackBar.open(msg, undefined, SnackbarDefaults.defaultError) 44 | return throwError(err) 45 | }), 46 | finalize(() => { 47 | this.isLoading.set(false) 48 | }) 49 | ) 50 | .subscribe((results) => { 51 | this.networkResults = results 52 | }) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cypress/e2e/integration/cira/delete.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Tests the creation of a cira-config 7 | 8 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 9 | import { ciraFixtures } from '../../fixtures//formEntry/cira' 10 | import { ciraConfig } from 'cypress/e2e/fixtures/api/cira' 11 | import { empty } from 'cypress/e2e/fixtures/api/general' 12 | 13 | // ---------------------------- Test section ---------------------------- 14 | 15 | describe('Test CIRA Config Page', () => { 16 | beforeEach('Clear cache and login', () => { 17 | cy.setup() 18 | }) 19 | 20 | it('deletes the default cira config', () => { 21 | cy.myIntercept('DELETE', /.*ciraconfigs.*/, { 22 | statusCode: httpCodes.NO_CONTENT 23 | }).as('delete-config') 24 | 25 | cy.myIntercept('GET', 'ciraconfigs?$top=25&$skip=0&$count=true', { 26 | statusCode: httpCodes.SUCCESS, 27 | body: ciraConfig.getAll.success.response 28 | }).as('get-configs') 29 | 30 | // Delete CIRA Config (but cancel) 31 | cy.goToPage('CIRA Configs') 32 | cy.wait('@get-configs') 33 | 34 | cy.get('mat-cell').contains('delete').click() 35 | cy.get('button').contains('No').click() 36 | 37 | // Check that the config was not deleted 38 | cy.get('mat-cell').contains(ciraFixtures.default.name) 39 | cy.get('mat-cell').contains(Cypress.env('FQDN')) 40 | cy.get('mat-cell').contains(Cypress.env('MPS_USERNAME')) 41 | 42 | // Change api response 43 | cy.myIntercept('GET', 'ciraconfigs?$top=25&$skip=0&$count=true', { 44 | statusCode: httpCodes.SUCCESS, 45 | body: empty.response 46 | }).as('get-configs') 47 | 48 | // Delete CIRA Config 49 | cy.get('mat-cell').contains('delete').click() 50 | cy.get('button').contains('Yes').click() 51 | cy.wait('@delete-config') 52 | 53 | // Check that the config was deleted properly 54 | cy.contains(ciraFixtures.default.name).should('not.exist') 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { MatDividerModule } from '@angular/material/divider' 8 | import { MatIconModule } from '@angular/material/icon' 9 | import { MatListModule } from '@angular/material/list' 10 | import { NavbarComponent } from './navbar.component' 11 | import { ActivatedRoute, RouterModule } from '@angular/router' 12 | import { of } from 'rxjs' 13 | import { provideHttpClient } from '@angular/common/http' 14 | import { provideHttpClientTesting } from '@angular/common/http/testing' 15 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 16 | import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader' 17 | 18 | describe('NavbarComponent', () => { 19 | let component: NavbarComponent 20 | let fixture: ComponentFixture 21 | let translate: TranslateService 22 | 23 | beforeEach(() => { 24 | TestBed.configureTestingModule({ 25 | imports: [ 26 | MatIconModule, 27 | MatDividerModule, 28 | MatListModule, 29 | RouterModule, 30 | NavbarComponent, 31 | TranslateModule.forRoot() 32 | ], 33 | providers: [ 34 | { provide: ActivatedRoute, useValue: { params: of({ id: 'guid' }) } }, 35 | { provide: TRANSLATE_HTTP_LOADER_CONFIG, useValue: { prefix: '/assets/i18n/', suffix: '.json' } }, 36 | TranslateService, 37 | provideHttpClient(), 38 | provideHttpClientTesting() 39 | ] 40 | }) 41 | fixture = TestBed.createComponent(NavbarComponent) 42 | component = fixture.componentInstance 43 | translate = TestBed.inject(TranslateService) 44 | translate.setFallbackLang('en') 45 | fixture.detectChanges() 46 | }) 47 | 48 | afterEach(() => { 49 | TestBed.resetTestingModule() 50 | }) 51 | 52 | it('should create', () => { 53 | expect(component).toBeTruthy() 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /cypress/e2e/integration/domain/delete.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { domains } from 'cypress/e2e/fixtures/api/domain' 7 | import { empty } from 'cypress/e2e/fixtures/api/general' 8 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 9 | import { domainFixtures } from 'cypress/e2e/fixtures/formEntry/domain' 10 | 11 | // ---------------------------- Test section ---------------------------- 12 | 13 | describe('Test Domain Page', () => { 14 | beforeEach('before', () => { 15 | cy.setup() 16 | }) 17 | 18 | it('deletes the default domain', () => { 19 | // Stub requests 20 | cy.myIntercept('DELETE', /.*domains.*/, { 21 | statusCode: httpCodes.NO_CONTENT, 22 | body: domains.delete.success.response 23 | }).as('delete-domain') 24 | 25 | cy.myIntercept('GET', 'domains?$top=25&$skip=0&$count=true', { 26 | statusCode: httpCodes.SUCCESS, 27 | body: domains.getAll.success.response 28 | }).as('get-domains3') 29 | 30 | cy.goToPage('Domains') 31 | cy.wait('@get-domains3') 32 | 33 | // Delete Domain (but cancel) 34 | cy.get('mat-cell').contains('delete').click() 35 | cy.get('button').contains('No').click() 36 | 37 | // Check that the domain was not deleted 38 | cy.get('mat-cell').contains(domainFixtures.default.profileName) 39 | cy.get('mat-cell').contains(Cypress.env('DOMAIN_SUFFIX')) 40 | 41 | // Change api response 42 | cy.myIntercept('GET', 'domains?$top=25&$skip=0&$count=true', { 43 | statusCode: httpCodes.SUCCESS, 44 | body: empty.response 45 | }).as('get-domains4') 46 | 47 | // Delete Domain 48 | cy.get('mat-cell').contains('delete').click() 49 | cy.get('button').contains('Yes').click() 50 | cy.wait('@delete-domain') 51 | cy.wait('@get-domains4') 52 | 53 | // Check that the Domain was deleted properly 54 | cy.contains(domainFixtures.default.profileName).should('not.exist') 55 | cy.contains(Cypress.env('DOMAIN_SUFFIX')).should('not.exist') 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /src/app/devices/audit-log/audit-log.component.html: -------------------------------------------------------------------------------- 1 | @if (isLoading()) { 2 | 3 | } 4 | 5 | 6 | {{ 'auditLog.title.value' | translate }} ({{ auditLogData.totalCnt }}) 7 | @if (!isCloudMode) { 8 | 11 | } 12 | 13 | 14 | @if (isNoData()) { 15 |

{{ 'auditLog.missing.value' | translate }}

16 | } @else { 17 | 18 | 19 | 20 | {{ 'auditLog.event.value' | translate }} 21 | {{ element.Event }} 22 | 23 | @if (!isCloudMode) { 24 | 25 | 26 | {{ 'auditLog.description.value' | translate }} 27 | {{ element.ExStr }} 28 | 29 | } 30 | 31 | 32 | {{ 'auditLog.time.value' | translate }} 33 | 34 | {{ element.Time | amDateFormatter: 'MMMM d, yyyy h:mma' }} ({{ element.Time | amTimeAgoFormatter: true }}) 35 | 36 | 37 | 38 | 39 | 40 | } 41 | 42 | 43 |
44 |
45 | -------------------------------------------------------------------------------- /cypress/e2e/fixtures/formEntry/cira.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | const ciraFixtures = { 7 | default: { 8 | name: 'happyPath', 9 | format: 'FQDN', 10 | addr: '192.168.8.50', 11 | port: '4433', 12 | username: 'standalone', 13 | password: 'G@ppm0ym' 14 | }, 15 | 16 | wrong: { 17 | name: 'asdf -%^7', 18 | format: 'IPV4', 19 | ip: '12345', 20 | port: '3', 21 | username: 'stand alone', 22 | password: 'password' 23 | }, 24 | 25 | MpsCertificate: 26 | '-----BEGIN CERTIFICATE-----\nMIIEOzCCAqOgAwIBAgIDA5h4MA0GCSqGSIb3DQEBDAUAMD0xFzAVBgNVBAMTDk1Q\nU1Jvb3QtN2ZjN2NhMRAwDgYDVQQKEwd1bmtub3duMRAwDgYDVQQGEwd1bmtub3du\nMCAXDTIwMDExODAxNDAxMloYDzIwNTEwMTE4MDE0MDEyWjA9MRcwFQYDVQQDEw5N\nUFNSb290LTdmYzdjYTEQMA4GA1UEChMHdW5rbm93bjEQMA4GA1UEBhMHdW5rbm93\nbjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMEOeTOVcOY3bnJ6wLK+\ngc5U/to401ggiWugdqo4y5lfT0zLkM2VGjd2974Pm98OsKZj3cGp7E7t4gjmS5wo\nJzaxJ9HZmsy5radbSW1NYMwCMettnvtknt95uQUxdfO8hi0u2/fgA/CttQYI+87y\nAlQTkNRfkGrD5rCCL0jTpOFiOiM3yM0dLXPmIJs6t84Lyu0mWlLoITdBPBYVFkN6\nmshoK1zXEzkhlT9PiOKkLKeQJfLq8VVv+olv41TfTijyY3HV/Pk+Tn2IXpuC1EdC\nwuuWW1CKmYnM+BmY5h/PwoSMQzGzrjoAL+TDi1RNNIkr6oae95MNo5IMrG/VnFrx\nfKpELGjWr0Y3E2ETiwjt8Ztz2kAflg4OLZ692Kmc6JkP7PZFM2KmxPcHUXE/FmZp\nMPRNKUzc5HBkQD64p3Q1j+RAntqwtz5WzL93K8GEjzdDi2uthP5P+s1WvJnGxEb0\ndRFGyS31eTugIfdGr2zPtMydkYCAGYOyHX3kwMc5tyE6rwIDAQABo0IwQDAMBgNV\nHRMEBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIABzAdBgNVHQ4EFgQUf8fK7M9m+9EH\nFm1yvoOWau391WEwDQYJKoZIhvcNAQEMBQADggGBAE/+K9xaf5Ib+OGGhvyU4NxM\nkQeNsB/7BxEARG8jJ3a1Wr0L0xaABI/7TQzxk+FEwCstm/Z8Qos8GrXtLrEcZONR\nZWLNdTH2itX9eYvx8uPLGP89ILnavivlbQ8DvFnV8EXXOaqz3zzIDzp+HxVumXSG\n/YZWbQjyDCzthD/zuhlvXJca9/pZtqxLkBfT0ZBAGxzpBl82KytF/+LRw/wGBiI2\nqNiHIp2nZUfvpYkBWtRzlVrvLzwlOGpdptkGgrmvjyJ25lllfDOOrizZpIsYVlgQ\nQtulx4EAn1ZuC0jPBxBrYqdkapFdNMY8LC1MMcnX75pfaRaus45WN/ds4JHOfw56\ndkJZs9gmpWSocTeWHL85d1vLVruviQU5pJHUw8QyvLuXRxZef9+8reyoGBnmEVqk\noDOUjCYq53fLPH4czcMsZfh0lWuZc8oSg+rLEfozbSvxBNn2zn7cnFiRWrbFz6hE\ni0IG+SRRxPly82In20v8J4mF1WntTZTYHyuRb/NO7Q==\n-----END CERTIFICATE-----' 27 | } 28 | export { ciraFixtures } 29 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | const isCI = process.env.CI || process.env.GITHUB_ACTIONS 6 | 7 | config.set({ 8 | basePath: '', 9 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 10 | plugins: [ 11 | require('karma-jasmine'), 12 | require('karma-junit-reporter'), 13 | require('karma-chrome-launcher'), 14 | require('karma-jasmine-html-reporter'), 15 | require('karma-coverage'), 16 | require('@angular-devkit/build-angular/plugins/karma') 17 | ], 18 | client: { 19 | jasmine: { 20 | // you can add configuration options for Jasmine here 21 | // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html 22 | // for example, you can disable the random execution with `random: false` 23 | // or set a specific seed with `seed: 4321` 24 | }, 25 | clearContext: false // leave Jasmine Spec Runner output visible in browser 26 | }, 27 | jasmineHtmlReporter: { 28 | suppressAll: true // removes the duplicated traces 29 | }, 30 | coverageReporter: { 31 | dir: require('path').join(__dirname, './coverage/samplewebui'), 32 | subdir: '.', 33 | reporters: [ 34 | { type: 'html' }, 35 | { type: 'text-summary' }, 36 | { type: 'lcov' }] 37 | }, 38 | reporters: [ 39 | 'progress', 40 | 'coverage', 41 | 'junit', 42 | 'kjhtml' 43 | ], 44 | customLaunchers: { 45 | ChromeHeadlessCI: { 46 | base: 'ChromeHeadless', 47 | flags: [ 48 | '--no-sandbox', 49 | '--disable-gpu', 50 | '--disable-dev-shm-usage', 51 | '--disable-web-security' 52 | ] 53 | } 54 | }, 55 | port: 9876, 56 | colors: true, 57 | logLevel: config.LOG_INFO, 58 | autoWatch: true, 59 | browsers: ['ChromeHeadless'], 60 | singleRun: false, 61 | restartOnFileChange: true, 62 | // CI-specific configurations 63 | ...(isCI && { 64 | browsers: ['ChromeHeadlessCI'], 65 | concurrency: 1, 66 | singleRun: true 67 | }) 68 | }) 69 | } 70 | -------------------------------------------------------------------------------- /cypress/e2e/integration/wireless/create-error.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { httpCodes } from '../../fixtures/api/httpCodes' 7 | import { wirelessFixtures } from '../../fixtures/formEntry/wireless' 8 | import { urlFixtures } from '../../fixtures/formEntry/urls' 9 | import { badRequest, empty } from 'cypress/e2e/fixtures/api/general' 10 | import * as api8021x from '../../fixtures/api/ieee8021x' 11 | 12 | const baseUrl: string = Cypress.env('BASEURL') 13 | 14 | describe('Test wireless creation page', () => { 15 | beforeEach('clear cache and login', () => { 16 | cy.setup() 17 | api8021x.interceptGetAll(httpCodes.SUCCESS, api8021x.wirelessConfigsResponse).as('intercept8021xGetAll') 18 | }) 19 | 20 | beforeEach('Set up the api stubs', () => { 21 | cy.myIntercept('GET', 'wirelessconfigs?$top=25&$skip=0&$count=true', { 22 | statuscode: httpCodes.SUCCESS, 23 | body: empty.response 24 | }).as('get-wireless3') 25 | 26 | cy.myIntercept('POST', 'wirelessconfigs', { 27 | statusCode: httpCodes.BAD_REQUEST, 28 | body: badRequest.response 29 | }).as('post-wireless') 30 | 31 | cy.goToPage('Wireless') 32 | cy.wait('@get-wireless3') 33 | 34 | cy.get('button').contains('Add New').click() 35 | cy.wait('@intercept8021xGetAll') 36 | }) 37 | 38 | it('invalid profile name', () => { 39 | cy.enterWirelessInfo( 40 | wirelessFixtures.wrong.profileName, 41 | Cypress.env('WIFI_SSID'), 42 | Cypress.env('WIFI_PSK_PASSPHRASE'), 43 | wirelessFixtures.happyPath.authenticationMethod, 44 | wirelessFixtures.happyPath.encryptionMethod 45 | ) 46 | }) 47 | 48 | afterEach('Check for error', () => { 49 | cy.get('button[type=submit]').click() 50 | 51 | // Wait for requests to finish and check their responses 52 | cy.wait('@post-wireless').its('response.statusCode').should('eq', httpCodes.BAD_REQUEST) 53 | 54 | // Check that the wireless config creation failed 55 | cy.url().should('eq', baseUrl + urlFixtures.page.wireless + '/' + urlFixtures.extensions.creation) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /src/app/devices/explorer/explorer.component.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
{{ 'explorer.WSMANExplorer.value' | translate }}
4 |
5 | 6 | {{ 'explorer.WSMANCall.value' | translate }} 7 | 15 | 16 | @for (item of filteredOptions | async; track item) { 17 | {{ item }} 18 | } 19 | 20 | 21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 |

{{ 'common.input.value' | translate }}

29 |
30 | 31 | 35 | 36 |
37 |
38 |
39 | 40 | 41 |

{{ 'common.output.value' | translate }}

42 |
43 | 44 | 48 | 49 |
50 |
51 |
52 | -------------------------------------------------------------------------------- /cypress/e2e/integration/wireless/create.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { empty } from 'cypress/e2e/fixtures/api/general' 7 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 8 | import { wirelessConfigs } from 'cypress/e2e/fixtures/api/wireless' 9 | import { wirelessFixtures } from 'cypress/e2e/fixtures/formEntry/wireless' 10 | 11 | describe('create a wireless profile', () => { 12 | beforeEach('clear cache and login', () => { 13 | cy.setup() 14 | }) 15 | 16 | it('creates a default profile', () => { 17 | cy.myIntercept('GET', 'wirelessconfigs?$count=true', { 18 | statusCode: httpCodes.SUCCESS, 19 | body: wirelessConfigs.getAll.success.response 20 | }).as('wirelessconfigsGetAll') 21 | 22 | cy.myIntercept('POST', 'wirelessconfigs', { 23 | statusCode: httpCodes.CREATED, 24 | body: wirelessConfigs.create.success.response 25 | }).as('post-wireless') 26 | 27 | cy.myIntercept('GET', 'wirelessconfigs?$top=25&$skip=0&$count=true', { 28 | statusCode: httpCodes.SUCCESS, 29 | body: empty.response 30 | }).as('get-wireless') 31 | 32 | cy.goToPage('Wireless') 33 | cy.wait('@get-wireless') 34 | 35 | // change api response 36 | cy.myIntercept('GET', 'wirelessconfigs?$top=25&$skip=0&$count=true', { 37 | statusCode: httpCodes.SUCCESS, 38 | body: wirelessConfigs.getAll.success.response 39 | }).as('get-wireless2') 40 | 41 | cy.get('button').contains('Add New').click() 42 | cy.enterWirelessInfo( 43 | wirelessFixtures.happyPath.profileName, 44 | Cypress.env('WIFI_SSID'), 45 | Cypress.env('WIFI_PSK_PASSPHRASE'), 46 | wirelessFixtures.happyPath.authenticationMethod, 47 | wirelessFixtures.happyPath.encryptionMethod 48 | ) 49 | cy.get('button[type=submit]').click() 50 | 51 | cy.wait('@post-wireless').then((req) => { 52 | cy.wrap(req).its('response.statusCode').should('eq', httpCodes.CREATED) 53 | 54 | // Check that the wireless config was successful 55 | cy.get('mat-cell').contains(wirelessFixtures.happyPath.profileName) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /src/app/profiles/profiles.constants.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { FormOption } from 'src/models/models' 7 | 8 | export const ActivationModes: FormOption[] = [ 9 | { value: 'acmactivate', label: 'profileDetail.activationModeAdmin.value' }, 10 | { value: 'ccmactivate', label: 'profileDetail.activationModeClient.value' } 11 | ] 12 | 13 | export const UserConsentModes: FormOption[] = [ 14 | { value: 'All', label: 'userConsentModes.all.value' }, 15 | { value: 'KVM', label: 'userConsentModes.kvmOnly.value' }, 16 | { value: 'None', label: 'userConsentModes.none.value' } 17 | ] 18 | 19 | export const TlsModes: FormOption[] = [ 20 | { value: 1, label: 'tlsModes.serverAuthOnly.value' }, 21 | { value: 2, label: 'tlsModes.serverAndNonTls.value' }, 22 | { value: 3, label: 'tlsModes.mutualTlsOnly.value' }, 23 | { value: 4, label: 'tlsModes.mutualAndNonTls.value' } 24 | ] 25 | 26 | export const TlsSigningAuthorities: FormOption[] = [ 27 | { value: 'SelfSigned', label: 'tlsAuthorities.selfSigned.value' }, 28 | { value: 'MicrosoftCA', label: 'tlsAuthorities.microsoftCA.value' } 29 | ] 30 | 31 | // unfortunately wifiConfigs is what the REST interface expects 32 | // even though not really a wireless config 33 | export interface WiFiConfig { 34 | profileName: string 35 | priority: number 36 | } 37 | 38 | export interface proxyConfig { 39 | priority: number 40 | name: string 41 | } 42 | 43 | export interface Profile { 44 | proxyConfigs?: proxyConfig[] 45 | profileName: string 46 | activation: string 47 | iderEnabled: boolean 48 | kvmEnabled: boolean 49 | solEnabled: boolean 50 | userConsent: string 51 | generateRandomPassword: boolean 52 | amtPassword?: string 53 | generateRandomMEBxPassword: boolean 54 | mebxPassword?: string 55 | dhcpEnabled: boolean 56 | ipSyncEnabled: boolean 57 | localWifiSyncEnabled: boolean 58 | ieee8021xProfileName?: string 59 | wifiConfigs?: WiFiConfig[] 60 | tags: string[] 61 | tlsMode?: number 62 | tlsSigningAuthority?: string 63 | ciraConfigName?: string 64 | version?: string 65 | uefiWifiSyncEnabled: boolean 66 | } 67 | -------------------------------------------------------------------------------- /cypress/e2e/integration/ieee8021x/delete.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Config } from '../../../../src/app/ieee8021x/ieee8021x.constants' 7 | import { httpCodes } from '../../fixtures/api/httpCodes' 8 | import * as api8021x from '../../fixtures/api/ieee8021x' 9 | import { wiredConfigs, wirelessConfigs } from '../../fixtures/formEntry/ieee8021x' 10 | 11 | describe('Test IEEE 8021x Page', () => { 12 | beforeEach(() => { 13 | cy.setup() 14 | api8021x.interceptDelete(httpCodes.NO_CONTENT, null) 15 | }) 16 | 17 | it('should not delete when cancelled', () => { 18 | api8021x 19 | .interceptGetAll(httpCodes.SUCCESS, { data: allConfigs, totalCount: allConfigs.length }) 20 | .as('interceptGetAll') 21 | cy.goToPage('IEEE 802.1x') 22 | cy.wait('@interceptGetAll') 23 | cy.get('mat-cell').contains('delete').click() 24 | cy.get('button').contains('No').click() 25 | }) 26 | 27 | const allConfigs = [...wiredConfigs, ...wirelessConfigs] 28 | const remainingConfigs = [...allConfigs] 29 | 30 | allConfigs.forEach((config) => { 31 | it(`should delete ${config.profileName}`, () => { 32 | const initialNavConfigs: Config[] = [] 33 | for (const cfg of remainingConfigs) { 34 | initialNavConfigs.push(cfg) 35 | } 36 | let i = remainingConfigs.length 37 | while (i--) { 38 | if (remainingConfigs[i].profileName === config.profileName) { 39 | remainingConfigs.splice(i, 1) 40 | break 41 | } 42 | } 43 | api8021x 44 | .interceptGetAll(httpCodes.SUCCESS, { data: initialNavConfigs, totalCount: initialNavConfigs.length }) 45 | .as('getAllNumber01') 46 | cy.goToPage('IEEE 802.1x') 47 | cy.wait('@getAllNumber01') 48 | api8021x 49 | .interceptGetAll(httpCodes.SUCCESS, { data: remainingConfigs, totalCount: remainingConfigs.length }) 50 | .as('getAllNumber02') 51 | // Delete profile 52 | cy.get('mat-row').contains(config.profileName).parent().contains('delete').click() 53 | cy.get('button').contains('Yes').click() 54 | cy.wait('@getAllNumber02') 55 | }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | import { of } from 'rxjs' 8 | import { DevicesService } from '../devices/devices.service' 9 | import { DashboardComponent } from './dashboard.component' 10 | import { ActivatedRoute, RouterModule } from '@angular/router' 11 | import { provideHttpClient } from '@angular/common/http' 12 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 13 | import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader' 14 | import { provideHttpClientTesting } from '@angular/common/http/testing' 15 | 16 | describe('DashboardComponent', () => { 17 | let component: DashboardComponent 18 | let fixture: ComponentFixture 19 | let getStatsSpy: jasmine.Spy 20 | let translate: TranslateService 21 | 22 | beforeEach(async () => { 23 | const devicesService = jasmine.createSpyObj('DevicesService', ['getStats']) 24 | 25 | getStatsSpy = devicesService.getStats.and.returnValue(of({})) 26 | TestBed.configureTestingModule({ 27 | imports: [ 28 | RouterModule, 29 | DashboardComponent, 30 | TranslateModule.forRoot() 31 | ], 32 | providers: [ 33 | { provide: DevicesService, useValue: devicesService }, 34 | { 35 | provide: ActivatedRoute, 36 | useValue: {} 37 | }, 38 | { provide: TRANSLATE_HTTP_LOADER_CONFIG, useValue: { prefix: '/assets/i18n/', suffix: '.json' } }, 39 | TranslateService, 40 | provideHttpClient(), 41 | provideHttpClientTesting() 42 | ] 43 | }) 44 | }) 45 | 46 | beforeEach(() => { 47 | fixture = TestBed.createComponent(DashboardComponent) 48 | component = fixture.componentInstance 49 | translate = TestBed.inject(TranslateService) 50 | translate.setFallbackLang('en') 51 | fixture.detectChanges() 52 | }) 53 | 54 | afterEach(() => { 55 | TestBed.resetTestingModule() 56 | }) 57 | 58 | it('should create', () => { 59 | expect(component).toBeTruthy() 60 | expect(getStatsSpy).toHaveBeenCalled() 61 | }) 62 | }) 63 | -------------------------------------------------------------------------------- /cypress/e2e/integration/cira/create-error.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Tests the creation of a cira-config 7 | 8 | import { badRequest, empty } from 'cypress/e2e/fixtures/api/general' 9 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 10 | import { ciraFixtures } from '../../fixtures/formEntry/cira' 11 | import { urlFixtures } from '../../fixtures/formEntry/urls' 12 | const baseUrl: string = Cypress.env('BASEURL') 13 | 14 | // ---------------------------- Test section ---------------------------- 15 | 16 | describe('Test CIRA Config Page', () => { 17 | beforeEach('Clear cache and login', () => { 18 | cy.setup() 19 | }) 20 | 21 | beforeEach('fills out the config', () => { 22 | cy.myIntercept('GET', 'ciracert', { 23 | statusCode: httpCodes.SUCCESS, 24 | body: ciraFixtures.MpsCertificate 25 | }).as('certificate1') 26 | 27 | cy.intercept('POST', 'ciraconfigs', { 28 | statusCode: httpCodes.BAD_REQUEST, 29 | body: badRequest.response 30 | }).as('post-config1') 31 | 32 | cy.intercept('GET', 'ciraconfigs?$top=25&$skip=0&$count=true', { 33 | statusCode: httpCodes.SUCCESS, 34 | body: empty.response 35 | }).as('get-configs') 36 | 37 | cy.goToPage('CIRA Configs') 38 | cy.wait('@get-configs') 39 | 40 | cy.get('button').contains('Add New').click() 41 | }) 42 | 43 | it('invalid config name', () => { 44 | cy.enterCiraInfo( 45 | ciraFixtures.wrong.name, 46 | ciraFixtures.default.format, 47 | ciraFixtures.default.addr, 48 | Cypress.env('MPS_USERNAME') 49 | ) 50 | }) 51 | 52 | it('invalid username', () => { 53 | cy.enterCiraInfo( 54 | ciraFixtures.wrong.name, 55 | ciraFixtures.default.format, 56 | ciraFixtures.default.addr, 57 | ciraFixtures.wrong.username 58 | ) 59 | }) 60 | 61 | afterEach('Check that the error occured', () => { 62 | cy.get('button[type=submit]').click() 63 | 64 | cy.wait('@certificate1') 65 | cy.wait('@post-config1').its('response.statusCode').should('eq', 400) 66 | 67 | const url = baseUrl + urlFixtures.page.cira + '/' + urlFixtures.extensions.creation 68 | cy.url().should('eq', url) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /.github/workflows/cypress.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the sample web ui in a docker container and run tests on the ui isolated from mps and rps 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Cypress CI 5 | 6 | on: 7 | push: 8 | branches: [main] 9 | pull_request: 10 | branches: [main] 11 | workflow_dispatch: 12 | permissions: 13 | contents: read 14 | actions: read 15 | 16 | jobs: 17 | build: 18 | permissions: 19 | contents: read 20 | actions: read 21 | checks: write 22 | 23 | runs-on: ubuntu-latest 24 | 25 | strategy: 26 | matrix: 27 | node-version: [20.x] 28 | 29 | steps: 30 | - name: Harden Runner 31 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 32 | with: 33 | egress-policy: audit 34 | 35 | - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 36 | - name: Use Node.js ${{ matrix.node-version }} 37 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 38 | with: 39 | node-version: ${{ matrix.node-version }} 40 | - run: npm install cypress 41 | - run: docker build . --file Dockerfile --tag vprodemo.azurecr.ui/samplewebui:latest 42 | - run: docker run -d -p 4200:80 vprodemo.azurecr.ui/samplewebui:latest 43 | - run: npm run cy-runner 44 | - name: Publish Cypress Test Results 45 | uses: dorny/test-reporter@fe45e9537387dac839af0d33ba56eed8e24189e8 # v2.3.0 46 | if: always() 47 | with: 48 | name: Cypress Tests 49 | path: cypress-ui-test-output-*.xml 50 | reporter: java-junit 51 | fail-on-error: false 52 | - name: Upload Cypress UI Test Results 53 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 54 | if: always() 55 | with: 56 | name: sample-web-ui-ui-test 57 | path: cypress-ui-test-output-*.xml 58 | - name: Upload Cypress UI Images 59 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 60 | if: always() 61 | with: 62 | name: sample-web-ui-ui-test-screenshots 63 | path: /home/runner/work/sample-web-ui/sample-web-ui/cypress/screenshots/**/*.png 64 | -------------------------------------------------------------------------------- /src/app/event-channel/event-channel.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | // import { BrowserAnimationsModule } from '@angular/platform-browser/animations' 8 | // // // import { EventChannelComponent } from './event-channel.component' 9 | // import { MQTTService } from './event-channel.service' 10 | // import { of } from 'rxjs' 11 | 12 | // describe('EventChannelComponent', () => { 13 | // let component: EventChannelComponent 14 | // let fixture: ComponentFixture 15 | // const eventChannelStub = { 16 | // mqttConfig: { hostname: 'test', port: '', path: 'test' }, 17 | // connect: jasmine.createSpy('connect'), 18 | // subscribeToTopic: jasmine.createSpy('connect'), 19 | // messageSource: of(), 20 | // connectionStatusSubject: of(), 21 | // changeConnection: jasmine.createSpy('changeConnection'), 22 | // destroy: jasmine.createSpy('destroy') 23 | // } 24 | // beforeEach(async () => { 25 | // await TestBed.configureTestingModule({ 26 | // imports: [BrowserAnimationsModule, RouterModule], 27 | // declarations: [EventChannelComponent], 28 | // providers: [{ provide: MQTTService, useValue: eventChannelStub }] 29 | // }).compileComponents() 30 | // }) 31 | 32 | // beforeEach(() => { 33 | // fixture = TestBed.createComponent(EventChannelComponent) 34 | // component = fixture.componentInstance 35 | // fixture.detectChanges() 36 | // }) 37 | 38 | // afterEach(() => { 39 | // TestBed.resetTestingModule() 40 | // }) 41 | 42 | // it('should create', () => { 43 | // expect(component).toBeTruthy() 44 | // }) 45 | 46 | // it('should call onSubmit', async () => { 47 | // component.onSubmit() 48 | // component.eventChannelService.changeConnection(component.eventChannelForm.value) 49 | // expect(component.eventChannelService.changeConnection).toHaveBeenCalled() 50 | // }) 51 | 52 | // it('should test noData', () => { 53 | // component.dataSource.data = [{ message: 'Sent domains', methods: ['getAllDomains'], timestamp: 1634026109505, type: 'success' }] 54 | // expect(component.dataSource.data.length).toBe(1) 55 | // }) 56 | // }) 57 | -------------------------------------------------------------------------------- /src/app/devices/certificates/add-cert-dialog/add-cert-dialog.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2025 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | import { ChangeDetectorRef, Component, inject } from '@angular/core' 6 | import { MatSelectModule } from '@angular/material/select' 7 | import { FormsModule } from '@angular/forms' 8 | import { MatDialogContent, MatDialogActions, MatDialogRef, MatDialogModule } from '@angular/material/dialog' 9 | import { MatCardModule } from '@angular/material/card' 10 | import { MatButtonModule } from '@angular/material/button' 11 | import { CertInfo } from 'src/models/models' 12 | import { MatCheckboxModule } from '@angular/material/checkbox' 13 | import { MatIconModule } from '@angular/material/icon' 14 | import { TranslatePipe } from '@ngx-translate/core' 15 | 16 | @Component({ 17 | selector: 'app-add-cert-dialog', 18 | imports: [ 19 | MatDialogModule, 20 | MatDialogContent, 21 | MatDialogActions, 22 | MatSelectModule, 23 | MatButtonModule, 24 | FormsModule, 25 | MatCardModule, 26 | MatCheckboxModule, 27 | MatIconModule, 28 | TranslatePipe 29 | ], 30 | templateUrl: './add-cert-dialog.component.html', 31 | styleUrl: './add-cert-dialog.component.scss' 32 | }) 33 | export class AddCertDialogComponent { 34 | private readonly dialogRef = inject(MatDialogRef) 35 | private readonly cdr = inject(ChangeDetectorRef) 36 | 37 | certInfo: CertInfo = { 38 | cert: '', 39 | isTrusted: false 40 | } 41 | 42 | onFileSelected(e: Event): void { 43 | if (typeof FileReader !== 'undefined') { 44 | const reader = new FileReader() 45 | 46 | reader.onload = (e2: ProgressEvent) => { 47 | const base64: string = e2.target?.result as string 48 | const index: number = base64.indexOf('base64,') 49 | const cert = base64.substring(index + 7, base64.length) 50 | this.certInfo.cert = cert 51 | this.cdr.detectChanges() 52 | } 53 | if (e.target != null) { 54 | const target = e.target as HTMLInputElement 55 | const files = target.files 56 | if (files != null && files.length > 0) { 57 | reader.readAsDataURL(files[0]) 58 | } 59 | } 60 | } 61 | } 62 | 63 | onCancel(): void { 64 | this.dialogRef.close() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cypress/e2e/integration/domain/create.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { domains } from 'cypress/e2e/fixtures/api/domain' 7 | import { empty } from 'cypress/e2e/fixtures/api/general' 8 | import { httpCodes } from 'cypress/e2e/fixtures/api/httpCodes' 9 | import { domainFixtures } from 'cypress/e2e/fixtures/formEntry/domain' 10 | 11 | // ---------------------------- Test section ---------------------------- 12 | 13 | describe('Test Domain Page', () => { 14 | beforeEach('before', () => { 15 | cy.setup() 16 | }) 17 | 18 | it('creates the default domain with cert from fixture file upload', () => { 19 | // Stub the get and post requests 20 | cy.myIntercept('GET', 'domains?$top=25&$skip=0&$count=true', { 21 | statusCode: httpCodes.SUCCESS, 22 | body: empty.response 23 | }).as('get-domains') 24 | 25 | cy.myIntercept('POST', 'domains', { 26 | statusCode: httpCodes.CREATED, 27 | body: domains.create.success.response 28 | }).as('post-domain') 29 | 30 | cy.goToPage('Domains') 31 | cy.wait('@get-domains') 32 | 33 | // Fill out the profile 34 | cy.get('button').contains('Add New').click({ force: true }) 35 | // Change api response 36 | cy.myIntercept('GET', 'domains?$top=25&$skip=0&$count=true', { 37 | statusCode: httpCodes.SUCCESS, 38 | body: domains.getAll.success.response 39 | }).as('get-domains2') 40 | 41 | // handle file on disk or in-memory file 42 | const certFixtureData: Cypress.FileReference = { 43 | fileName: 'test-cert.pfx', 44 | contents: Cypress.Buffer.from(Cypress.env('PROVISIONING_CERT'), 'base64') 45 | } 46 | 47 | cy.enterDomainInfo( 48 | domainFixtures.default.profileName, 49 | Cypress.env('DOMAIN_SUFFIX'), 50 | certFixtureData, 51 | Cypress.env('PROVISIONING_CERT_PASSWORD') 52 | ) 53 | cy.get('button').contains('SAVE').click({ force: true }) 54 | cy.wait('@post-domain').its('response.statusCode').should('eq', httpCodes.CREATED) 55 | cy.wait('@get-domains2').its('response.statusCode').should('eq', httpCodes.SUCCESS) 56 | // Check that the config was successful 57 | cy.get('mat-cell').contains(domainFixtures.default.profileName) 58 | cy.get('mat-cell').contains(Cypress.env('DOMAIN_SUFFIX')) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | #********************************************************************* 2 | # Copyright (c) Intel Corporation 2023 3 | # SPDX-License-Identifier: Apache-2.0 4 | #*********************************************************************/ 5 | 6 | # This workflow will release new versions when required using semantic-release 7 | 8 | name: Semantic-Release CI 9 | 10 | on: 11 | push: 12 | branches: [main] 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | release: 19 | permissions: 20 | contents: write # for Git to git push 21 | runs-on: ubuntu-latest 22 | 23 | steps: 24 | - name: Harden Runner 25 | uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 26 | with: 27 | egress-policy: audit 28 | 29 | - name: Checkout 30 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 31 | with: 32 | persist-credentials: false 33 | - name: Use Node.js 20.x 34 | uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 35 | with: 36 | node-version: '20.x' 37 | - name: Docker Setup Buildx 38 | uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 39 | - run: npm ci 40 | - run: npm run build 41 | - name: Docker Login 42 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 43 | with: 44 | registry: vprodemo.azurecr.io 45 | username: ${{ secrets.DOCKER_USERNAME }} 46 | password: ${{ secrets.DOCKER_PASSWORD }} 47 | logout: true 48 | - name: Docker Login DockerHub 49 | uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 50 | with: 51 | registry: docker.io 52 | username: ${{ secrets.INTC_DOCKER_USERNAME }} 53 | password: ${{ secrets.INTC_DOCKER_PASSWORD }} 54 | logout: true 55 | - name: Semantic Release 56 | uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6.0.0 57 | with: 58 | semantic_version: 59 | 19.0.5 # It is recommended to specify a version range 60 | # for semantic-release when using 61 | # semantic-release-action lower than @v3 62 | extra_plugins: | 63 | @semantic-release/exec@6.0.3 64 | env: 65 | GITHUB_TOKEN: ${{ secrets.ROSIE_TOKEN }} 66 | -------------------------------------------------------------------------------- /src/app/core/navbar/navbar.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | logo 4 | 5 | 6 | 7 | 8 | home 9 | {{ 'dashboard.header.dashboardTitle.value' | translate }} 10 | 11 | 12 | 13 | security 14 | {{ 'ieee8021x.headerd.ieeeTitle.value' | translate }} 15 | 16 | 17 | wifi{{ 'wireless.header.wirelessTitle.value' | translate }} 19 | 20 | 26 | settings_ethernet{{ 'configs.header.ciraTitle.value' | translate }} 28 | 29 | @if (cloudMode === true) { 30 | 31 | router{{ 'proxyConfigs.header.proxyTitle.value' | translate }} 33 | 34 | } 35 | 36 | 37 | settings_system_daydream{{ 'profiles.header.profileTitle.value' | translate }} 39 | 40 | 41 | http{{ 'domains.header.domainsTitle.value' | translate }} 43 | 44 | 45 | 46 | devices{{ 'devices.headerd.devicesTitle.value' | translate }} 48 | 49 | 52 | 53 | -------------------------------------------------------------------------------- /src/app/shared/add-device/add-device.component.html: -------------------------------------------------------------------------------- 1 |

{{ 'addDevice.title.value' | translate }}

2 | 3 |
4 |

{{ 'addDevice.description.value' | translate }}

5 | 6 |
7 |
8 | 9 | {{ 'addDevice.noSelfSignedCertCheck.value' | translate }} 10 | 11 | 12 | {{ 'addDevice.showVerbose.value' | translate }} 13 | 14 |
15 | 16 |
17 | 18 | {{ 'addDevice.chooseProfile.value' | translate }} 19 | 20 | @for (profile of profiles().data; track profile) { 21 | 22 | {{ profile.profileName }} 23 | 24 | } 25 | 26 | {{ 'addDevice.profileRequired.value' | translate }} 27 | 28 |
29 | 30 |
31 | 37 | 38 | 39 | 40 | 41 |
42 | 43 |
44 | 45 | 46 | 58 | 59 |
60 |
61 |
62 | -------------------------------------------------------------------------------- /src/app/devices/tls/tls.component.html: -------------------------------------------------------------------------------- 1 | @if (isLoading()) { 2 | 3 | } 4 | 5 | 6 | 7 |

{{ 'tls.title.value' | translate }}

8 |
9 |
10 | 11 |
12 | @for (setting of tlsData; track $index) { 13 |
14 |

{{ 'tls.elementName.label.value' | translate }}

15 |
16 |
17 | {{ setting.ElementName }} 18 |
19 |
20 |

{{ 'tls.instanceId.label.value' | translate }}

21 |
22 |
23 | {{ setting.InstanceID }} 24 |
25 |
26 |

{{ 'tls.mutualAuth.label.value' | translate }}

27 |
28 |
29 | {{ 30 | setting.MutualAuthentication ? ('common.yes.value' | translate) : ('common.no.value' | translate) 31 | }} 32 |
33 |
34 |

{{ 'tls.enabled.label.value' | translate }}

35 |
36 |
37 | {{ 38 | setting.Enabled ? ('common.yes.value' | translate) : ('common.no.value' | translate) 39 | }} 40 |
41 |
42 |

{{ 'tls.acceptNonSecure.label.value' | translate }}

43 |
44 |
45 | {{ 46 | setting.AcceptNonSecureConnections ? ('common.yes.value' | translate) : ('common.no.value' | translate) 47 | }} 48 |
49 |
50 |

{{ 'tls.nonSecureSupported.label.value' | translate }}

51 |
52 |
53 | {{ 54 | setting.NonSecureConnectionsSupported ? ('common.yes.value' | translate) : ('common.no.value' | translate) 55 | }} 56 |
57 | 58 | } 59 |
60 |
61 |
62 | -------------------------------------------------------------------------------- /cypress/README.md: -------------------------------------------------------------------------------- 1 | # Cypress UI Validation Tool 2 | 3 | Visit the [documentation for Cypress](https://docs.cypress.io/guides/overview/why-cypress) for an in-depth look of all cypress features 4 | 5 | ## How to use 6 | 7 | ### Locally 8 | 9 | First spin up a local instance of the sample web ui using `npm run start` 10 | 11 | > You may need to configure `device-management-toolkit/docker-compose.yml` to prevent network errors 12 | 13 | Once the server is up on `http://localhost:4200/`, open a new terminal and run `npm run cypress`. This will start the cypress testing gui. 14 | 15 | 16 | 17 | From here you can choose to run any of the test cases stored in the integration folder. 18 | 19 | ### Through GitHub Actions 20 | 21 | In github click on the `Actions` tab and choose the `Cypress CI` workflow. Now simply click `Run workflow` and choose the branch you wish to test. This will spin up a container to run through all the UI tests in the integration folder sequentially and return a log reporting if the tests were successful or not. To change how this action is triggered or what it runs, go to `sample-web-ui/.github/workflows/cypress.yml`. 22 | 23 | ## Code Layout 24 | 25 | The core groupings of code within this cypress project are tests, fixtures, commands and enviornment variables. 26 | 27 | ### Tests 28 | 29 | `sample-web-ui/cypress/integartion/*.spec.js`
30 | This is where all of the test cases are stored. Currently each page of the ui has its own test file, which goes through a happy path use case of that page. 31 | 32 | ### Fixtures 33 | 34 | `sample-web-ui/cypress/fixtures/*.json`
35 | This is where data for filling out certain fields, verfiying urls and mocking api responses is stored. 36 | 37 | ### Commands 38 | 39 | `sample-web-ui/cypress/support/commands.js`
40 | This is where new functions can be added to cypress to help reduce redundancy within test cases. See [documentation](https://docs.cypress.io/api/cypress-api/custom-commands) for more information on this feature. 41 | 42 | ### Environment Variables 43 | 44 | `sample-web-ui/cypress.json`
45 | This is where important variables such as the base url of the server, passwords and whether cypress should mock api reponses are stored. You can add a new variable and call it in a test case with `Cypress.env("VARIABLE_NAME")`. If you wish to change an environment variable for a single instance of cypress, you can run `npm run cypress -- --env VAR_NAME=VALUE,VAR_NAME2=VALUE2` instead of the usual command. 46 | -------------------------------------------------------------------------------- /src/app/devices/general/general.component.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { ComponentFixture, TestBed } from '@angular/core/testing' 7 | 8 | import { GeneralComponent } from './general.component' 9 | import { ActivatedRoute } from '@angular/router' 10 | import { of } from 'rxjs' 11 | import { DevicesService } from '../devices.service' 12 | import { TranslateModule } from '@ngx-translate/core' 13 | 14 | describe('GeneralComponent', () => { 15 | let component: GeneralComponent 16 | let fixture: ComponentFixture 17 | let devicesServiceSpy: jasmine.SpyObj 18 | 19 | beforeEach(() => { 20 | devicesServiceSpy = jasmine.createSpyObj('DevicesService', [ 21 | 'getDevices', 22 | 'updateDevice', 23 | 'getTags', 24 | 'getPowerState', 25 | 'getAMTVersion', 26 | 'getAMTFeatures', 27 | 'getGeneralSettings', 28 | 'PowerStates', 29 | 'sendPowerAction', 30 | 'bulkPowerAction', 31 | 'sendDeactivate', 32 | 'sendBulkDeactivate', 33 | 'getWsmanOperations' 34 | ]) 35 | devicesServiceSpy.getAMTFeatures.and.returnValue( 36 | of({ 37 | userConsent: 'ALL', 38 | KVM: true, 39 | SOL: true, 40 | IDER: true, 41 | redirection: true, 42 | optInState: 1, 43 | kvmAvailable: true, 44 | httpsBootSupported: true, 45 | ocr: true, 46 | winREBootSupported: true, 47 | localPBABootSupported: true, 48 | remoteErase: true, 49 | pbaBootFilesPath: [], 50 | winREBootFilesPath: { 51 | instanceID: '', 52 | biosBootString: '', 53 | bootString: '' 54 | } 55 | }) 56 | ) 57 | devicesServiceSpy.getGeneralSettings.and.returnValue(of({})) 58 | devicesServiceSpy.getAMTVersion.and.returnValue(of([''])) 59 | TestBed.configureTestingModule({ 60 | imports: [GeneralComponent, TranslateModule.forRoot()], 61 | providers: [ 62 | { provide: ActivatedRoute, useValue: { params: of({ id: 1 }) } }, 63 | { provide: DevicesService, useValue: devicesServiceSpy } 64 | ] 65 | }) 66 | 67 | fixture = TestBed.createComponent(GeneralComponent) 68 | component = fixture.componentInstance 69 | fixture.detectChanges() 70 | }) 71 | 72 | it('should create', () => { 73 | expect(component).toBeTruthy() 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /cypress/e2e/integration/device/device.spec.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | // Tests the creation of a profile 7 | import { httpCodes } from '../../fixtures/api/httpCodes' 8 | import { devices } from '../../fixtures/api/device' 9 | import { tags } from 'cypress/e2e/fixtures/api/tags' 10 | 11 | // ---------------------------- Test section ---------------------------- 12 | 13 | describe('Test Device Page', () => { 14 | beforeEach('', () => { 15 | cy.setup() 16 | 17 | cy.myIntercept('GET', /tags$/, { 18 | statusCode: httpCodes.SUCCESS, 19 | body: tags.getAll.success.response 20 | }).as('get-tags') 21 | 22 | cy.myIntercept('GET', 'devices?$top=25&$skip=0&$count=true', { 23 | statusCode: httpCodes.SUCCESS, 24 | body: devices.getAll.success.response 25 | }).as('get-devices') 26 | 27 | cy.myIntercept('GET', /.*power.*/, { 28 | statusCode: httpCodes.SUCCESS, 29 | body: { powerState: 2 } 30 | }).as('get-powerstate') 31 | }) 32 | 33 | it('loads all the devices', () => { 34 | cy.goToPage('Devices') 35 | cy.wait('@get-devices').its('response.statusCode').should('eq', 200) 36 | cy.wait('@get-powerstate').its('response.statusCode').should('eq', 200) 37 | }) 38 | 39 | // UI Only 40 | it('filters for windows devices', () => { 41 | if (Cypress.env('ISOLATE').charAt(0).toLowerCase() !== 'n') { 42 | cy.myIntercept('GET', '**/devices?tags=Windows&$top=25&$skip=0&$count=true', { 43 | statusCode: httpCodes.SUCCESS, 44 | body: devices.getAll.windows.response.data 45 | }).as('get-windows') 46 | 47 | cy.goToPage('Devices') 48 | cy.wait('@get-tags') 49 | cy.wait('@get-devices') 50 | 51 | // Filter for Windows devices 52 | cy.get('[data-cy="filterTags"]').click() 53 | 54 | cy.contains('mat-option', 'Windows').click() 55 | 56 | cy.wait('@get-windows').its('response.statusCode').should('eq', 200) 57 | 58 | // Remove Filter for Windows devices 59 | cy.contains('mat-option', 'Windows').click() 60 | cy.wait('@get-devices').its('response.statusCode').should('eq', 200) 61 | } 62 | }) 63 | 64 | it('selects the first device', () => { 65 | cy.goToPage('Devices') 66 | cy.wait('@get-devices').its('response.statusCode').should('eq', 200) 67 | cy.wait('@get-tags').its('response.statusCode').should('eq', 200) 68 | 69 | cy.get('mat-table mat-row:first').click() 70 | }) 71 | }) 72 | -------------------------------------------------------------------------------- /src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { Component, OnInit, inject, signal } from '@angular/core' 7 | import { MatSnackBar } from '@angular/material/snack-bar' 8 | import { throwError } from 'rxjs' 9 | import { catchError, finalize } from 'rxjs/operators' 10 | import { DeviceStats } from 'src/models/models' 11 | import { DevicesService } from '../devices/devices.service' 12 | import SnackbarDefaults from '../shared/config/snackBarDefault' 13 | import { RouterLink } from '@angular/router' 14 | import { MatIconButton } from '@angular/material/button' 15 | import { MatTooltip } from '@angular/material/tooltip' 16 | import { MatDivider } from '@angular/material/divider' 17 | import { MatIcon } from '@angular/material/icon' 18 | import { MatCard } from '@angular/material/card' 19 | import { MatProgressBar } from '@angular/material/progress-bar' 20 | import { environment } from 'src/environments/environment' 21 | import { TranslateModule, TranslateService } from '@ngx-translate/core' 22 | 23 | @Component({ 24 | selector: 'app-dashboard', 25 | templateUrl: './dashboard.component.html', 26 | styleUrls: ['./dashboard.component.scss'], 27 | imports: [ 28 | MatProgressBar, 29 | MatCard, 30 | MatIcon, 31 | MatDivider, 32 | MatTooltip, 33 | MatIconButton, 34 | RouterLink, 35 | TranslateModule 36 | ] 37 | }) 38 | export class DashboardComponent implements OnInit { 39 | // Dependency Injection 40 | private readonly snackBar = inject(MatSnackBar) 41 | private readonly devicesService = inject(DevicesService) 42 | private readonly translate = inject(TranslateService) 43 | public cloudMode = environment.cloud 44 | public isLoading = signal(true) 45 | public stats?: DeviceStats 46 | 47 | ngOnInit(): void { 48 | this.isLoading.set(true) 49 | this.devicesService 50 | .getStats() 51 | .pipe( 52 | catchError((err) => { 53 | // TODO: handle error better 54 | const msg: string = this.translate.instant('dashboard.error.value') 55 | 56 | this.snackBar.open(msg, undefined, SnackbarDefaults.defaultError) 57 | return throwError(err) 58 | }), 59 | finalize(() => { 60 | this.isLoading.set(false) 61 | }) 62 | ) 63 | .subscribe((data) => { 64 | this.stats = data 65 | }) 66 | } 67 | 68 | navigateTo(url: string): void { 69 | window.open(url, '_blank') 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/devices/certificates/certificates.component.html: -------------------------------------------------------------------------------- 1 | @if (isLoading()) { 2 | 3 | } 4 | 5 | 6 | 7 | {{ 'certificates.title.value' | translate }} 8 | {{ 'certificates.list.value' | translate }} 9 |
10 | 13 |
14 |
15 | 16 | @if (isLoading()) { 17 | {{ 'certificates.loading.value' | translate }} 18 | } @else if (isCertEmpty()) { 19 | {{ 'certificates.empty.value' | translate }} 20 | } @else { 21 | @for (cert of certInfo.certificates?.publicKeyCertificateItems; track cert) { 22 | 23 | {{ cert.displayName }} 24 | 25 | {{ 'certificates.type.value' | translate }}: 26 | {{ 27 | cert.trustedRootCertificate 28 | ? ('certificates.typeRoot.value' | translate) 29 | : ('certificates.typeClient.value' | translate) 30 | }} 31 | 32 | 33 | {{ 'certificates.profileAssociations.value' | translate }}: 34 | @if (!cert.associatedProfiles || cert.associatedProfiles.length === 0) { 35 | {{ 'certificates.profileAssociationsUnassociated.value' | translate }} 36 | } @else { 37 | {{ cert.associatedProfiles }} 38 | } 39 | 40 |
41 | 47 | 55 |
56 |
57 | } 58 | } 59 |
60 |
61 | -------------------------------------------------------------------------------- /src/app/ieee8021x/ieee8021x.constants.ts: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Copyright (c) Intel Corporation 2022 3 | * SPDX-License-Identifier: Apache-2.0 4 | **********************************************************************/ 5 | 6 | import { FormOption } from '../../models/models' 7 | 8 | // AMT Authentication ProtocoL 9 | // https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/HTMLDocuments/WS-Management_Class_Reference/AMT_8021XProfile.htm 10 | // CIM Authentication ProtocoL 11 | // https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=HTMLDocuments%2FWS-Management_Class_Reference%2FCIM_IEEE8021xSettings.htm 12 | // IPS Authentication Protocol 13 | // https://software.intel.com/sites/manageability/AMT_Implementation_and_Reference_Guide/default.htm?turl=HTMLDocuments%2FWS-Management_Class_Reference%2FIPS_IEEE8021xSettings.htm 14 | // ------------------------------------------------------------------------------------- 15 | // | AMT | CIM | IPS 16 | // ------------------------------------------------------------------------------------- 17 | // 0 | TLS | EAP-TLS | EAP-TLS 18 | // 1 | TTLS_MSCHAPv2 | EAP-TTLS/MSCHAPv2 | EAP-TTLS/MSCHAPv2 19 | // 2 | PEAP_MSCHAPv2 | PEAPv0/EAP-MSCHAPv2 | PEAPv0/EAP-MSCHAPv2 20 | // 3 | EAP_GTC | PEAPv1/EAP-GTC | PEAPv1/EAP-GTC 21 | // 4 | EAPFAST_MSCHAPv2 | EAP-FAST/MSCHAPv2 | EAP-FAST/MSCHAPv2 22 | // 5 | EAPFAST_GTC | EAP-FAST/GTC | EAP-FAST/GTC 23 | // 6 | EAPFAST_TLS | EAP-MD5 | EAP-MD5 24 | // 7 | | EAP-PSK | EAP-PSK 25 | // 8 | | EAP-SIM | EAP-SIM 26 | // 9 | | EAP-AKA | EAP-AKA 27 | // 10 | | EAP-FAST/TLS | EAP-FAST/TLS 28 | // 11 | | DMTF Reserved | DMTF Reserved 29 | 30 | export const AuthenticationProtocols: FormOption[] = [ 31 | { value: 0, mode: 'both', label: 'EAP-TLS' }, 32 | //{ value: 1, mode: 'wired', label: 'EAP-TTLS/MSCHAPv2' }, 33 | { value: 2, mode: 'both', label: 'PEAPv0/EAP-MSCHAPv2' }, 34 | { value: 3, mode: 'wired', label: 'PEAPv1/EAP-GTC' }, 35 | // { value: 4, mode: 'wired', label: 'EAP-FAST/MSCHAPv2' }, 36 | { value: 5, mode: 'wired', label: 'EAP-FAST/GTC' }, 37 | // { value: 6, mode: 'wired', label: 'EAP-MD5' }, 38 | // { value: 7, mode: 'wired', label: 'EAP-PSK' }, 39 | // { value: 8, mode: 'wired', label: 'EAP-SIM' }, 40 | // { value: 9, mode: 'wired', label: 'EAP-AKA' }, 41 | { value: 10, mode: 'wired', label: 'EAP-FAST/TLS' } 42 | ] 43 | --------------------------------------------------------------------------------