[PS-788] - Item links use `cipherId` in query string (#2880)

* Replace 'cipherId' query param with 'itemId' for linkable ciphers
This commit is contained in:
Shane Melton 2022-06-13 07:14:09 -07:00 committed by GitHub
parent 4f9079dd4f
commit 2d72201650
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 20 deletions

View File

@ -7,7 +7,7 @@ import {
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from "@angular/core"; } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Params, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { VaultFilter } from "jslib-angular/modules/vault-filter/models/vault-filter.model"; import { VaultFilter } from "jslib-angular/modules/vault-filter/models/vault-filter.model";
@ -23,7 +23,6 @@ import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.se
import { StateService } from "jslib-common/abstractions/state.service"; import { StateService } from "jslib-common/abstractions/state.service";
import { SyncService } from "jslib-common/abstractions/sync.service"; import { SyncService } from "jslib-common/abstractions/sync.service";
import { TokenService } from "jslib-common/abstractions/token.service"; import { TokenService } from "jslib-common/abstractions/token.service";
import { CipherType } from "jslib-common/enums/cipherType";
import { CipherView } from "jslib-common/models/view/cipherView"; import { CipherView } from "jslib-common/models/view/cipherView";
import { UpdateKeyComponent } from "../../../../settings/update-key.component"; import { UpdateKeyComponent } from "../../../../settings/update-key.component";
@ -111,9 +110,11 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
this.filterComponent.reloadOrganizations(); this.filterComponent.reloadOrganizations();
this.showUpdateKey = !(await this.cryptoService.hasEncKey()); this.showUpdateKey = !(await this.cryptoService.hasEncKey());
if (params.cipherId) { const cipherId = getCipherIdFromParams(params);
if (cipherId) {
const cipherView = new CipherView(); const cipherView = new CipherView();
cipherView.id = params.cipherId; cipherView.id = cipherId;
if (params.action === "clone") { if (params.action === "clone") {
await this.cloneCipher(cipherView); await this.cloneCipher(cipherView);
} else if (params.action === "edit") { } else if (params.action === "edit") {
@ -123,9 +124,10 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
await this.ciphersComponent.reload(); await this.ciphersComponent.reload();
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
if (params.cipherId) { const cipherId = getCipherIdFromParams(params);
if ((await this.cipherService.get(params.cipherId)) != null) { if (cipherId) {
this.editCipherId(params.cipherId); if ((await this.cipherService.get(cipherId)) != null) {
this.editCipherId(cipherId);
} else { } else {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
@ -133,7 +135,7 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
this.i18nService.t("unknownCipher") this.i18nService.t("unknownCipher")
); );
this.router.navigate([], { this.router.navigate([], {
queryParams: { cipherId: null }, queryParams: { itemId: null, cipherId: null },
queryParamsHandling: "merge", queryParamsHandling: "merge",
}); });
} }
@ -356,7 +358,7 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
const cipher = await this.cipherService.get(id); const cipher = await this.cipherService.get(id);
if (cipher != null && cipher.reprompt != 0) { if (cipher != null && cipher.reprompt != 0) {
if (!(await this.passwordRepromptService.showPasswordPrompt())) { if (!(await this.passwordRepromptService.showPasswordPrompt())) {
this.go({ cipherId: null }); this.go({ cipherId: null, itemId: null });
return; return;
} }
} }
@ -382,7 +384,7 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
); );
modal.onClosedPromise().then(() => { modal.onClosedPromise().then(() => {
this.go({ cipherId: null }); this.go({ cipherId: null, itemId: null });
}); });
return childComponent; return childComponent;
@ -416,3 +418,11 @@ export class IndividualVaultComponent implements OnInit, OnDestroy {
}); });
} }
} }
/**
* Allows backwards compatibility with
* old links that used the original `cipherId` param
*/
const getCipherIdFromParams = (params: Params): string => {
return params["itemId"] || params["cipherId"];
};

View File

@ -7,7 +7,7 @@ import {
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from "@angular/core"; } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Params, Router } from "@angular/router";
import { first } from "rxjs/operators"; import { first } from "rxjs/operators";
import { VaultFilter } from "jslib-angular/modules/vault-filter/models/vault-filter.model"; import { VaultFilter } from "jslib-angular/modules/vault-filter/models/vault-filter.model";
@ -127,13 +127,14 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
} }
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
if (params.cipherId) { const cipherId = getCipherIdFromParams(params);
if (cipherId) {
if ( if (
// Handle users with implicit collection access since they use the admin endpoint // Handle users with implicit collection access since they use the admin endpoint
this.organization.canEditAnyCollection || this.organization.canEditAnyCollection ||
(await this.cipherService.get(params.cipherId)) != null (await this.cipherService.get(cipherId)) != null
) { ) {
this.editCipherId(params.cipherId); this.editCipherId(cipherId);
} else { } else {
this.platformUtilsService.showToast( this.platformUtilsService.showToast(
"error", "error",
@ -141,7 +142,7 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
this.i18nService.t("unknownCipher") this.i18nService.t("unknownCipher")
); );
this.router.navigate([], { this.router.navigate([], {
queryParams: { cipherId: null }, queryParams: { cipherId: null, itemId: null },
queryParamsHandling: "merge", queryParamsHandling: "merge",
}); });
} }
@ -273,7 +274,7 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
const cipher = await this.cipherService.get(cipherId); const cipher = await this.cipherService.get(cipherId);
if (cipher != null && cipher.reprompt != 0) { if (cipher != null && cipher.reprompt != 0) {
if (!(await this.passwordRepromptService.showPasswordPrompt())) { if (!(await this.passwordRepromptService.showPasswordPrompt())) {
this.go({ cipherId: null }); this.go({ cipherId: null, itemId: null });
return; return;
} }
} }
@ -300,7 +301,7 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
); );
modal.onClosedPromise().then(() => { modal.onClosedPromise().then(() => {
this.go({ cipherId: null }); this.go({ cipherId: null, itemId: null });
}); });
return childComponent; return childComponent;
@ -353,3 +354,11 @@ export class OrganizationVaultComponent implements OnInit, OnDestroy {
}); });
} }
} }
/**
* Allows backwards compatibility with
* old links that used the original `cipherId` param
*/
const getCipherIdFromParams = (params: Params): string => {
return params["itemId"] || params["cipherId"];
};

View File

@ -41,10 +41,12 @@ export class PermissionsGuard implements CanActivate {
if (permissions != null && !org.hasAnyPermission(permissions)) { if (permissions != null && !org.hasAnyPermission(permissions)) {
// Handle linkable ciphers for organizations the user only has view access to // Handle linkable ciphers for organizations the user only has view access to
// https://bitwarden.atlassian.net/browse/EC-203 // https://bitwarden.atlassian.net/browse/EC-203
if (state.root.queryParamMap.has("cipherId")) { const cipherId =
state.root.queryParamMap.get("itemId") || state.root.queryParamMap.get("cipherId");
if (cipherId) {
return this.router.createUrlTree(["/vault"], { return this.router.createUrlTree(["/vault"], {
queryParams: { queryParams: {
cipherId: state.root.queryParamMap.get("cipherId"), itemId: cipherId,
}, },
}); });
} }

View File

@ -19,7 +19,7 @@
<a <a
appStopProp appStopProp
[routerLink]="[]" [routerLink]="[]"
[queryParams]="{ cipherId: c.id }" [queryParams]="{ itemId: c.id }"
queryParamsHandling="merge" queryParamsHandling="merge"
title="{{ 'editItem' | i18n }}" title="{{ 'editItem' | i18n }}"
>{{ c.name }}</a >{{ c.name }}</a