-
-
-
-
-
-
- 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 @@