mirror of
https://github.com/bitwarden/browser.git
synced 2025-03-12 13:39:14 +01:00
Merge branch 'main' into autofill/pm-6546-blurring-of-autofilled-elements-causes-problems-in-blur-event-listeners
This commit is contained in:
commit
d59373fafb
@ -119,7 +119,7 @@
|
||||
"message": "Дадаць лагін"
|
||||
},
|
||||
"addCardMenu": {
|
||||
"message": "Add card"
|
||||
"message": "Дадаць картку"
|
||||
},
|
||||
"addIdentityMenu": {
|
||||
"message": "Add identity"
|
||||
@ -269,7 +269,7 @@
|
||||
"message": "Даўжыня"
|
||||
},
|
||||
"passwordMinLength": {
|
||||
"message": "Minimum password length"
|
||||
"message": "Мінімальная даўжыня пароля"
|
||||
},
|
||||
"uppercase": {
|
||||
"message": "Вялікія літары (A-Z)"
|
||||
@ -369,7 +369,7 @@
|
||||
"message": "Iншае"
|
||||
},
|
||||
"unlockMethodNeededToChangeTimeoutActionDesc": {
|
||||
"message": "Set up an unlock method to change your vault timeout action."
|
||||
"message": "Наладзіць метад разблакіроўкі для змянення дзеяння часу чакання вашага сховішча."
|
||||
},
|
||||
"unlockMethodNeeded": {
|
||||
"message": "Set up an unlock method in Settings"
|
||||
@ -415,7 +415,7 @@
|
||||
"message": "Заблакіраваць зараз"
|
||||
},
|
||||
"lockAll": {
|
||||
"message": "Lock all"
|
||||
"message": "Заблакаваць усе"
|
||||
},
|
||||
"immediately": {
|
||||
"message": "Адразу"
|
||||
@ -494,7 +494,7 @@
|
||||
"message": "Ваш уліковы запіс створаны! Цяпер вы можаце ўвайсці ў яго."
|
||||
},
|
||||
"youSuccessfullyLoggedIn": {
|
||||
"message": "You successfully logged in"
|
||||
"message": "Вы паспяхова аўтарызаваны"
|
||||
},
|
||||
"youMayCloseThisWindow": {
|
||||
"message": "You may close this window"
|
||||
@ -2005,7 +2005,7 @@
|
||||
"message": "Выбраць папку..."
|
||||
},
|
||||
"noFoldersFound": {
|
||||
"message": "No folders found",
|
||||
"message": "Папкі не знойдзены",
|
||||
"description": "Used as a message within the notification bar when no folders are found"
|
||||
},
|
||||
"orgPermissionsUpdatedMustSetPassword": {
|
||||
@ -2215,7 +2215,7 @@
|
||||
"message": "Версія сервера"
|
||||
},
|
||||
"selfHostedServer": {
|
||||
"message": "self-hosted"
|
||||
"message": "уласнае размяшчэнне"
|
||||
},
|
||||
"thirdParty": {
|
||||
"message": "Іншы пастаўшчык"
|
||||
@ -2356,25 +2356,25 @@
|
||||
}
|
||||
},
|
||||
"loggingInOn": {
|
||||
"message": "Logging in on"
|
||||
"message": "Увайсці на"
|
||||
},
|
||||
"opensInANewWindow": {
|
||||
"message": "Адкрываць у новым акне"
|
||||
},
|
||||
"deviceApprovalRequired": {
|
||||
"message": "Device approval required. Select an approval option below:"
|
||||
"message": "Патрабуецца ўхваленне прылады. Выберыце параметры ўхвалення ніжэй:"
|
||||
},
|
||||
"rememberThisDevice": {
|
||||
"message": "Remember this device"
|
||||
"message": "Запомніць гэту прыладу"
|
||||
},
|
||||
"uncheckIfPublicDevice": {
|
||||
"message": "Uncheck if using a public device"
|
||||
"message": "Здыміце пазнаку, калі выкарыстоўваеце агульнадаступную прыладу"
|
||||
},
|
||||
"approveFromYourOtherDevice": {
|
||||
"message": "Approve from your other device"
|
||||
"message": "Ухваліць з іншай вашай прылады"
|
||||
},
|
||||
"requestAdminApproval": {
|
||||
"message": "Request admin approval"
|
||||
"message": "Запытаць ухваленне адміністратара"
|
||||
},
|
||||
"approveWithMasterPassword": {
|
||||
"message": "Approve with master password"
|
||||
@ -2402,7 +2402,7 @@
|
||||
"message": "Адлюстраванне"
|
||||
},
|
||||
"accountSuccessfullyCreated": {
|
||||
"message": "Account successfully created!"
|
||||
"message": "Уліковы запіс паспяхова створаны!"
|
||||
},
|
||||
"adminApprovalRequested": {
|
||||
"message": "Admin approval requested"
|
||||
|
@ -688,7 +688,7 @@
|
||||
"message": "A bejelentkezési jelszó frissítésének kérése, ha változást lett érzékelve egy webhelyen. Minden bejelentkezett fiókra vonatkozik."
|
||||
},
|
||||
"enableUsePasskeys": {
|
||||
"message": "Kérés a jhozzáférési kulcs mentésére és használatára"
|
||||
"message": "Kérés a hozzáférési kulcs mentésére és használatára"
|
||||
},
|
||||
"usePasskeysDesc": {
|
||||
"message": "Kérés az új hozzáféréi kulcsok mentésére vagy bejelentkezés a széfben tárolt hozzáférési kulcsokkal. Minden bejelentkezett fiókra vonatkozik."
|
||||
|
@ -0,0 +1,38 @@
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
|
||||
import {
|
||||
ApiServiceInitOptions,
|
||||
apiServiceFactory,
|
||||
} from "../../../platform/background/service-factories/api-service.factory";
|
||||
import {
|
||||
CachedServices,
|
||||
factory,
|
||||
FactoryOptions,
|
||||
} from "../../../platform/background/service-factories/factory-options";
|
||||
import {
|
||||
stateProviderFactory,
|
||||
StateProviderInitOptions,
|
||||
} from "../../../platform/background/service-factories/state-provider.factory";
|
||||
|
||||
type AvatarServiceFactoryOptions = FactoryOptions;
|
||||
|
||||
export type AvatarServiceInitOptions = AvatarServiceFactoryOptions &
|
||||
ApiServiceInitOptions &
|
||||
StateProviderInitOptions;
|
||||
|
||||
export function avatarServiceFactory(
|
||||
cache: { avatarService?: AvatarServiceAbstraction } & CachedServices,
|
||||
opts: AvatarServiceInitOptions,
|
||||
): Promise<AvatarServiceAbstraction> {
|
||||
return factory(
|
||||
cache,
|
||||
"avatarService",
|
||||
opts,
|
||||
async () =>
|
||||
new AvatarService(
|
||||
await apiServiceFactory(cache, opts),
|
||||
await stateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
@ -1,32 +1,61 @@
|
||||
import { Location } from "@angular/common";
|
||||
import { Component } from "@angular/core";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
import { Observable, combineLatest, switchMap } from "rxjs";
|
||||
|
||||
import { CurrentAccountService } from "./services/current-account.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export type CurrentAccount = {
|
||||
id: UserId;
|
||||
name: string | undefined;
|
||||
email: string;
|
||||
status: AuthenticationStatus;
|
||||
avatarColor: string;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-current-account",
|
||||
templateUrl: "current-account.component.html",
|
||||
})
|
||||
export class CurrentAccountComponent {
|
||||
currentAccount$: Observable<CurrentAccount>;
|
||||
|
||||
constructor(
|
||||
private currentAccountService: CurrentAccountService,
|
||||
private accountService: AccountService,
|
||||
private avatarService: AvatarService,
|
||||
private router: Router,
|
||||
private location: Location,
|
||||
private route: ActivatedRoute,
|
||||
) {}
|
||||
) {
|
||||
this.currentAccount$ = combineLatest([
|
||||
this.accountService.activeAccount$,
|
||||
this.avatarService.avatarColor$,
|
||||
]).pipe(
|
||||
switchMap(async ([account, avatarColor]) => {
|
||||
if (account == null) {
|
||||
return null;
|
||||
}
|
||||
const currentAccount: CurrentAccount = {
|
||||
id: account.id,
|
||||
name: account.name || account.email,
|
||||
email: account.email,
|
||||
status: account.status,
|
||||
avatarColor,
|
||||
};
|
||||
|
||||
get currentAccount$() {
|
||||
return this.currentAccountService.currentAccount$;
|
||||
return currentAccount;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async currentAccountClicked() {
|
||||
if (this.route.snapshot.data.state.includes("account-switcher")) {
|
||||
this.location.back();
|
||||
} else {
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.router.navigate(["/account-switcher"]);
|
||||
await this.router.navigate(["/account-switcher"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { matches, mock } from "jest-mock-extended";
|
||||
import { BehaviorSubject, firstValueFrom, timeout } from "rxjs";
|
||||
import { BehaviorSubject, firstValueFrom, of, timeout } from "rxjs";
|
||||
|
||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { AccountSwitcherService } from "./account-switcher.service";
|
||||
@ -16,7 +16,7 @@ describe("AccountSwitcherService", () => {
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>(null);
|
||||
|
||||
const accountService = mock<AccountService>();
|
||||
const stateService = mock<StateService>();
|
||||
const avatarService = mock<AvatarService>();
|
||||
const messagingService = mock<MessagingService>();
|
||||
const environmentService = mock<EnvironmentService>();
|
||||
const logService = mock<LogService>();
|
||||
@ -25,11 +25,13 @@ describe("AccountSwitcherService", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
||||
accountService.accounts$ = accountsSubject;
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
|
||||
accountSwitcherService = new AccountSwitcherService(
|
||||
accountService,
|
||||
stateService,
|
||||
avatarService,
|
||||
messagingService,
|
||||
environmentService,
|
||||
logService,
|
||||
@ -44,6 +46,7 @@ describe("AccountSwitcherService", () => {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
};
|
||||
|
||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||
accountsSubject.next({
|
||||
"1": user1AccountInfo,
|
||||
} as Record<UserId, AccountInfo>);
|
||||
@ -72,6 +75,7 @@ describe("AccountSwitcherService", () => {
|
||||
status: AuthenticationStatus.Unlocked,
|
||||
};
|
||||
}
|
||||
avatarService.getUserAvatarColor$.mockReturnValue(of("#cccccc"));
|
||||
accountsSubject.next(seedAccounts);
|
||||
activeAccountSubject.next(
|
||||
Object.assign(seedAccounts["1" as UserId], { id: "1" as UserId }),
|
||||
|
@ -11,11 +11,11 @@ import {
|
||||
} from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { fromChromeEvent } from "../../../../platform/browser/from-chrome-event";
|
||||
@ -44,7 +44,7 @@ export class AccountSwitcherService {
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private stateService: StateService,
|
||||
private avatarService: AvatarService,
|
||||
private messagingService: MessagingService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logService: LogService,
|
||||
@ -68,7 +68,9 @@ export class AccountSwitcherService {
|
||||
server: await this.environmentService.getHost(id),
|
||||
status: account.status,
|
||||
isActive: id === activeAccount?.id,
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: id }),
|
||||
avatarColor: await firstValueFrom(
|
||||
this.avatarService.getUserAvatarColor$(id as UserId),
|
||||
),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
@ -1,44 +0,0 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { Observable, switchMap } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
export type CurrentAccount = {
|
||||
id: UserId;
|
||||
name: string | undefined;
|
||||
email: string;
|
||||
status: AuthenticationStatus;
|
||||
avatarColor: string;
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class CurrentAccountService {
|
||||
currentAccount$: Observable<CurrentAccount>;
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.currentAccount$ = this.accountService.activeAccount$.pipe(
|
||||
switchMap(async (account) => {
|
||||
if (account == null) {
|
||||
return null;
|
||||
}
|
||||
const currentAccount: CurrentAccount = {
|
||||
id: account.id,
|
||||
name: account.name || account.email,
|
||||
email: account.email,
|
||||
status: account.status,
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: account.id }),
|
||||
};
|
||||
|
||||
return currentAccount;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -3380,6 +3380,34 @@ describe("AutofillService", () => {
|
||||
|
||||
expect(value).toBe(false);
|
||||
});
|
||||
|
||||
it("validates attribute identifiers with mixed camel case and non-alpha characters", () => {
|
||||
const attributes: Record<string, boolean> = {
|
||||
_$1_go_look: true,
|
||||
go_look: true,
|
||||
goLook: true,
|
||||
go1look: true,
|
||||
"go look": true,
|
||||
look_go: true,
|
||||
findPerson: true,
|
||||
query$1: true,
|
||||
look_goo: false,
|
||||
golook: false,
|
||||
lookgo: false,
|
||||
logonField: false,
|
||||
ego_input: false,
|
||||
"Gold Password": false,
|
||||
searching_for: false,
|
||||
person_finder: false,
|
||||
};
|
||||
const autofillFieldMocks = Object.keys(attributes).map((key) =>
|
||||
createAutofillFieldMock({ htmlID: key }),
|
||||
);
|
||||
autofillFieldMocks.forEach((field) => {
|
||||
const value = AutofillService["isSearchField"](field);
|
||||
expect(value).toBe(attributes[field.htmlID]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("isFieldMatch", () => {
|
||||
|
@ -44,6 +44,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
private openPasswordRepromptPopoutDebounce: NodeJS.Timeout;
|
||||
private currentlyOpeningPasswordRepromptPopout = false;
|
||||
private autofillScriptPortsSet = new Set<chrome.runtime.Port>();
|
||||
static searchFieldNamesSet = new Set(AutoFillConstants.SearchFieldNames);
|
||||
|
||||
constructor(
|
||||
private cipherService: CipherService,
|
||||
@ -1380,11 +1381,33 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return excludedTypes.indexOf(type) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if a passed field contains text artifacts that identify it as a search field.
|
||||
*
|
||||
* @param field - The autofill field that we are validating as a search field
|
||||
*/
|
||||
private static isSearchField(field: AutofillField) {
|
||||
const matchFieldAttributeValues = [field.type, field.htmlName, field.htmlID, field.placeholder];
|
||||
const matchPattern = new RegExp(AutoFillConstants.SearchFieldNames.join("|"), "gi");
|
||||
for (let attrIndex = 0; attrIndex < matchFieldAttributeValues.length; attrIndex++) {
|
||||
if (!matchFieldAttributeValues[attrIndex]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Boolean(matchFieldAttributeValues.join(" ").match(matchPattern));
|
||||
// Separate camel case words and case them to lower case values
|
||||
const camelCaseSeparatedFieldAttribute = matchFieldAttributeValues[attrIndex]
|
||||
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
||||
.toLowerCase();
|
||||
// Split the attribute by non-alphabetical characters to get the keywords
|
||||
const attributeKeywords = camelCaseSeparatedFieldAttribute.split(/[^a-z]/gi);
|
||||
|
||||
for (let keywordIndex = 0; keywordIndex < attributeKeywords.length; keywordIndex++) {
|
||||
if (AutofillService.searchFieldNamesSet.has(attributeKeywords[keywordIndex])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static isExcludedFieldType(field: AutofillField, excludedTypes: string[]) {
|
||||
@ -1397,11 +1420,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
}
|
||||
|
||||
// Check if the input is an untyped/mistyped search input
|
||||
if (this.isSearchField(field)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return this.isSearchField(field);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1525,11 +1544,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !AutoFillConstants.PasswordFieldExcludeList.some((i) => cleanedValue.indexOf(i) > -1);
|
||||
}
|
||||
|
||||
static fieldHasDisqualifyingAttributeValue(field: AutofillField) {
|
||||
@ -1572,7 +1587,11 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
const arr: AutofillField[] = [];
|
||||
|
||||
pageDetails.fields.forEach((f) => {
|
||||
if (AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)) {
|
||||
const isPassword = f.type === "password";
|
||||
if (
|
||||
!isPassword &&
|
||||
AutofillService.isExcludedFieldType(f, AutoFillConstants.ExcludedAutofillLoginTypes)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1581,23 +1600,16 @@ export default class AutofillService implements AutofillServiceInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
const isPassword = f.type === "password";
|
||||
|
||||
const isLikePassword = () => {
|
||||
if (f.type !== "text") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.htmlID)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.htmlName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AutofillService.valueIsLikePassword(f.placeholder)) {
|
||||
return true;
|
||||
const testedValues = [f.htmlID, f.htmlName, f.placeholder];
|
||||
for (let i = 0; i < testedValues.length; i++) {
|
||||
if (AutofillService.valueIsLikePassword(testedValues[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -755,6 +755,9 @@ class CollectAutofillContentService implements CollectAutofillContentServiceInte
|
||||
|
||||
// Prioritize capturing text content from elements rather than nodes.
|
||||
currentElement = currentElement.parentElement || currentElement.parentNode;
|
||||
if (!currentElement) {
|
||||
return textContentItems;
|
||||
}
|
||||
|
||||
let siblingElement = nodeIsElement(currentElement)
|
||||
? currentElement.previousElementSibling
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
AuthRequestServiceAbstraction,
|
||||
AuthRequestService,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { AvatarUpdateService as AvatarUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
@ -39,6 +38,7 @@ import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authenticatio
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
|
||||
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
@ -117,7 +117,6 @@ import { DefaultStateProvider } from "@bitwarden/common/platform/state/implement
|
||||
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
|
||||
/* eslint-enable import/no-restricted-paths */
|
||||
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
@ -125,6 +124,7 @@ import { EventUploadService } from "@bitwarden/common/services/event/event-uploa
|
||||
import { NotificationsService } from "@bitwarden/common/services/notifications.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/src/auth/abstractions/avatar.service";
|
||||
import {
|
||||
PasswordGenerationService,
|
||||
PasswordGenerationServiceAbstraction,
|
||||
@ -288,7 +288,7 @@ export default class MainBackground {
|
||||
fido2UserInterfaceService: Fido2UserInterfaceServiceAbstraction;
|
||||
fido2AuthenticatorService: Fido2AuthenticatorServiceAbstraction;
|
||||
fido2ClientService: Fido2ClientServiceAbstraction;
|
||||
avatarUpdateService: AvatarUpdateServiceAbstraction;
|
||||
avatarService: AvatarServiceAbstraction;
|
||||
mainContextMenuHandler: MainContextMenuHandler;
|
||||
cipherContextMenuHandler: CipherContextMenuHandler;
|
||||
configService: BrowserConfigService;
|
||||
@ -409,8 +409,7 @@ export default class MainBackground {
|
||||
);
|
||||
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
|
||||
this.accountService,
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
this.singleUserStateProvider,
|
||||
);
|
||||
this.derivedStateProvider = new BackgroundDerivedStateProvider(
|
||||
this.memoryStorageForStateProviders,
|
||||
@ -685,7 +684,11 @@ export default class MainBackground {
|
||||
this.fileUploadService,
|
||||
this.sendService,
|
||||
);
|
||||
|
||||
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
||||
|
||||
this.providerService = new ProviderService(this.stateProvider);
|
||||
|
||||
this.syncService = new SyncService(
|
||||
this.apiService,
|
||||
this.domainSettingsService,
|
||||
@ -703,6 +706,7 @@ export default class MainBackground {
|
||||
this.folderApiService,
|
||||
this.organizationService,
|
||||
this.sendApiService,
|
||||
this.avatarService,
|
||||
logoutCallback,
|
||||
);
|
||||
this.eventUploadService = new EventUploadService(
|
||||
@ -943,8 +947,6 @@ export default class MainBackground {
|
||||
this.apiService,
|
||||
);
|
||||
|
||||
this.avatarUpdateService = new AvatarUpdateService(this.apiService, this.stateService);
|
||||
|
||||
if (!this.popupOnlyContext) {
|
||||
this.mainContextMenuHandler = new MainContextMenuHandler(
|
||||
this.stateService,
|
||||
|
@ -97,6 +97,10 @@ export default class RuntimeBackground {
|
||||
case "unlocked": {
|
||||
let item: LockedVaultPendingNotificationsData;
|
||||
|
||||
if (msg.command === "loggedIn") {
|
||||
await this.sendBwInstalledMessageToVault();
|
||||
}
|
||||
|
||||
if (this.lockedVaultPendingNotifications?.length > 0) {
|
||||
item = this.lockedVaultPendingNotifications.pop();
|
||||
await closeUnlockPopout();
|
||||
@ -130,9 +134,6 @@ export default class RuntimeBackground {
|
||||
await this.main.refreshBadge();
|
||||
await this.main.refreshMenu();
|
||||
}, 2000);
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.main.avatarUpdateService.loadColorFromState();
|
||||
this.configService.triggerServerConfigFetch();
|
||||
}
|
||||
break;
|
||||
@ -354,8 +355,6 @@ export default class RuntimeBackground {
|
||||
if (await this.environmentService.hasManagedEnvironment()) {
|
||||
await this.environmentService.setUrlsToManagedEnvironment();
|
||||
}
|
||||
|
||||
await this.sendBwInstalledMessageToVault();
|
||||
}
|
||||
|
||||
this.onInstalledReason = null;
|
||||
|
@ -9,20 +9,15 @@ import {
|
||||
|
||||
import { CachedServices, FactoryOptions, factory } from "./factory-options";
|
||||
import {
|
||||
StateEventRegistrarServiceInitOptions,
|
||||
stateEventRegistrarServiceFactory,
|
||||
} from "./state-event-registrar-service.factory";
|
||||
import {
|
||||
StorageServiceProviderInitOptions,
|
||||
storageServiceProviderFactory,
|
||||
} from "./storage-service-provider.factory";
|
||||
SingleUserStateProviderInitOptions,
|
||||
singleUserStateProviderFactory,
|
||||
} from "./single-user-state-provider.factory";
|
||||
|
||||
type ActiveUserStateProviderFactory = FactoryOptions;
|
||||
|
||||
export type ActiveUserStateProviderInitOptions = ActiveUserStateProviderFactory &
|
||||
AccountServiceInitOptions &
|
||||
StorageServiceProviderInitOptions &
|
||||
StateEventRegistrarServiceInitOptions;
|
||||
SingleUserStateProviderInitOptions;
|
||||
|
||||
export async function activeUserStateProviderFactory(
|
||||
cache: { activeUserStateProvider?: ActiveUserStateProvider } & CachedServices,
|
||||
@ -35,8 +30,7 @@ export async function activeUserStateProviderFactory(
|
||||
async () =>
|
||||
new DefaultActiveUserStateProvider(
|
||||
await accountServiceFactory(cache, opts),
|
||||
await storageServiceProviderFactory(cache, opts),
|
||||
await stateEventRegistrarServiceFactory(cache, opts),
|
||||
await singleUserStateProviderFactory(cache, opts),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -114,8 +114,10 @@ export class OptionsComponent implements OnInit {
|
||||
this.autofillSettingsService.enableContextMenu$,
|
||||
);
|
||||
|
||||
this.showCardsCurrentTab = !(await this.stateService.getDontShowCardsCurrentTab());
|
||||
this.showIdentitiesCurrentTab = !(await this.stateService.getDontShowIdentitiesCurrentTab());
|
||||
this.showCardsCurrentTab = await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$);
|
||||
this.showIdentitiesCurrentTab = await firstValueFrom(
|
||||
this.vaultSettingsService.showIdentitiesCurrentTab$,
|
||||
);
|
||||
|
||||
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
|
||||
|
||||
@ -178,11 +180,11 @@ export class OptionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async updateShowCardsCurrentTab() {
|
||||
await this.stateService.setDontShowCardsCurrentTab(!this.showCardsCurrentTab);
|
||||
await this.vaultSettingsService.setShowCardsCurrentTab(this.showCardsCurrentTab);
|
||||
}
|
||||
|
||||
async updateShowIdentitiesCurrentTab() {
|
||||
await this.stateService.setDontShowIdentitiesCurrentTab(!this.showIdentitiesCurrentTab);
|
||||
await this.vaultSettingsService.setShowIdentitiesCurrentTab(this.showIdentitiesCurrentTab);
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
|
@ -10,10 +10,10 @@ import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/s
|
||||
import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { SyncService } from "@bitwarden/common/vault/abstractions/sync/sync.service.abstraction";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
@ -65,11 +65,11 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private syncService: SyncService,
|
||||
private searchService: SearchService,
|
||||
private stateService: StateService,
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private passwordRepromptService: PasswordRepromptService,
|
||||
private organizationService: OrganizationService,
|
||||
private vaultFilterService: VaultFilterService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -271,8 +271,10 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
const otherTypes: CipherType[] = [];
|
||||
const dontShowCards = await this.stateService.getDontShowCardsCurrentTab();
|
||||
const dontShowIdentities = await this.stateService.getDontShowIdentitiesCurrentTab();
|
||||
const dontShowCards = !(await firstValueFrom(this.vaultSettingsService.showCardsCurrentTab$));
|
||||
const dontShowIdentities = !(await firstValueFrom(
|
||||
this.vaultSettingsService.showIdentitiesCurrentTab$,
|
||||
));
|
||||
this.showOrganizations = this.organizationService.hasOrganizations();
|
||||
if (!dontShowCards) {
|
||||
otherTypes.push(CipherType.Card);
|
||||
|
@ -23,10 +23,12 @@ import { PolicyApiService } from "@bitwarden/common/admin-console/services/polic
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||
import { ProviderService } from "@bitwarden/common/admin-console/services/provider.service";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
import { KeyConnectorService } from "@bitwarden/common/auth/services/key-connector.service";
|
||||
@ -216,6 +218,7 @@ export class Main {
|
||||
derivedStateProvider: DerivedStateProvider;
|
||||
stateProvider: StateProvider;
|
||||
loginStrategyService: LoginStrategyServiceAbstraction;
|
||||
avatarService: AvatarServiceAbstraction;
|
||||
stateEventRunnerService: StateEventRunnerService;
|
||||
biometricStateService: BiometricStateService;
|
||||
|
||||
@ -291,8 +294,7 @@ export class Main {
|
||||
|
||||
this.activeUserStateProvider = new DefaultActiveUserStateProvider(
|
||||
this.accountService,
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
this.singleUserStateProvider,
|
||||
);
|
||||
|
||||
this.derivedStateProvider = new DefaultDerivedStateProvider(
|
||||
@ -555,6 +557,8 @@ export class Main {
|
||||
null,
|
||||
);
|
||||
|
||||
this.avatarService = new AvatarService(this.apiService, this.stateProvider);
|
||||
|
||||
this.syncService = new SyncService(
|
||||
this.apiService,
|
||||
this.domainSettingsService,
|
||||
@ -572,6 +576,7 @@ export class Main {
|
||||
this.folderApiService,
|
||||
this.organizationService,
|
||||
this.sendApiService,
|
||||
this.avatarService,
|
||||
async (expired: boolean) => await this.logout(),
|
||||
);
|
||||
|
||||
|
@ -2,9 +2,10 @@ import { animate, state, style, transition, trigger } from "@angular/animations"
|
||||
import { ConnectedPosition } from "@angular/cdk/overlay";
|
||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { concatMap, Subject, takeUntil } from "rxjs";
|
||||
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
@ -12,6 +13,7 @@ import { MessagingService } from "@bitwarden/common/platform/abstractions/messag
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { Account } from "@bitwarden/common/platform/models/domain/account";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
type ActiveAccount = {
|
||||
id: string;
|
||||
@ -84,6 +86,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private authService: AuthService,
|
||||
private avatarService: AvatarService,
|
||||
private messagingService: MessagingService,
|
||||
private router: Router,
|
||||
private tokenService: TokenService,
|
||||
@ -101,7 +104,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
id: await this.tokenService.getUserId(),
|
||||
name: (await this.tokenService.getName()) ?? (await this.tokenService.getEmail()),
|
||||
email: await this.tokenService.getEmail(),
|
||||
avatarColor: await this.stateService.getAvatarColor(),
|
||||
avatarColor: await firstValueFrom(this.avatarService.avatarColor$),
|
||||
server: await this.environmentService.getHost(),
|
||||
};
|
||||
} catch {
|
||||
@ -154,7 +157,7 @@ export class AccountSwitcherComponent implements OnInit, OnDestroy {
|
||||
name: baseAccounts[userId].profile.name,
|
||||
email: baseAccounts[userId].profile.email,
|
||||
authenticationStatus: await this.authService.getAuthStatus(userId),
|
||||
avatarColor: await this.stateService.getAvatarColor({ userId: userId }),
|
||||
avatarColor: await firstValueFrom(this.avatarService.getUserAvatarColor$(userId as UserId)),
|
||||
server: await this.environmentService.getHost(userId),
|
||||
};
|
||||
}
|
||||
|
@ -124,13 +124,14 @@ export class Main {
|
||||
storageServiceProvider,
|
||||
);
|
||||
|
||||
const singleUserStateProvider = new DefaultSingleUserStateProvider(
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
);
|
||||
|
||||
const stateProvider = new DefaultStateProvider(
|
||||
new DefaultActiveUserStateProvider(
|
||||
accountService,
|
||||
storageServiceProvider,
|
||||
stateEventRegistrarService,
|
||||
),
|
||||
new DefaultSingleUserStateProvider(storageServiceProvider, stateEventRegistrarService),
|
||||
new DefaultActiveUserStateProvider(accountService, singleUserStateProvider),
|
||||
singleUserStateProvider,
|
||||
globalStateProvider,
|
||||
new DefaultDerivedStateProvider(this.memoryStorageForStateProviders),
|
||||
);
|
||||
|
@ -399,7 +399,11 @@
|
||||
</bit-tab>
|
||||
<bit-tab *ngIf="organization.useGroups" [label]="'groups' | i18n">
|
||||
<div class="tw-mb-6">
|
||||
{{ "groupAccessUserDesc" | i18n }}
|
||||
{{
|
||||
(restrictedAccess$ | async)
|
||||
? ("restrictedGroupAccess" | i18n)
|
||||
: ("groupAccessUserDesc" | i18n)
|
||||
}}
|
||||
</div>
|
||||
<bit-access-selector
|
||||
formControlName="groups"
|
||||
@ -408,10 +412,14 @@
|
||||
[selectorLabelText]="'selectGroups' | i18n"
|
||||
[emptySelectionText]="'noGroupsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
></bit-access-selector>
|
||||
</bit-tab>
|
||||
<bit-tab [label]="'collections' | i18n">
|
||||
<div *ngIf="organization.useGroups" class="tw-mb-6">
|
||||
<div class="tw-mb-6" *ngIf="restrictedAccess$ | async">
|
||||
{{ "restrictedCollectionAccess" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="organization.useGroups && !(restrictedAccess$ | async)" class="tw-mb-6">
|
||||
{{ "userPermissionOverrideHelper" | i18n }}
|
||||
</div>
|
||||
<div *ngIf="!organization.flexibleCollections" class="tw-mb-6">
|
||||
@ -441,6 +449,7 @@
|
||||
[selectorLabelText]="'selectCollections' | i18n"
|
||||
[emptySelectionText]="'noCollectionsAdded' | i18n"
|
||||
[flexibleCollectionsEnabled]="organization.flexibleCollections"
|
||||
[hideMultiSelect]="restrictedAccess$ | async"
|
||||
></bit-access-selector
|
||||
></bit-tab>
|
||||
</bit-tab-group>
|
||||
|
@ -4,6 +4,7 @@ import { FormBuilder, Validators } from "@angular/forms";
|
||||
import {
|
||||
combineLatest,
|
||||
firstValueFrom,
|
||||
map,
|
||||
Observable,
|
||||
of,
|
||||
shareReplay,
|
||||
@ -20,7 +21,9 @@ import {
|
||||
} from "@bitwarden/common/admin-console/enums";
|
||||
import { PermissionsApi } from "@bitwarden/common/admin-console/models/api/permissions.api";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { ProductType } from "@bitwarden/common/enums";
|
||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
@ -99,6 +102,8 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
groups: [[] as AccessItemValue[]],
|
||||
});
|
||||
|
||||
protected restrictedAccess$: Observable<boolean>;
|
||||
|
||||
protected permissionsGroup = this.formBuilder.group({
|
||||
manageAssignedCollectionsGroup: this.formBuilder.group<Record<string, boolean>>({
|
||||
manageAssignedCollections: false,
|
||||
@ -144,6 +149,7 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
private organizationUserService: OrganizationUserService,
|
||||
private dialogService: DialogService,
|
||||
private configService: ConfigServiceAbstraction,
|
||||
private accountService: AccountService,
|
||||
organizationService: OrganizationService,
|
||||
) {
|
||||
this.organization$ = organizationService
|
||||
@ -162,12 +168,43 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
),
|
||||
);
|
||||
|
||||
const userDetails$ = this.params.organizationUserId
|
||||
? this.userService.get(this.params.organizationId, this.params.organizationUserId)
|
||||
: of(null);
|
||||
|
||||
// The orgUser cannot manage their own Group assignments if collection access is restricted
|
||||
// TODO: fix disabled state of access-selector rows so that any controls are hidden
|
||||
this.restrictedAccess$ = combineLatest([
|
||||
this.organization$,
|
||||
userDetails$,
|
||||
this.accountService.activeAccount$,
|
||||
this.configService.getFeatureFlag$(FeatureFlag.FlexibleCollectionsV1),
|
||||
]).pipe(
|
||||
map(
|
||||
([organization, userDetails, activeAccount, flexibleCollectionsV1Enabled]) =>
|
||||
// Feature flag conditionals
|
||||
flexibleCollectionsV1Enabled &&
|
||||
organization.flexibleCollections &&
|
||||
// Business logic conditionals
|
||||
userDetails != null &&
|
||||
userDetails.userId == activeAccount.id &&
|
||||
!organization.allowAdminAccessToAllCollectionItems,
|
||||
),
|
||||
shareReplay({ refCount: true, bufferSize: 1 }),
|
||||
);
|
||||
|
||||
this.restrictedAccess$.pipe(takeUntil(this.destroy$)).subscribe((restrictedAccess) => {
|
||||
if (restrictedAccess) {
|
||||
this.formGroup.controls.groups.disable();
|
||||
} else {
|
||||
this.formGroup.controls.groups.enable();
|
||||
}
|
||||
});
|
||||
|
||||
combineLatest({
|
||||
organization: this.organization$,
|
||||
collections: this.collectionAdminService.getAll(this.params.organizationId),
|
||||
userDetails: this.params.organizationUserId
|
||||
? this.userService.get(this.params.organizationId, this.params.organizationUserId)
|
||||
: of(null),
|
||||
userDetails: userDetails$,
|
||||
groups: groups$,
|
||||
})
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
@ -369,7 +406,11 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
userView.collections = this.formGroup.value.access
|
||||
.filter((v) => v.type === AccessItemType.Collection)
|
||||
.map(convertToSelectionView);
|
||||
userView.groups = this.formGroup.value.groups.map((m) => m.id);
|
||||
|
||||
userView.groups = (await firstValueFrom(this.restrictedAccess$))
|
||||
? null
|
||||
: this.formGroup.value.groups.map((m) => m.id);
|
||||
|
||||
userView.accessSecretsManager = this.formGroup.value.accessSecretsManager;
|
||||
|
||||
if (this.editMode) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<!-- Please remove this disable statement when editing this file! -->
|
||||
<!-- eslint-disable tailwindcss/no-custom-classname -->
|
||||
<div class="tw-flex">
|
||||
<div class="tw-flex" *ngIf="!hideMultiSelect">
|
||||
<bit-form-field *ngIf="permissionMode == 'edit'" class="tw-mr-3 tw-shrink-0">
|
||||
<bit-label>{{ "permission" | i18n }}</bit-label>
|
||||
<!--
|
||||
|
@ -197,6 +197,11 @@ export class AccessSelectorComponent implements ControlValueAccessor, OnInit, On
|
||||
this.permissionList = getPermissionList(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the multi-select so that new items cannot be added
|
||||
*/
|
||||
@Input() hideMultiSelect = false;
|
||||
|
||||
private _flexibleCollectionsEnabled: boolean;
|
||||
|
||||
constructor(
|
||||
|
@ -9,9 +9,9 @@ import {
|
||||
ViewChild,
|
||||
ViewEncapsulation,
|
||||
} from "@angular/core";
|
||||
import { BehaviorSubject, debounceTime, Subject, takeUntil } from "rxjs";
|
||||
import { BehaviorSubject, debounceTime, firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { ProfileResponse } from "@bitwarden/common/models/response/profile.response";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
@ -55,7 +55,7 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy {
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private logService: LogService,
|
||||
private accountUpdateService: AvatarUpdateService,
|
||||
private avatarService: AvatarService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@ -73,9 +73,7 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy {
|
||||
this.currentSelection = color;
|
||||
});
|
||||
|
||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.setSelection(await this.accountUpdateService.loadColorFromState());
|
||||
await this.setSelection(await firstValueFrom(this.avatarService.avatarColor$));
|
||||
}
|
||||
|
||||
async showCustomPicker() {
|
||||
@ -93,7 +91,7 @@ export class ChangeAvatarComponent implements OnInit, OnDestroy {
|
||||
async submit() {
|
||||
try {
|
||||
if (Utils.validateHexColor(this.currentSelection) || this.currentSelection == null) {
|
||||
await this.accountUpdateService.pushUpdate(this.currentSelection);
|
||||
await this.avatarService.setAvatarColor(this.currentSelection);
|
||||
this.changeColor.emit(this.currentSelection);
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("avatarUpdated"));
|
||||
} else {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component, Input, OnDestroy } from "@angular/core";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { Subject } from "rxjs";
|
||||
|
||||
import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
|
||||
import { SharedModule } from "../shared";
|
||||
|
||||
@ -29,14 +29,14 @@ export class DynamicAvatarComponent implements OnDestroy {
|
||||
@Input() text: string;
|
||||
@Input() title: string;
|
||||
@Input() size: SizeTypes = "default";
|
||||
color$: Observable<string | null>;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(private accountUpdateService: AvatarUpdateService) {
|
||||
color$ = this.avatarService.avatarColor$;
|
||||
|
||||
constructor(private avatarService: AvatarService) {
|
||||
if (this.text) {
|
||||
this.text = this.text.toUpperCase();
|
||||
}
|
||||
this.color$ = this.accountUpdateService.avatarUpdate$;
|
||||
}
|
||||
|
||||
async ngOnDestroy() {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { importProvidersFrom } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { applicationConfig, Meta, moduleMetadata, Story } from "@storybook/angular";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BehaviorSubject, of } from "rxjs";
|
||||
|
||||
import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { OrganizationUserType } from "@bitwarden/common/admin-console/enums";
|
||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { ConfigServiceAbstraction } from "@bitwarden/common/platform/abstractions/config/config.service.abstraction";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
@ -72,12 +72,10 @@ export default {
|
||||
} as Partial<SettingsService>,
|
||||
},
|
||||
{
|
||||
provide: AvatarUpdateService,
|
||||
provide: AvatarService,
|
||||
useValue: {
|
||||
async loadColorFromState() {
|
||||
return "#FF0000";
|
||||
},
|
||||
} as Partial<AvatarUpdateService>,
|
||||
avatarColor$: of("#FF0000"),
|
||||
} as Partial<AvatarService>,
|
||||
},
|
||||
{
|
||||
provide: TokenService,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Component, Input, OnChanges } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AvatarUpdateService } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { TokenService } from "@bitwarden/common/auth/abstractions/token.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
@ -24,7 +25,7 @@ export class OrganizationNameBadgeComponent implements OnChanges {
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private avatarService: AvatarUpdateService,
|
||||
private avatarService: AvatarService,
|
||||
private tokenService: TokenService,
|
||||
) {}
|
||||
|
||||
@ -35,7 +36,7 @@ export class OrganizationNameBadgeComponent implements OnChanges {
|
||||
|
||||
if (this.isMe) {
|
||||
this.name = this.i18nService.t("me");
|
||||
this.color = await this.avatarService.loadColorFromState();
|
||||
this.color = await firstValueFrom(this.avatarService.avatarColor$);
|
||||
if (this.color == null) {
|
||||
const userId = await this.tokenService.getUserId();
|
||||
if (userId != null) {
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provayder Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Портал за доставчици"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portal del proveïdor"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portál poskytovatele"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Udbyderportal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Anbieterportal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Toimittajaportaali"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portail fournisseur"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Szolgáltató portál"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portale Fornitori"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "プロバイダーポータル"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "あなた自身をグループに追加することはできません。"
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "コレクションに自分自身を追加することはできません。"
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Nodrošinātāju portāls"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Providerportaal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portal dostawcy"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portal do fornecedor"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "Não se pode adicionar a si próprio a grupos."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "Não se pode adicionar a si próprio a coleções."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Портал провайдера"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Portál poskytovateľa"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Портал провајдера"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Sağlayıcı Portalı"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Портал провайдера"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "提供商门户"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -7605,5 +7605,11 @@
|
||||
},
|
||||
"providerPortal": {
|
||||
"message": "Provider Portal"
|
||||
},
|
||||
"restrictedGroupAccess": {
|
||||
"message": "You cannot add yourself to groups."
|
||||
},
|
||||
"restrictedCollectionAccess": {
|
||||
"message": "You cannot add yourself to collections."
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
LoginStrategyServiceAbstraction,
|
||||
LoginStrategyService,
|
||||
} from "@bitwarden/auth/common";
|
||||
import { AvatarUpdateService as AccountUpdateServiceAbstraction } from "@bitwarden/common/abstractions/account/avatar-update.service";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
import { EventCollectionService as EventCollectionServiceAbstraction } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||
@ -51,6 +50,7 @@ import {
|
||||
} from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AnonymousHubService as AnonymousHubServiceAbstraction } from "@bitwarden/common/auth/abstractions/anonymous-hub.service";
|
||||
import { AuthService as AuthServiceAbstraction } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "@bitwarden/common/auth/abstractions/avatar.service";
|
||||
import { DeviceTrustCryptoServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust-crypto.service.abstraction";
|
||||
import { DevicesServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices/devices.service.abstraction";
|
||||
import { DevicesApiServiceAbstraction } from "@bitwarden/common/auth/abstractions/devices-api.service.abstraction";
|
||||
@ -69,6 +69,7 @@ import { AccountApiServiceImplementation } from "@bitwarden/common/auth/services
|
||||
import { AccountServiceImplementation } from "@bitwarden/common/auth/services/account.service";
|
||||
import { AnonymousHubService } from "@bitwarden/common/auth/services/anonymous-hub.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
import { AvatarService } from "@bitwarden/common/auth/services/avatar.service";
|
||||
import { DeviceTrustCryptoService } from "@bitwarden/common/auth/services/device-trust-crypto.service.implementation";
|
||||
import { DevicesServiceImplementation } from "@bitwarden/common/auth/services/devices/devices.service.implementation";
|
||||
import { DevicesApiServiceImplementation } from "@bitwarden/common/auth/services/devices-api.service.implementation";
|
||||
@ -163,7 +164,6 @@ import {
|
||||
DefaultThemeStateService,
|
||||
ThemeStateService,
|
||||
} from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
@ -452,9 +452,9 @@ const typesafeProviders: Array<SafeProvider> = [
|
||||
useExisting: InternalAccountService,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AccountUpdateServiceAbstraction,
|
||||
useClass: AvatarUpdateService,
|
||||
deps: [ApiServiceAbstraction, StateServiceAbstraction],
|
||||
provide: AvatarServiceAbstraction,
|
||||
useClass: AvatarService,
|
||||
deps: [ApiServiceAbstraction, StateProvider],
|
||||
}),
|
||||
safeProvider({ provide: LogService, useFactory: () => new ConsoleLogService(false), deps: [] }),
|
||||
safeProvider({
|
||||
@ -561,6 +561,7 @@ const typesafeProviders: Array<SafeProvider> = [
|
||||
FolderApiServiceAbstraction,
|
||||
InternalOrganizationServiceAbstraction,
|
||||
SendApiServiceAbstraction,
|
||||
AvatarServiceAbstraction,
|
||||
LOGOUT_CALLBACK,
|
||||
],
|
||||
}),
|
||||
@ -953,7 +954,7 @@ const typesafeProviders: Array<SafeProvider> = [
|
||||
safeProvider({
|
||||
provide: ActiveUserStateProvider,
|
||||
useClass: DefaultActiveUserStateProvider,
|
||||
deps: [AccountServiceAbstraction, StorageServiceProvider, StateEventRegistrarService],
|
||||
deps: [AccountServiceAbstraction, SingleUserStateProvider],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: SingleUserStateProvider,
|
||||
|
@ -119,7 +119,7 @@ export class FakeActiveUserStateProvider implements ActiveUserStateProvider {
|
||||
states: Map<string, FakeActiveUserState<unknown>> = new Map();
|
||||
|
||||
constructor(public accountService: FakeAccountService) {
|
||||
this.activeUserId$ = accountService.activeAccountSubject.asObservable().pipe(map((a) => a.id));
|
||||
this.activeUserId$ = accountService.activeAccountSubject.asObservable().pipe(map((a) => a?.id));
|
||||
}
|
||||
|
||||
get<T>(keyDefinition: KeyDefinition<T> | UserKeyDefinition<T>): ActiveUserState<T> {
|
||||
|
@ -1,8 +0,0 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ProfileResponse } from "../../models/response/profile.response";
|
||||
export abstract class AvatarUpdateService {
|
||||
avatarUpdate$ = new Observable<string | null>();
|
||||
abstract pushUpdate(color: string): Promise<ProfileResponse | void>;
|
||||
abstract loadColorFromState(): Promise<string | null>;
|
||||
}
|
@ -8,7 +8,6 @@ import {
|
||||
OrganizationUserInviteRequest,
|
||||
OrganizationUserResetPasswordEnrollmentRequest,
|
||||
OrganizationUserResetPasswordRequest,
|
||||
OrganizationUserUpdateGroupsRequest,
|
||||
OrganizationUserUpdateRequest,
|
||||
} from "./requests";
|
||||
import {
|
||||
@ -165,18 +164,6 @@ export abstract class OrganizationUserService {
|
||||
request: OrganizationUserUpdateRequest,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Update an organization user's groups
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
* @param groupIds - List of group ids to associate the user with
|
||||
*/
|
||||
abstract putOrganizationUserGroups(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
groupIds: OrganizationUserUpdateGroupsRequest,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Update an organization user's reset password enrollment
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
|
@ -6,4 +6,3 @@ export * from "./organization-user-invite.request";
|
||||
export * from "./organization-user-reset-password.request";
|
||||
export * from "./organization-user-reset-password-enrollment.request";
|
||||
export * from "./organization-user-update.request";
|
||||
export * from "./organization-user-update-groups.request";
|
||||
|
@ -1,3 +0,0 @@
|
||||
export class OrganizationUserUpdateGroupsRequest {
|
||||
groupIds: string[] = [];
|
||||
}
|
@ -9,7 +9,6 @@ import {
|
||||
OrganizationUserInviteRequest,
|
||||
OrganizationUserResetPasswordEnrollmentRequest,
|
||||
OrganizationUserResetPasswordRequest,
|
||||
OrganizationUserUpdateGroupsRequest,
|
||||
OrganizationUserUpdateRequest,
|
||||
} from "../../abstractions/organization-user/requests";
|
||||
import {
|
||||
@ -233,20 +232,6 @@ export class OrganizationUserServiceImplementation implements OrganizationUserSe
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserGroups(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
request: OrganizationUserUpdateGroupsRequest,
|
||||
): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"PUT",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/groups",
|
||||
request,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
putOrganizationUserResetPasswordEnrollment(
|
||||
organizationId: string,
|
||||
userId: string,
|
||||
|
29
libs/common/src/auth/abstractions/avatar.service.ts
Normal file
29
libs/common/src/auth/abstractions/avatar.service.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "../../types/guid";
|
||||
|
||||
export abstract class AvatarService {
|
||||
/**
|
||||
* An observable monitoring the active user's avatar color.
|
||||
* The observable updates when the avatar color changes.
|
||||
*/
|
||||
avatarColor$: Observable<string | null>;
|
||||
/**
|
||||
* Sets the avatar color of the active user
|
||||
*
|
||||
* @param color the color to set the avatar color to
|
||||
* @returns a promise that resolves when the avatar color is set
|
||||
*/
|
||||
abstract setAvatarColor(color: string): Promise<void>;
|
||||
/**
|
||||
* Gets the avatar color of the specified user.
|
||||
*
|
||||
* @remarks This is most useful for account switching where we show an
|
||||
* avatar for each account. If you only need the active user's
|
||||
* avatar color, use the avatarColor$ observable above instead.
|
||||
*
|
||||
* @param userId the userId of the user whose avatar color should be retreived
|
||||
* @return an Observable that emits a string of the avatar color of the specified user
|
||||
*/
|
||||
abstract getUserAvatarColor$(userId: UserId): Observable<string | null>;
|
||||
}
|
33
libs/common/src/auth/services/avatar.service.ts
Normal file
33
libs/common/src/auth/services/avatar.service.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ApiService } from "../../abstractions/api.service";
|
||||
import { UpdateAvatarRequest } from "../../models/request/update-avatar.request";
|
||||
import { AVATAR_DISK, StateProvider, UserKeyDefinition } from "../../platform/state";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { AvatarService as AvatarServiceAbstraction } from "../abstractions/avatar.service";
|
||||
|
||||
const AVATAR_COLOR = new UserKeyDefinition<string>(AVATAR_DISK, "avatarColor", {
|
||||
deserializer: (value) => value,
|
||||
clearOn: [],
|
||||
});
|
||||
|
||||
export class AvatarService implements AvatarServiceAbstraction {
|
||||
avatarColor$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private stateProvider: StateProvider,
|
||||
) {
|
||||
this.avatarColor$ = this.stateProvider.getActive(AVATAR_COLOR).state$;
|
||||
}
|
||||
|
||||
async setAvatarColor(color: string): Promise<void> {
|
||||
const { avatarColor } = await this.apiService.putAvatar(new UpdateAvatarRequest(color));
|
||||
|
||||
await this.stateProvider.setUserState(AVATAR_COLOR, avatarColor);
|
||||
}
|
||||
|
||||
getUserAvatarColor$(userId: UserId): Observable<string | null> {
|
||||
return this.stateProvider.getUser(userId, AVATAR_COLOR).state$;
|
||||
}
|
||||
}
|
@ -42,7 +42,7 @@ const AUTOFILL_ON_PAGE_LOAD_POLICY_TOAST_HAS_DISPLAYED = new KeyDefinition(
|
||||
);
|
||||
|
||||
const AUTO_COPY_TOTP = new KeyDefinition(AUTOFILL_SETTINGS_DISK, "autoCopyTotp", {
|
||||
deserializer: (value: boolean) => value ?? false,
|
||||
deserializer: (value: boolean) => value ?? true,
|
||||
});
|
||||
|
||||
const INLINE_MENU_VISIBILITY = new KeyDefinition(
|
||||
@ -144,7 +144,7 @@ export class AutofillSettingsService implements AutofillSettingsServiceAbstracti
|
||||
);
|
||||
|
||||
this.autoCopyTotpState = this.stateProvider.getActive(AUTO_COPY_TOTP);
|
||||
this.autoCopyTotp$ = this.autoCopyTotpState.state$.pipe(map((x) => x ?? false));
|
||||
this.autoCopyTotp$ = this.autoCopyTotpState.state$.pipe(map((x) => x ?? true));
|
||||
|
||||
this.inlineMenuVisibilityState = this.stateProvider.getGlobal(INLINE_MENU_VISIBILITY);
|
||||
this.inlineMenuVisibility$ = this.inlineMenuVisibilityState.state$.pipe(
|
||||
|
@ -4,7 +4,7 @@ import { ProfileOrganizationResponse } from "../../admin-console/models/response
|
||||
import { ProfileProviderOrganizationResponse } from "../../admin-console/models/response/profile-provider-organization.response";
|
||||
import { ProfileProviderResponse } from "../../admin-console/models/response/profile-provider.response";
|
||||
import { KdfConfig } from "../../auth/models/domain/kdf-config";
|
||||
import { OrganizationId, ProviderId } from "../../types/guid";
|
||||
import { OrganizationId, ProviderId, UserId } from "../../types/guid";
|
||||
import { UserKey, MasterKey, OrgKey, ProviderKey, PinKey, CipherKey } from "../../types/key";
|
||||
import { KeySuffixOptions, KdfType, HashPurpose } from "../enums";
|
||||
import { EncArrayBuffer } from "../models/domain/enc-array-buffer";
|
||||
@ -62,12 +62,15 @@ export abstract class CryptoService {
|
||||
getUserKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise<UserKey>;
|
||||
|
||||
/**
|
||||
* Determines whether the user key is available for the given user.
|
||||
* @param userId The desired user. If not provided, the active user will be used. If no active user exists, the method will return false.
|
||||
* @returns True if the user key is available
|
||||
*/
|
||||
hasUserKey: () => Promise<boolean>;
|
||||
hasUserKey: (userId?: UserId) => Promise<boolean>;
|
||||
/**
|
||||
* @param userId The desired user
|
||||
* @returns True if the user key is set in memory
|
||||
* Determines whether the user key is available for the given user in memory.
|
||||
* @param userId The desired user. If not provided, the active user will be used. If no active user exists, the method will return false.
|
||||
* @returns True if the user key is available
|
||||
*/
|
||||
hasUserKeyInMemory: (userId?: string) => Promise<boolean>;
|
||||
/**
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user