1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-23 03:33:54 +00:00

Move web to apps/web and bitwarden_license/bit-web

This commit is contained in:
Hinton
2022-06-02 11:55:37 +02:00
parent fb35805202
commit 02fe715903
619 changed files with 2 additions and 7 deletions

View File

@@ -0,0 +1,63 @@
import { Directive, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import { first } from "rxjs/operators";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { StateService } from "jslib-common/abstractions/state.service";
@Directive()
export abstract class BaseAcceptComponent implements OnInit {
loading = true;
authed = false;
email: string;
actionPromise: Promise<any>;
protected requiredParameters: string[] = [];
protected failedShortMessage = "inviteAcceptFailedShort";
protected failedMessage = "inviteAcceptFailed";
constructor(
protected router: Router,
protected platformUtilService: PlatformUtilsService,
protected i18nService: I18nService,
protected route: ActivatedRoute,
protected stateService: StateService
) {}
abstract authedHandler(qParams: any): Promise<void>;
abstract unauthedHandler(qParams: any): Promise<void>;
ngOnInit() {
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
let error = this.requiredParameters.some((e) => qParams?.[e] == null || qParams[e] === "");
let errorMessage: string = null;
if (!error) {
this.authed = await this.stateService.getIsAuthenticated();
if (this.authed) {
try {
await this.authedHandler(qParams);
} catch (e) {
error = true;
errorMessage = e.message;
}
} else {
this.email = qParams.email;
await this.unauthedHandler(qParams);
}
}
if (error) {
const message =
errorMessage != null
? this.i18nService.t(this.failedShortMessage, errorMessage)
: this.i18nService.t(this.failedMessage);
this.platformUtilService.showToast("error", null, message, { timeout: 10000 });
this.router.navigate(["/"]);
}
this.loading = false;
});
}
}

View File

@@ -0,0 +1,178 @@
import { Directive } from "@angular/core";
import { ExportService } from "jslib-common/abstractions/export.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { EventResponse } from "jslib-common/models/response/eventResponse";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { EventView } from "jslib-common/models/view/eventView";
import { EventService } from "src/app/services/event.service";
@Directive()
export abstract class BaseEventsComponent {
loading = true;
loaded = false;
events: EventView[];
start: string;
end: string;
dirtyDates = true;
continuationToken: string;
refreshPromise: Promise<any>;
exportPromise: Promise<any>;
morePromise: Promise<any>;
abstract readonly exportFileName: string;
constructor(
protected eventService: EventService,
protected i18nService: I18nService,
protected exportService: ExportService,
protected platformUtilsService: PlatformUtilsService,
protected logService: LogService
) {
const defaultDates = this.eventService.getDefaultDateFilters();
this.start = defaultDates[0];
this.end = defaultDates[1];
}
async exportEvents() {
if (this.appApiPromiseUnfulfilled() || this.dirtyDates) {
return;
}
this.loading = true;
const dates = this.parseDates();
if (dates == null) {
return;
}
try {
this.exportPromise = this.export(dates[0], dates[1]);
await this.exportPromise;
} catch (e) {
this.logService.error(`Handled exception: ${e}`);
}
this.exportPromise = null;
this.loading = false;
}
async loadEvents(clearExisting: boolean) {
if (this.appApiPromiseUnfulfilled()) {
return;
}
const dates = this.parseDates();
if (dates == null) {
return;
}
this.loading = true;
let events: EventView[] = [];
try {
const promise = this.loadAndParseEvents(
dates[0],
dates[1],
clearExisting ? null : this.continuationToken
);
if (clearExisting) {
this.refreshPromise = promise;
} else {
this.morePromise = promise;
}
const result = await promise;
this.continuationToken = result.continuationToken;
events = result.events;
} catch (e) {
this.logService.error(`Handled exception: ${e}`);
}
if (!clearExisting && this.events != null && this.events.length > 0) {
this.events = this.events.concat(events);
} else {
this.events = events;
}
this.dirtyDates = false;
this.loading = false;
this.morePromise = null;
this.refreshPromise = null;
}
protected abstract requestEvents(
startDate: string,
endDate: string,
continuationToken: string
): Promise<ListResponse<EventResponse>>;
protected abstract getUserName(r: EventResponse, userId: string): { name: string; email: string };
protected async loadAndParseEvents(
startDate: string,
endDate: string,
continuationToken: string
) {
const response = await this.requestEvents(startDate, endDate, continuationToken);
const events = await Promise.all(
response.data.map(async (r) => {
const userId = r.actingUserId == null ? r.userId : r.actingUserId;
const eventInfo = await this.eventService.getEventInfo(r);
const user = this.getUserName(r, userId);
const userName = user != null ? user.name : this.i18nService.t("unknown");
return new EventView({
message: eventInfo.message,
humanReadableMessage: eventInfo.humanReadableMessage,
appIcon: eventInfo.appIcon,
appName: eventInfo.appName,
userId: userId,
userName: r.installationId != null ? `Installation: ${r.installationId}` : userName,
userEmail: user != null ? user.email : "",
date: r.date,
ip: r.ipAddress,
type: r.type,
installationId: r.installationId,
});
})
);
return { continuationToken: response.continuationToken, events: events };
}
protected parseDates() {
let dates: string[] = null;
try {
dates = this.eventService.formatDateFilters(this.start, this.end);
} catch (e) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("invalidDateRange")
);
return null;
}
return dates;
}
protected appApiPromiseUnfulfilled() {
return this.refreshPromise != null || this.morePromise != null || this.exportPromise != null;
}
private async export(start: string, end: string) {
let continuationToken = this.continuationToken;
let events = [].concat(this.events);
while (continuationToken != null) {
const result = await this.loadAndParseEvents(start, end, continuationToken);
continuationToken = result.continuationToken;
events = events.concat(result.events);
}
const data = await this.exportService.getEventExport(events);
const fileName = this.exportService.getFileName(this.exportFileName, "csv");
this.platformUtilsService.saveFile(window, data, { type: "text/plain" }, fileName);
}
}

