mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 15:23:33 +00:00
Move CLI to apps/cli
This commit is contained in:
252
apps/cli/src/commands/list.command.ts
Normal file
252
apps/cli/src/commands/list.command.ts
Normal file
@@ -0,0 +1,252 @@
|
||||
import { ApiService } from "jslib-common/abstractions/api.service";
|
||||
import { CipherService } from "jslib-common/abstractions/cipher.service";
|
||||
import { CollectionService } from "jslib-common/abstractions/collection.service";
|
||||
import { FolderService } from "jslib-common/abstractions/folder.service";
|
||||
import { OrganizationService } from "jslib-common/abstractions/organization.service";
|
||||
import { SearchService } from "jslib-common/abstractions/search.service";
|
||||
import { Utils } from "jslib-common/misc/utils";
|
||||
import { CollectionData } from "jslib-common/models/data/collectionData";
|
||||
import { Collection } from "jslib-common/models/domain/collection";
|
||||
import {
|
||||
CollectionDetailsResponse as ApiCollectionDetailsResponse,
|
||||
CollectionResponse as ApiCollectionResponse,
|
||||
} from "jslib-common/models/response/collectionResponse";
|
||||
import { ListResponse as ApiListResponse } from "jslib-common/models/response/listResponse";
|
||||
import { CipherView } from "jslib-common/models/view/cipherView";
|
||||
import { Response } from "jslib-node/cli/models/response";
|
||||
import { ListResponse } from "jslib-node/cli/models/response/listResponse";
|
||||
|
||||
import { CipherResponse } from "../models/response/cipherResponse";
|
||||
import { CollectionResponse } from "../models/response/collectionResponse";
|
||||
import { FolderResponse } from "../models/response/folderResponse";
|
||||
import { OrganizationResponse } from "../models/response/organizationResponse";
|
||||
import { OrganizationUserResponse } from "../models/response/organizationUserResponse";
|
||||
import { CliUtils } from "../utils";
|
||||
|
||||
export class ListCommand {
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
private folderService: FolderService,
|
||||
private collectionService: CollectionService,
|
||||
private organizationService: OrganizationService,
|
||||
private searchService: SearchService,
|
||||
private apiService: ApiService
|
||||
) {}
|
||||
|
||||
async run(object: string, cmdOptions: Record<string, any>): Promise<Response> {
|
||||
const normalizedOptions = new Options(cmdOptions);
|
||||
switch (object.toLowerCase()) {
|
||||
case "items":
|
||||
return await this.listCiphers(normalizedOptions);
|
||||
case "folders":
|
||||
return await this.listFolders(normalizedOptions);
|
||||
case "collections":
|
||||
return await this.listCollections(normalizedOptions);
|
||||
case "org-collections":
|
||||
return await this.listOrganizationCollections(normalizedOptions);
|
||||
case "org-members":
|
||||
return await this.listOrganizationMembers(normalizedOptions);
|
||||
case "organizations":
|
||||
return await this.listOrganizations(normalizedOptions);
|
||||
default:
|
||||
return Response.badRequest("Unknown object.");
|
||||
}
|
||||
}
|
||||
|
||||
private async listCiphers(options: Options) {
|
||||
let ciphers: CipherView[];
|
||||
options.trash = options.trash || false;
|
||||
if (options.url != null && options.url.trim() !== "") {
|
||||
ciphers = await this.cipherService.getAllDecryptedForUrl(options.url);
|
||||
} else {
|
||||
ciphers = await this.cipherService.getAllDecrypted();
|
||||
}
|
||||
|
||||
if (
|
||||
options.folderId != null ||
|
||||
options.collectionId != null ||
|
||||
options.organizationId != null
|
||||
) {
|
||||
ciphers = ciphers.filter((c) => {
|
||||
if (options.trash !== c.isDeleted) {
|
||||
return false;
|
||||
}
|
||||
if (options.folderId != null) {
|
||||
if (options.folderId === "notnull" && c.folderId != null) {
|
||||
return true;
|
||||
}
|
||||
const folderId = options.folderId === "null" ? null : options.folderId;
|
||||
if (folderId === c.folderId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.organizationId != null) {
|
||||
if (options.organizationId === "notnull" && c.organizationId != null) {
|
||||
return true;
|
||||
}
|
||||
const organizationId = options.organizationId === "null" ? null : options.organizationId;
|
||||
if (organizationId === c.organizationId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.collectionId != null) {
|
||||
if (
|
||||
options.collectionId === "notnull" &&
|
||||
c.collectionIds != null &&
|
||||
c.collectionIds.length > 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const collectionId = options.collectionId === "null" ? null : options.collectionId;
|
||||
if (collectionId == null && (c.collectionIds == null || c.collectionIds.length === 0)) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
collectionId != null &&
|
||||
c.collectionIds != null &&
|
||||
c.collectionIds.indexOf(collectionId) > -1
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} else if (options.search == null || options.search.trim() === "") {
|
||||
ciphers = ciphers.filter((c) => options.trash === c.isDeleted);
|
||||
}
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
ciphers = this.searchService.searchCiphersBasic(ciphers, options.search, options.trash);
|
||||
}
|
||||
|
||||
const res = new ListResponse(ciphers.map((o) => new CipherResponse(o)));
|
||||
return Response.success(res);
|
||||
}
|
||||
|
||||
private async listFolders(options: Options) {
|
||||
let folders = await this.folderService.getAllDecrypted();
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
folders = CliUtils.searchFolders(folders, options.search);
|
||||
}
|
||||
|
||||
const res = new ListResponse(folders.map((o) => new FolderResponse(o)));
|
||||
return Response.success(res);
|
||||
}
|
||||
|
||||
private async listCollections(options: Options) {
|
||||
let collections = await this.collectionService.getAllDecrypted();
|
||||
|
||||
if (options.organizationId != null) {
|
||||
collections = collections.filter((c) => {
|
||||
if (options.organizationId === c.organizationId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
collections = CliUtils.searchCollections(collections, options.search);
|
||||
}
|
||||
|
||||
const res = new ListResponse(collections.map((o) => new CollectionResponse(o)));
|
||||
return Response.success(res);
|
||||
}
|
||||
|
||||
private async listOrganizationCollections(options: Options) {
|
||||
if (options.organizationId == null || options.organizationId === "") {
|
||||
return Response.badRequest("`organizationid` option is required.");
|
||||
}
|
||||
if (!Utils.isGuid(options.organizationId)) {
|
||||
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
||||
}
|
||||
const organization = await this.organizationService.get(options.organizationId);
|
||||
if (organization == null) {
|
||||
return Response.error("Organization not found.");
|
||||
}
|
||||
|
||||
try {
|
||||
let response: ApiListResponse<ApiCollectionResponse>;
|
||||
if (organization.canViewAllCollections) {
|
||||
response = await this.apiService.getCollections(options.organizationId);
|
||||
} else {
|
||||
response = await this.apiService.getUserCollections();
|
||||
}
|
||||
const collections = response.data
|
||||
.filter((c) => c.organizationId === options.organizationId)
|
||||
.map((r) => new Collection(new CollectionData(r as ApiCollectionDetailsResponse)));
|
||||
let decCollections = await this.collectionService.decryptMany(collections);
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
decCollections = CliUtils.searchCollections(decCollections, options.search);
|
||||
}
|
||||
const res = new ListResponse(decCollections.map((o) => new CollectionResponse(o)));
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
return Response.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async listOrganizationMembers(options: Options) {
|
||||
if (options.organizationId == null || options.organizationId === "") {
|
||||
return Response.badRequest("`organizationid` option is required.");
|
||||
}
|
||||
if (!Utils.isGuid(options.organizationId)) {
|
||||
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
||||
}
|
||||
const organization = await this.organizationService.get(options.organizationId);
|
||||
if (organization == null) {
|
||||
return Response.error("Organization not found.");
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await this.apiService.getOrganizationUsers(options.organizationId);
|
||||
const res = new ListResponse(
|
||||
response.data.map((r) => {
|
||||
const u = new OrganizationUserResponse();
|
||||
u.email = r.email;
|
||||
u.name = r.name;
|
||||
u.id = r.id;
|
||||
u.status = r.status;
|
||||
u.type = r.type;
|
||||
u.twoFactorEnabled = r.twoFactorEnabled;
|
||||
return u;
|
||||
})
|
||||
);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
return Response.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async listOrganizations(options: Options) {
|
||||
let organizations = await this.organizationService.getAll();
|
||||
|
||||
if (options.search != null && options.search.trim() !== "") {
|
||||
organizations = CliUtils.searchOrganizations(organizations, options.search);
|
||||
}
|
||||
|
||||
const res = new ListResponse(organizations.map((o) => new OrganizationResponse(o)));
|
||||
return Response.success(res);
|
||||
}
|
||||
}
|
||||
|
||||
class Options {
|
||||
organizationId: string;
|
||||
collectionId: string;
|
||||
folderId: string;
|
||||
search: string;
|
||||
url: string;
|
||||
trash: boolean;
|
||||
|
||||
constructor(passedOptions: Record<string, any>) {
|
||||
this.organizationId = passedOptions?.organizationid || passedOptions?.organizationId;
|
||||
this.collectionId = passedOptions?.collectionid || passedOptions?.collectionId;
|
||||
this.folderId = passedOptions?.folderid || passedOptions?.folderId;
|
||||
this.search = passedOptions?.search;
|
||||
this.url = passedOptions?.url;
|
||||
this.trash = CliUtils.convertBooleanOption(passedOptions?.trash);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user