mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
[SM-501] Revoke Access Tokens (#4746)
* Add support for revoking access tokens
This commit is contained in:
parent
d269439391
commit
55741445ec
@ -5952,6 +5952,16 @@
|
||||
"revokeAccessToken": {
|
||||
"message": "Revoke access token"
|
||||
},
|
||||
"revokeAccessTokens": {
|
||||
"message": "Revoke access tokens"
|
||||
},
|
||||
"revokeAccessTokenDesc": {
|
||||
"message": "Revoking access tokens is permanent and irreversible."
|
||||
},
|
||||
"accessTokenRevoked": {
|
||||
"message": "Access tokens revoked",
|
||||
"description": "Toast message after deleting one or multiple access tokens."
|
||||
},
|
||||
"submenu": {
|
||||
"message": "Submenu"
|
||||
},
|
||||
|
@ -40,6 +40,7 @@
|
||||
type="button"
|
||||
bitIconButton="bwi-ellipsis-v"
|
||||
buttonType="main"
|
||||
[bitMenuTriggerFor]="tableMenu"
|
||||
[title]="'options' | i18n"
|
||||
[attr.aria-label]="'options' | i18n"
|
||||
></button>
|
||||
@ -73,7 +74,7 @@
|
||||
</td>
|
||||
|
||||
<bit-menu #tokenMenu>
|
||||
<button type="button" bitMenuItem>
|
||||
<button type="button" bitMenuItem (click)="revokeAccessTokensEvent.emit([token])">
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-minus-circle" aria-hidden="true"></i>
|
||||
{{ "revokeAccessToken" | i18n }}
|
||||
@ -83,3 +84,10 @@
|
||||
</tr>
|
||||
</ng-template>
|
||||
</bit-table>
|
||||
|
||||
<bit-menu #tableMenu>
|
||||
<button type="button" bitMenuItem (click)="revokeSelected()">
|
||||
<i class="bwi bwi-fw bwi-minus-circle" aria-hidden="true"></i>
|
||||
<span class="tw-text-danger">{{ "revokeAccessTokens" | i18n }}</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
|
@ -19,6 +19,7 @@ export class AccessListComponent {
|
||||
private _tokens: AccessTokenView[];
|
||||
|
||||
@Output() newAccessTokenEvent = new EventEmitter();
|
||||
@Output() revokeAccessTokensEvent = new EventEmitter<AccessTokenView[]>();
|
||||
|
||||
protected selection = new SelectionModel<string>(true, []);
|
||||
|
||||
@ -34,6 +35,11 @@ export class AccessListComponent {
|
||||
: this.selection.select(...this.tokens.map((s) => s.id));
|
||||
}
|
||||
|
||||
protected revokeSelected() {
|
||||
const selected = this.tokens.filter((s) => this.selection.selected.includes(s.id));
|
||||
this.revokeAccessTokensEvent.emit(selected);
|
||||
}
|
||||
|
||||
protected permission(token: AccessTokenView) {
|
||||
return "canRead";
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<sm-access-list
|
||||
[tokens]="accessTokens$ | async"
|
||||
(newAccessTokenEvent)="openNewAccessTokenDialog()"
|
||||
(revokeAccessTokensEvent)="revoke($event)"
|
||||
></sm-access-list>
|
||||
|
@ -2,7 +2,10 @@ import { Component, OnInit } from "@angular/core";
|
||||
import { ActivatedRoute } from "@angular/router";
|
||||
import { combineLatestWith, Observable, startWith, switchMap } from "rxjs";
|
||||
|
||||
import { ModalService } from "@bitwarden/angular/services/modal.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
import { UserVerificationPromptComponent } from "@bitwarden/web-vault/app/components/user-verification-prompt.component";
|
||||
|
||||
import { AccessTokenView } from "../models/view/access-token.view";
|
||||
|
||||
@ -22,7 +25,9 @@ export class AccessTokenComponent implements OnInit {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private accessService: AccessService,
|
||||
private dialogService: DialogService
|
||||
private dialogService: DialogService,
|
||||
private modalService: ModalService,
|
||||
private platformUtilsService: PlatformUtilsService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -37,8 +42,17 @@ export class AccessTokenComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
private async getAccessTokens(): Promise<AccessTokenView[]> {
|
||||
return await this.accessService.getAccessTokens(this.organizationId, this.serviceAccountId);
|
||||
protected async revoke(tokens: AccessTokenView[]) {
|
||||
if (!(await this.verifyUser())) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.accessService.revokeAccessTokens(
|
||||
this.serviceAccountId,
|
||||
tokens.map((t) => t.id)
|
||||
);
|
||||
|
||||
this.platformUtilsService.showToast("success", null, "Access tokens revoked.");
|
||||
}
|
||||
|
||||
protected openNewAccessTokenDialog() {
|
||||
@ -48,4 +62,25 @@ export class AccessTokenComponent implements OnInit {
|
||||
this.organizationId
|
||||
);
|
||||
}
|
||||
|
||||
private verifyUser() {
|
||||
const ref = this.modalService.open(UserVerificationPromptComponent, {
|
||||
allowMultipleModals: true,
|
||||
data: {
|
||||
confirmDescription: "revokeAccessTokenDesc",
|
||||
confirmButtonText: "revokeAccessToken",
|
||||
modalTitle: "revokeAccessToken",
|
||||
},
|
||||
});
|
||||
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return ref.onClosedPromise();
|
||||
}
|
||||
|
||||
private async getAccessTokens(): Promise<AccessTokenView[]> {
|
||||
return await this.accessService.getAccessTokens(this.organizationId, this.serviceAccountId);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-cr
|
||||
import { ListResponse } from "@bitwarden/common/models/response/list.response";
|
||||
|
||||
import { AccessTokenRequest } from "../models/requests/access-token.request";
|
||||
import { RevokeAccessTokensRequest } from "../models/requests/revoke-access-tokens.request";
|
||||
import { AccessTokenCreationResponse } from "../models/responses/access-token-creation.response";
|
||||
import { AccessTokenResponse } from "../models/responses/access-tokens.response";
|
||||
import { AccessTokenView } from "../models/view/access-token.view";
|
||||
@ -80,6 +81,21 @@ export class AccessService {
|
||||
return `${this._accessTokenVersion}.${result.id}.${result.clientSecret}:${b64Key}`;
|
||||
}
|
||||
|
||||
async revokeAccessTokens(serviceAccountId: string, accessTokenIds: string[]): Promise<void> {
|
||||
const request = new RevokeAccessTokensRequest();
|
||||
request.ids = accessTokenIds;
|
||||
|
||||
await this.apiService.send(
|
||||
"POST",
|
||||
"/service-accounts/" + serviceAccountId + "/access-tokens/revoke",
|
||||
request,
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
this._accessToken.next(null);
|
||||
}
|
||||
|
||||
private async createAccessTokenRequest(
|
||||
organizationId: string,
|
||||
encryptionKey: SymmetricCryptoKey,
|
||||
|
@ -0,0 +1,3 @@
|
||||
export class RevokeAccessTokensRequest {
|
||||
ids: string[];
|
||||
}
|
Loading…
Reference in New Issue
Block a user