View File

@@ -0,0 +1,345 @@
import { Directive, ViewChild, ViewContainerRef } from "@angular/core";
import { SearchPipe } from "jslib-angular/pipes/search.pipe";
import { UserNamePipe } from "jslib-angular/pipes/user-name.pipe";
import { ModalService } from "jslib-angular/services/modal.service";
import { ValidationService } from "jslib-angular/services/validation.service";
import { ApiService } from "jslib-common/abstractions/api.service";
import { CryptoService } from "jslib-common/abstractions/crypto.service";
import { I18nService } from "jslib-common/abstractions/i18n.service";
import { LogService } from "jslib-common/abstractions/log.service";
import { PlatformUtilsService } from "jslib-common/abstractions/platformUtils.service";
import { SearchService } from "jslib-common/abstractions/search.service";
import { StateService } from "jslib-common/abstractions/state.service";
import { OrganizationUserStatusType } from "jslib-common/enums/organizationUserStatusType";
import { OrganizationUserType } from "jslib-common/enums/organizationUserType";
import { ProviderUserStatusType } from "jslib-common/enums/providerUserStatusType";
import { ProviderUserType } from "jslib-common/enums/providerUserType";
import { Utils } from "jslib-common/misc/utils";
import { ListResponse } from "jslib-common/models/response/listResponse";
import { OrganizationUserUserDetailsResponse } from "jslib-common/models/response/organizationUserResponse";
import { ProviderUserUserDetailsResponse } from "jslib-common/models/response/provider/providerUserResponse";
import { UserConfirmComponent } from "../organizations/manage/user-confirm.component";
type StatusType = OrganizationUserStatusType | ProviderUserStatusType;
const MaxCheckedCount = 500;
@Directive()
export abstract class BasePeopleComponent<
UserType extends ProviderUserUserDetailsResponse | OrganizationUserUserDetailsResponse
> {
@ViewChild("confirmTemplate", { read: ViewContainerRef, static: true })
confirmModalRef: ViewContainerRef;
get allCount() {
return this.allUsers != null ? this.allUsers.length : 0;
}
get invitedCount() {
return this.statusMap.has(this.userStatusType.Invited)
? this.statusMap.get(this.userStatusType.Invited).length
: 0;
}
get acceptedCount() {
return this.statusMap.has(this.userStatusType.Accepted)
? this.statusMap.get(this.userStatusType.Accepted).length
: 0;
}
get confirmedCount() {
return this.statusMap.has(this.userStatusType.Confirmed)
? this.statusMap.get(this.userStatusType.Confirmed).length
: 0;
}
get showConfirmUsers(): boolean {
return (
this.allUsers != null &&
this.statusMap != null &&
this.allUsers.length > 1 &&
this.confirmedCount > 0 &&
this.confirmedCount < 3 &&
this.acceptedCount > 0
);
}
get showBulkConfirmUsers(): boolean {
return this.acceptedCount > 0;
}
abstract userType: typeof OrganizationUserType | typeof ProviderUserType;
abstract userStatusType: typeof OrganizationUserStatusType | typeof ProviderUserStatusType;
loading = true;
statusMap = new Map<StatusType, UserType[]>();
status: StatusType;
users: UserType[] = [];
pagedUsers: UserType[] = [];
searchText: string;
actionPromise: Promise<any>;
protected allUsers: UserType[] = [];
protected didScroll = false;
protected pageSize = 100;
private pagedUsersCount = 0;
constructor(
protected apiService: ApiService,
private searchService: SearchService,
protected i18nService: I18nService,
protected platformUtilsService: PlatformUtilsService,
protected cryptoService: CryptoService,
protected validationService: ValidationService,
protected modalService: ModalService,
private logService: LogService,
private searchPipe: SearchPipe,
protected userNamePipe: UserNamePipe,
protected stateService: StateService
) {}
abstract edit(user: UserType): void;
abstract getUsers(): Promise<ListResponse<UserType>>;
abstract deleteUser(id: string): Promise<any>;
abstract reinviteUser(id: string): Promise<any>;
abstract confirmUser(user: UserType, publicKey: Uint8Array): Promise<any>;
async load() {
const response = await this.getUsers();
this.statusMap.clear();
for (const status of Utils.iterateEnum(this.userStatusType)) {
this.statusMap.set(status, []);
}
this.allUsers = response.data != null && response.data.length > 0 ? response.data : [];
this.allUsers.sort(Utils.getSortFunction(this.i18nService, "email"));
this.allUsers.forEach((u) => {
if (!this.statusMap.has(u.status)) {
this.statusMap.set(u.status, [u]);
} else {
this.statusMap.get(u.status).push(u);
}
});
this.filter(this.status);
this.loading = false;
}
filter(status: StatusType) {
this.status = status;
if (this.status != null) {
this.users = this.statusMap.get(this.status);
} else {
this.users = this.allUsers;
}
// Reset checkbox selecton
this.selectAll(false);
this.resetPaging();
}
loadMore() {
if (!this.users || this.users.length <= this.pageSize) {
return;
}
const pagedLength = this.pagedUsers.length;
let pagedSize = this.pageSize;
if (pagedLength === 0 && this.pagedUsersCount > this.pageSize) {
pagedSize = this.pagedUsersCount;
}
if (this.users.length > pagedLength) {
this.pagedUsers = this.pagedUsers.concat(
this.users.slice(pagedLength, pagedLength + pagedSize)
);
}
this.pagedUsersCount = this.pagedUsers.length;
this.didScroll = this.pagedUsers.length > this.pageSize;
}
checkUser(user: OrganizationUserUserDetailsResponse, select?: boolean) {
(user as any).checked = select == null ? !(user as any).checked : select;
}
selectAll(select: boolean) {
if (select) {
this.selectAll(false);
}
const filteredUsers = this.searchPipe.transform(
this.users,
this.searchText,
"name",
"email",
"id"
);
const selectCount =
select && filteredUsers.length > MaxCheckedCount ? MaxCheckedCount : filteredUsers.length;
for (let i = 0; i < selectCount; i++) {
this.checkUser(filteredUsers[i], select);
}
}
async resetPaging() {
this.pagedUsers = [];
this.loadMore();
}
invite() {
this.edit(null);
}
async remove(user: UserType) {
const confirmed = await this.platformUtilsService.showDialog(
this.deleteWarningMessage(user),
this.userNamePipe.transform(user),
this.i18nService.t("yes"),
this.i18nService.t("no"),
"warning"
);
if (!confirmed) {
return false;
}
this.actionPromise = this.deleteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("removedUserId", this.userNamePipe.transform(user))
);
this.removeUser(user);
} catch (e) {
this.validationService.showError(e);
}
this.actionPromise = null;
}
async reinvite(user: UserType) {
if (this.actionPromise != null) {
return;
}
this.actionPromise = this.reinviteUser(user.id);
try {
await this.actionPromise;
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenReinvited", this.userNamePipe.transform(user))
);
} catch (e) {
this.validationService.showError(e);
}
this.actionPromise = null;
}
async confirm(user: UserType) {
function updateUser(self: BasePeopleComponent<UserType>) {
user.status = self.userStatusType.Confirmed;
const mapIndex = self.statusMap.get(self.userStatusType.Accepted).indexOf(user);
if (mapIndex > -1) {
self.statusMap.get(self.userStatusType.Accepted).splice(mapIndex, 1);
self.statusMap.get(self.userStatusType.Confirmed).push(user);
}
}
const confirmUser = async (publicKey: Uint8Array) => {
try {
this.actionPromise = this.confirmUser(user, publicKey);
await this.actionPromise;
updateUser(this);
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("hasBeenConfirmed", this.userNamePipe.transform(user))
);
} catch (e) {
this.validationService.showError(e);
throw e;
} finally {
this.actionPromise = null;
}
};
if (this.actionPromise != null) {
return;
}
try {
const publicKeyResponse = await this.apiService.getUserPublicKey(user.userId);
const publicKey = Utils.fromB64ToArray(publicKeyResponse.publicKey);
const autoConfirm = await this.stateService.getAutoConfirmFingerPrints();
if (autoConfirm == null || !autoConfirm) {
const [modal] = await this.modalService.openViewRef(
UserConfirmComponent,
this.confirmModalRef,
(comp) => {
comp.name = this.userNamePipe.transform(user);
comp.userId = user != null ? user.userId : null;
comp.publicKey = publicKey;
comp.onConfirmedUser.subscribe(async () => {
try {
comp.formPromise = confirmUser(publicKey);
await comp.formPromise;
modal.close();
} catch (e) {
this.logService.error(e);
}
});
}
);
return;
}
try {
const fingerprint = await this.cryptoService.getFingerprint(user.userId, publicKey.buffer);
this.logService.info(`User's fingerprint: ${fingerprint.join("-")}`);
} catch (e) {
this.logService.error(e);
}
await confirmUser(publicKey);
} catch (e) {
this.logService.error(`Handled exception: ${e}`);
}
}
isSearching() {
return this.searchService.isSearchable(this.searchText);
}
isPaging() {
const searching = this.isSearching();
if (searching && this.didScroll) {
this.resetPaging();
}
return !searching && this.users && this.users.length > this.pageSize;
}
protected deleteWarningMessage(user: UserType): string {
return this.i18nService.t("removeUserConfirmation");
}
protected getCheckedUsers() {
return this.users.filter((u) => (u as any).checked);
}
protected removeUser(user: UserType) {
let index = this.users.indexOf(user);
if (index > -1) {
this.users.splice(index, 1);
this.resetPaging();
}
if (this.statusMap.has(user.status)) {
index = this.statusMap.get(user.status).indexOf(user);
if (index > -1) {
this.statusMap.get(user.status).splice(index, 1);
}
}
}
}