Skip to content

Commit 35a3e6d

Browse files
committed
mgr/dashboard: securely store remote cluster token
Instead of using the localStorage use cookies for storing the token more securely Fixes: https://tracker.ceph.com/issues/64958 Signed-off-by: Nizamudeen A <[email protected]>
1 parent 758a0bc commit 35a3e6d

File tree

7 files changed

+76
-8
lines changed

7 files changed

+76
-8
lines changed

src/pybind/mgr/dashboard/frontend/package-lock.json

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/pybind/mgr/dashboard/frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"ng-block-ui": "3.0.2",
7373
"ng-click-outside": "7.0.0",
7474
"ng2-charts": "4.1.1",
75+
"ngx-cookie-service": "17.1.0",
7576
"ngx-pipe-function": "1.0.0",
7677
"ngx-toastr": "17.0.2",
7778
"rxjs": "6.6.3",

src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/multi-cluster/multi-cluster-list/multi-cluster-list.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
1818
import { MultiCluster } from '~/app/shared/models/multi-cluster';
1919
import { SummaryService } from '~/app/shared/services/summary.service';
2020
import { Router } from '@angular/router';
21+
import { CookiesService } from '~/app/shared/services/cookie.service';
2122

2223
@Component({
2324
selector: 'cd-multi-cluster-list',
@@ -48,7 +49,8 @@ export class MultiClusterListComponent {
4849
public actionLabels: ActionLabelsI18n,
4950
private notificationService: NotificationService,
5051
private authStorageService: AuthStorageService,
51-
private modalService: ModalService
52+
private modalService: ModalService,
53+
private cookieService: CookiesService
5254
) {
5355
this.tableActions = [
5456
{
@@ -189,6 +191,7 @@ export class MultiClusterListComponent {
189191
itemNames: [cluster['cluster_alias'] + ' - ' + cluster['user']],
190192
submitAction: () =>
191193
this.multiClusterService.deleteCluster(cluster['name'], cluster['user']).subscribe(() => {
194+
this.cookieService.deleteToken(`${cluster['name']}-${cluster['user']}`);
192195
this.modalRef.close();
193196
this.notificationService.show(
194197
NotificationType.success,

src/pybind/mgr/dashboard/frontend/src/app/core/navigation/navigation/navigation.component.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Icons } from '~/app/shared/enum/icons.enum';
99
import { MultiCluster } from '~/app/shared/models/multi-cluster';
1010
import { Permissions } from '~/app/shared/models/permissions';
1111
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
12+
import { CookiesService } from '~/app/shared/services/cookie.service';
1213
import {
1314
FeatureTogglesMap$,
1415
FeatureTogglesService
@@ -56,7 +57,8 @@ export class NavigationComponent implements OnInit, OnDestroy {
5657
private featureToggles: FeatureTogglesService,
5758
private telemetryNotificationService: TelemetryNotificationService,
5859
public prometheusAlertService: PrometheusAlertService,
59-
private motdNotificationService: MotdNotificationService
60+
private motdNotificationService: MotdNotificationService,
61+
private cookieService: CookiesService
6062
) {
6163
this.permissions = this.authStorageService.getPermissions();
6264
this.enabledFeature$ = this.featureToggles.get();
@@ -178,7 +180,12 @@ export class NavigationComponent implements OnInit, OnDestroy {
178180
onClusterSelection(value: object) {
179181
this.multiClusterService.setCluster(value).subscribe(
180182
(resp: any) => {
181-
localStorage.setItem('cluster_api_url', value['url']);
183+
if (value['cluster_alias'] === 'local-cluster') {
184+
localStorage.setItem('cluster_api_url', '');
185+
} else {
186+
localStorage.setItem('current_cluster_name', `${value['name']}-${value['user']}`);
187+
localStorage.setItem('cluster_api_url', value['url']);
188+
}
182189
this.selectedCluster = this.clustersMap.get(`${value['url']}-${value['user']}`) || {};
183190
const clustersConfig = resp['config'];
184191
if (clustersConfig && typeof clustersConfig === 'object') {
@@ -192,9 +199,10 @@ export class NavigationComponent implements OnInit, OnDestroy {
192199

193200
if (
194201
clusterName === this.selectedCluster['name'] &&
195-
clusterUser === this.selectedCluster['user']
202+
clusterUser === this.selectedCluster['user'] &&
203+
clusterDetails['cluster_alias'] !== 'local-cluster'
196204
) {
197-
localStorage.setItem('token_of_selected_cluster', clusterToken);
205+
this.cookieService.setToken(`${clusterName}-${clusterUser}`, clusterToken);
198206
}
199207
});
200208
});

src/pybind/mgr/dashboard/frontend/src/app/shared/services/api-interceptor.service.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { NotificationService } from './notification.service';
2020
import { MultiClusterService } from '../api/multi-cluster.service';
2121
import { SummaryService } from './summary.service';
2222
import { AuthStorageService } from './auth-storage.service';
23+
import { CookiesService } from './cookie.service';
2324

2425
export class CdHttpErrorResponse extends HttpErrorResponse {
2526
preventDefault: Function;
@@ -37,7 +38,8 @@ export class ApiInterceptorService implements HttpInterceptor {
3738
public notificationService: NotificationService,
3839
private summaryService: SummaryService,
3940
private authStorageService: AuthStorageService,
40-
private multiClusterService: MultiClusterService
41+
private multiClusterService: MultiClusterService,
42+
private cookieService: CookiesService
4143
) {
4244
this.multiClusterService.subscribe((resp: any) => {
4345
const clustersConfig = resp['config'];
@@ -91,14 +93,13 @@ export class ApiInterceptorService implements HttpInterceptor {
9193
'api/multi-cluster/auth'
9294
];
9395

94-
const token = localStorage.getItem('token_of_selected_cluster');
95-
9696
if (
9797
!currentRoute.includes('login') &&
9898
!ALWAYS_TO_HUB_APIs.includes(request.url) &&
9999
apiUrl &&
100100
!apiUrl.includes(origin)
101101
) {
102+
const token = this.cookieService.getToken(localStorage.getItem('current_cluster_name'));
102103
reqWithVersion = reqWithVersion.clone({
103104
url: `${apiUrl}${reqWithVersion.url}`,
104105
setHeaders: {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { CookiesService } from './cookie.service';
4+
5+
describe('CookieService', () => {
6+
let service: CookiesService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(CookiesService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Injectable } from '@angular/core';
2+
import { CookieService } from 'ngx-cookie-service';
3+
4+
@Injectable({
5+
providedIn: 'root'
6+
})
7+
export class CookiesService {
8+
constructor(private cookieService: CookieService) {}
9+
10+
setToken(name: string, token: string) {
11+
this.cookieService.set(name, token, null, null, null, true, 'Strict');
12+
}
13+
14+
getToken(name: string): string {
15+
return this.cookieService.get(name);
16+
}
17+
18+
deleteToken(name: string) {
19+
this.cookieService.delete(name);
20+
}
21+
}

0 commit comments

Comments
 (0)