diff --git a/knox-homepage-ui/eslint.config.js b/knox-homepage-ui/eslint.config.js index ed79a134c4..b75383502f 100644 --- a/knox-homepage-ui/eslint.config.js +++ b/knox-homepage-ui/eslint.config.js @@ -16,7 +16,7 @@ export default [ languageOptions: { parser: tsParser, parserOptions: { - project: ["./tsconfig.json"], + project: ["home/tsconfig.json"], tsconfigRootDir: import.meta.dirname, }, globals: { diff --git a/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.html b/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.html index 5dbab765cd..e157f811c5 100644 --- a/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.html +++ b/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.html @@ -1,16 +1,16 @@

{{ data?.shortDesc }} @@ -22,7 +22,9 @@

Knox Service Name
{{ data?.serviceName }} - (v{{ data?.version }}) + @if (data?.version) { + (v{{ data?.version }}) + }
@@ -34,16 +36,16 @@

Sample(s)
- + @if (data.samples?.sample?.length > 0) {
-
-
{{ sample.description }}
-
{{ sample.value }}
-
+ @for (sample of data.samples.sample; track sample) { +
+
{{ sample.description }}
+
{{ sample.value }}
+
+ }
-
- - + } @else {

No samples found in service metadata.

@@ -53,7 +55,8 @@

-
+ } +
diff --git a/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.ts b/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.ts index 3ad82fb6e0..c0ecb1055f 100644 --- a/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.ts +++ b/knox-homepage-ui/home/app/apiservice-dialog/apiservice-dialog.component.ts @@ -20,11 +20,11 @@ import { MatButtonModule } from '@angular/material/button'; import { MatGridListModule } from '@angular/material/grid-list'; import { MatListModule } from '@angular/material/list'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { CommonModule } from '@angular/common'; + @Component({ selector: 'app-apiservice-dialog', - imports: [MatDialogModule, MatButtonModule, MatGridListModule, MatListModule, CommonModule], + imports: [MatDialogModule, MatButtonModule, MatGridListModule, MatListModule], templateUrl: './apiservice-dialog.component.html', styleUrl: './apiservice-dialog.component.css' }) diff --git a/knox-homepage-ui/home/app/app.module.ts b/knox-homepage-ui/home/app/app.module.ts index 9b22cf7355..44765d2a25 100644 --- a/knox-homepage-ui/home/app/app.module.ts +++ b/knox-homepage-ui/home/app/app.module.ts @@ -16,7 +16,6 @@ */ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { HttpClientModule, HttpClientXsrfModule } from '@angular/common/http'; import { MatGridListModule } from '@angular/material/grid-list'; import { RouterModule } from '@angular/router'; @@ -25,8 +24,6 @@ import { HomepageService } from './service/homepage.service'; @NgModule({ imports: [ BrowserModule, - HttpClientModule, - HttpClientXsrfModule, MatGridListModule, RouterModule.forRoot([]), ], diff --git a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html index 2f0a4e979b..fb3b097ceb 100644 --- a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html +++ b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.html @@ -1,85 +1,107 @@
-

- remove - add - General Proxy Information -

+

+ @if (this['showGeneralProxyInformation']) { + remove + } + @if (!this['showGeneralProxyInformation']) { + add + } + General Proxy Information +

-
+@if (this['showGeneralProxyInformation']) { +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Knox Version{{ getVersion() }}
Hostname{{ getHostname() }}
TLS Public Certificate - PEM -   |   - JKS -
Admin UI URL{{ getAdminUiUrl() }}
- Admin API Details - - - {{ getAdminApiBookUrl() }} -
- Metadata API - - General Proxy Information -   |   - Topologies -
- Integration Tokens - - Token Management -   |   - Token Generation -
Web Shell{{ getWebShellUrl() }}
-
\ No newline at end of file + + + + + + @if (this['showKnoxVersion']) { + + Knox Version + {{ getVersion() }} + + } + @if (this['showKnoxHostname']) { + + Hostname + {{ getHostname() }} + + } + @if (this['showPublicCerts']) { + + TLS Public Certificate + + PEM +   |   + JKS + + + } + @if (this['showAdminUI']) { + + Admin UI URL + {{ getAdminUiUrl() }} + + } + @if (this['showAdminAPI']) { + + + Admin API Details + + + + {{ getAdminApiBookUrl() }} + + + } + @if (this['showMetadataAPI']) { + + + Metadata API + + + General Proxy Information +   |   + Topologies + + + } + @if (this.isTokenManagementEnabled() && this['showTokens']) { + + + Integration Tokens + + + Token Management +   |   + Token Generation + + + } + @if (this.isWebshellEnabled() && this['showWebShell']) { + + Web Shell + {{ getWebShellUrl() }} + + } + + +
+} \ No newline at end of file diff --git a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts index efa6c087d2..9bba05fc00 100644 --- a/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts +++ b/knox-homepage-ui/home/app/generalProxyInformation/general.proxy.information.component.ts @@ -18,14 +18,14 @@ import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {HomepageService} from '../service/homepage.service'; import {GeneralProxyInformation} from '../model/general.proxy.information'; -import { CommonModule } from '@angular/common'; + import { MatIconModule } from '@angular/material/icon'; @Component({ selector: 'app-general-proxy-information', templateUrl: './general.proxy.information.component.html', providers: [HomepageService], - imports: [CommonModule, MatIconModule] + imports: [MatIconModule] }) export class GeneralProxyInformationComponent implements OnInit { @@ -34,7 +34,7 @@ export class GeneralProxyInformationComponent implements OnInit { profile: JSON; constructor(private homepageService: HomepageService, private route: ActivatedRoute) { - this['showGeneralProxyInformation'] = false; + this['showGeneralProxyInformation'] = true; this['showKnoxVersion'] = true; this['showKnoxHostname'] = true; this['showPublicCerts'] = true; diff --git a/knox-homepage-ui/home/app/service/homepage.service.ts b/knox-homepage-ui/home/app/service/homepage.service.ts index b4ef43d7d3..3fce7834c5 100644 --- a/knox-homepage-ui/home/app/service/homepage.service.ts +++ b/knox-homepage-ui/home/app/service/homepage.service.ts @@ -65,7 +65,7 @@ export class HomepageService { getProfile(profileName: string): Promise { let headers = this.addJsonHeaders(new HttpHeaders()); - let url = `${this.apiUrl}/profiles/${profileName}`; + let url = `${this.apiUrl}profiles/${profileName}`; return firstValueFrom(this.http.get(url, { headers })) .catch(err => this.handleError(err)); } diff --git a/knox-homepage-ui/home/app/sessionInformation/session.information.component.html b/knox-homepage-ui/home/app/sessionInformation/session.information.component.html index 2860a89e8f..5118f7697f 100644 --- a/knox-homepage-ui/home/app/sessionInformation/session.information.component.html +++ b/knox-homepage-ui/home/app/sessionInformation/session.information.component.html @@ -1,17 +1,19 @@
Welcome {{ getUser() }}
-
logout
+@if (logoutSupported) { +
logout
+}
diff --git a/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts b/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts index 3c148daed7..f450e8f623 100644 --- a/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts +++ b/knox-homepage-ui/home/app/sessionInformation/session.information.component.ts @@ -17,14 +17,14 @@ import {Component, OnInit} from '@angular/core'; import {HomepageService} from '../service/homepage.service'; import {SessionInformation} from '../model/session.information'; -import { CommonModule } from '@angular/common'; + import { SafeHtmlPipe } from '../util/safehtml'; @Component({ selector: 'app-session-information', templateUrl: './session.information.component.html', providers: [HomepageService], - imports: [CommonModule, SafeHtmlPipe] + imports: [SafeHtmlPipe] }) export class SessionInformationComponent implements OnInit { diff --git a/knox-homepage-ui/home/app/topologies/topology.information.component.html b/knox-homepage-ui/home/app/topologies/topology.information.component.html index 9293f22265..351f2d29dc 100644 --- a/knox-homepage-ui/home/app/topologies/topology.information.component.html +++ b/knox-homepage-ui/home/app/topologies/topology.information.component.html @@ -1,151 +1,187 @@
-

- remove - addTopologies +

+ @if (this['showTopologies']) { + remove + } + @if (!this['showTopologies']) { + add + }Topologies

-
-
- +
+ @if (this['showTopologies']) { +
+ @for (topology of topologies; track topology) {
- remove - add - {{topology.topology}} - - push_pin - settings + @if (this['showTopology_' + topology.topology]) { + remove + } + @if (!this['showTopology_' + topology.topology]) { + add + } + {{topology.topology}} + @if (topology.pinned) { + push_pin + } + @if (!topology.pinned) { + settings + }
- -
- -
UI Services
- + @if (this['showTopology_' + topology.topology]) { +
+ @if (topology.uiServices.service.length > 0) { +
UI Services
+ } - - + @for (service of topology.uiServices.service; track service) { + - -
- + @if (service.serviceUrls.length === 1) { +
+ @if (!this['enableServiceText_' + service.serviceName.toLowerCase()]) { + - - + (error)="enableServiceText('enableServiceText_' + service.serviceName.toLowerCase())" /> + + } + @if (this['enableServiceText_' + service.serviceName.toLowerCase()]) { + {{service.shortDesc}} - + + } -

{{service.shortDesc}} (v{{service.version}})

+

{{service.shortDesc}} @if (service.version) { + (v{{service.version}}) + }

-

{{service.description}}

+

{{service.description}}

-
- +
+ } -
+ @if (service.serviceUrls.length > 1) { +
+ (error)="enableServiceText('enableServiceText_' + service.serviceName.toLowerCase())" /> -

{{service.shortDesc}} ({{service.serviceUrls.length}})

+

{{service.shortDesc}} ({{service.serviceUrls.length}})

-
- - - - +
+ } +
+ } +
+ } - -
API Services
- -
- + @if (topology.apiServices.service.length > 0) { +
API Services
+ } + @if (topology.apiServicesViewVersion === 'v1') { +
- -
No API services found
-
- + @if (topology.apiServices.service.length === 0) { +
No API services found
+ } - - + @if (topology.apiServices.service.length > 0) { +
- - + + - - - + + - - -
Description -
- info - {{ service.shortDesc }} - (v{{ service.version }}) -
-
Description +
+ info + {{ service.shortDesc }} + @if (service.version) { + (v{{ service.version }}) + } +
+
URLs - - {{ url }} -
-
-
URLs + @for (url of service.serviceUrls; track url; let i = $index) { + {{ url }} + @if (service.serviceUrls.length > 1 && i < service.serviceUrls.length - 1) { +
+ } + } +
- + + } - 0) { + - -
- - - + + } +
+ } + @if (topology.apiServicesViewVersion === 'v2') { + + @for (service of topology.apiServices.service; track service) { + - + @if (!this['enableServiceText_' + service.serviceName.toLowerCase()]) { + - - + (error)="enableServiceText('enableServiceText_' + service.serviceName.toLowerCase())" /> + + } + @if (this['enableServiceText_' + service.serviceName.toLowerCase()]) { + {{service.shortDesc}} - + + } -

{{service.shortDesc}} (v{{service.version}})

+

{{service.shortDesc}} @if (service.version) { + (v{{service.version}}) + }

-

{{service.description}}

+

{{service.description}}

-
-
-
- -
-
\ No newline at end of file + + } + + } +
+ } + } +
+ + } \ No newline at end of file diff --git a/knox-homepage-ui/home/app/topologies/topology.information.component.ts b/knox-homepage-ui/home/app/topologies/topology.information.component.ts index 4ede4b0a61..df8fa70dc7 100644 --- a/knox-homepage-ui/home/app/topologies/topology.information.component.ts +++ b/knox-homepage-ui/home/app/topologies/topology.information.component.ts @@ -24,7 +24,7 @@ import { ActivatedRoute } from '@angular/router'; import { HomepageService } from '../service/homepage.service'; import { TopologyInformation } from '../model/topology.information'; import { Service } from '../model/service'; -import { CommonModule } from '@angular/common'; + import { MatGridListModule } from '@angular/material/grid-list'; import { ApiserviceDialogComponent } from '../apiservice-dialog/apiservice-dialog.component'; import { UiserviceDialogComponent } from '../uiservice-dialog/uiservice-dialog.component'; @@ -33,7 +33,7 @@ import { UiserviceDialogComponent } from '../uiservice-dialog/uiservice-dialog.c templateUrl: './topology.information.component.html', styleUrls: ['./topology.information.component.css'], providers: [HomepageService], - imports: [CommonModule, MatGridListModule, MatTooltipModule, MatIconModule, MatTableModule, MatPaginatorModule] + imports: [MatGridListModule, MatTooltipModule, MatIconModule, MatTableModule, MatPaginatorModule] }) export class TopologyInformationsComponent implements OnInit { diff --git a/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.html b/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.html index 3600905810..6842560116 100644 --- a/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.html +++ b/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.html @@ -1,53 +1,61 @@ -

- {{ data.shortDesc }} - (v{{ data.version }}) -

+@if (data) { +

+ {{ data.shortDesc }} + @if (data.version) { + (v{{ data.version }}) + } +

+} - -

{{ data.description }}

- - - Search by hostname, port... - - - - - - - - - - - {{ data.shortDesc }} - - - -

{{ data.shortDesc }} {{ getServiceUrlHostAndPort(serviceUrl) }}

-
-
-
-
+@if (data) { + +

{{ data.description }}

+ + Search by hostname, port... + + + + @for (serviceUrl of filteredServiceUrls; track serviceUrl) { + + @if (!enableServiceText) { + + + + } + @if (enableServiceText) { + + {{ data.shortDesc }} + + } + +

{{ data.shortDesc }} {{ getServiceUrlHostAndPort(serviceUrl) }}

+
+
+ } +
+
+} - diff --git a/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.ts b/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.ts index 88042c2154..6e7d4c164e 100644 --- a/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.ts +++ b/knox-homepage-ui/home/app/uiservice-dialog/uiservice-dialog.component.ts @@ -21,11 +21,11 @@ import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatGridListModule } from '@angular/material/grid-list'; -import { CommonModule } from '@angular/common'; + @Component({ selector: 'app-uiservice-dialog', - imports: [MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatGridListModule, CommonModule], + imports: [MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatGridListModule], templateUrl: './uiservice-dialog.component.html', styleUrl: './uiservice-dialog.component.css' }) diff --git a/knox-homepage-ui/home/index.html b/knox-homepage-ui/home/index.html index 7ce0fd1906..0dbf1dc039 100644 --- a/knox-homepage-ui/home/index.html +++ b/knox-homepage-ui/home/index.html @@ -19,9 +19,7 @@ Apache Knox Home - - + + - -
- - Apache Knox Home -
- - + + +
+ +
+ diff --git a/knox-token-generation-ui/token-generation/main.ts b/knox-token-generation-ui/token-generation/main.ts index be5f383523..8acc0100e5 100644 --- a/knox-token-generation-ui/token-generation/main.ts +++ b/knox-token-generation-ui/token-generation/main.ts @@ -16,13 +16,34 @@ */ import './polyfills.ts'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {enableProdMode} from '@angular/core'; import {environment} from './environments/environment'; -import {AppModule} from './app/app.module'; +import { SessionInformationComponent } from './app/sessionInformation/session.information.component.js'; +import { TokenGenerationComponent } from './app/token-generation/token-generation.component.js'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { enableProdMode, importProvidersFrom, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { APP_BASE_HREF } from '@angular/common'; +import { provideHttpClient } from '@angular/common/http'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +const bootstrapComponents = [ + SessionInformationComponent, + TokenGenerationComponent +]; + +bootstrapComponents.forEach(component => { + bootstrapApplication(component, { + providers: [ + provideZoneChangeDetection(),importProvidersFrom(), + provideHttpClient(), + provideRouter([]), + { + provide: APP_BASE_HREF, + useValue: window['base-href'] || '/' + } + ] + }).catch(err => console.error(err)); +}); diff --git a/knox-token-generation-ui/token-generation/polyfills.ts b/knox-token-generation-ui/token-generation/polyfills.ts index 560dbecd0a..aebd58c4ed 100644 --- a/knox-token-generation-ui/token-generation/polyfills.ts +++ b/knox-token-generation-ui/token-generation/polyfills.ts @@ -14,22 +14,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// This file includes polyfills needed by Angular 2 and is loaded before -// the app. You can add your own extra polyfills to this file. -import 'core-js/es/symbol'; -import 'core-js/es/object'; -import 'core-js/es/function'; -import 'core-js/es/parse-int'; -import 'core-js/es/parse-float'; -import 'core-js/es/number'; -import 'core-js/es/math'; -import 'core-js/es/string'; -import 'core-js/es/date'; -import 'core-js/es/array'; -import 'core-js/es/regexp'; -import 'core-js/es/map'; -import 'core-js/es/set'; -import 'core-js/es/reflect'; - -import 'core-js/es/reflect'; -import 'zone.js/dist/zone'; +import 'zone.js'; diff --git a/knox-token-management-ui/token-management/styles.css b/knox-token-generation-ui/token-generation/styles.scss similarity index 71% rename from knox-token-management-ui/token-management/styles.css rename to knox-token-generation-ui/token-generation/styles.scss index 7240de9e32..a35a96aebb 100644 --- a/knox-token-management-ui/token-management/styles.css +++ b/knox-token-generation-ui/token-generation/styles.scss @@ -15,10 +15,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@use '@angular/material' as mat; -/* You can add global styles to this file, and also import other style files */ - -@import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; +:root { + @include mat.theme(( + color: ( + primary: mat.$green-palette + ) + )); +} .navbar-static-top { min-height: 110px; @@ -27,3 +32,21 @@ .clickable { cursor: pointer; } + +html, body { + font-size: 14px; +} + +a { + color: #3ea211; + text-decoration: none; +} + +.mat-mdc-button, +.mat-mdc-button-base { + border-radius: 0 !important; +} + +.mat-mdc-outlined-button { + border-color: rgb(182, 182, 182) !important; +} \ No newline at end of file diff --git a/knox-token-generation-ui/token-generation/tsconfig.json b/knox-token-generation-ui/token-generation/tsconfig.json index 6f9eac1f6d..50820bd582 100644 --- a/knox-token-generation-ui/token-generation/tsconfig.json +++ b/knox-token-generation-ui/token-generation/tsconfig.json @@ -4,16 +4,11 @@ "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "lib": [ - "es2017", - "dom" - ], - "mapRoot": "./", - "module": "es6", - "moduleResolution": "node", + "module": "ES2022", + "moduleResolution": "bundler", "outDir": "../dist/out-tsc", "sourceMap": true, - "target": "es5", + "target": "ES2022", "typeRoots": [ "../node_modules/@types" ] diff --git a/knox-token-management-ui/.eslintrc.json b/knox-token-management-ui/.eslintrc.json deleted file mode 100644 index e143690256..0000000000 --- a/knox-token-management-ui/.eslintrc.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "root": true, - "overrides": [ - { - "files": [ - "*.ts" - ], - "parserOptions": { - "project": [ - "token-management/tsconfig.json" - ], - "createDefaultProgram": true - }, - "extends": [ - "plugin:@angular-eslint/recommended", - "plugin:@angular-eslint/template/process-inline-templates" - ], - "rules": { - "@typescript-eslint/naming-convention": [ - "error", - { - "selector": ["class"], - "format": ["PascalCase"] - } - ], - "spaced-comment": "error", - "curly": "error", - "eol-last": "error", - "guard-for-in": "error", - "no-unused-labels": "error", - "max-len": [ - "error", - { "code": 140 } - ], - "@typescript-eslint/explicit-member-accessibility": [ - "off", - {"accessibility": "no-public"} - ], - "@typescript-eslint/member-ordering": [ - "error", - { "default": ["static-field", "instance-field", "field", "method"] } - ], - "no-caller": "error", - "no-bitwise": "error", - "no-console": "off", - "no-new-wrappers": "error", - "no-debugger": "error", - "no-redeclare": "off", - "no-empty": "error", - "no-eval": "error", - "@typescript-eslint/no-inferrable-types": "error", - "no-shadow": "error", - "dot-notation": "off", - "no-fallthrough": "error", - "no-trailing-spaces": "error", - "no-unused-expressions": "error", - "no-var": "error", - "sort-keys": "off", - "brace-style": ["error","1tbs", { "allowSingleLine": true }], - "quotes": ["error", "single"], - "radix": "error", - "@typescript-eslint/semi": "error", - "eqeqeq": ["error", "always", {"null": "ignore"}], - "@typescript-eslint/type-annotation-spacing": "error", - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "app", - "style": "camelCase" - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "app", - "style": "kebab-case" - } - ], - "@angular-eslint/no-input-rename": "off", - "@angular-eslint/no-output-rename": "error", - "@angular-eslint/use-pipe-transform-interface": "error", - "@angular-eslint/component-class-suffix": "error", - "@angular-eslint/directive-class-suffix": "error" - } - }, - { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended" - ], - "rules": {} - } - ] -} diff --git a/knox-token-management-ui/angular.json b/knox-token-management-ui/angular.json index 761ec958a4..8589fc4c03 100644 --- a/knox-token-management-ui/angular.json +++ b/knox-token-management-ui/angular.json @@ -36,12 +36,14 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular/build:application", "options": { - "outputPath": "target/classes/token-management/app", + "outputPath": { + "base": "target/classes/token-management/app", + "browser": "" + }, "index": "token-management/index.html", - "main": "token-management/main.ts", - "polyfills": "token-management/polyfills.ts", + "polyfills": ["token-management/polyfills.ts"], "tsConfig": "token-management/tsconfig.json", "assets": [ "token-management/favicon.ico", @@ -49,16 +51,16 @@ ], "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", - "token-management/styles.css" + "token-management/styles.scss" ], "scripts": [ "node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.js" - ] + ], + "browser": "token-management/main.ts" }, "configurations": { "production": { - "buildOptimizer": false, "aot": false, "fileReplacements": [ { @@ -69,10 +71,8 @@ "outputHashing": "all" }, "development": { - "buildOptimizer": false, "aot": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, "sourceMap": true, "namedChunks": true @@ -81,21 +81,21 @@ "defaultConfiguration": "production" }, "serve": { - "builder": "@angular-devkit/build-angular:dev-server", + "builder": "@angular/build:dev-server", "configurations": { "production": { - "browserTarget": "token-management:build:production" + "buildTarget": "token-management:build:production" }, "development": { - "browserTarget": "token-management:build:development" + "buildTarget": "token-management:build:development" } }, "defaultConfiguration": "development" }, "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", + "builder": "@angular/build:extract-i18n", "options": { - "browserTarget": "token-management:build" + "buildTarget": "token-management:build" } }, "lint": { @@ -109,12 +109,5 @@ } } } - }, - "defaultProject": "token-management", - "cli": { - "schematicCollections": [ - "@angular-eslint/schematics", - "@angular-eslint/schematics" - ] } } diff --git a/knox-token-management-ui/eslint.config.js b/knox-token-management-ui/eslint.config.js new file mode 100644 index 0000000000..45ab1adc3f --- /dev/null +++ b/knox-token-management-ui/eslint.config.js @@ -0,0 +1,75 @@ +import js from "@eslint/js"; +import tseslint from "typescript-eslint"; +import angular from "@angular-eslint/eslint-plugin"; +import angularTemplate from "@angular-eslint/eslint-plugin-template"; +import tsParser from "@typescript-eslint/parser"; + +export default [ + { + ignores: ["**/dist/**", "**/node_modules/**"], + }, + + js.configs.recommended, + + { + files: ["**/*.ts"], + languageOptions: { + parser: tsParser, + parserOptions: { + project: ["./token-management/tsconfig.json"], + tsconfigRootDir: import.meta.dirname, + }, + globals: { + console: "readonly", + window: "readonly", + document: "readonly", + setTimeout: "readonly", + atob: "readonly" + }, + }, + plugins: { + "@typescript-eslint": tseslint.plugin, + "@angular-eslint": angular, + }, + rules: { + "@typescript-eslint/naming-convention": [ + "error", + {selector: "class", format: ["PascalCase"]}, + ], + + curly: "error", + eqeqeq: ["error", "always", {null: "ignore"}], + "guard-for-in": "error", + "max-len": ["error", {code: 140}], + "no-bitwise": "error", + "no-caller": "error", + "no-console": "off", + "no-debugger": "error", + "no-empty": "error", + "no-eval": "error", + "no-fallthrough": "error", + "no-trailing-spaces": "error", + "no-unused-expressions": "error", + "no-unused-labels": "error", + "no-var": "error", + quotes: ["error", "single"], + radix: "error", + semi: ["error", "always"], + "spaced-comment": "error", + "brace-style": ["error", "1tbs", {allowSingleLine: true}], + + "@angular-eslint/directive-selector": [ + "error", + {type: "attribute", prefix: "app", style: "camelCase"}, + ], + "@angular-eslint/component-selector": [ + "error", + {type: "element", prefix: "app", style: "kebab-case"}, + ], + "@angular-eslint/no-output-rename": "error", + "@angular-eslint/use-pipe-transform-interface": "error", + "@angular-eslint/component-class-suffix": "error", + "@angular-eslint/directive-class-suffix": "error", + }, + }, +]; \ No newline at end of file diff --git a/knox-token-management-ui/package.json b/knox-token-management-ui/package.json index b70d3d8937..c3c363404e 100644 --- a/knox-token-management-ui/package.json +++ b/knox-token-management-ui/package.json @@ -2,6 +2,7 @@ "name": "ng-knox-token-management", "version": "1.0.0", "license": "Apache-2.0", + "type": "module", "scripts": { "start": "ng serve --verbose=true", "build": "ng build", @@ -10,47 +11,38 @@ }, "private": true, "dependencies": { - "@angular/animations": "^13.0.1", - "@angular/cdk": "^13.0.1", - "@angular/common": "^13.0.1", - "@angular/compiler": "^13.0.1", - "@angular/core": "^13.0.1", - "@angular/forms": "^13.0.1", - "@angular/material": "^13.0.1", - "@angular/platform-browser": "^13.0.1", - "@angular/platform-browser-dynamic": "^13.0.1", - "@angular/router": "^13.0.1", - "bootstrap": "^3.4.1", - "core-js": "^2.6.11", - "jquery": "^3.5.1", - "js-yaml": "^3.13.1", - "ng2-bs3-modal": "^0.15.0", - "popper.js": "^1.16.1", - "rxjs": "^6.6.7", - "rxjs-compat": "^6.6.7", - "sweetalert2": "^11.6.5", - "ts-helpers": "^1.1.1", - "vkbeautify": "^0.99.3", - "zone.js": "~0.11.4" + "@angular/animations": "^21.0.5", + "@angular/cdk": "^21.0.3", + "@angular/common": "^21.0.5", + "@angular/compiler": "^21.0.5", + "@angular/core": "^21.0.5", + "@angular/forms": "^21.0.5", + "@angular/material": "^21.0.3", + "@angular/platform-browser": "^21.0.5", + "@angular/platform-browser-dynamic": "^21.0.5", + "@angular/router": "^21.0.5", + "bootstrap": "^5.3.8", + "core-js": "^3.47.0", + "jquery": "^3.7.1", + "sweetalert2": "^11.26.3", + "zone.js": "~0.15.1" }, "devDependencies": { - "@angular-devkit/build-angular": "^13.3.11", - "@angular-eslint/builder": "14.1.2", - "@angular-eslint/eslint-plugin": "14.1.2", - "@angular-eslint/eslint-plugin-template": "14.1.2", - "@angular-eslint/schematics": "14.1.2", - "@angular-eslint/template-parser": "14.1.2", - "@angular/cli": "^14.2.9", - "@angular/compiler-cli": "^13.0.1", - "@angular/language-service": "^5.2.0", + "@angular-devkit/build-angular": "^21.0.3", + "@angular-eslint/builder": "21.1.0", + "@angular-eslint/eslint-plugin": "21.1.0", + "@angular-eslint/eslint-plugin-template": "21.1.0", + "@angular-eslint/schematics": "21.1.0", + "@angular-eslint/template-parser": "21.1.0", + "@angular/cli": "^21.0.3", + "@angular/compiler-cli": "^21.0.5", + "@angular/language-service": "^21.0.5", "@types/jasmine": "~2.5.53", "@types/jasminewd2": "^2.0.8", - "@types/node": "16.18.11", - "@typescript-eslint/eslint-plugin": "5.37.0", - "@typescript-eslint/parser": "5.37.0", - "eslint": "^8.23.1", - "ts-node": "~3.2.0", - "typescript": "~4.4.4", - "webdriver-manager": "10.2.5" + "@types/node": "^24.5.2", + "eslint": "^9.39.0", + "ts-node": "~10.9.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.2" } } diff --git a/knox-token-management-ui/pom.xml b/knox-token-management-ui/pom.xml index b6a6cfe697..a89a724100 100644 --- a/knox-token-management-ui/pom.xml +++ b/knox-token-management-ui/pom.xml @@ -37,7 +37,11 @@ - node_modules + ${project.basedir} + + package-lock.json + node_modules/** + @@ -65,7 +69,7 @@ npm - install --legacy-peer-deps + install diff --git a/knox-token-management-ui/token-management/app/app.module.ts b/knox-token-management-ui/token-management/app/app.module.ts index f64a49437e..cf3acd2d68 100644 --- a/knox-token-management-ui/token-management/app/app.module.ts +++ b/knox-token-management-ui/token-management/app/app.module.ts @@ -14,49 +14,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {NgModule} from '@angular/core'; -import {BrowserModule} from '@angular/platform-browser'; -import {HttpClientModule, HttpClientXsrfModule} from '@angular/common/http'; -import {MatGridListModule} from '@angular/material/grid-list'; -import {BsModalModule} from 'ng2-bs3-modal'; -import {MatTableModule, MatTableDataSource} from '@angular/material/table'; -import {MatSortModule} from '@angular/material/sort'; -import {MatPaginatorModule} from '@angular/material/paginator'; -import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import {MatInputModule} from '@angular/material/input'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; -import {MatCheckboxModule} from '@angular/material/checkbox'; -import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {FormsModule, ReactiveFormsModule} from '@angular/forms'; - - -import {TokenManagementComponent} from './token.management.component'; -import {TokenManagementService} from './token.management.service'; -import {SessionInformationComponent} from './session.information.component'; -import {SafeHtmlPipe} from './session.information.component'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; +import { TokenManagementService } from './service/token.management.service'; @NgModule({ - imports: [BrowserModule, - BrowserAnimationsModule, - HttpClientModule, - HttpClientXsrfModule, - MatGridListModule, - BsModalModule, - FormsModule, - ReactiveFormsModule - ], - exports: [MatTableModule, - MatTableDataSource, - MatSortModule, - MatPaginatorModule, - MatProgressSpinnerModule, - MatInputModule, - MatSlideToggleModule, - MatCheckboxModule - ], - declarations: [TokenManagementComponent, SessionInformationComponent, SafeHtmlPipe], - providers: [TokenManagementService], - bootstrap: [TokenManagementComponent, SessionInformationComponent] + imports: [BrowserModule, ReactiveFormsModule], + providers: [TokenManagementService] }) export class AppModule { } + diff --git a/knox-token-management-ui/token-management/app/knox.token.ts b/knox-token-management-ui/token-management/app/model/knox.token.ts similarity index 100% rename from knox-token-management-ui/token-management/app/knox.token.ts rename to knox-token-management-ui/token-management/app/model/knox.token.ts diff --git a/knox-token-management-ui/token-management/app/metadata.ts b/knox-token-management-ui/token-management/app/model/metadata.ts similarity index 100% rename from knox-token-management-ui/token-management/app/metadata.ts rename to knox-token-management-ui/token-management/app/model/metadata.ts diff --git a/knox-token-management-ui/token-management/app/session.information.ts b/knox-token-management-ui/token-management/app/model/session.information.ts similarity index 100% rename from knox-token-management-ui/token-management/app/session.information.ts rename to knox-token-management-ui/token-management/app/model/session.information.ts diff --git a/knox-token-management-ui/token-management/app/token.management.service.ts b/knox-token-management-ui/token-management/app/service/token.management.service.ts similarity index 88% rename from knox-token-management-ui/token-management/app/token.management.service.ts rename to knox-token-management-ui/token-management/app/service/token.management.service.ts index 0cb52705a1..30759bac3b 100644 --- a/knox-token-management-ui/token-management/app/token.management.service.ts +++ b/knox-token-management-ui/token-management/app/service/token.management.service.ts @@ -15,13 +15,11 @@ * limitations under the License. */ import {Injectable} from '@angular/core'; -import {HttpClient, HttpErrorResponse, HttpHeaders} from '@angular/common/http'; -import Swal from 'sweetalert2'; - -import 'rxjs/add/operator/toPromise'; - -import {KnoxToken} from './knox.token'; -import {SessionInformation} from './session.information'; +import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; +import Swal from 'sweetalert2/dist/sweetalert2.esm.all.js'; +import { firstValueFrom } from 'rxjs'; +import { KnoxToken } from '../model/knox.token'; +import { SessionInformation } from '../model/session.information'; @Injectable() export class TokenManagementService { @@ -46,8 +44,7 @@ export class TokenManagementService { let headers = new HttpHeaders(); headers = this.addJsonHeaders(headers); let url = canSeeAllTokens ? this.getAllKnoxTokensUrl : (this.getKnoxTokensUrl + userName); - return this.http.get(url, { headers: headers}) - .toPromise() + return firstValueFrom(this.http.get(url, { headers: headers})) .then(response => response['tokens'] as KnoxToken[]) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> getKnoxTokens() --> ' + this.getKnoxTokensUrl + '\n error: ' + err.message); @@ -63,8 +60,7 @@ export class TokenManagementService { let xheaders = new HttpHeaders(); xheaders = this.addJsonHeaders(xheaders); let urlToUse = enable ? this.enableKnoxTokenUrl : this.disableKnoxTokenUrl; - return this.http.put(urlToUse, tokenId, {headers: xheaders, responseType: 'text'}) - .toPromise() + return firstValueFrom(this.http.put(urlToUse, tokenId, {headers: xheaders, responseType: 'text'})) .then(response => response) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> setEnabledDisabledFlag() --> ' + urlToUse @@ -81,8 +77,7 @@ export class TokenManagementService { let xheaders = new HttpHeaders(); xheaders = this.addJsonHeaders(xheaders); let urlToUse = enable ? this.enableKnoxTokensBatchUrl : this.disableKnoxTokensBatchUrl; - return this.http.put(urlToUse, JSON.stringify(tokenIds), {headers: xheaders, responseType: 'text'}) - .toPromise() + return firstValueFrom(this.http.put(urlToUse, JSON.stringify(tokenIds), {headers: xheaders, responseType: 'text'})) .then(response => response) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> setEnabledDisabledFlagsInBatch() --> ' + urlToUse @@ -115,9 +110,8 @@ export class TokenManagementService { revokeTokensInBatch(tokenIds: string[]) { let xheaders = new HttpHeaders(); xheaders = this.addJsonHeaders(xheaders); - return this.http.request('DELETE', this.revokeKnoxTokensBatchUrl, - {headers: xheaders, body: JSON.stringify(tokenIds), responseType: 'text'}) - .toPromise() + return firstValueFrom(this.http.request('DELETE', this.revokeKnoxTokensBatchUrl, + {headers: xheaders, body: JSON.stringify(tokenIds), responseType: 'text'})) .then(response => response) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> revokeTokensInBatch() --> ' + this.revokeKnoxTokensBatchUrl @@ -133,8 +127,7 @@ export class TokenManagementService { isTokenHashKeyPresent(): Promise { let headers = new HttpHeaders(); headers = this.addJsonHeaders(headers); - return this.http.get(this.metadataInfoUrl, { headers: headers}) - .toPromise() + return firstValueFrom(this.http.get(this.metadataInfoUrl, { headers: headers})) .then(response => { return response['generalProxyInfo']?.['enableTokenManagement'] === 'true'; }) @@ -152,8 +145,7 @@ export class TokenManagementService { getSessionInformation(): Promise { let headers = new HttpHeaders(); headers = this.addJsonHeaders(headers); - return this.http.get(this.sessionUrl, { headers: headers}) - .toPromise() + return firstValueFrom(this.http.get(this.sessionUrl, { headers: headers})) .then(response => response['sessioninfo'] as SessionInformation) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> getSessionInformation() --> ' + this.sessionUrl + '\n error: ' + err.message); @@ -168,8 +160,7 @@ export class TokenManagementService { getImpersonationEnabled(): Promise { let headers = new HttpHeaders(); headers = this.addJsonHeaders(headers); - return this.http.get(this.getTssStatusUrl, { headers: headers}) - .toPromise() + return firstValueFrom(this.http.get(this.getTssStatusUrl, { headers: headers})) .then(response => response['impersonationEnabled'] as string) .catch((err: HttpErrorResponse) => { console.debug('TokenManagementService --> getImpersonationEnabled() --> ' + this.getTssStatusUrl diff --git a/knox-token-management-ui/token-management/app/session.information.component.html b/knox-token-management-ui/token-management/app/sessioninformation/session.information.component.html similarity index 100% rename from knox-token-management-ui/token-management/app/session.information.component.html rename to knox-token-management-ui/token-management/app/sessioninformation/session.information.component.html diff --git a/knox-token-management-ui/token-management/app/session.information.component.ts b/knox-token-management-ui/token-management/app/sessioninformation/session.information.component.ts similarity index 73% rename from knox-token-management-ui/token-management/app/session.information.component.ts rename to knox-token-management-ui/token-management/app/sessioninformation/session.information.component.ts index 3475d2202b..964bbe5ce8 100644 --- a/knox-token-management-ui/token-management/app/session.information.component.ts +++ b/knox-token-management-ui/token-management/app/sessioninformation/session.information.component.ts @@ -14,24 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {Component, OnInit, Pipe, PipeTransform} from '@angular/core'; -import {DomSanitizer} from '@angular/platform-browser'; -import {TokenManagementService} from './token.management.service'; -import {SessionInformation} from './session.information'; - -@Pipe({ name: 'safeHtml' }) -export class SafeHtmlPipe implements PipeTransform { - constructor(private sanitizer: DomSanitizer) {} - - transform(value) { - return this.sanitizer.bypassSecurityTrustHtml(value); - } -} +import { Component, OnInit } from '@angular/core'; +import { TokenManagementService } from '../service/token.management.service'; +import { SessionInformation } from '../model/session.information'; +import { SafeHtmlPipe } from '../util/safehtml'; @Component({ selector: 'app-session-information', templateUrl: './session.information.component.html', - providers: [TokenManagementService] + providers: [TokenManagementService], + imports: [ SafeHtmlPipe ] }) export class SessionInformationComponent implements OnInit { @@ -45,10 +37,10 @@ export class SessionInformationComponent implements OnInit { getUser() { if (this.sessionInformation) { - return this.sessionInformation.user; + return this.sessionInformation.user; } else { - console.debug('SessionInformationComponent --> getUser() --> dr.who'); - return 'dr.who'; + console.debug('SessionInformationComponent --> getUser() --> dr.who'); + return 'dr.who'; } } @@ -67,7 +59,7 @@ export class SessionInformationComponent implements OnInit { } private setSessionInformation(sessionInformation: SessionInformation) { - this.sessionInformation = sessionInformation; + this.sessionInformation = sessionInformation; console.debug('SessionInformationComponent --> setSessionInformation() --> ' + this.sessionInformation.user); } diff --git a/knox-token-management-ui/token-management/app/token.management.component.html b/knox-token-management-ui/token-management/app/token.management.component.html deleted file mode 100644 index 6260b964c3..0000000000 --- a/knox-token-management-ui/token-management/app/token.management.component.html +++ /dev/null @@ -1,151 +0,0 @@ - -
- -
- - - - - Show Disabled KnoxSSO Cookies - - -
- -
- - - Show My Tokens Only - - -
- -
- - Search by Token ID, (Impersonated) User Name, Comment or Metadata... - - - - - - - - - - - - - - - - - Token ID - -
{{knoxToken.tokenId}}
-
{{knoxToken.tokenId}}
-
-
- - - Issued - {{formatDateTime(knoxToken.issueTimeLong)}} - - - - Expires - {{formatDateTime(knoxToken.expirationLong)}} - - - - User Name - {{knoxToken.metadata.userName}} - - - - Impersonated - -
- - -

{{knoxToken.metadata.createdBy}}

-
-
-
- - - Type - - - - - - - Comment - {{knoxToken.metadata.comment}} - - - - Additional Metadata - -
    -
  • - {{metadata[0]}} = {{metadata[1]}} -
  • -
-
-
- - - Actions - - - - -

Previously Disabled SSO Cookie!

-
-
- - - - -
- -
- -
- - - -
- -
- Expired tokens cannot be disabled in batches (nor individually). -
-
- Expired tokens cannot be enabed in batches (nor individually). -
-
- KnoxSSO Cookies cannot be revoked in batches (nor individually). -
- -
- diff --git a/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.css b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.css new file mode 100644 index 0000000000..d84fc7dd70 --- /dev/null +++ b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.css @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.example-container { + display: flex; + flex-direction: column; + min-width: 300px; +} + +.example-header { + min-height: 64px; + padding: 8px 24px 0; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} + +.mat-table { + overflow: auto; + max-height: 500px; +} + +.select-column { + width: 70px; + max-width: 70px; + min-width: 70px; +} + +.reduce-icon-size { + transform: scale(0.65); +} + +::ng-deep .mat-mdc-paginator { + padding: 8px 12px; + min-height: 48px; + font-size: 13px; +} + +::ng-deep .mat-mdc-paginator .mat-mdc-icon-button { + width: 36px; + height: 36px; +} + +::ng-deep .mat-mdc-paginator .mat-mdc-select { + margin: 0 8px; + min-width: 56px; +} + +::ng-deep div.mat-mdc-select-panel { + background-color: white !important; +} + +::ng-deep .mat-mdc-paginator .mat-mdc-select .mat-mdc-select-value { + color: #333; + font-size: 13px; +} + +::ng-deep .mat-mdc-paginator .mat-mdc-select .mat-mdc-select-arrow { + color: #666; +} + +::ng-deep .mat-mdc-paginator .mat-mdc-select-trigger { + padding: 4px 8px; + border-radius: 4px; + border: 1px solid #ddd; +} + +::ng-deep .mdc-notched-outline > * { + border: none !important; +} \ No newline at end of file diff --git a/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.html b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.html new file mode 100644 index 0000000000..1b85beb82a --- /dev/null +++ b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.html @@ -0,0 +1,215 @@ + +
+ +
+ + + + + Show Disabled KnoxSSO Cookies + + +
+ + @if (userCanSeeAllTokens()) { +
+ + + Show My Tokens Only + + +
+ } + +
+ + Search by Token ID, (Impersonated) User Name, Comment or Metadata... + + + + + + + + + + + @if (!isDisabledKnoxSSoCookie(knoxToken)) { + + + } + + + + + Token ID + + @if (knoxToken.metadata.enabled) { +
{{knoxToken.tokenId}}
+ } + @if (!knoxToken.metadata.enabled) { +
{{knoxToken.tokenId}}
+ } +
+
+ + + Issued + {{formatDateTime(knoxToken.issueTimeLong)}} + + + + Expires + {{formatDateTime(knoxToken.expirationLong)}} + + + + User Name + {{knoxToken.metadata.userName}} + + + + Impersonated + +
+ @if (!knoxToken.metadata.createdBy) { + + } + @if (knoxToken.metadata.createdBy) { + + } + @if (knoxToken.metadata.createdBy) { +

{{knoxToken.metadata.createdBy}}

+ } +
+
+
+ + + Type + + + + + + + Comment + {{knoxToken.metadata.comment}} + + + + Additional + Metadata + +
    + @for (metadata of getCustomMetadataArray(knoxToken); track metadata) { +
  • + {{metadata[0]}} = {{metadata[1]}} +
  • + } +
+
+
+ + + Actions + + @if (knoxToken.metadata.enabled && !isTokenExpired(knoxToken.expirationLong)) { + + } + @if (!isKnoxSsoCookie(knoxToken) && !knoxToken.metadata.enabled && !isTokenExpired(knoxToken.expirationLong)) + { + + } + @if (!isKnoxSsoCookie(knoxToken)) { + + } + @if (isDisabledKnoxSsoCookie(knoxToken)) { +

Previously Disabled SSO Cookie! +

+ } +
+
+ + + + +
+ +
+ +
+ @if (showDisableSelectedTokensButton) { + + } + @if (showEnableSelectedTokensButton) { + + } + @if (showRevokeSelectedTokensButton) { + + } +
+ + @if (!selection.isEmpty() && !showDisableSelectedTokensButton) { +
+ Expired tokens cannot be disabled in batches + (nor individually). +
+ } + @if (!selection.isEmpty() && !showEnableSelectedTokensButton) { +
+ Expired tokens cannot be enabed in batches + (nor individually). +
+ } + @if (!selection.isEmpty() && !showRevokeSelectedTokensButton) { +
+ KnoxSSO Cookies cannot be revoked in batches + (nor individually). +
+ } + +
\ No newline at end of file diff --git a/knox-token-management-ui/token-management/app/token.management.component.ts b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.ts similarity index 62% rename from knox-token-management-ui/token-management/app/token.management.component.ts rename to knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.ts index 74777b7a4f..b1f4012fd9 100644 --- a/knox-token-management-ui/token-management/app/token.management.component.ts +++ b/knox-token-management-ui/token-management/app/tokenmanagement/token.management.component.ts @@ -14,21 +14,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {Component, OnInit, ViewChild} from '@angular/core'; -import {TokenManagementService} from './token.management.service'; -import {KnoxToken} from './knox.token'; -import {MatTableDataSource} from '@angular/material/table'; -import {MatPaginator} from '@angular/material/paginator'; -import {MatSort} from '@angular/material/sort'; -import {MatSlideToggleChange} from '@angular/material/slide-toggle'; -import {SelectionModel} from '@angular/cdk/collections'; -import Swal from 'sweetalert2'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { TokenManagementService } from '../service/token.management.service'; +import { KnoxToken } from '../model/knox.token'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatSlideToggleChange } from '@angular/material/slide-toggle'; +import { SelectionModel } from '@angular/cdk/collections'; +import Swal from 'sweetalert2/dist/sweetalert2.esm.all.js'; +import { MatSlideToggleModule } from '@angular/material/slide-toggle'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSortModule } from '@angular/material/sort'; +import { MatPaginatorModule } from '@angular/material/paginator'; + +import { FormsModule } from '@angular/forms'; +import { MatTableModule } from '@angular/material/table'; +import { MatInputModule } from '@angular/material/input'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { MatButtonModule } from '@angular/material/button'; @Component({ selector: 'app-token-management', templateUrl: './token.management.component.html', - styleUrls: ['../assets/token-management-ui.css'], - providers: [TokenManagementService] + styleUrls: ['./token.management.component.css'], + providers: [TokenManagementService], + imports: [MatSlideToggleModule, MatFormFieldModule, MatSortModule, MatPaginatorModule, FormsModule, MatTableModule, MatInputModule, + MatCheckboxModule, MatIconModule, MatButtonModule], + standalone: true }) export class TokenManagementComponent implements OnInit { @@ -59,37 +73,38 @@ export class TokenManagementComponent implements OnInit { constructor(private tokenManagementService: TokenManagementService) { this.showDisabledKnoxSsoCookies = true; let isMatch: (record: KnoxToken, filter: String) => boolean = (record, filter) => { - let normalizedFilter = filter.trim().toLocaleLowerCase(); - let matchesTokenId = record.tokenId.toLocaleLowerCase().includes(normalizedFilter); - let matchesComment = record.metadata.comment && record.metadata.comment.toLocaleLowerCase().includes(normalizedFilter); - let matchesUserName = record.metadata.userName.toLocaleLowerCase().includes(normalizedFilter); - let matchesCreatedBy = record.metadata.createdBy && record.metadata.createdBy.toLocaleLowerCase().includes(normalizedFilter); - let matchesCustomMetadata = false; - if (record.metadata.customMetadataMap) { - for (let entry of Array.from(Object.entries(record.metadata.customMetadataMap))) { - if (entry[0].toLocaleLowerCase().includes(normalizedFilter) || entry[1].toLocaleLowerCase().includes(normalizedFilter)) { - matchesCustomMetadata = true; - break; - } + let normalizedFilter = filter.trim().toLocaleLowerCase(); + let matchesTokenId = record.tokenId.toLocaleLowerCase().includes(normalizedFilter); + let matchesComment = record.metadata.comment && record.metadata.comment.toLocaleLowerCase().includes(normalizedFilter); + let matchesUserName = record.metadata.userName.toLocaleLowerCase().includes(normalizedFilter); + let matchesCreatedBy = record.metadata.createdBy && record.metadata.createdBy.toLocaleLowerCase().includes(normalizedFilter); + let matchesCustomMetadata = false; + if (record.metadata.customMetadataMap) { + for (let entry of Array.from(Object.entries(record.metadata.customMetadataMap))) { + if (entry[0].toLocaleLowerCase().includes(normalizedFilter) + || entry[1].toLocaleLowerCase().includes(normalizedFilter)) { + matchesCustomMetadata = true; + break; + } + } + } else { + matchesCustomMetadata = true; // nothing to match } - } else { - matchesCustomMetadata = true; // nothing to match - } - return matchesTokenId || matchesComment || matchesCustomMetadata || matchesUserName || matchesCreatedBy; + return matchesTokenId || matchesComment || matchesCustomMetadata || matchesUserName || matchesCreatedBy; }; this.knoxTokens.filterPredicate = function (record, filter) { - return isMatch(record, filter); + return isMatch(record, filter); }; this.knoxTokens.sortingDataAccessor = (item, property) => { - switch(property) { - case 'metadata.comment': return item.metadata.comment; - case 'metadata.username': return item.metadata.userName; - case 'metadata.createdBy': return item.metadata.createdBy; - default: return item[property]; - } + switch (property) { + case 'metadata.comment': return item.metadata.comment; + case 'metadata.username': return item.metadata.userName; + case 'metadata.createdBy': return item.metadata.createdBy; + default: return item[property]; + } }; } @@ -111,18 +126,18 @@ export class TokenManagementComponent implements OnInit { if (!tokenHashKeyPresent) { this.showMissingKnoxTokenHashKeyPopup(); } + + this.tokenManagementService.getSessionInformation() + .then(sessionInformation => { + this.canSeeAllTokens = sessionInformation.canSeeAllTokens; + this.currentKnoxSsoCookieTokenId = sessionInformation.currentKnoxSsoCookieTokenId; + this.setUserName(sessionInformation.user); + }); } ); - - this.tokenManagementService.getSessionInformation() - .then(sessionInformation => { - this.canSeeAllTokens = sessionInformation.canSeeAllTokens; - this.currentKnoxSsoCookieTokenId = sessionInformation.currentKnoxSsoCookieTokenId; - this.setUserName(sessionInformation.user); - }); } - isTokenHashKeyPresent(): boolean{ + isTokenHashKeyPresent(): boolean { return this.tokenHashKeyPresent; } @@ -145,7 +160,7 @@ export class TokenManagementComponent implements OnInit { } private isMyToken(token: KnoxToken): boolean { - return token.metadata.userName === this.userName || (token.metadata.createdBy && token.metadata.createdBy === this.userName); + return token.metadata.userName === this.userName || (token.metadata.createdBy && token.metadata.createdBy === this.userName); } private isDisabledKnoxSsoCookie(token: KnoxToken): boolean { @@ -160,31 +175,34 @@ export class TokenManagementComponent implements OnInit { } private actualizeTokensToDisplay(): void { - let tokensToDisplay = this.allKnoxTokens; + if (!this.allKnoxTokens) { + return; + } + let tokensToDisplay = this.allKnoxTokens; if (!this.showDisabledKnoxSsoCookies) { tokensToDisplay = tokensToDisplay.filter(token => !this.isDisabledKnoxSsoCookie(token)); } if (this.showMyTokensOnly) { - tokensToDisplay = tokensToDisplay.filter(token => this.isMyToken(token)); - } + tokensToDisplay = tokensToDisplay.filter(token => this.isMyToken(token)); + } - this.knoxTokens.data = tokensToDisplay; + this.knoxTokens.data = tokensToDisplay; - setTimeout(() => { + setTimeout(() => { this.knoxTokens.paginator = this.paginator; this.knoxTokens.sort = this.sort; - }); + }); } disableToken(tokenId: string) { - this.tokenManagementService.setEnabledDisabledFlag(false, tokenId).then((response: string) => this.fetchKnoxTokens()); + this.tokenManagementService.setEnabledDisabledFlag(false, tokenId).then(() => this.fetchKnoxTokens()); } disableSelectedTokens(): void { this.tokenManagementService.setEnabledDisabledFlagsInBatch(false, this.getSelectedTokenIds()) - .then((response: string) => this.fetchKnoxTokens()); + .then(() => this.fetchKnoxTokens()); } private getSelectedTokenIds(): string[] { @@ -194,20 +212,20 @@ export class TokenManagementComponent implements OnInit { } enableToken(tokenId: string) { - this.tokenManagementService.setEnabledDisabledFlag(true, tokenId).then((response: string) => this.fetchKnoxTokens()); + this.tokenManagementService.setEnabledDisabledFlag(true, tokenId).then(() => this.fetchKnoxTokens()); } enableSelectedTokens(): void { this.tokenManagementService.setEnabledDisabledFlagsInBatch(true, this.getSelectedTokenIds()) - .then((response: string) => this.fetchKnoxTokens()); + .then(() => this.fetchKnoxTokens()); } revokeToken(tokenId: string) { - this.tokenManagementService.revokeToken(tokenId).then((response: string) => this.fetchKnoxTokens()); + this.tokenManagementService.revokeToken(tokenId).then(() => this.fetchKnoxTokens()); } revokeSelectedTokens() { - this.tokenManagementService.revokeTokensInBatch(this.getSelectedTokenIds()).then((response: string) => this.fetchKnoxTokens()); + this.tokenManagementService.revokeTokensInBatch(this.getSelectedTokenIds()).then(() => this.fetchKnoxTokens()); } gotoTokenGenerationPage() { @@ -227,20 +245,20 @@ export class TokenManagementComponent implements OnInit { } getCustomMetadataArray(knoxToken: KnoxToken): [string, string][] { - let mdMap = new Map(); - if (knoxToken.metadata.customMetadataMap) { - mdMap = new Map(Object.entries(knoxToken.metadata.customMetadataMap)); - } + let mdMap = new Map(); + if (knoxToken.metadata.customMetadataMap) { + mdMap = new Map(Object.entries(knoxToken.metadata.customMetadataMap)); + } - return Array.from(mdMap); + return Array.from(mdMap); } isKnoxSsoCookie(knoxToken: KnoxToken): boolean { - return 'KNOXSSO_COOKIE' === knoxToken.metadata.type; + return 'KNOXSSO_COOKIE' === knoxToken.metadata.type; } isDisabledKnoxSSoCookie(knoxToken: KnoxToken): boolean { - return this.isKnoxSsoCookie(knoxToken) && !knoxToken.metadata.enabled; + return this.isKnoxSsoCookie(knoxToken) && !knoxToken.metadata.enabled; } applyFilter(filterValue: string) { @@ -251,9 +269,9 @@ export class TokenManagementComponent implements OnInit { /** Whether the number of selected elements matches the total number of rows. */ isAllSelected(): boolean { - const numSelected = this.selection.selected.length; - const numRows = this.knoxTokens.filteredData.length; - return numSelected === numRows; + const numSelected = this.selection.selected.length; + const numRows = this.knoxTokens.filteredData.length; + return numSelected === numRows; } /** Selects all rows if they are not all selected; otherwise clear selection. */ @@ -262,7 +280,7 @@ export class TokenManagementComponent implements OnInit { this.selection.clear(); } else { this.knoxTokens.filteredData.forEach(row => { - if (!this.isDisabledKnoxSsoCookie(row)) { + if (!this.isDisabledKnoxSsoCookie(row)) { this.selection.select(row); } }); @@ -276,15 +294,15 @@ export class TokenManagementComponent implements OnInit { } showHideBatchOperations() { - if (this.selection.isEmpty()) { - this.showDisableSelectedTokensButton = false; + if (this.selection.isEmpty()) { + this.showDisableSelectedTokensButton = false; this.showEnableSelectedTokensButton = false; this.showRevokeSelectedTokensButton = false; - } else { + } else { this.showDisableSelectedTokensButton = this.selectionHasZeroExpiredToken(); // expired tokens must not be disabled this.showEnableSelectedTokensButton = this.selectionHasZeroExpiredToken(); // expired tokens must not be enabled this.showRevokeSelectedTokensButton = this.selectionHasZeroKnoxSsoCookie(); // KnoxSSO cookies must not be revoked - } + } } private selectionHasZeroKnoxSsoCookie(): boolean { diff --git a/knox-token-management-ui/token-management/app/util/safehtml.ts b/knox-token-management-ui/token-management/app/util/safehtml.ts new file mode 100644 index 0000000000..cdf6aef477 --- /dev/null +++ b/knox-token-management-ui/token-management/app/util/safehtml.ts @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Pipe, PipeTransform} from '@angular/core'; +import {DomSanitizer} from '@angular/platform-browser'; + +@Pipe({ name: 'safeHtml' }) +export class SafeHtmlPipe implements PipeTransform { + constructor(private sanitizer: DomSanitizer) {} + + transform(value) { + return this.sanitizer.bypassSecurityTrustHtml(value); + } +} diff --git a/knox-token-management-ui/token-management/assets/sticky-footer.css b/knox-token-management-ui/token-management/assets/sticky-footer.css deleted file mode 100644 index 59b129bd78..0000000000 --- a/knox-token-management-ui/token-management/assets/sticky-footer.css +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Sticky footer styles --------------------------------------------------- */ -html { - position: relative; - min-height: 100%; -} - -body { - /* Margin bottom by footer height */ - margin-bottom: 60px; -} - -.footer { - position: absolute; - bottom: 0; - width: 100%; - /* Set the fixed height of the footer here */ - height: 60px; - background-color: #f5f5f5; -} - -.jumbotron { - padding: 0.5em 0.6em; -} diff --git a/knox-token-management-ui/token-management/index.html b/knox-token-management-ui/token-management/index.html index f347e1afc0..c088ffafa2 100644 --- a/knox-token-management-ui/token-management/index.html +++ b/knox-token-management-ui/token-management/index.html @@ -19,38 +19,36 @@ Apache Knox Token Management - - - - + + - - + + \ No newline at end of file diff --git a/knox-token-management-ui/token-management/main.ts b/knox-token-management-ui/token-management/main.ts index be5f383523..c1e55feb8d 100644 --- a/knox-token-management-ui/token-management/main.ts +++ b/knox-token-management-ui/token-management/main.ts @@ -16,13 +16,34 @@ */ import './polyfills.ts'; -import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; -import {enableProdMode} from '@angular/core'; -import {environment} from './environments/environment'; -import {AppModule} from './app/app.module'; +import { environment } from './environments/environment'; +import { SessionInformationComponent } from './app/sessioninformation/session.information.component.js'; +import { TokenManagementComponent } from './app/tokenmanagement/token.management.component.js'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { enableProdMode, importProvidersFrom, provideZoneChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { APP_BASE_HREF } from '@angular/common'; +import { provideHttpClient } from '@angular/common/http'; if (environment.production) { - enableProdMode(); + enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +const bootstrapComponents = [ + SessionInformationComponent, + TokenManagementComponent +]; + +bootstrapComponents.forEach(component => { + bootstrapApplication(component, { + providers: [ + provideZoneChangeDetection(),importProvidersFrom(), + provideHttpClient(), + provideRouter([]), + { + provide: APP_BASE_HREF, + useValue: window['base-href'] || '/' + } + ] + }).catch(err => console.error(err)); +}); \ No newline at end of file diff --git a/knox-token-management-ui/token-management/polyfills.ts b/knox-token-management-ui/token-management/polyfills.ts index 78cba47477..aebd58c4ed 100644 --- a/knox-token-management-ui/token-management/polyfills.ts +++ b/knox-token-management-ui/token-management/polyfills.ts @@ -14,22 +14,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// This file includes polyfills needed by Angular 2 and is loaded before -// the app. You can add your own extra polyfills to this file. -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/set'; -import 'core-js/es6/reflect'; - -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; +import 'zone.js'; diff --git a/knox-token-generation-ui/token-generation/styles.css b/knox-token-management-ui/token-management/styles.scss similarity index 62% rename from knox-token-generation-ui/token-generation/styles.css rename to knox-token-management-ui/token-management/styles.scss index 813bed9162..cf55f03c90 100644 --- a/knox-token-generation-ui/token-generation/styles.css +++ b/knox-token-management-ui/token-management/styles.scss @@ -16,7 +16,15 @@ * limitations under the License. */ -/* You can add global styles to this file, and also import other style files */ +@use '@angular/material' as mat; + +:root { + @include mat.theme(( + color: ( + primary: mat.$green-palette + ) + )); +} .navbar-static-top { min-height: 110px; @@ -24,4 +32,30 @@ .clickable { cursor: pointer; +} + +html, body { + font-size: 14px; +} + +.mat-mdc-table { + background-color: white !important; +} + +.mat-mdc-paginator { + background-color: white !important; +} + +.mat-mdc-button, +.mat-mdc-button-base { + border-radius: 0 !important; +} + +.mat-mdc-outlined-button { + border-color: rgb(182, 182, 182) !important; +} + +.mat-mdc-outlined-button>.mat-icon { + margin-right: 0px !important; + margin-left: 0px !important; } \ No newline at end of file diff --git a/knox-token-management-ui/token-management/tsconfig.json b/knox-token-management-ui/token-management/tsconfig.json index 5ceb8f1978..543de1c19d 100644 --- a/knox-token-management-ui/token-management/tsconfig.json +++ b/knox-token-management-ui/token-management/tsconfig.json @@ -4,16 +4,11 @@ "declaration": false, "emitDecoratorMetadata": true, "experimentalDecorators": true, - "lib": [ - "es2017", - "dom" - ], - "mapRoot": "./", - "module": "es6", - "moduleResolution": "node", + "module": "ES2022", + "moduleResolution": "bundler", "outDir": "../dist/out-tsc", "sourceMap": true, - "target": "es5", + "target": "ES2022", "typeRoots": [ "../node_modules/@types" ]