mirror of
https://github.com/bitwarden/directory-connector
synced 2026-02-26 01:13:14 +00:00
Compare commits
17 Commits
renovate/n
...
jslib-remo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a74973ccfa | ||
|
|
4eb74cdeb4 | ||
|
|
b1a3859516 | ||
|
|
4c7afc0e64 | ||
|
|
4b079a3ec9 | ||
|
|
77873c3075 | ||
|
|
9997e988e6 | ||
|
|
abdddacb06 | ||
|
|
d5566c56b1 | ||
|
|
a019555143 | ||
|
|
b3cb369ed8 | ||
|
|
06edf4cf91 | ||
|
|
623382f9e1 | ||
|
|
a0e74948bd | ||
|
|
9f8018e8f8 | ||
|
|
0bff38c459 | ||
|
|
94ff20f69f |
@@ -1,3 +1,7 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
# Bitwarden Directory Connector
|
||||
|
||||
## Project Overview
|
||||
@@ -20,6 +24,392 @@ Directory Connector is a TypeScript application that synchronizes users and grou
|
||||
- Node
|
||||
- Jest for testing
|
||||
|
||||
### Current Project Status
|
||||
|
||||
**Mission Critical but Deprioritized:** Directory Connector is used to sync customer directory services with their Bitwarden organization. While SCIM is the more modern cloud-hosted solution, not all directory services support SCIM, and SCIM is only available on Enterprise plans. Therefore, DC remains mission-critical infrastructure for many paying customers, but it's deprioritized in the codebase due to infrequent changes.
|
||||
|
||||
**Isolated Repository:** Unlike other Bitwarden client applications that live in a monorepo with shared core libraries, Directory Connector was kept separate when other TypeScript clients moved to the monorepo. It got its own copy of the jslib repo to avoid unnecessary regressions from apparently unrelated code changes in other clients. This severed it from the rest of the codebase, causing:
|
||||
|
||||
- Outdated dependencies that can't be updated (ES modules vs CommonJS conflicts)
|
||||
- File/folder structure that doesn't match modern Bitwarden client patterns
|
||||
- Accumulated technical debt requiring significant investment to pay down
|
||||
- jslib contains unused code from all clients, but cannot be deleted due to monolithic/tightly coupled architecture
|
||||
|
||||
**Critical Issues (Current Status):**
|
||||
|
||||
- ✅ ~~Electron, Node, and Angular are on unmaintained versions~~ **RESOLVED** - All updated (Electron 39, Node 20, Angular 21, TypeScript 5.9)
|
||||
- ❌ `keytar` is archived (Dec 2022) and incompatible with Node v22, **blocking Node upgrades beyond v20** - **PRIMARY BLOCKER**
|
||||
- ❌ No ESM support blocks dependency upgrades: googleapis, lowdb, chalk, inquirer, node-fetch, electron-store
|
||||
- ⚠️ 70 dev dependencies + 31 runtime dependencies = excessive maintenance burden (count increased with Angular 21 tooling)
|
||||
- ❌ StateService is a large pre-StateProvider monolith containing every getter/setter for all clients (PM-31159 In Progress)
|
||||
- ✅ ~~Angular CLI not used~~ **RESOLVED** - Angular CLI 21.1.2 now integrated with angular.json configuration
|
||||
|
||||
**Development Approach:** When working on this codebase, prioritize sustainability and maintainability over adding new features. Consider how changes will affect long-term maintenance burden.
|
||||
|
||||
## Tech Debt Roadmap
|
||||
|
||||
### Progress Summary
|
||||
|
||||
**Completed:**
|
||||
|
||||
- ✅ Phase 0 (Immediate Priority): All major dependencies upgraded (Node 20, Angular 21, TypeScript 5.9, Electron 39)
|
||||
- ✅ Phase 6: Angular CLI integration complete
|
||||
|
||||
**In Progress:**
|
||||
|
||||
- 🔄 Phase 1: StateService rewrite (PM-31159)
|
||||
|
||||
**Blocked/Todo:**
|
||||
|
||||
- ❌ Phase 2: Remove remaining jslib code (blocked by Phase 1)
|
||||
- ❌ Phase 3: Repository restructure (should be done before Phase 5)
|
||||
- ⚠️ Phase 4: Replace Keytar **[CRITICAL BLOCKER]** - blocking Node v22+ upgrades
|
||||
- ❌ Phase 5: ESM Support (blocked by Phase 3, needed for googleapis, lowdb, chalk, inquirer, etc.)
|
||||
|
||||
**Primary Blocker:** Keytar removal (Phase 4) is the most critical task as it blocks Node upgrades beyond v20.
|
||||
|
||||
---
|
||||
|
||||
### ✅ Immediate Priority: Unsupported Dependencies (COMPLETED)
|
||||
|
||||
**Upgrade Path (July 2025 release) - STATUS: COMPLETE**
|
||||
|
||||
All major version upgrades have been completed and exceeded targets:
|
||||
|
||||
1. ✅ Node 18.20.8 → 20.18 → **COMPLETE** (engines: `~20`, .nvmrc: `v20`)
|
||||
2. ✅ Angular 17 → 18.2.x → **EXCEEDED** (now at **21.1.1**)
|
||||
3. ✅ TypeScript 5.4.5 → 5.6.0 → **EXCEEDED** (now at **5.9.3**)
|
||||
4. ✅ Electron 34 → 36 → **EXCEEDED** (now at **39.2.1**)
|
||||
5. ✅ Angular matches clients monorepo version (21.x)
|
||||
|
||||
**Current Versions:**
|
||||
|
||||
- Node: v20 (project target), blocked from v22+ by keytar
|
||||
- TypeScript: 5.9.3
|
||||
- Angular: 21.1.1 (all packages)
|
||||
- Electron: 39.2.1 (well beyond EOL target of 36)
|
||||
- @yao-pkg/pkg: 5.16.1 (community fork replacing archived pkg)
|
||||
|
||||
**Note:** Further Node upgrades to v22+ are **blocked by keytar** (see Phase 4). Electron 36 was EOL October 2028, but we're already on 39.2.1.
|
||||
|
||||
### Phase 1: StateService Rewrite (PM-31159, In Progress)
|
||||
|
||||
**Problem:** StateService is a post-account-switching, pre-StateProvider monolith containing every getter/setter for all clients. This prevents deletion of unused data models and code. Never very stable, and more complex than DC needs (DC doesn't need account switching).
|
||||
|
||||
**Current Status:** 🔄 **Active PR** - [#990](https://github.com/bitwarden/directory-connector/pull/990) (Open, Author: @BTreston)
|
||||
|
||||
- PR created: Feb 2, 2026
|
||||
- Last updated: Feb 5, 2026
|
||||
- Files changed: 17 files (+1,512, -41 lines)
|
||||
- Commits: 4 (scaffold, add tests, fix type issues, fix integration test)
|
||||
|
||||
**Implementation Details:**
|
||||
|
||||
**New Architecture:**
|
||||
|
||||
- Created `StateServiceVNext` interface (`src/abstractions/state-vNext.service.ts`)
|
||||
- New implementation: `StateServiceVNextImplementation` (`src/services/state-service/state-vNext.service.ts`)
|
||||
- New state model with flat key-value structure (`src/models/state.model.ts`)
|
||||
- Comprehensive test suite: `state-vNext.service.spec.ts` (488 lines of tests)
|
||||
|
||||
**Storage Key Structure:**
|
||||
|
||||
```typescript
|
||||
// vNext Storage Keys (Flat key-value structure)
|
||||
StorageKeysVNext = {
|
||||
stateVersion: "stateVersion",
|
||||
directoryType: "directoryType",
|
||||
organizationId: "organizationId",
|
||||
directory_ldap: "directory_ldap",
|
||||
directory_gsuite: "directory_gsuite",
|
||||
directory_entra: "directory_entra",
|
||||
directory_okta: "directory_okta",
|
||||
directory_onelogin: "directory_onelogin",
|
||||
sync: "sync",
|
||||
syncingDir: "syncingDir",
|
||||
};
|
||||
|
||||
// Secure storage keys for sensitive data
|
||||
SecureStorageKeysVNext = {
|
||||
ldap: "secret_ldap",
|
||||
gsuite: "secret_gsuite",
|
||||
azure: "secret_azure", // Backwards compatible with old name
|
||||
entra: "secret_entra",
|
||||
okta: "secret_okta",
|
||||
oneLogin: "secret_oneLogin",
|
||||
userDelta: "userDeltaToken",
|
||||
groupDelta: "groupDeltaToken",
|
||||
lastUserSync: "lastUserSync",
|
||||
lastGroupSync: "lastGroupSync",
|
||||
lastSyncHash: "lastSyncHash",
|
||||
};
|
||||
```
|
||||
|
||||
**Migration Strategy:**
|
||||
|
||||
- State version bumped to `StateVersion.Five` (`jslib/common/src/enums/stateVersion.ts`)
|
||||
- Enhanced `StateMigrationService` to handle migration from old account-based structure to new flat structure
|
||||
- Migration keys defined for backwards compatibility (`MigrationKeys`, `SecureStorageKeysMigration`)
|
||||
- Temporary keys used during migration (`TempKeys`) to preserve data during transition
|
||||
|
||||
**File Organization:**
|
||||
|
||||
- State-related files moved to `src/services/state-service/` subdirectory:
|
||||
- `state-vNext.service.ts` (new implementation)
|
||||
- `state-vNext.service.spec.ts` (488 lines of tests)
|
||||
- `state.service.ts` (legacy, moved from `src/services/`)
|
||||
- `stateMigration.service.ts` (enhanced for v5 migration)
|
||||
- New abstraction: `src/abstractions/state-vNext.service.ts`
|
||||
- New model: `src/models/state.model.ts` (defines all storage keys)
|
||||
|
||||
**Integration:**
|
||||
|
||||
- Both old `StateService` and new `StateServiceVNext` injected in parallel during migration phase
|
||||
- `DirectoryFactoryService` updated to accept both services
|
||||
- Services module provides both implementations
|
||||
- CLI (`bwdc.ts`) and GUI (`main.ts`) both instantiate new service alongside old one
|
||||
|
||||
**Chosen Approach Benefits:**
|
||||
|
||||
- Clean break with old StateService - high degree of certainty
|
||||
- Simple and focused on DC's needs (no account switching, no rxjs)
|
||||
- Flat key-value structure easier to maintain
|
||||
- Versioning and migration capabilities included
|
||||
- Keeps existing data.json around during transition
|
||||
- All getters/setters in one place (acceptable for small application)
|
||||
|
||||
**Rejected Approaches:**
|
||||
|
||||
- Copy StateProvider from clients: Too complex (supports account switching, rxjs, syncing background/foreground contexts)
|
||||
- Rewrite simplified StateService keeping current data structure: Commits us to previous decisions, keeps monolithic account objects
|
||||
|
||||
**Next Steps:**
|
||||
|
||||
- Complete PR review and merge
|
||||
- Monitor for regressions during initial rollout
|
||||
- After several releases, can remove old StateService and migration code
|
||||
- Begin Phase 2: Remove remaining jslib code that was only needed by old StateService
|
||||
|
||||
### Phase 2: Remove Remaining jslib Code
|
||||
|
||||
After StateService is removed, review and delete old models and remaining services that referenced each other. jslib contains unused code from all clients that DC doesn't need.
|
||||
|
||||
### Phase 3: Restructure Repository (PM-31852, To Do)
|
||||
|
||||
**Current Structure:**
|
||||
|
||||
```
|
||||
src/ # Both Electron and CLI app code
|
||||
src-cli/ # package.json entry point for CLI only, no code
|
||||
jslib/
|
||||
├── common/ # Shared common code
|
||||
├── node/ # Node specific code used in CLI
|
||||
└── electron/ # Electron specific code used in GUI
|
||||
```
|
||||
|
||||
**Target Structure:**
|
||||
|
||||
```
|
||||
src-gui/ # Electron specific code only (combining src (partial) + jslib/electron)
|
||||
src-cli/ # Node and CLI specific code only (combining src (partial) + jslib/node)
|
||||
libs/ # Shared app-independent DC code, e.g. sync services (combining src (partial) + jslib/common)
|
||||
```
|
||||
|
||||
**Why:** Makes subsequent changes (code reorganizing, ESM support) much easier. This should be done early in the modernization process.
|
||||
|
||||
### Phase 4: Replace Keytar (PM-12436, To Do) ⚠️ **CRITICAL BLOCKER**
|
||||
|
||||
**Problem:** `keytar` (OS secure storage for secrets) was archived December 2022 and is incompatible with Node v22, **actively blocking Node upgrades beyond v20**.
|
||||
|
||||
**Current Status:**
|
||||
|
||||
- `keytar`: **7.9.0** (still present in dependencies)
|
||||
- **This is the #1 blocker preventing Node v22+ upgrades**
|
||||
- All "Immediate Priority" dependencies have been upgraded, but further progress requires removing keytar
|
||||
|
||||
**Solution:** Migrate to Bitwarden's Rust implementation in `desktop_native` (same as clients monorepo did)
|
||||
|
||||
1. Implement Rust <-> NAPI integration (like `desktop_native/napi`) from Electron app to Rust code
|
||||
2. Copy, rename, and expose necessary functions
|
||||
3. Point to `desktop_native` crate using git link from DC repo (no need for SDK yet):
|
||||
```rust
|
||||
desktop_core = { git = "https://github.com/bitwarden/clients", rev = "00cf24972d944638bbd1adc00a0ae3eeabb6eb9a" }
|
||||
```
|
||||
|
||||
**Important:** `keytar` uses wrong encoding on Windows (UTF-8 instead of UTF-16). Bitwarden uses UTF-16. Code should contain a migration - ensure old values are migrated correctly during testing.
|
||||
|
||||
**Priority:** This should be prioritized as it's blocking the Node upgrade path and has been archived for over 2 years.
|
||||
|
||||
### Phase 5: Add ESM Support (PM-31850, To Do)
|
||||
|
||||
**Problem:** No ESM module support prevents upgrading key dependencies.
|
||||
|
||||
**Blocked Dependencies (Current Status):**
|
||||
|
||||
- ❌ `googleapis`: **149.0.0** → current (major dependency, disabled in renovate.json5)
|
||||
- ❌ `lowdb`: **1.0.0** → v7
|
||||
- ❌ `@types/lowdb`: **1.0.15** (can be deleted once inquirer is upgraded)
|
||||
- ❌ `@electron/notarize`: **2.5.0** → v3.0.1
|
||||
- ❌ `chalk`: **4.1.2** → v5.3.0
|
||||
- ❌ `inquirer`: **8.2.6** → v12.1.0
|
||||
- ❌ `@types/inquirer`: **8.2.10** (should be deleted when inquirer upgraded)
|
||||
- ❌ `node-fetch`: **2.7.0** → v3.3.2 (should use native Node fetch API when on Node >=21)
|
||||
- ❌ `electron-store`: **8.2.0** → v10.1.0
|
||||
|
||||
**Status:** These dependencies remain blocked as expected. They will stay on old versions until:
|
||||
|
||||
1. Phase 3 (Repository Restructure) is complete
|
||||
2. ESM support is implemented
|
||||
3. Note: These ESM dependencies are primarily used in CLI build, so restructuring first (Phase 3) will limit the impact of ESM migration.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. Update tsconfig.json and package.json configurations
|
||||
2. Update import/export syntax to no longer use `require` statements
|
||||
3. Upgrade dependencies to move away from CommonJS (ESM can import CommonJS, but not vice versa)
|
||||
4. Trial and error
|
||||
|
||||
**Reference:** [Pure ESM package guide](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c)
|
||||
|
||||
### Phase 6: Add Angular CLI (PM-31849, In Progress / Possibly Complete?)
|
||||
|
||||
**Problem:** Angular CLI provides great DX and makes it easier to manage Angular changes (e.g. auto-migrations). DC didn't use it.
|
||||
|
||||
**Current Status:**
|
||||
|
||||
- ✅ `@angular/cli`: **21.1.2** is now present in **runtime dependencies**
|
||||
- ✅ `@angular/build`: **21.1.2** is present in dev dependencies
|
||||
- ✅ All Angular tooling has been updated to v21.x
|
||||
|
||||
**Status:** ✅ **COMPLETE** - Angular CLI has been successfully integrated:
|
||||
|
||||
- `angular.json` configuration file exists
|
||||
- `.angular/` cache directory present
|
||||
- `@angular/cli` 21.1.2 in runtime dependencies
|
||||
- `@angular/build` 21.1.2 in dev dependencies
|
||||
- All Angular packages updated to v21.x
|
||||
|
||||
This migration provides improved DX and access to Angular's auto-migration tools for future updates.
|
||||
|
||||
### Additional Considerations
|
||||
|
||||
**Reduce Dependency Count:** Current state is 70 dev dependencies + 31 runtime dependencies (101 total). The dev dependency count increased from the original 66 due to Angular 21 upgrade adding additional tooling. After removing old code, review dependency list:
|
||||
|
||||
- Can we remove some after code cleanup?
|
||||
- Could we reintegrate with monorepo to leverage Component Library and shared platform dependencies?
|
||||
- **Risk:** Becomes tightly coupled with monorepo code → regression risk, move slower due to coupling
|
||||
|
||||
**GitHub Workflows:** Need review and modernization:
|
||||
|
||||
- PM-20478: Add check-run workflow for CI on community PRs
|
||||
- PM-18290: Add linting workflow
|
||||
- PM-18289: Update build workflow
|
||||
- `pkg` and `pkg-fetch` for packaging Node runtime in CLI release are archived (fork exists but untrusted; clients vets all changes manually)
|
||||
- Options: Make our own fork, or use Node's single executable binary support (investigate)
|
||||
|
||||
## Common Development Commands
|
||||
|
||||
### Desktop App (Electron + Angular)
|
||||
|
||||
**Initial Setup:**
|
||||
|
||||
```bash
|
||||
npm install # Install dependencies (runs git submodule init automatically)
|
||||
npm run rebuild # Rebuild native modules for Electron
|
||||
```
|
||||
|
||||
**Development:**
|
||||
|
||||
```bash
|
||||
npm run electron # Build and run desktop app with hot reload and debugging
|
||||
npm run electron:ignore # Same as above but ignores certificate errors
|
||||
```
|
||||
|
||||
**Building:**
|
||||
|
||||
```bash
|
||||
npm run build # Build both main and renderer processes
|
||||
npm run build:main # Build Electron main process only
|
||||
npm run build:renderer # Build Angular renderer process only
|
||||
npm run build:renderer:watch # Build renderer with file watching
|
||||
```
|
||||
|
||||
**Distribution:**
|
||||
|
||||
```bash
|
||||
npm run dist:mac # Create macOS distributable
|
||||
npm run dist:win # Create Windows distributable
|
||||
npm run dist:lin # Create Linux distributable
|
||||
```
|
||||
|
||||
### CLI (bwdc)
|
||||
|
||||
**Development:**
|
||||
|
||||
```bash
|
||||
npm run build:cli:watch # Build CLI with file watching
|
||||
node ./build-cli/bwdc.js --help # Run the CLI from build output
|
||||
```
|
||||
|
||||
**Production Build:**
|
||||
|
||||
```bash
|
||||
npm run build:cli:prod # Build CLI for production
|
||||
npm run dist:cli # Create platform-specific CLI executables (all platforms)
|
||||
npm run dist:cli:mac # Create macOS CLI executable only
|
||||
npm run dist:cli:win # Create Windows CLI executable only
|
||||
npm run dist:cli:lin # Create Linux CLI executable only
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
**Unit Tests:**
|
||||
|
||||
```bash
|
||||
npm test # Run unit tests (excludes integration tests)
|
||||
npm run test:watch # Run unit tests in watch mode
|
||||
npm run test:watch:all # Run unit tests in watch mode (all files)
|
||||
npm run test:types # Run TypeScript type checking without emitting files
|
||||
```
|
||||
|
||||
**Integration Tests:**
|
||||
|
||||
```bash
|
||||
npm run test:integration:setup # Set up Docker containers for LDAP testing
|
||||
npm run test:integration # Run integration tests
|
||||
npm run test:integration:watch # Run integration tests in watch mode
|
||||
```
|
||||
|
||||
Integration tests require Docker and test against live directory services. The setup command creates OpenLDAP containers using docker-compose.yml.
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
```bash
|
||||
npm run lint # Run ESLint and Prettier checks
|
||||
npm run lint:fix # Auto-fix ESLint issues
|
||||
npm run prettier # Format all files with Prettier
|
||||
```
|
||||
|
||||
### Submodule Management
|
||||
|
||||
The `jslib` folder is a git submodule containing shared Bitwarden libraries:
|
||||
|
||||
```bash
|
||||
npm run sub:update # Update submodule to latest remote version
|
||||
npm run sub:pull # Pull latest changes in submodule
|
||||
npm run sub:commit # Pull and commit submodule update
|
||||
```
|
||||
|
||||
### Utility Commands
|
||||
|
||||
```bash
|
||||
npm run reset # Remove keytar modules and reinstall (use when switching between CLI/desktop)
|
||||
npm run clean:dist # Clean desktop distribution files
|
||||
npm run clean:dist:cli # Clean CLI distribution files
|
||||
```
|
||||
|
||||
**Important:** When switching between developing the desktop app and CLI, run `npm run reset` to avoid native module conflicts.
|
||||
|
||||
## Code Architecture & Structure
|
||||
|
||||
### Directory Organization
|
||||
@@ -45,6 +435,32 @@ jslib/ # Legacy folder structure (mix of deprecated/unused and c
|
||||
3. **Directory Service Pattern**: Each directory provider implements `IDirectoryService` interface
|
||||
4. **Separation of Concerns**: GUI (Angular app) and CLI (commands) share the same service layer
|
||||
|
||||
### Core Synchronization Flow
|
||||
|
||||
The sync process follows this pattern:
|
||||
|
||||
1. **DirectoryFactoryService** (`src/services/directory-factory.service.ts`) - Creates the appropriate directory service based on DirectoryType configuration
|
||||
2. **IDirectoryService** implementation (`src/services/directory-services/*.service.ts`) - Each provider (LDAP, Entra ID, Google, Okta, OneLogin) implements:
|
||||
- `getEntries(force, test)` - Returns `[GroupEntry[], UserEntry[]]`
|
||||
- Provider-specific authentication and API calls
|
||||
3. **SyncService** (`src/services/sync.service.ts`) - Orchestrates the sync:
|
||||
- Calls directory service to get entries
|
||||
- Filters and deduplicates users/groups
|
||||
- Uses BatchRequestBuilder or SingleRequestBuilder to format API requests
|
||||
- Generates hash to detect changes and avoid redundant syncs
|
||||
- Sends data to Bitwarden API via ApiService
|
||||
4. **Request Builders** (`src/services/*-request-builder.ts`) - Transform directory entries into Bitwarden API format
|
||||
|
||||
### Shared Library (jslib)
|
||||
|
||||
The `jslib` folder is a git submodule containing shared Bitwarden code:
|
||||
|
||||
- Common services (API, Crypto, Storage, Auth)
|
||||
- Platform utilities
|
||||
- Shared models and abstractions
|
||||
|
||||
**Important:** This is legacy structure - do not add new code to jslib. New code should go in `src/`.
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### Code Organization
|
||||
|
||||
239
.claude/plan.md
Normal file
239
.claude/plan.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Phase 2 PR #1: Flatten Account Model - IMPLEMENTATION COMPLETE
|
||||
|
||||
## Status: ✅ COMPLETED
|
||||
|
||||
**Implementation Date:** February 13, 2026
|
||||
**All tests passing:** 120/120 ✅
|
||||
**TypeScript compilation:** Success ✅
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Successfully implemented Phase 2 PR #1: Flatten Account Model. The Account model has been simplified from 177 lines (51 + 126 inherited) to 51 lines, removing the BaseAccount inheritance and flattening nested structures into direct properties.
|
||||
|
||||
## Changes Implemented
|
||||
|
||||
### Files Modified (7 files)
|
||||
|
||||
1. **`jslib/common/src/enums/stateVersion.ts`**
|
||||
- Added `StateVersion.Five` for the flattened Account structure
|
||||
- Updated `StateVersion.Latest = Five`
|
||||
|
||||
2. **`src/models/account.ts`**
|
||||
- Removed `extends BaseAccount` inheritance
|
||||
- Removed `ClientKeys` class (redundant)
|
||||
- Flattened 6 authentication fields to top level:
|
||||
- `userId`, `entityId`, `apiKeyClientId`
|
||||
- `accessToken`, `refreshToken`, `apiKeyClientSecret`
|
||||
- Kept `DirectoryConfigurations` and `DirectorySettings` unchanged
|
||||
- Added compatibility fields with FIXME comment for jslib infrastructure:
|
||||
- `data?`, `keys?`, `profile?`, `settings?`, `tokens?` (optional, unused)
|
||||
- Simplified constructor without Object.assign
|
||||
|
||||
3. **`src/services/stateMigration.service.ts`**
|
||||
- Added `migrateStateFrom3To4()` placeholder migration
|
||||
- Added `migrateStateFrom4To5()` to flatten nested → flat Account structure
|
||||
- Updated `migrate()` method with new case statements for v3→v4 and v4→v5
|
||||
- Updated `migrateStateFrom1To2()` to use flattened structure (removed `account.profile`, `account.clientKeys`)
|
||||
|
||||
4. **`src/services/auth.service.ts`**
|
||||
- Removed imports: `AccountKeys`, `AccountProfile`, `AccountTokens`
|
||||
- Simplified account creation from 26 lines to 10 lines (62% reduction)
|
||||
- Direct property assignment instead of nested objects with spread operators
|
||||
|
||||
5. **`src/services/state.service.ts`**
|
||||
- Changed `account.profile.userId` → `account.userId`
|
||||
- Removed `account.settings` from `scaffoldNewAccountDiskStorage`
|
||||
- Added `settings` back to `resetAccount` for base class compatibility (unused but required)
|
||||
|
||||
6. **`src/services/authService.spec.ts`**
|
||||
- Removed imports: `AccountKeys`, `AccountProfile`, `AccountTokens`
|
||||
- Updated test expectations to match new flat Account structure
|
||||
|
||||
### Files Created (1 file)
|
||||
|
||||
7. **`src/services/stateMigration.service.spec.ts`**
|
||||
- Comprehensive migration test suite (5 tests, 210 lines)
|
||||
- Tests flattening nested account structure
|
||||
- Tests handling missing nested objects gracefully
|
||||
- Tests empty account list
|
||||
- Tests preservation of directory configurations and settings
|
||||
- Tests state version update
|
||||
|
||||
## Code Reduction Achieved
|
||||
|
||||
- **Account model:** 177 lines (51 + 126 inherited) → 51 lines (71% reduction)
|
||||
- **AuthService account creation:** 26 lines → 10 lines (62% reduction)
|
||||
- **Import statements removed:** 5 jslib imports across multiple files
|
||||
|
||||
## Migration Logic
|
||||
|
||||
### State Version v4 → v5 Migration
|
||||
|
||||
The `migrateStateFrom4To5()` method handles conversion from nested to flat structure:
|
||||
|
||||
```typescript
|
||||
// OLD (nested structure):
|
||||
{
|
||||
profile: {
|
||||
userId: "CLIENT_ID",
|
||||
entityId: "CLIENT_ID",
|
||||
apiKeyClientId: "organization.CLIENT_ID"
|
||||
},
|
||||
tokens: {
|
||||
accessToken: "token",
|
||||
refreshToken: "refresh"
|
||||
},
|
||||
keys: {
|
||||
apiKeyClientSecret: "secret"
|
||||
}
|
||||
}
|
||||
|
||||
// NEW (flat structure):
|
||||
{
|
||||
userId: "CLIENT_ID",
|
||||
entityId: "CLIENT_ID",
|
||||
apiKeyClientId: "organization.CLIENT_ID",
|
||||
accessToken: "token",
|
||||
refreshToken: "refresh",
|
||||
apiKeyClientSecret: "secret"
|
||||
}
|
||||
```
|
||||
|
||||
**Migration Safety:**
|
||||
|
||||
- Null-safe property access with `??` operator
|
||||
- Preserves all directory configurations and settings
|
||||
- Falls back to userId if profile.userId doesn't exist
|
||||
- Handles empty account lists gracefully
|
||||
|
||||
## Test Results
|
||||
|
||||
### Unit Tests: ✅ PASS
|
||||
|
||||
```
|
||||
Test Suites: 14 passed, 14 total
|
||||
Tests: 120 passed, 120 total
|
||||
```
|
||||
|
||||
New tests added:
|
||||
|
||||
- `should flatten nested account structure` ✅
|
||||
- `should handle missing nested objects gracefully` ✅
|
||||
- `should handle empty account list` ✅
|
||||
- `should preserve directory configurations and settings` ✅
|
||||
- `should update state version after successful migration` ✅
|
||||
|
||||
### TypeScript Compilation: ✅ PASS
|
||||
|
||||
```
|
||||
npm run test:types
|
||||
```
|
||||
|
||||
All type checks pass with zero errors.
|
||||
|
||||
## Technical Notes
|
||||
|
||||
### Compatibility Fields
|
||||
|
||||
Added optional compatibility fields to Account model to satisfy jslib infrastructure type constraints:
|
||||
|
||||
```typescript
|
||||
// FIXME: Remove these compatibility fields after StateServiceVNext migration (PR #990) is merged
|
||||
// These fields are unused but required for type compatibility with jslib's StateService infrastructure
|
||||
data?: any;
|
||||
keys?: any;
|
||||
profile?: any;
|
||||
settings?: any;
|
||||
tokens?: any;
|
||||
```
|
||||
|
||||
These will be removed after PR #990 (StateServiceVNext) merges and old StateService is deleted.
|
||||
|
||||
### Key Architectural Decision
|
||||
|
||||
Chose to add compatibility fields rather than refactor entire jslib infrastructure because:
|
||||
|
||||
1. PR #990 (StateServiceVNext) will eventually replace this infrastructure
|
||||
2. Minimizes changes needed in this PR
|
||||
3. Avoids conflicts with PR #990
|
||||
4. Can be cleaned up later
|
||||
|
||||
## What This Enables
|
||||
|
||||
### Immediate Benefits
|
||||
|
||||
- ✅ Simplified Account model (71% code reduction)
|
||||
- ✅ Clearer authentication field structure
|
||||
- ✅ Easier debugging (no nested property access)
|
||||
- ✅ Self-documenting code (obvious what DC needs)
|
||||
|
||||
### Enables Future Work
|
||||
|
||||
- **Phase 2 PR #2:** Remove StateFactory infrastructure
|
||||
- **Phase 2 PR #3:** Delete ~90 unused jslib files including:
|
||||
- EncString (only used by old nested Account)
|
||||
- SymmetricCryptoKey (only used by old nested Account)
|
||||
- OrganizationData (completely unused)
|
||||
- ProviderData (completely unused)
|
||||
- AccountKeys, AccountProfile, AccountTokens, AccountData, AccountSettings
|
||||
|
||||
## Merge Strategy
|
||||
|
||||
**Conflict Management:**
|
||||
|
||||
- This PR targets current codebase (with old StateService)
|
||||
- Will conflict with PR #990 (StateServiceVNext) when it merges
|
||||
- Plan: Rebase this PR after #990 merges
|
||||
- Expected conflicts: StateService files, Account model structure
|
||||
- Resolution: Keep StateServiceVNext changes, apply Account flattening to new structure
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review & Test:** Thorough code review and manual testing
|
||||
2. **Create PR:** Open PR with comprehensive description and test results
|
||||
3. **Manual Testing Scenarios:**
|
||||
- Fresh installation → authentication flow
|
||||
- Existing installation → migration runs successfully
|
||||
- All directory types → configuration persists correctly
|
||||
- CLI authentication → flat structure works
|
||||
4. **After Merge:**
|
||||
- Begin Phase 2 PR #2: Remove StateFactory Infrastructure
|
||||
- Monitor for any migration issues in production
|
||||
|
||||
## Related Work
|
||||
|
||||
- **Depends On:** None (can merge independently)
|
||||
- **Blocks:** Phase 2 PR #2 (Remove StateFactory), Phase 2 PR #3 (Delete Unused jslib Files)
|
||||
- **Conflicts With:** PR #990 (StateServiceVNext) - plan to rebase after #990 merges
|
||||
- **Part Of:** Phase 2 tech debt cleanup (see CLAUDE.md)
|
||||
|
||||
---
|
||||
|
||||
## Original Implementation Plan
|
||||
|
||||
[The original detailed step-by-step plan from the conversation has been preserved below for reference]
|
||||
|
||||
### Context
|
||||
|
||||
Directory Connector's Account model currently extends jslib's BaseAccount, inheriting 126 lines of complex nested structures designed for multi-account password manager features that DC doesn't use. This inheritance creates unnecessary coupling and blocks cleanup of unused jslib dependencies.
|
||||
|
||||
**Current State:**
|
||||
|
||||
- Account extends BaseAccount with nested objects: `profile.userId`, `tokens.accessToken`, `keys.apiKeyClientSecret`
|
||||
- Only 6 fields from BaseAccount are actually used by DC
|
||||
- 120+ lines of inherited code (AccountData, AccountKeys, AccountProfile, AccountSettings, AccountTokens) are unused
|
||||
- Creates dependencies on EncString, SymmetricCryptoKey, OrganizationData, ProviderData that DC never uses
|
||||
|
||||
**Problem:**
|
||||
|
||||
- Unnecessary complexity for a single-account application
|
||||
- Blocks deletion of unused jslib models (Phase 2 goal)
|
||||
- Verbose account creation code (26 lines to set 6 fields)
|
||||
- Difficult to understand what DC actually needs
|
||||
|
||||
**Goal:**
|
||||
Flatten Account model to contain only the 8 fields DC uses, removing BaseAccount inheritance. This enables Phase 2 PR #2 and PR #3 to delete ~90 unused jslib files.
|
||||
|
||||
[Rest of original plan preserved in conversation transcript]
|
||||
130
.claude/skills/commonjs-to-esm/skill.md
Normal file
130
.claude/skills/commonjs-to-esm/skill.md
Normal file
@@ -0,0 +1,130 @@
|
||||
---
|
||||
userInvocable: true
|
||||
---
|
||||
|
||||
# CommonJS to ESM Conversion
|
||||
|
||||
Convert a file (or files) from CommonJS module syntax to ECMAScript Modules (ESM).
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/commonjs-to-esm <file-path> [additional-file-paths...]
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
- `file-path` - Path to the file(s) to convert from CommonJS to ESM
|
||||
|
||||
## Examples
|
||||
|
||||
```
|
||||
/commonjs-to-esm src/services/auth.service.ts
|
||||
/commonjs-to-esm src/utils/helper.ts src/utils/parser.ts
|
||||
```
|
||||
|
||||
## Process
|
||||
|
||||
This skill performs a comprehensive analysis and planning process:
|
||||
|
||||
### 1. Analyze Target File(s)
|
||||
|
||||
For each file to convert:
|
||||
|
||||
- Read the file contents
|
||||
- Identify its purpose and functionality
|
||||
- Catalog all CommonJS patterns used:
|
||||
- `require()` statements
|
||||
- `module.exports` assignments
|
||||
- `exports.x = ...` assignments
|
||||
- Dynamic requires
|
||||
- `__dirname` and `__filename` usage
|
||||
|
||||
### 2. Find Dependents
|
||||
|
||||
- Search for all files that import/require the target file(s)
|
||||
- Identify the import patterns used by dependents
|
||||
- Map the dependency tree to understand impact scope
|
||||
|
||||
### 3. Analyze Dependencies
|
||||
|
||||
- List all modules the target file(s) depend on
|
||||
- Determine if dependencies support ESM
|
||||
- Identify potential blocking dependencies (CommonJS-only packages)
|
||||
- Check for dynamic imports that may need special handling
|
||||
|
||||
### 4. Identify Conversion Challenges
|
||||
|
||||
Common issues to flag:
|
||||
|
||||
- `__dirname` and `__filename` (need `import.meta.url` conversion)
|
||||
- Dynamic `require()` calls (need `import()` conversion)
|
||||
- Conditional requires (need refactoring)
|
||||
- JSON imports (need `assert { type: 'json' }`)
|
||||
- CommonJS-only dependencies (may block conversion)
|
||||
- Circular dependencies (may need restructuring)
|
||||
|
||||
### 5. Generate Conversion Plan
|
||||
|
||||
Create a step-by-step plan that includes:
|
||||
|
||||
**Target File Changes:**
|
||||
|
||||
- Convert `require()` to `import` statements
|
||||
- Convert `module.exports` to `export` statements
|
||||
- Update `__dirname`/`__filename` to use `import.meta.url`
|
||||
- Handle dynamic imports appropriately
|
||||
- Update file extensions if needed (e.g., `.js` to `.mjs`)
|
||||
|
||||
**Dependent File Changes:**
|
||||
|
||||
- Update all import statements in dependent files
|
||||
- Ensure consistent naming (default vs named exports)
|
||||
- Update path references if extensions change
|
||||
|
||||
**Configuration Changes:**
|
||||
|
||||
- `package.json`: Add `"type": "module"` or use `.mjs` extension
|
||||
- `tsconfig.json`: Update `module` and `moduleResolution` settings
|
||||
- Build tools: Update bundler/compiler configurations
|
||||
|
||||
**Testing Strategy:**
|
||||
|
||||
- Run unit tests after conversion
|
||||
- Verify no runtime errors from import changes
|
||||
- Check that all exports are accessible
|
||||
- Test dynamic import scenarios
|
||||
|
||||
### 6. Risk Assessment
|
||||
|
||||
Evaluate:
|
||||
|
||||
- Number of files affected
|
||||
- Complexity of CommonJS patterns used
|
||||
- Presence of blocking dependencies
|
||||
- Potential for breaking changes
|
||||
|
||||
### 7. Present Plan
|
||||
|
||||
Output a structured plan with:
|
||||
|
||||
- Summary of changes needed
|
||||
- Ordered steps for execution
|
||||
- List of files to modify
|
||||
- Configuration changes required
|
||||
- Testing checkpoints
|
||||
- Risk factors and mitigation strategies
|
||||
- Estimated scope (small/medium/large change)
|
||||
|
||||
## Notes
|
||||
|
||||
- ESM is **not** compatible with CommonJS in all cases - ESM can import CommonJS, but CommonJS **cannot** require ESM
|
||||
- This means conversions should generally proceed from leaf dependencies upward
|
||||
- Some packages remain CommonJS-only and may block full conversion
|
||||
- The skill generates a plan but does NOT automatically execute the conversion - review and approve first
|
||||
|
||||
## References
|
||||
|
||||
- [Pure ESM package guide](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c)
|
||||
- [Node.js ESM documentation](https://nodejs.org/api/esm.html)
|
||||
- [TypeScript ESM support](https://www.typescriptlang.org/docs/handbook/esm-node.html)
|
||||
@@ -10,7 +10,7 @@
|
||||
"output": "dist",
|
||||
"app": "build"
|
||||
},
|
||||
"afterSign": "scripts/notarize.js",
|
||||
"afterSign": "scripts/notarize.mjs",
|
||||
"mac": {
|
||||
"artifactName": "Bitwarden-Connector-${version}-mac.${ext}",
|
||||
"category": "public.app-category.productivity",
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
"win": {
|
||||
"target": ["portable", "nsis"],
|
||||
"sign": "scripts/sign.js"
|
||||
"sign": "scripts/sign.mjs"
|
||||
},
|
||||
"linux": {
|
||||
"category": "Utility",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const { pathsToModuleNameMapper } = require("ts-jest");
|
||||
const { compilerOptions } = require("./tsconfig");
|
||||
import { pathsToModuleNameMapper } from "ts-jest";
|
||||
import tsconfig from "./tsconfig.json" with { type: "json" };
|
||||
|
||||
const tsPreset = require("ts-jest/jest-preset");
|
||||
const angularPreset = require("jest-preset-angular/jest-preset");
|
||||
const { defaultTransformerOptions } = require("jest-preset-angular/presets");
|
||||
import angularPresetsModule from "jest-preset-angular/presets/index.js";
|
||||
|
||||
const { defaultTransformerOptions } = angularPresetsModule;
|
||||
|
||||
const { compilerOptions } = tsconfig;
|
||||
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
// ...tsPreset,
|
||||
// ...angularPreset,
|
||||
export default {
|
||||
preset: "jest-preset-angular",
|
||||
|
||||
reporters: ["default", "jest-junit"],
|
||||
745
jslib-removal-plan.md
Normal file
745
jslib-removal-plan.md
Normal file
@@ -0,0 +1,745 @@
|
||||
# Plan: Remove StateService and jslib Dependencies
|
||||
|
||||
## Context
|
||||
|
||||
Directory Connector currently depends on StateService from jslib, which is a massive pre-StateProvider monolith containing 200+ getter/setter methods for all Bitwarden clients. This creates significant maintenance burden and blocks deletion of unused jslib code.
|
||||
|
||||
**Current State (Phase 1 Complete):**
|
||||
|
||||
- ✅ StateServiceVNext has been implemented with a flat key-value structure
|
||||
- ✅ Migration service handles transition from old account-based structure to new flat structure
|
||||
- ⚠️ Both old and new StateService implementations coexist during migration
|
||||
- ❌ Three jslib services still depend on old StateService: TokenService, CryptoService, EnvironmentService
|
||||
- ❌ Two Electron components depend on old StateService: WindowMain, TrayMain
|
||||
|
||||
**Problem:**
|
||||
The old StateService cannot be removed until all dependencies are eliminated. Analysis reveals:
|
||||
|
||||
- **TokenService**: Used for API authentication (9/32 methods actually used)
|
||||
- **CryptoService**: Completely unused by DC (0/61 methods used) - carried over from monolith
|
||||
- **EnvironmentService**: Used for custom server URLs (4/11 methods used)
|
||||
- **WindowMain/TrayMain**: Used for Electron window/tray state persistence (6 methods total)
|
||||
|
||||
**Goal:**
|
||||
Replace jslib services with simplified DC-specific implementations that use StateServiceVNext, enabling complete removal of old StateService and unlocking Phase 2 (jslib code cleanup).
|
||||
|
||||
**User Decisions:**
|
||||
|
||||
1. ✅ Create simplified DC-specific versions of Token/Environment services (clean break from jslib)
|
||||
2. ✅ Keep WindowMain/TrayMain as-is (minimize scope, focus on StateService removal)
|
||||
3. ✅ Automatic migration on first launch (transparent to users)
|
||||
|
||||
## Critical Files
|
||||
|
||||
### Files to Create (New Implementations)
|
||||
|
||||
- `src/services/token/token.service.ts` - DC-specific token service
|
||||
- `src/abstractions/token.service.ts` - Token service interface
|
||||
- `src/services/environment/environment.service.ts` - DC-specific environment service
|
||||
- `src/abstractions/environment.service.ts` - Environment service interface
|
||||
- `src/utils/jwt.util.ts` - JWT decoding utility (no dependencies)
|
||||
|
||||
### Files to Modify (Update Dependencies)
|
||||
|
||||
- `src/services/api.service.ts` - Switch from jslib TokenService to DC TokenService
|
||||
- `src/services/auth.service.ts` - Update EnvironmentService import
|
||||
- `src/services/sync.service.ts` - Update EnvironmentService import
|
||||
- `src/commands/config.command.ts` - Update EnvironmentService import
|
||||
- `src/bwdc.ts` - Remove old StateService, instantiate new services
|
||||
- `src/main.ts` - Remove old StateService, instantiate new services
|
||||
- `src/app/services/services.module.ts` - Remove old StateService, provide new services
|
||||
- `src/app/app.component.ts` - Update TokenService import
|
||||
- `jslib/electron/src/window.main.ts` - Adapt to use StateServiceVNext
|
||||
- `jslib/electron/src/tray.main.ts` - Adapt to use StateServiceVNext
|
||||
|
||||
### Files to Delete (After Migration)
|
||||
|
||||
- `jslib/common/src/services/token.service.ts` - jslib TokenService
|
||||
- `jslib/common/src/abstractions/token.service.ts` - jslib TokenService interface
|
||||
- `jslib/common/src/services/crypto.service.ts` - Unused CryptoService
|
||||
- `jslib/common/src/abstractions/crypto.service.ts` - Unused CryptoService interface
|
||||
- `jslib/common/src/services/environment.service.ts` - jslib EnvironmentService
|
||||
- `jslib/common/src/abstractions/environment.service.ts` - jslib EnvironmentService interface
|
||||
- `src/services/state-service/state.service.ts` - Old DC StateService
|
||||
- `src/abstractions/state.service.ts` - Old DC StateService interface
|
||||
- `jslib/common/src/services/state.service.ts` - Old jslib StateService
|
||||
- `jslib/common/src/abstractions/state.service.ts` - Old jslib StateService interface
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Step 1: Create JWT Utility (No Dependencies)
|
||||
|
||||
Create `src/utils/jwt.util.ts` with standalone JWT decoding function:
|
||||
|
||||
```typescript
|
||||
export interface DecodedToken {
|
||||
exp: number;
|
||||
iat: number;
|
||||
nbf: number;
|
||||
sub: string; // user ID
|
||||
client_id?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export function decodeJwt(token: string): DecodedToken {
|
||||
// Validate JWT structure (3 parts: header.payload.signature)
|
||||
const parts = token.split(".");
|
||||
if (parts.length !== 3) {
|
||||
throw new Error("Invalid JWT format");
|
||||
}
|
||||
|
||||
// Decode payload (base64url to JSON)
|
||||
const payload = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
||||
|
||||
return JSON.parse(atob(payload));
|
||||
}
|
||||
|
||||
export function getTokenExpirationDate(token: string): Date | null {
|
||||
const decoded = decodeJwt(token);
|
||||
if (!decoded.exp) return null;
|
||||
return new Date(decoded.exp * 1000);
|
||||
}
|
||||
|
||||
export function tokenSecondsRemaining(token: string, offsetSeconds = 0): number {
|
||||
const expDate = getTokenExpirationDate(token);
|
||||
if (!expDate) return 0;
|
||||
|
||||
const msRemaining = expDate.getTime() - Date.now() - offsetSeconds * 1000;
|
||||
return Math.floor(msRemaining / 1000);
|
||||
}
|
||||
|
||||
export function tokenNeedsRefresh(token: string, minutesBeforeExpiration = 5): boolean {
|
||||
const secondsRemaining = tokenSecondsRemaining(token);
|
||||
return secondsRemaining < minutesBeforeExpiration * 60;
|
||||
}
|
||||
```
|
||||
|
||||
**Why:** Standalone utility avoids service dependencies, can be tested independently, reusable.
|
||||
|
||||
### Step 2: Create DC TokenService
|
||||
|
||||
Create `src/abstractions/token.service.ts`:
|
||||
|
||||
```typescript
|
||||
export interface TokenService {
|
||||
// Token storage
|
||||
setTokens(
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
clientIdClientSecret?: [string, string],
|
||||
): Promise<void>;
|
||||
getToken(): Promise<string | null>;
|
||||
getRefreshToken(): Promise<string | null>;
|
||||
clearToken(): Promise<void>;
|
||||
|
||||
// API key authentication
|
||||
getClientId(): Promise<string | null>;
|
||||
getClientSecret(): Promise<string | null>;
|
||||
|
||||
// Two-factor token (rarely used)
|
||||
getTwoFactorToken(): Promise<string | null>;
|
||||
clearTwoFactorToken(): Promise<void>;
|
||||
|
||||
// Token validation (delegates to jwt.util)
|
||||
decodeToken(token?: string): Promise<DecodedToken | null>;
|
||||
tokenNeedsRefresh(minutesBeforeExpiration?: number): Promise<boolean>;
|
||||
}
|
||||
```
|
||||
|
||||
Create `src/services/token/token.service.ts`:
|
||||
|
||||
```typescript
|
||||
import { StateServiceVNext } from "@/abstractions/state-vNext.service";
|
||||
import { SecureStorageService } from "@/jslib/common/src/abstractions/storage.service";
|
||||
import { TokenService as ITokenService } from "@/abstractions/token.service";
|
||||
import {
|
||||
decodeJwt,
|
||||
tokenNeedsRefresh as checkTokenNeedsRefresh,
|
||||
DecodedToken,
|
||||
} from "@/utils/jwt.util";
|
||||
|
||||
export class TokenService implements ITokenService {
|
||||
// Storage keys
|
||||
private TOKEN_KEY = "accessToken";
|
||||
private REFRESH_TOKEN_KEY = "refreshToken";
|
||||
private CLIENT_ID_KEY = "apiKeyClientId";
|
||||
private CLIENT_SECRET_KEY = "apiKeyClientSecret";
|
||||
private TWO_FACTOR_TOKEN_KEY = "twoFactorToken";
|
||||
|
||||
constructor(
|
||||
private stateService: StateServiceVNext,
|
||||
private secureStorageService: SecureStorageService,
|
||||
) {}
|
||||
|
||||
async setTokens(
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
clientIdClientSecret?: [string, string],
|
||||
): Promise<void> {
|
||||
await this.secureStorageService.save(this.TOKEN_KEY, accessToken);
|
||||
await this.secureStorageService.save(this.REFRESH_TOKEN_KEY, refreshToken);
|
||||
|
||||
if (clientIdClientSecret) {
|
||||
await this.secureStorageService.save(this.CLIENT_ID_KEY, clientIdClientSecret[0]);
|
||||
await this.secureStorageService.save(this.CLIENT_SECRET_KEY, clientIdClientSecret[1]);
|
||||
}
|
||||
}
|
||||
|
||||
async getToken(): Promise<string | null> {
|
||||
return await this.secureStorageService.get<string>(this.TOKEN_KEY);
|
||||
}
|
||||
|
||||
async getRefreshToken(): Promise<string | null> {
|
||||
return await this.secureStorageService.get<string>(this.REFRESH_TOKEN_KEY);
|
||||
}
|
||||
|
||||
async clearToken(): Promise<void> {
|
||||
await this.secureStorageService.remove(this.TOKEN_KEY);
|
||||
await this.secureStorageService.remove(this.REFRESH_TOKEN_KEY);
|
||||
await this.secureStorageService.remove(this.CLIENT_ID_KEY);
|
||||
await this.secureStorageService.remove(this.CLIENT_SECRET_KEY);
|
||||
}
|
||||
|
||||
async getClientId(): Promise<string | null> {
|
||||
return await this.secureStorageService.get<string>(this.CLIENT_ID_KEY);
|
||||
}
|
||||
|
||||
async getClientSecret(): Promise<string | null> {
|
||||
return await this.secureStorageService.get<string>(this.CLIENT_SECRET_KEY);
|
||||
}
|
||||
|
||||
async getTwoFactorToken(): Promise<string | null> {
|
||||
return await this.secureStorageService.get<string>(this.TWO_FACTOR_TOKEN_KEY);
|
||||
}
|
||||
|
||||
async clearTwoFactorToken(): Promise<void> {
|
||||
await this.secureStorageService.remove(this.TWO_FACTOR_TOKEN_KEY);
|
||||
}
|
||||
|
||||
async decodeToken(token?: string): Promise<DecodedToken | null> {
|
||||
const tokenToUse = token ?? (await this.getToken());
|
||||
if (!tokenToUse) return null;
|
||||
|
||||
try {
|
||||
return decodeJwt(tokenToUse);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async tokenNeedsRefresh(minutesBeforeExpiration = 5): Promise<boolean> {
|
||||
const token = await this.getToken();
|
||||
if (!token) return true;
|
||||
|
||||
try {
|
||||
return checkTokenNeedsRefresh(token, minutesBeforeExpiration);
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create `src/services/token/token.service.spec.ts` with comprehensive tests covering:
|
||||
|
||||
- Token storage/retrieval
|
||||
- Token clearing
|
||||
- JWT decoding
|
||||
- Token expiration logic
|
||||
- Error handling for malformed tokens
|
||||
|
||||
### Step 3: Create DC EnvironmentService
|
||||
|
||||
Create `src/abstractions/environment.service.ts`:
|
||||
|
||||
```typescript
|
||||
export interface EnvironmentUrls {
|
||||
base?: string;
|
||||
api?: string;
|
||||
identity?: string;
|
||||
webVault?: string;
|
||||
icons?: string;
|
||||
notifications?: string;
|
||||
events?: string;
|
||||
keyConnector?: string;
|
||||
}
|
||||
|
||||
export interface EnvironmentService {
|
||||
setUrls(urls: EnvironmentUrls): Promise<void>;
|
||||
setUrlsFromStorage(): Promise<void>;
|
||||
|
||||
hasBaseUrl(): boolean;
|
||||
getApiUrl(): string;
|
||||
getIdentityUrl(): string;
|
||||
getWebVaultUrl(): string;
|
||||
getIconsUrl(): string;
|
||||
getNotificationsUrl(): string;
|
||||
getEventsUrl(): string;
|
||||
getKeyConnectorUrl(): string;
|
||||
}
|
||||
```
|
||||
|
||||
Create `src/services/environment/environment.service.ts`:
|
||||
|
||||
```typescript
|
||||
import { StateServiceVNext } from "@/abstractions/state-vNext.service";
|
||||
import {
|
||||
EnvironmentService as IEnvironmentService,
|
||||
EnvironmentUrls,
|
||||
} from "@/abstractions/environment.service";
|
||||
|
||||
export class EnvironmentService implements IEnvironmentService {
|
||||
private readonly DEFAULT_URLS = {
|
||||
api: "https://api.bitwarden.com",
|
||||
identity: "https://identity.bitwarden.com",
|
||||
webVault: "https://vault.bitwarden.com",
|
||||
icons: "https://icons.bitwarden.net",
|
||||
notifications: "https://notifications.bitwarden.com",
|
||||
events: "https://events.bitwarden.com",
|
||||
};
|
||||
|
||||
private urls: EnvironmentUrls = {};
|
||||
|
||||
constructor(private stateService: StateServiceVNext) {}
|
||||
|
||||
async setUrls(urls: EnvironmentUrls): Promise<void> {
|
||||
// Normalize URLs: trim whitespace, remove trailing slashes, add https:// if missing
|
||||
const normalized: EnvironmentUrls = {};
|
||||
|
||||
for (const [key, value] of Object.entries(urls)) {
|
||||
if (!value) continue;
|
||||
|
||||
let url = value.trim();
|
||||
url = url.replace(/\/+$/, ""); // Remove trailing slashes
|
||||
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
url = `https://${url}`;
|
||||
}
|
||||
|
||||
normalized[key] = url;
|
||||
}
|
||||
|
||||
this.urls = normalized;
|
||||
await this.stateService.setEnvironmentUrls(normalized);
|
||||
}
|
||||
|
||||
async setUrlsFromStorage(): Promise<void> {
|
||||
const stored = await this.stateService.getEnvironmentUrls();
|
||||
this.urls = stored ?? {};
|
||||
}
|
||||
|
||||
hasBaseUrl(): boolean {
|
||||
return !!this.urls.base;
|
||||
}
|
||||
|
||||
getApiUrl(): string {
|
||||
return this.urls.api ?? this.urls.base + "/api" ?? this.DEFAULT_URLS.api;
|
||||
}
|
||||
|
||||
getIdentityUrl(): string {
|
||||
return this.urls.identity ?? this.urls.base + "/identity" ?? this.DEFAULT_URLS.identity;
|
||||
}
|
||||
|
||||
getWebVaultUrl(): string {
|
||||
return this.urls.webVault ?? this.urls.base ?? this.DEFAULT_URLS.webVault;
|
||||
}
|
||||
|
||||
getIconsUrl(): string {
|
||||
return this.urls.icons ?? this.urls.base + "/icons" ?? this.DEFAULT_URLS.icons;
|
||||
}
|
||||
|
||||
getNotificationsUrl(): string {
|
||||
return (
|
||||
this.urls.notifications ??
|
||||
this.urls.base + "/notifications" ??
|
||||
this.DEFAULT_URLS.notifications
|
||||
);
|
||||
}
|
||||
|
||||
getEventsUrl(): string {
|
||||
return this.urls.events ?? this.urls.base + "/events" ?? this.DEFAULT_URLS.events;
|
||||
}
|
||||
|
||||
getKeyConnectorUrl(): string {
|
||||
return this.urls.keyConnector ?? "";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create `src/services/environment/environment.service.spec.ts` with tests covering:
|
||||
|
||||
- URL normalization (trailing slashes, https prefix)
|
||||
- Storage persistence
|
||||
- Default URL fallbacks
|
||||
- Custom URL override
|
||||
- Base URL derivation
|
||||
|
||||
### Step 4: Add Environment URL Storage to StateServiceVNext
|
||||
|
||||
Update `src/models/state.model.ts` to add environment URL storage key:
|
||||
|
||||
```typescript
|
||||
export const StorageKeysVNext = {
|
||||
// ... existing keys ...
|
||||
environmentUrls: "environmentUrls",
|
||||
};
|
||||
```
|
||||
|
||||
Update `src/abstractions/state-vNext.service.ts` to add methods:
|
||||
|
||||
```typescript
|
||||
export interface StateServiceVNext {
|
||||
// ... existing methods ...
|
||||
getEnvironmentUrls(): Promise<EnvironmentUrls | null>;
|
||||
setEnvironmentUrls(urls: EnvironmentUrls): Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
Update `src/services/state-service/state-vNext.service.ts` implementation to add storage methods.
|
||||
|
||||
### Step 5: Update StateMigrationService for Token/Environment Data
|
||||
|
||||
Update `src/services/state-service/stateMigration.service.ts` to migrate:
|
||||
|
||||
**Token data (from secure storage):**
|
||||
|
||||
- `accessToken` → `accessToken` (same key, no change needed)
|
||||
- `refreshToken` → `refreshToken` (same key, no change needed)
|
||||
- `apiKeyClientId` → `apiKeyClientId` (same key, no change needed)
|
||||
- `apiKeyClientSecret` → `apiKeyClientSecret` (same key, no change needed)
|
||||
|
||||
**Environment URLs (from account state):**
|
||||
|
||||
- `environmentUrls` from account → `environmentUrls` in flat structure
|
||||
|
||||
Add migration test cases to `src/services/state-service/stateMigration.service.spec.ts`.
|
||||
|
||||
### Step 6: Remove CryptoService Dependencies
|
||||
|
||||
Since CryptoService is completely unused by DC:
|
||||
|
||||
1. Search for all imports of `CryptoService` in `src/` code
|
||||
2. Remove all instantiations and injections
|
||||
3. Verify no methods are actually called
|
||||
4. Remove from DI containers (services.module.ts, bwdc.ts, main.ts)
|
||||
|
||||
Expected: Zero usage, straightforward removal.
|
||||
|
||||
### Step 7: Update WindowMain/TrayMain to Use StateServiceVNext
|
||||
|
||||
Update `jslib/electron/src/window.main.ts`:
|
||||
|
||||
```typescript
|
||||
// Change constructor to accept StateServiceVNext instead of StateService
|
||||
constructor(
|
||||
private stateService: StateServiceVNext, // Changed from StateService
|
||||
// ... other params
|
||||
) {}
|
||||
|
||||
// Update method calls to use StateServiceVNext interface
|
||||
async getWindowSettings(): Promise<any> {
|
||||
return await this.stateService.getWindowSettings();
|
||||
}
|
||||
|
||||
async setWindowSettings(settings: any): Promise<void> {
|
||||
await this.stateService.setWindowSettings(settings);
|
||||
}
|
||||
```
|
||||
|
||||
Update `jslib/electron/src/tray.main.ts` similarly:
|
||||
|
||||
```typescript
|
||||
constructor(
|
||||
private stateService: StateServiceVNext, // Changed from StateService
|
||||
// ... other params
|
||||
) {}
|
||||
|
||||
// Update method calls
|
||||
async getEnableTray(): Promise<boolean> {
|
||||
return await this.stateService.getEnableTray();
|
||||
}
|
||||
// ... etc for other tray settings
|
||||
```
|
||||
|
||||
**Required:** Add window/tray setting storage to StateServiceVNext:
|
||||
|
||||
- `getWindowSettings()` / `setWindowSettings()`
|
||||
- `getEnableTray()` / `getEnableMinimizeToTray()` / `getEnableCloseToTray()` / `getAlwaysShowDock()`
|
||||
|
||||
### Step 8: Update Service Registrations
|
||||
|
||||
**In `src/app/services/services.module.ts`:**
|
||||
|
||||
```typescript
|
||||
// Remove old services
|
||||
- import { StateService } from '@/services/state-service/state.service';
|
||||
- import { TokenService } from '@/jslib/common/src/services/token.service';
|
||||
- import { CryptoService } from '@/jslib/common/src/services/crypto.service';
|
||||
- import { EnvironmentService } from '@/jslib/common/src/services/environment.service';
|
||||
|
||||
// Add new services
|
||||
+ import { TokenService } from '@/services/token/token.service';
|
||||
+ import { EnvironmentService } from '@/services/environment/environment.service';
|
||||
|
||||
providers: [
|
||||
// Remove old StateService provider
|
||||
- { provide: StateService, useClass: StateService },
|
||||
|
||||
// Add new service providers
|
||||
+ { provide: TokenService, useClass: TokenService },
|
||||
+ { provide: EnvironmentService, useClass: EnvironmentService },
|
||||
|
||||
// Keep StateServiceVNext
|
||||
{ provide: StateServiceVNext, useClass: StateServiceVNextImplementation },
|
||||
]
|
||||
```
|
||||
|
||||
**In `src/bwdc.ts` (CLI):**
|
||||
|
||||
```typescript
|
||||
// Remove old service instantiations
|
||||
- this.stateService = new StateService(/* ... */);
|
||||
- this.cryptoService = new CryptoService(/* ... */);
|
||||
- this.tokenService = new TokenService(/* ... */);
|
||||
- this.environmentService = new EnvironmentService(this.stateService);
|
||||
|
||||
// Add new service instantiations
|
||||
+ this.tokenService = new TokenService(this.stateServiceVNext, secureStorageService);
|
||||
+ this.environmentService = new EnvironmentService(this.stateServiceVNext);
|
||||
```
|
||||
|
||||
**In `src/main.ts` (Electron):**
|
||||
|
||||
```typescript
|
||||
// Remove old service instantiations
|
||||
- this.stateService = new StateService(/* ... */);
|
||||
- this.cryptoService = new CryptoService(/* ... */);
|
||||
- this.tokenService = new TokenService(/* ... */);
|
||||
- this.environmentService = new EnvironmentService(this.stateService);
|
||||
|
||||
// Add new service instantiations
|
||||
+ this.tokenService = new TokenService(this.stateServiceVNext, secureStorageService);
|
||||
+ this.environmentService = new EnvironmentService(this.stateServiceVNext);
|
||||
|
||||
// Update WindowMain/TrayMain to use StateServiceVNext
|
||||
- this.windowMain = new WindowMain(this.stateService, /* ... */);
|
||||
- this.trayMain = new TrayMain(this.stateService, /* ... */);
|
||||
+ this.windowMain = new WindowMain(this.stateServiceVNext, /* ... */);
|
||||
+ this.trayMain = new TrayMain(this.stateServiceVNext, /* ... */);
|
||||
```
|
||||
|
||||
### Step 9: Update Import Statements
|
||||
|
||||
Update all files that import Token/Environment services:
|
||||
|
||||
**Files to update:**
|
||||
|
||||
- `src/services/api.service.ts` - Change TokenService import to DC version
|
||||
- `src/services/auth.service.ts` - Change EnvironmentService import to DC version
|
||||
- `src/services/sync.service.ts` - Change EnvironmentService import to DC version
|
||||
- `src/commands/config.command.ts` - Change EnvironmentService import to DC version
|
||||
- `src/app/app.component.ts` - Change TokenService import to DC version
|
||||
|
||||
**Pattern:**
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
import { TokenService } from "@/jslib/common/src/services/token.service";
|
||||
import { EnvironmentService } from "@/jslib/common/src/services/environment.service";
|
||||
|
||||
// After
|
||||
import { TokenService } from "@/abstractions/token.service";
|
||||
import { EnvironmentService } from "@/abstractions/environment.service";
|
||||
```
|
||||
|
||||
### Step 10: Delete Old StateService and jslib Services
|
||||
|
||||
**Delete these files (after all references removed):**
|
||||
|
||||
```bash
|
||||
# Old StateService implementations
|
||||
src/services/state-service/state.service.ts
|
||||
src/abstractions/state.service.ts
|
||||
jslib/common/src/services/state.service.ts
|
||||
jslib/common/src/abstractions/state.service.ts
|
||||
|
||||
# jslib Token/Crypto/Environment services
|
||||
jslib/common/src/services/token.service.ts
|
||||
jslib/common/src/abstractions/token.service.ts
|
||||
jslib/common/src/services/crypto.service.ts
|
||||
jslib/common/src/abstractions/crypto.service.ts
|
||||
jslib/common/src/services/environment.service.ts
|
||||
jslib/common/src/abstractions/environment.service.ts
|
||||
```
|
||||
|
||||
**Rename StateServiceVNext to StateService:**
|
||||
|
||||
```bash
|
||||
# Rename files
|
||||
mv src/services/state-service/state-vNext.service.ts src/services/state-service/state.service.ts
|
||||
mv src/services/state-service/state-vNext.service.spec.ts src/services/state-service/state.service.spec.ts
|
||||
mv src/abstractions/state-vNext.service.ts src/abstractions/state.service.ts
|
||||
|
||||
# Update all imports from StateServiceVNext to StateService
|
||||
# Find and replace: StateServiceVNext → StateService
|
||||
```
|
||||
|
||||
### Step 11: Update Tests
|
||||
|
||||
**Update existing tests that mock StateService:**
|
||||
|
||||
- Update mocks to use new StateService interface (flat key-value structure)
|
||||
- Remove mocks for Token/Crypto/Environment services where they inject old versions
|
||||
- Add mocks for new DC Token/Environment services
|
||||
|
||||
**Add new test files:**
|
||||
|
||||
- `src/services/token/token.service.spec.ts` (created in Step 2)
|
||||
- `src/services/environment/environment.service.spec.ts` (created in Step 3)
|
||||
- `src/utils/jwt.util.spec.ts` (JWT utility tests)
|
||||
|
||||
**Update integration tests:**
|
||||
|
||||
- Verify token storage/retrieval works correctly
|
||||
- Verify environment URL configuration persists
|
||||
- Verify window/tray settings persist in Electron app
|
||||
|
||||
## Verification Plan
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```bash
|
||||
npm test # Run all unit tests
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
|
||||
- All new service tests pass (TokenService, EnvironmentService, JWT util)
|
||||
- All existing tests pass with updated mocks
|
||||
- No test failures due to StateService removal
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```bash
|
||||
npm run test:integration
|
||||
```
|
||||
|
||||
**Expected:**
|
||||
|
||||
- LDAP sync tests pass
|
||||
- Authentication flow works correctly
|
||||
- Configuration persistence works
|
||||
|
||||
### Manual Testing - CLI
|
||||
|
||||
```bash
|
||||
# Build and run CLI
|
||||
npm run build:cli:watch
|
||||
node ./build-cli/bwdc.js --help
|
||||
|
||||
# Test authentication
|
||||
node ./build-cli/bwdc.js config server https://vault.bitwarden.com
|
||||
node ./build-cli/bwdc.js login --apikey
|
||||
|
||||
# Test sync
|
||||
node ./build-cli/bwdc.js config directory ldap
|
||||
node ./build-cli/bwdc.js config ldap.hostname ldap.example.com
|
||||
node ./build-cli/bwdc.js sync
|
||||
|
||||
# Test logout
|
||||
node ./build-cli/bwdc.js logout
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
|
||||
- ✅ Server URL configuration persists
|
||||
- ✅ Login stores tokens correctly
|
||||
- ✅ Token refresh works automatically
|
||||
- ✅ Sync completes successfully
|
||||
- ✅ Logout clears tokens
|
||||
|
||||
### Manual Testing - Desktop App
|
||||
|
||||
```bash
|
||||
# Build and run desktop app
|
||||
npm run electron
|
||||
```
|
||||
|
||||
**Verify:**
|
||||
|
||||
- ✅ Window position/size persists across restarts
|
||||
- ✅ "Always on top" setting persists
|
||||
- ✅ Tray icon shows/hides based on settings
|
||||
- ✅ Minimize/close to tray works
|
||||
- ✅ Login/logout flow works
|
||||
- ✅ Sync functionality works
|
||||
- ✅ Custom server URL configuration works
|
||||
|
||||
### Migration Testing
|
||||
|
||||
**Test migration from existing installation:**
|
||||
|
||||
1. Install current production version
|
||||
2. Configure directory connection and run sync
|
||||
3. Install new version with StateService removal
|
||||
4. Launch app - verify automatic migration occurs
|
||||
5. Verify all settings preserved:
|
||||
- Directory configuration
|
||||
- Organization ID
|
||||
- Server URLs
|
||||
- Window/tray settings
|
||||
- Authentication tokens
|
||||
|
||||
**Expected:**
|
||||
|
||||
- ✅ Migration runs automatically on first launch
|
||||
- ✅ All user data preserved
|
||||
- ✅ No user action required
|
||||
- ✅ App functions identically to before
|
||||
|
||||
### Regression Testing
|
||||
|
||||
Run through all major workflows:
|
||||
|
||||
1. **Configuration**: Set up each directory type (LDAP, Entra, Google, Okta, OneLogin)
|
||||
2. **Authentication**: Login with API key, verify token refresh
|
||||
3. **Sync**: Full sync, incremental sync (delta tokens), detect changes via hash
|
||||
4. **Custom server**: Configure self-hosted Bitwarden server
|
||||
5. **Electron features**: Window management, tray behavior
|
||||
|
||||
**Expected:** No regressions in functionality.
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If critical issues discovered post-deployment:
|
||||
|
||||
1. **Revert commit** removing StateService
|
||||
2. **Keep StateServiceVNext in parallel** (already coexisting)
|
||||
3. **Debug issues** in development
|
||||
4. **Re-attempt removal** after fixes
|
||||
|
||||
**Risk Assessment:** Low - StateServiceVNext has been in production since Phase 1 PR merge, proven stable.
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- ✅ All old StateService implementations deleted
|
||||
- ✅ StateServiceVNext renamed to StateService (becomes primary)
|
||||
- ✅ jslib TokenService, CryptoService, EnvironmentService deleted
|
||||
- ✅ DC-specific Token/Environment services implemented and tested
|
||||
- ✅ All unit tests pass
|
||||
- ✅ All integration tests pass
|
||||
- ✅ Manual testing shows no regressions
|
||||
- ✅ Migration from old state structure works automatically
|
||||
- ✅ WindowMain/TrayMain adapted to new StateService
|
||||
- ✅ Zero references to old StateService in codebase
|
||||
|
||||
## Next Steps (After Completion)
|
||||
|
||||
This unblocks **Phase 2: Remove Remaining jslib Code**:
|
||||
|
||||
- Delete unused jslib models (AccountData, AccountSettings, etc.)
|
||||
- Delete unused jslib services that referenced StateService
|
||||
- Clean up jslib/common folder of unused client code
|
||||
- Potentially merge remaining jslib code into src/ (flatten structure)
|
||||
|
||||
Estimated effort: 2-3 days for experienced developer familiar with codebase.
|
||||
@@ -1,28 +0,0 @@
|
||||
import { webcrypto } from "crypto";
|
||||
import "jest-preset-angular/setup-jest";
|
||||
|
||||
Object.defineProperty(window, "CSS", { value: null });
|
||||
Object.defineProperty(window, "getComputedStyle", {
|
||||
value: () => {
|
||||
return {
|
||||
display: "none",
|
||||
appearance: ["-webkit-appearance"],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(document, "doctype", {
|
||||
value: "<!DOCTYPE html>",
|
||||
});
|
||||
Object.defineProperty(document.body.style, "transform", {
|
||||
value: () => {
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
Object.defineProperty(window, "crypto", {
|
||||
value: webcrypto,
|
||||
});
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Directive, EventEmitter, Output } from "@angular/core";
|
||||
|
||||
import { EnvironmentService } from "@/jslib/common/src/abstractions/environment.service";
|
||||
import { I18nService } from "@/jslib/common/src/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@/jslib/common/src/abstractions/platformUtils.service";
|
||||
|
||||
@Directive()
|
||||
export class EnvironmentComponent {
|
||||
@Output() onSaved = new EventEmitter();
|
||||
|
||||
iconsUrl: string;
|
||||
identityUrl: string;
|
||||
apiUrl: string;
|
||||
webVaultUrl: string;
|
||||
notificationsUrl: string;
|
||||
baseUrl: string;
|
||||
showCustom = false;
|
||||
|
||||
constructor(
|
||||
protected platformUtilsService: PlatformUtilsService,
|
||||
protected environmentService: EnvironmentService,
|
||||
protected i18nService: I18nService,
|
||||
) {
|
||||
const urls = this.environmentService.getUrls();
|
||||
|
||||
this.baseUrl = urls.base || "";
|
||||
this.webVaultUrl = urls.webVault || "";
|
||||
this.apiUrl = urls.api || "";
|
||||
this.identityUrl = urls.identity || "";
|
||||
this.iconsUrl = urls.icons || "";
|
||||
this.notificationsUrl = urls.notifications || "";
|
||||
}
|
||||
|
||||
async submit() {
|
||||
const resUrls = await this.environmentService.setUrls({
|
||||
base: this.baseUrl,
|
||||
api: this.apiUrl,
|
||||
identity: this.identityUrl,
|
||||
webVault: this.webVaultUrl,
|
||||
icons: this.iconsUrl,
|
||||
notifications: this.notificationsUrl,
|
||||
});
|
||||
|
||||
// re-set urls since service can change them, ex: prefixing https://
|
||||
this.baseUrl = resUrls.base;
|
||||
this.apiUrl = resUrls.api;
|
||||
this.identityUrl = resUrls.identity;
|
||||
this.webVaultUrl = resUrls.webVault;
|
||||
this.iconsUrl = resUrls.icons;
|
||||
this.notificationsUrl = resUrls.notifications;
|
||||
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t("environmentSaved"));
|
||||
this.saved();
|
||||
}
|
||||
|
||||
toggleCustom() {
|
||||
this.showCustom = !this.showCustom;
|
||||
}
|
||||
|
||||
protected saved() {
|
||||
this.onSaved.emit();
|
||||
}
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
import { LOCALE_ID, NgModule } from "@angular/core";
|
||||
|
||||
import { ApiService as ApiServiceAbstraction } from "@/jslib/common/src/abstractions/api.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@/jslib/common/src/abstractions/appId.service";
|
||||
import { BroadcasterService as BroadcasterServiceAbstraction } from "@/jslib/common/src/abstractions/broadcaster.service";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "@/jslib/common/src/abstractions/crypto.service";
|
||||
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@/jslib/common/src/abstractions/cryptoFunction.service";
|
||||
import { EnvironmentService as EnvironmentServiceAbstraction } from "@/jslib/common/src/abstractions/environment.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@/jslib/common/src/abstractions/i18n.service";
|
||||
import { LogService } from "@/jslib/common/src/abstractions/log.service";
|
||||
import { MessagingService as MessagingServiceAbstraction } from "@/jslib/common/src/abstractions/messaging.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@/jslib/common/src/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "@/jslib/common/src/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "@/jslib/common/src/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "@/jslib/common/src/abstractions/storage.service";
|
||||
import { TokenService as TokenServiceAbstraction } from "@/jslib/common/src/abstractions/token.service";
|
||||
import { StateFactory } from "@/jslib/common/src/factories/stateFactory";
|
||||
import { Account } from "@/jslib/common/src/models/domain/account";
|
||||
import { GlobalState } from "@/jslib/common/src/models/domain/globalState";
|
||||
import { ApiService } from "@/jslib/common/src/services/api.service";
|
||||
import { AppIdService } from "@/jslib/common/src/services/appId.service";
|
||||
import { ConsoleLogService } from "@/jslib/common/src/services/consoleLog.service";
|
||||
import { CryptoService } from "@/jslib/common/src/services/crypto.service";
|
||||
import { EnvironmentService } from "@/jslib/common/src/services/environment.service";
|
||||
import { StateService } from "@/jslib/common/src/services/state.service";
|
||||
import { StateMigrationService } from "@/jslib/common/src/services/stateMigration.service";
|
||||
import { TokenService } from "@/jslib/common/src/services/token.service";
|
||||
|
||||
import {
|
||||
SafeInjectionToken,
|
||||
SECURE_STORAGE,
|
||||
WINDOW,
|
||||
} from "../../../../src/app/services/injection-tokens";
|
||||
import { SafeProvider, safeProvider } from "../../../../src/app/services/safe-provider";
|
||||
|
||||
import { BroadcasterService } from "./broadcaster.service";
|
||||
import { ModalService } from "./modal.service";
|
||||
import { ValidationService } from "./validation.service";
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
providers: [
|
||||
safeProvider({ provide: WINDOW, useValue: window }),
|
||||
safeProvider({
|
||||
provide: LOCALE_ID as SafeInjectionToken<string>,
|
||||
useFactory: (i18nService: I18nServiceAbstraction) => i18nService.translationLocale,
|
||||
deps: [I18nServiceAbstraction],
|
||||
}),
|
||||
safeProvider(ValidationService),
|
||||
safeProvider(ModalService),
|
||||
safeProvider({
|
||||
provide: AppIdServiceAbstraction,
|
||||
useClass: AppIdService,
|
||||
deps: [StorageServiceAbstraction],
|
||||
}),
|
||||
safeProvider({ provide: LogService, useFactory: () => new ConsoleLogService(false), deps: [] }),
|
||||
safeProvider({
|
||||
provide: EnvironmentServiceAbstraction,
|
||||
useClass: EnvironmentService,
|
||||
deps: [StateServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: TokenServiceAbstraction,
|
||||
useClass: TokenService,
|
||||
deps: [StateServiceAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: CryptoServiceAbstraction,
|
||||
useClass: CryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: ApiServiceAbstraction,
|
||||
useFactory: (
|
||||
tokenService: TokenServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
environmentService: EnvironmentServiceAbstraction,
|
||||
messagingService: MessagingServiceAbstraction,
|
||||
appIdService: AppIdServiceAbstraction,
|
||||
) =>
|
||||
new ApiService(
|
||||
tokenService,
|
||||
platformUtilsService,
|
||||
environmentService,
|
||||
appIdService,
|
||||
async (expired: boolean) => messagingService.send("logout", { expired: expired }),
|
||||
),
|
||||
deps: [
|
||||
TokenServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
EnvironmentServiceAbstraction,
|
||||
MessagingServiceAbstraction,
|
||||
AppIdServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: BroadcasterServiceAbstraction,
|
||||
useClass: BroadcasterService,
|
||||
useAngularDecorators: true,
|
||||
}),
|
||||
safeProvider({
|
||||
provide: StateServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction,
|
||||
logService: LogService,
|
||||
stateMigrationService: StateMigrationServiceAbstraction,
|
||||
) =>
|
||||
new StateService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
logService,
|
||||
stateMigrationService,
|
||||
new StateFactory(GlobalState, Account),
|
||||
),
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
SECURE_STORAGE,
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useFactory: (
|
||||
storageService: StorageServiceAbstraction,
|
||||
secureStorageService: StorageServiceAbstraction,
|
||||
) =>
|
||||
new StateMigrationService(
|
||||
storageService,
|
||||
secureStorageService,
|
||||
new StateFactory(GlobalState, Account),
|
||||
),
|
||||
deps: [StorageServiceAbstraction, SECURE_STORAGE],
|
||||
}),
|
||||
] satisfies SafeProvider[],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
@@ -1,69 +0,0 @@
|
||||
import { EncryptionType } from "@/jslib/common/src/enums/encryptionType";
|
||||
import { SymmetricCryptoKey } from "@/jslib/common/src/models/domain/symmetricCryptoKey";
|
||||
|
||||
import { makeStaticByteArray } from "../utils";
|
||||
|
||||
describe("SymmetricCryptoKey", () => {
|
||||
it("errors if no key", () => {
|
||||
const t = () => {
|
||||
new SymmetricCryptoKey(null);
|
||||
};
|
||||
|
||||
expect(t).toThrow("Must provide key");
|
||||
});
|
||||
|
||||
describe("guesses encKey from key length", () => {
|
||||
it("AesCbc256_B64", () => {
|
||||
const key = makeStaticByteArray(32);
|
||||
const cryptoKey = new SymmetricCryptoKey(key);
|
||||
|
||||
expect(cryptoKey).toEqual({
|
||||
encKey: key,
|
||||
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||
encType: 0,
|
||||
key: key,
|
||||
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||
macKey: null,
|
||||
});
|
||||
});
|
||||
|
||||
it("AesCbc128_HmacSha256_B64", () => {
|
||||
const key = makeStaticByteArray(32);
|
||||
const cryptoKey = new SymmetricCryptoKey(key, EncryptionType.AesCbc128_HmacSha256_B64);
|
||||
|
||||
expect(cryptoKey).toEqual({
|
||||
encKey: key.slice(0, 16),
|
||||
encKeyB64: "AAECAwQFBgcICQoLDA0ODw==",
|
||||
encType: 1,
|
||||
key: key,
|
||||
keyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||
macKey: key.slice(16, 32),
|
||||
macKeyB64: "EBESExQVFhcYGRobHB0eHw==",
|
||||
});
|
||||
});
|
||||
|
||||
it("AesCbc256_HmacSha256_B64", () => {
|
||||
const key = makeStaticByteArray(64);
|
||||
const cryptoKey = new SymmetricCryptoKey(key);
|
||||
|
||||
expect(cryptoKey).toEqual({
|
||||
encKey: key.slice(0, 32),
|
||||
encKeyB64: "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=",
|
||||
encType: 2,
|
||||
key: key,
|
||||
keyB64:
|
||||
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw==",
|
||||
macKey: key.slice(32, 64),
|
||||
macKeyB64: "ICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8=",
|
||||
});
|
||||
});
|
||||
|
||||
it("unknown length", () => {
|
||||
const t = () => {
|
||||
new SymmetricCryptoKey(makeStaticByteArray(30));
|
||||
};
|
||||
|
||||
expect(t).toThrow("Unable to determine encType.");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,127 +0,0 @@
|
||||
import { sequentialize } from "@/jslib/common/src/misc/sequentialize";
|
||||
|
||||
describe("sequentialize decorator", () => {
|
||||
it("should call the function once", async () => {
|
||||
const foo = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.bar(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(1);
|
||||
});
|
||||
|
||||
it("should call the function once for each instance of the object", async () => {
|
||||
const foo = new Foo();
|
||||
const foo2 = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.bar(1));
|
||||
promises.push(foo2.bar(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(1);
|
||||
expect(foo2.calls).toBe(1);
|
||||
});
|
||||
|
||||
it("should call the function once with key function", async () => {
|
||||
const foo = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.baz(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(1);
|
||||
});
|
||||
|
||||
it("should call the function again when already resolved", async () => {
|
||||
const foo = new Foo();
|
||||
await foo.bar(1);
|
||||
expect(foo.calls).toBe(1);
|
||||
await foo.bar(1);
|
||||
expect(foo.calls).toBe(2);
|
||||
});
|
||||
|
||||
it("should call the function again when already resolved with a key function", async () => {
|
||||
const foo = new Foo();
|
||||
await foo.baz(1);
|
||||
expect(foo.calls).toBe(1);
|
||||
await foo.baz(1);
|
||||
expect(foo.calls).toBe(2);
|
||||
});
|
||||
|
||||
it("should call the function for each argument", async () => {
|
||||
const foo = new Foo();
|
||||
await Promise.all([foo.bar(1), foo.bar(1), foo.bar(2), foo.bar(2), foo.bar(3), foo.bar(3)]);
|
||||
expect(foo.calls).toBe(3);
|
||||
});
|
||||
|
||||
it("should call the function for each argument with key function", async () => {
|
||||
const foo = new Foo();
|
||||
await Promise.all([foo.baz(1), foo.baz(1), foo.baz(2), foo.baz(2), foo.baz(3), foo.baz(3)]);
|
||||
expect(foo.calls).toBe(3);
|
||||
});
|
||||
|
||||
it("should return correct result for each call", async () => {
|
||||
const foo = new Foo();
|
||||
const allRes: number[] = [];
|
||||
|
||||
await Promise.all([
|
||||
foo.bar(1).then((res) => allRes.push(res)),
|
||||
foo.bar(1).then((res) => allRes.push(res)),
|
||||
foo.bar(2).then((res) => allRes.push(res)),
|
||||
foo.bar(2).then((res) => allRes.push(res)),
|
||||
foo.bar(3).then((res) => allRes.push(res)),
|
||||
foo.bar(3).then((res) => allRes.push(res)),
|
||||
]);
|
||||
expect(foo.calls).toBe(3);
|
||||
expect(allRes.length).toBe(6);
|
||||
allRes.sort();
|
||||
expect(allRes).toEqual([2, 2, 4, 4, 6, 6]);
|
||||
});
|
||||
|
||||
it("should return correct result for each call with key function", async () => {
|
||||
const foo = new Foo();
|
||||
const allRes: number[] = [];
|
||||
|
||||
await Promise.all([
|
||||
foo.baz(1).then((res) => allRes.push(res)),
|
||||
foo.baz(1).then((res) => allRes.push(res)),
|
||||
foo.baz(2).then((res) => allRes.push(res)),
|
||||
foo.baz(2).then((res) => allRes.push(res)),
|
||||
foo.baz(3).then((res) => allRes.push(res)),
|
||||
foo.baz(3).then((res) => allRes.push(res)),
|
||||
]);
|
||||
expect(foo.calls).toBe(3);
|
||||
expect(allRes.length).toBe(6);
|
||||
allRes.sort();
|
||||
expect(allRes).toEqual([3, 3, 6, 6, 9, 9]);
|
||||
});
|
||||
});
|
||||
|
||||
class Foo {
|
||||
calls = 0;
|
||||
|
||||
@sequentialize((args) => "bar" + args[0])
|
||||
bar(a: number): Promise<number> {
|
||||
this.calls++;
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res(a * 2);
|
||||
}, Math.random() * 100);
|
||||
});
|
||||
}
|
||||
|
||||
@sequentialize((args) => "baz" + args[0])
|
||||
baz(a: number): Promise<number> {
|
||||
this.calls++;
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
res(a * 3);
|
||||
}, Math.random() * 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
import { sequentialize } from "@/jslib/common/src/misc/sequentialize";
|
||||
import { throttle } from "@/jslib/common/src/misc/throttle";
|
||||
|
||||
describe("throttle decorator", () => {
|
||||
it("should call the function once at a time", async () => {
|
||||
const foo = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.bar(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(10);
|
||||
});
|
||||
|
||||
it("should call the function once at a time for each object", async () => {
|
||||
const foo = new Foo();
|
||||
const foo2 = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.bar(1));
|
||||
promises.push(foo2.bar(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(10);
|
||||
expect(foo2.calls).toBe(10);
|
||||
});
|
||||
|
||||
it("should call the function limit at a time", async () => {
|
||||
const foo = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.baz(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(10);
|
||||
});
|
||||
|
||||
it("should call the function limit at a time for each object", async () => {
|
||||
const foo = new Foo();
|
||||
const foo2 = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.baz(1));
|
||||
promises.push(foo2.baz(1));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(10);
|
||||
expect(foo2.calls).toBe(10);
|
||||
});
|
||||
|
||||
it("should work together with sequentialize", async () => {
|
||||
const foo = new Foo();
|
||||
const promises = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
promises.push(foo.qux(Math.floor(i / 2) * 2));
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
expect(foo.calls).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
class Foo {
|
||||
calls = 0;
|
||||
inflight = 0;
|
||||
|
||||
@throttle(1, () => "bar")
|
||||
bar(a: number) {
|
||||
this.calls++;
|
||||
this.inflight++;
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
expect(this.inflight).toBe(1);
|
||||
this.inflight--;
|
||||
res(a * 2);
|
||||
}, Math.random() * 10);
|
||||
});
|
||||
}
|
||||
|
||||
@throttle(5, () => "baz")
|
||||
baz(a: number) {
|
||||
this.calls++;
|
||||
this.inflight++;
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
expect(this.inflight).toBeLessThanOrEqual(5);
|
||||
this.inflight--;
|
||||
res(a * 3);
|
||||
}, Math.random() * 10);
|
||||
});
|
||||
}
|
||||
|
||||
@sequentialize((args) => "qux" + args[0])
|
||||
@throttle(1, () => "qux")
|
||||
qux(a: number) {
|
||||
this.calls++;
|
||||
this.inflight++;
|
||||
return new Promise((res) => {
|
||||
setTimeout(() => {
|
||||
expect(this.inflight).toBe(1);
|
||||
this.inflight--;
|
||||
res(a * 3);
|
||||
}, Math.random() * 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
import { ConsoleLogService } from "@/jslib/common/src/services/consoleLog.service";
|
||||
|
||||
const originalConsole = console;
|
||||
let caughtMessage: any;
|
||||
|
||||
declare let console: any;
|
||||
|
||||
export function interceptConsole(interceptions: any): object {
|
||||
console = {
|
||||
log: function () {
|
||||
interceptions.log = arguments;
|
||||
},
|
||||
warn: function () {
|
||||
interceptions.warn = arguments;
|
||||
},
|
||||
error: function () {
|
||||
interceptions.error = arguments;
|
||||
},
|
||||
};
|
||||
return interceptions;
|
||||
}
|
||||
|
||||
export function restoreConsole() {
|
||||
console = originalConsole;
|
||||
}
|
||||
|
||||
describe("ConsoleLogService", () => {
|
||||
let logService: ConsoleLogService;
|
||||
beforeEach(() => {
|
||||
caughtMessage = {};
|
||||
interceptConsole(caughtMessage);
|
||||
logService = new ConsoleLogService(true);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
restoreConsole();
|
||||
});
|
||||
|
||||
it("filters messages below the set threshold", () => {
|
||||
logService = new ConsoleLogService(true, () => true);
|
||||
logService.debug("debug");
|
||||
logService.info("info");
|
||||
logService.warning("warning");
|
||||
logService.error("error");
|
||||
|
||||
expect(caughtMessage).toEqual({});
|
||||
});
|
||||
it("only writes debug messages in dev mode", () => {
|
||||
logService = new ConsoleLogService(false);
|
||||
|
||||
logService.debug("debug message");
|
||||
expect(caughtMessage.log).toBeUndefined();
|
||||
});
|
||||
|
||||
it("writes debug/info messages to console.log", () => {
|
||||
logService.debug("this is a debug message");
|
||||
expect(caughtMessage).toMatchObject({
|
||||
log: { "0": "this is a debug message" },
|
||||
});
|
||||
|
||||
logService.info("this is an info message");
|
||||
expect(caughtMessage).toMatchObject({
|
||||
log: { "0": "this is an info message" },
|
||||
});
|
||||
});
|
||||
it("writes warning messages to console.warn", () => {
|
||||
logService.warning("this is a warning message");
|
||||
expect(caughtMessage).toMatchObject({
|
||||
warn: { 0: "this is a warning message" },
|
||||
});
|
||||
});
|
||||
it("writes error messages to console.error", () => {
|
||||
logService.error("this is an error message");
|
||||
expect(caughtMessage).toMatchObject({
|
||||
error: { 0: "this is an error message" },
|
||||
});
|
||||
});
|
||||
|
||||
it("times with output to info", async () => {
|
||||
logService.time();
|
||||
await new Promise((r) => setTimeout(r, 250));
|
||||
const duration = logService.timeEnd();
|
||||
expect(duration[0]).toBe(0);
|
||||
expect(duration[1]).toBeGreaterThan(0);
|
||||
expect(duration[1]).toBeLessThan(500 * 10e6);
|
||||
|
||||
expect(caughtMessage).toEqual(expect.arrayContaining([]));
|
||||
expect(caughtMessage.log.length).toBe(1);
|
||||
expect(caughtMessage.log[0]).toEqual(expect.stringMatching(/^default: \d+\.?\d*ms$/));
|
||||
});
|
||||
|
||||
it("filters time output", async () => {
|
||||
logService = new ConsoleLogService(true, () => true);
|
||||
logService.time();
|
||||
logService.timeEnd();
|
||||
|
||||
expect(caughtMessage).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
import { webcrypto } from "crypto";
|
||||
|
||||
Object.defineProperty(window, "crypto", {
|
||||
value: webcrypto,
|
||||
});
|
||||
@@ -1,86 +0,0 @@
|
||||
import { HashPurpose } from "../enums/hashPurpose";
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { KeySuffixOptions } from "../enums/keySuffixOptions";
|
||||
import { EncArrayBuffer } from "../models/domain/encArrayBuffer";
|
||||
import { EncString } from "../models/domain/encString";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
|
||||
import { ProfileOrganizationResponse } from "../models/response/profileOrganizationResponse";
|
||||
import { ProfileProviderOrganizationResponse } from "../models/response/profileProviderOrganizationResponse";
|
||||
import { ProfileProviderResponse } from "../models/response/profileProviderResponse";
|
||||
|
||||
export abstract class CryptoService {
|
||||
setKey: (key: SymmetricCryptoKey) => Promise<any>;
|
||||
setKeyHash: (keyHash: string) => Promise<void>;
|
||||
setEncKey: (encKey: string) => Promise<void>;
|
||||
setEncPrivateKey: (encPrivateKey: string) => Promise<void>;
|
||||
setOrgKeys: (
|
||||
orgs: ProfileOrganizationResponse[],
|
||||
providerOrgs: ProfileProviderOrganizationResponse[],
|
||||
) => Promise<void>;
|
||||
setProviderKeys: (orgs: ProfileProviderResponse[]) => Promise<void>;
|
||||
getKey: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
|
||||
getKeyFromStorage: (keySuffix: KeySuffixOptions, userId?: string) => Promise<SymmetricCryptoKey>;
|
||||
getKeyHash: () => Promise<string>;
|
||||
compareAndUpdateKeyHash: (masterPassword: string, key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
getEncKey: (key?: SymmetricCryptoKey) => Promise<SymmetricCryptoKey>;
|
||||
getPublicKey: () => Promise<ArrayBuffer>;
|
||||
getPrivateKey: () => Promise<ArrayBuffer>;
|
||||
getFingerprint: (userId: string, publicKey?: ArrayBuffer) => Promise<string[]>;
|
||||
getOrgKeys: () => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
getOrgKey: (orgId: string) => Promise<SymmetricCryptoKey>;
|
||||
getProviderKey: (providerId: string) => Promise<SymmetricCryptoKey>;
|
||||
hasKey: () => Promise<boolean>;
|
||||
hasKeyInMemory: (userId?: string) => Promise<boolean>;
|
||||
hasKeyStored: (keySuffix?: KeySuffixOptions, userId?: string) => Promise<boolean>;
|
||||
hasEncKey: () => Promise<boolean>;
|
||||
clearKey: (clearSecretStorage?: boolean, userId?: string) => Promise<any>;
|
||||
clearKeyHash: () => Promise<any>;
|
||||
clearEncKey: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearKeyPair: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearOrgKeys: (memoryOnly?: boolean, userId?: string) => Promise<any>;
|
||||
clearProviderKeys: (memoryOnly?: boolean) => Promise<any>;
|
||||
clearPinProtectedKey: () => Promise<any>;
|
||||
clearKeys: (userId?: string) => Promise<any>;
|
||||
toggleKey: () => Promise<any>;
|
||||
makeKey: (
|
||||
password: string,
|
||||
salt: string,
|
||||
kdf: KdfType,
|
||||
kdfIterations: number,
|
||||
) => Promise<SymmetricCryptoKey>;
|
||||
makeKeyFromPin: (
|
||||
pin: string,
|
||||
salt: string,
|
||||
kdf: KdfType,
|
||||
kdfIterations: number,
|
||||
protectedKeyCs?: EncString,
|
||||
) => Promise<SymmetricCryptoKey>;
|
||||
makeShareKey: () => Promise<[EncString, SymmetricCryptoKey]>;
|
||||
makeKeyPair: (key?: SymmetricCryptoKey) => Promise<[string, EncString]>;
|
||||
makePinKey: (
|
||||
pin: string,
|
||||
salt: string,
|
||||
kdf: KdfType,
|
||||
kdfIterations: number,
|
||||
) => Promise<SymmetricCryptoKey>;
|
||||
makeSendKey: (keyMaterial: ArrayBuffer) => Promise<SymmetricCryptoKey>;
|
||||
hashPassword: (
|
||||
password: string,
|
||||
key: SymmetricCryptoKey,
|
||||
hashPurpose?: HashPurpose,
|
||||
) => Promise<string>;
|
||||
makeEncKey: (key: SymmetricCryptoKey) => Promise<[SymmetricCryptoKey, EncString]>;
|
||||
remakeEncKey: (
|
||||
key: SymmetricCryptoKey,
|
||||
encKey?: SymmetricCryptoKey,
|
||||
) => Promise<[SymmetricCryptoKey, EncString]>;
|
||||
encrypt: (plainValue: string | ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncString>;
|
||||
encryptToBytes: (plainValue: ArrayBuffer, key?: SymmetricCryptoKey) => Promise<EncArrayBuffer>;
|
||||
rsaEncrypt: (data: ArrayBuffer, publicKey?: ArrayBuffer) => Promise<EncString>;
|
||||
rsaDecrypt: (encValue: string, privateKeyValue?: ArrayBuffer) => Promise<ArrayBuffer>;
|
||||
decryptToBytes: (encString: EncString, key?: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||
decryptToUtf8: (encString: EncString, key?: SymmetricCryptoKey) => Promise<string>;
|
||||
decryptFromBytes: (encBuf: ArrayBuffer, key: SymmetricCryptoKey) => Promise<ArrayBuffer>;
|
||||
randomNumber: (min: number, max: number) => Promise<number>;
|
||||
validateKey: (key: SymmetricCryptoKey) => Promise<boolean>;
|
||||
}
|
||||
@@ -1,34 +1,2 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
export type Urls = {
|
||||
base?: string;
|
||||
webVault?: string;
|
||||
api?: string;
|
||||
identity?: string;
|
||||
icons?: string;
|
||||
notifications?: string;
|
||||
events?: string;
|
||||
keyConnector?: string;
|
||||
};
|
||||
|
||||
export type PayPalConfig = {
|
||||
businessId?: string;
|
||||
buttonAction?: string;
|
||||
};
|
||||
|
||||
export abstract class EnvironmentService {
|
||||
urls: Observable<Urls>;
|
||||
|
||||
hasBaseUrl: () => boolean;
|
||||
getNotificationsUrl: () => string;
|
||||
getWebVaultUrl: () => string;
|
||||
getSendUrl: () => string;
|
||||
getIconsUrl: () => string;
|
||||
getApiUrl: () => string;
|
||||
getIdentityUrl: () => string;
|
||||
getEventsUrl: () => string;
|
||||
getKeyConnectorUrl: () => string;
|
||||
setUrlsFromStorage: () => Promise<void>;
|
||||
setUrls: (urls: Urls) => Promise<Urls>;
|
||||
getUrls: () => Urls;
|
||||
}
|
||||
// Stub file - re-exports DC EnvironmentService
|
||||
export { EnvironmentService, EnvironmentUrls } from "@/src/abstractions/environment.service";
|
||||
|
||||
@@ -1,218 +1,2 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { KdfType } from "../enums/kdfType";
|
||||
import { ThemeType } from "../enums/themeType";
|
||||
import { UriMatchType } from "../enums/uriMatchType";
|
||||
import { OrganizationData } from "../models/data/organizationData";
|
||||
import { ProviderData } from "../models/data/providerData";
|
||||
import { Account } from "../models/domain/account";
|
||||
import { EncString } from "../models/domain/encString";
|
||||
import { EnvironmentUrls } from "../models/domain/environmentUrls";
|
||||
import { StorageOptions } from "../models/domain/storageOptions";
|
||||
import { SymmetricCryptoKey } from "../models/domain/symmetricCryptoKey";
|
||||
import { WindowState } from "../models/domain/windowState";
|
||||
|
||||
export abstract class StateService<T extends Account = Account> {
|
||||
accounts$: Observable<{ [userId: string]: T }>;
|
||||
activeAccount$: Observable<string>;
|
||||
|
||||
addAccount: (account: T) => Promise<void>;
|
||||
setActiveUser: (userId: string) => Promise<void>;
|
||||
clean: (options?: StorageOptions) => Promise<void>;
|
||||
init: () => Promise<void>;
|
||||
|
||||
getAccessToken: (options?: StorageOptions) => Promise<string>;
|
||||
setAccessToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getAddEditCipherInfo: (options?: StorageOptions) => Promise<any>;
|
||||
setAddEditCipherInfo: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getAlwaysShowDock: (options?: StorageOptions) => Promise<boolean>;
|
||||
setAlwaysShowDock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getApiKeyClientId: (options?: StorageOptions) => Promise<string>;
|
||||
setApiKeyClientId: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getApiKeyClientSecret: (options?: StorageOptions) => Promise<string>;
|
||||
setApiKeyClientSecret: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getAutoConfirmFingerPrints: (options?: StorageOptions) => Promise<boolean>;
|
||||
setAutoConfirmFingerprints: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricAwaitingAcceptance: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricAwaitingAcceptance: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricFingerprintValidated: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricFingerprintValidated: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricLocked: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricLocked: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricText: (options?: StorageOptions) => Promise<string>;
|
||||
setBiometricText: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getBiometricUnlock: (options?: StorageOptions) => Promise<boolean>;
|
||||
setBiometricUnlock: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getCanAccessPremium: (options?: StorageOptions) => Promise<boolean>;
|
||||
getClearClipboard: (options?: StorageOptions) => Promise<number>;
|
||||
setClearClipboard: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getCollapsedGroupings: (options?: StorageOptions) => Promise<string[]>;
|
||||
setCollapsedGroupings: (value: string[], options?: StorageOptions) => Promise<void>;
|
||||
getConvertAccountToKeyConnector: (options?: StorageOptions) => Promise<boolean>;
|
||||
setConvertAccountToKeyConnector: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setCryptoMasterKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyAuto: (options?: StorageOptions) => Promise<string>;
|
||||
setCryptoMasterKeyAuto: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyB64: (options?: StorageOptions) => Promise<string>;
|
||||
setCryptoMasterKeyB64: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<string>;
|
||||
hasCryptoMasterKeyBiometric: (options?: StorageOptions) => Promise<boolean>;
|
||||
setCryptoMasterKeyBiometric: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getDecodedToken: (options?: StorageOptions) => Promise<any>;
|
||||
setDecodedToken: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setDecryptedCryptoSymmetricKey: (
|
||||
value: SymmetricCryptoKey,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getDecryptedOrganizationKeys: (
|
||||
options?: StorageOptions,
|
||||
) => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
setDecryptedOrganizationKeys: (
|
||||
value: Map<string, SymmetricCryptoKey>,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getDecryptedPinProtected: (options?: StorageOptions) => Promise<EncString>;
|
||||
setDecryptedPinProtected: (value: EncString, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedPrivateKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setDecryptedPrivateKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getDecryptedProviderKeys: (options?: StorageOptions) => Promise<Map<string, SymmetricCryptoKey>>;
|
||||
setDecryptedProviderKeys: (
|
||||
value: Map<string, SymmetricCryptoKey>,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getDefaultUriMatch: (options?: StorageOptions) => Promise<UriMatchType>;
|
||||
setDefaultUriMatch: (value: UriMatchType, options?: StorageOptions) => Promise<void>;
|
||||
getDisableAutoBiometricsPrompt: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableAutoBiometricsPrompt: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableAutoTotpCopy: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableAutoTotpCopy: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableBadgeCounter: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableBadgeCounter: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableContextMenuItem: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableContextMenuItem: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getDisableGa: (options?: StorageOptions) => Promise<boolean>;
|
||||
setDisableGa: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEmail: (options?: StorageOptions) => Promise<string>;
|
||||
setEmail: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEmailVerified: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEmailVerified: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableAlwaysOnTop: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableAlwaysOnTop: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableBiometric: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableBiometric: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableCloseToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableCloseToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableFullWidth: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableFullWidth: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableMinimizeToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableMinimizeToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableStartToTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableStartToTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEnableTray: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEnableTray: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedCryptoSymmetricKey: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedCryptoSymmetricKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedOrganizationKeys: (options?: StorageOptions) => Promise<any>;
|
||||
setEncryptedOrganizationKeys: (
|
||||
value: Map<string, SymmetricCryptoKey>,
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getEncryptedPinProtected: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedPinProtected: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedPrivateKey: (options?: StorageOptions) => Promise<string>;
|
||||
setEncryptedPrivateKey: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEncryptedProviderKeys: (options?: StorageOptions) => Promise<any>;
|
||||
setEncryptedProviderKeys: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getEntityId: (options?: StorageOptions) => Promise<string>;
|
||||
getEnvironmentUrls: (options?: StorageOptions) => Promise<EnvironmentUrls>;
|
||||
setEnvironmentUrls: (value: EnvironmentUrls, options?: StorageOptions) => Promise<void>;
|
||||
getEquivalentDomains: (options?: StorageOptions) => Promise<any>;
|
||||
setEquivalentDomains: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getEverBeenUnlocked: (options?: StorageOptions) => Promise<boolean>;
|
||||
setEverBeenUnlocked: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getForcePasswordReset: (options?: StorageOptions) => Promise<boolean>;
|
||||
setForcePasswordReset: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getInstalledVersion: (options?: StorageOptions) => Promise<string>;
|
||||
setInstalledVersion: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getIsAuthenticated: (options?: StorageOptions) => Promise<boolean>;
|
||||
getKdfIterations: (options?: StorageOptions) => Promise<number>;
|
||||
setKdfIterations: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getKdfType: (options?: StorageOptions) => Promise<KdfType>;
|
||||
setKdfType: (value: KdfType, options?: StorageOptions) => Promise<void>;
|
||||
getKeyHash: (options?: StorageOptions) => Promise<string>;
|
||||
setKeyHash: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLastActive: (options?: StorageOptions) => Promise<number>;
|
||||
setLastActive: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getLastSync: (options?: StorageOptions) => Promise<string>;
|
||||
setLastSync: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLegacyEtmKey: (options?: StorageOptions) => Promise<SymmetricCryptoKey>;
|
||||
setLegacyEtmKey: (value: SymmetricCryptoKey, options?: StorageOptions) => Promise<void>;
|
||||
getLocalData: (options?: StorageOptions) => Promise<any>;
|
||||
setLocalData: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLocale: (options?: StorageOptions) => Promise<string>;
|
||||
setLocale: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getLoginRedirect: (options?: StorageOptions) => Promise<any>;
|
||||
setLoginRedirect: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getMainWindowSize: (options?: StorageOptions) => Promise<number>;
|
||||
setMainWindowSize: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getMinimizeOnCopyToClipboard: (options?: StorageOptions) => Promise<boolean>;
|
||||
setMinimizeOnCopyToClipboard: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getNeverDomains: (options?: StorageOptions) => Promise<{ [id: string]: any }>;
|
||||
setNeverDomains: (value: { [id: string]: any }, options?: StorageOptions) => Promise<void>;
|
||||
getNoAutoPromptBiometrics: (options?: StorageOptions) => Promise<boolean>;
|
||||
setNoAutoPromptBiometrics: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getNoAutoPromptBiometricsText: (options?: StorageOptions) => Promise<string>;
|
||||
setNoAutoPromptBiometricsText: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getOpenAtLogin: (options?: StorageOptions) => Promise<boolean>;
|
||||
setOpenAtLogin: (value: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getOrganizationInvitation: (options?: StorageOptions) => Promise<any>;
|
||||
setOrganizationInvitation: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getOrganizations: (options?: StorageOptions) => Promise<{ [id: string]: OrganizationData }>;
|
||||
setOrganizations: (
|
||||
value: { [id: string]: OrganizationData },
|
||||
options?: StorageOptions,
|
||||
) => Promise<void>;
|
||||
getPasswordGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
||||
setPasswordGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getUsernameGenerationOptions: (options?: StorageOptions) => Promise<any>;
|
||||
setUsernameGenerationOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getGeneratorOptions: (options?: StorageOptions) => Promise<any>;
|
||||
setGeneratorOptions: (value: any, options?: StorageOptions) => Promise<void>;
|
||||
getProtectedPin: (options?: StorageOptions) => Promise<string>;
|
||||
setProtectedPin: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getProviders: (options?: StorageOptions) => Promise<{ [id: string]: ProviderData }>;
|
||||
setProviders: (value: { [id: string]: ProviderData }, options?: StorageOptions) => Promise<void>;
|
||||
getPublicKey: (options?: StorageOptions) => Promise<ArrayBuffer>;
|
||||
setPublicKey: (value: ArrayBuffer, options?: StorageOptions) => Promise<void>;
|
||||
getRefreshToken: (options?: StorageOptions) => Promise<string>;
|
||||
setRefreshToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getRememberedEmail: (options?: StorageOptions) => Promise<string>;
|
||||
setRememberedEmail: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSecurityStamp: (options?: StorageOptions) => Promise<string>;
|
||||
setSecurityStamp: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSettings: (options?: StorageOptions) => Promise<any>;
|
||||
setSettings: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoCodeVerifier: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoCodeVerifier: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoOrgIdentifier: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoOrganizationIdentifier: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getSsoState: (options?: StorageOptions) => Promise<string>;
|
||||
setSsoState: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getTheme: (options?: StorageOptions) => Promise<ThemeType>;
|
||||
setTheme: (value: ThemeType, options?: StorageOptions) => Promise<void>;
|
||||
getTwoFactorToken: (options?: StorageOptions) => Promise<string>;
|
||||
setTwoFactorToken: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getUserId: (options?: StorageOptions) => Promise<string>;
|
||||
getUsesKeyConnector: (options?: StorageOptions) => Promise<boolean>;
|
||||
setUsesKeyConnector: (vaule: boolean, options?: StorageOptions) => Promise<void>;
|
||||
getVaultTimeout: (options?: StorageOptions) => Promise<number>;
|
||||
setVaultTimeout: (value: number, options?: StorageOptions) => Promise<void>;
|
||||
getVaultTimeoutAction: (options?: StorageOptions) => Promise<string>;
|
||||
setVaultTimeoutAction: (value: string, options?: StorageOptions) => Promise<void>;
|
||||
getStateVersion: () => Promise<number>;
|
||||
setStateVersion: (value: number) => Promise<void>;
|
||||
getWindow: () => Promise<WindowState>;
|
||||
setWindow: (value: WindowState) => Promise<void>;
|
||||
}
|
||||
// Stub file - re-exports DC StateService
|
||||
export { StateService } from "@/src/abstractions/state.service";
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export abstract class StateMigrationService {
|
||||
needsMigration: () => Promise<boolean>;
|
||||
migrate: () => Promise<void>;
|
||||
}
|
||||
@@ -1,32 +1,2 @@
|
||||
import { IdentityTokenResponse } from "../models/response/identityTokenResponse";
|
||||
|
||||
export abstract class TokenService {
|
||||
setTokens: (
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
clientIdClientSecret: [string, string],
|
||||
) => Promise<any>;
|
||||
setToken: (token: string) => Promise<any>;
|
||||
getToken: () => Promise<string>;
|
||||
setRefreshToken: (refreshToken: string) => Promise<any>;
|
||||
getRefreshToken: () => Promise<string>;
|
||||
setClientId: (clientId: string) => Promise<any>;
|
||||
getClientId: () => Promise<string>;
|
||||
setClientSecret: (clientSecret: string) => Promise<any>;
|
||||
getClientSecret: () => Promise<string>;
|
||||
setTwoFactorToken: (tokenResponse: IdentityTokenResponse) => Promise<any>;
|
||||
getTwoFactorToken: () => Promise<string>;
|
||||
clearTwoFactorToken: () => Promise<any>;
|
||||
clearToken: (userId?: string) => Promise<any>;
|
||||
decodeToken: (token?: string) => any;
|
||||
getTokenExpirationDate: () => Promise<Date>;
|
||||
tokenSecondsRemaining: (offsetSeconds?: number) => Promise<number>;
|
||||
tokenNeedsRefresh: (minutes?: number) => Promise<boolean>;
|
||||
getUserId: () => Promise<string>;
|
||||
getEmail: () => Promise<string>;
|
||||
getEmailVerified: () => Promise<boolean>;
|
||||
getName: () => Promise<string>;
|
||||
getPremium: () => Promise<boolean>;
|
||||
getIssuer: () => Promise<string>;
|
||||
getIsExternal: () => Promise<boolean>;
|
||||
}
|
||||
// Stub file - re-exports DC TokenService
|
||||
export { TokenService } from "@/src/abstractions/token.service";
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum AuthenticationStatus {
|
||||
Locked = "locked",
|
||||
Unlocked = "unlocked",
|
||||
LoggedOut = "loggedOut",
|
||||
Active = "active",
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum CipherRepromptType {
|
||||
None = 0,
|
||||
Password = 1,
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum CipherType {
|
||||
Login = 1,
|
||||
SecureNote = 2,
|
||||
Card = 3,
|
||||
Identity = 4,
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum EmergencyAccessStatusType {
|
||||
Invited = 0,
|
||||
Accepted = 1,
|
||||
Confirmed = 2,
|
||||
RecoveryInitiated = 3,
|
||||
RecoveryApproved = 4,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum EmergencyAccessType {
|
||||
View = 0,
|
||||
Takeover = 1,
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum FieldType {
|
||||
Text = 0,
|
||||
Hidden = 1,
|
||||
Boolean = 2,
|
||||
Linked = 3,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum FileUploadType {
|
||||
Direct = 0,
|
||||
Azure = 1,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum HashPurpose {
|
||||
ServerAuthorization = 1,
|
||||
LocalAuthorization = 2,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum KeySuffixOptions {
|
||||
Auto = "auto",
|
||||
Biometric = "biometric",
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
export enum NotificationType {
|
||||
SyncCipherUpdate = 0,
|
||||
SyncCipherCreate = 1,
|
||||
SyncLoginDelete = 2,
|
||||
SyncFolderDelete = 3,
|
||||
SyncCiphers = 4,
|
||||
|
||||
SyncVault = 5,
|
||||
SyncOrgKeys = 6,
|
||||
SyncFolderCreate = 7,
|
||||
SyncFolderUpdate = 8,
|
||||
SyncCipherDelete = 9,
|
||||
SyncSettings = 10,
|
||||
|
||||
LogOut = 11,
|
||||
|
||||
SyncSendCreate = 12,
|
||||
SyncSendUpdate = 13,
|
||||
SyncSendDelete = 14,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum OrganizationUserStatusType {
|
||||
Invited = 0,
|
||||
Accepted = 1,
|
||||
Confirmed = 2,
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum OrganizationUserType {
|
||||
Owner = 0,
|
||||
Admin = 1,
|
||||
User = 2,
|
||||
Manager = 3,
|
||||
Custom = 4,
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export enum PaymentMethodType {
|
||||
Card = 0,
|
||||
BankAccount = 1,
|
||||
PayPal = 2,
|
||||
BitPay = 3,
|
||||
Credit = 4,
|
||||
WireTransfer = 5,
|
||||
AppleInApp = 6,
|
||||
GoogleInApp = 7,
|
||||
Check = 8,
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
export enum Permissions {
|
||||
AccessEventLogs,
|
||||
AccessImportExport,
|
||||
AccessReports,
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and
|
||||
* `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
ManageAllCollections,
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and
|
||||
* `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
ManageAssignedCollections,
|
||||
ManageGroups,
|
||||
ManageOrganization,
|
||||
ManagePolicies,
|
||||
ManageProvider,
|
||||
ManageUsers,
|
||||
ManageUsersPassword,
|
||||
CreateNewCollections,
|
||||
EditAnyCollection,
|
||||
DeleteAnyCollection,
|
||||
EditAssignedCollections,
|
||||
DeleteAssignedCollections,
|
||||
ManageSso,
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export enum PlanSponsorshipType {
|
||||
FamiliesForEnterprise = 0,
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
export enum PlanType {
|
||||
Free = 0,
|
||||
FamiliesAnnually2019 = 1,
|
||||
TeamsMonthly2019 = 2,
|
||||
TeamsAnnually2019 = 3,
|
||||
EnterpriseMonthly2019 = 4,
|
||||
EnterpriseAnnually2019 = 5,
|
||||
Custom = 6,
|
||||
FamiliesAnnually = 7,
|
||||
TeamsMonthly = 8,
|
||||
TeamsAnnually = 9,
|
||||
EnterpriseMonthly = 10,
|
||||
EnterpriseAnnually = 11,
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export enum ProductType {
|
||||
Free = 0,
|
||||
Families = 1,
|
||||
Teams = 2,
|
||||
Enterprise = 3,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export enum ProviderUserStatusType {
|
||||
Invited = 0,
|
||||
Accepted = 1,
|
||||
Confirmed = 2,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export enum ProviderUserType {
|
||||
ProviderAdmin = 0,
|
||||
ServiceUser = 1,
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
export enum SsoType {
|
||||
None = 0,
|
||||
OpenIdConnect = 1,
|
||||
Saml2 = 2,
|
||||
}
|
||||
|
||||
export enum OpenIdConnectRedirectBehavior {
|
||||
RedirectGet = 0,
|
||||
FormPost = 1,
|
||||
}
|
||||
|
||||
export enum Saml2BindingType {
|
||||
HttpRedirect = 1,
|
||||
HttpPost = 2,
|
||||
}
|
||||
|
||||
export enum Saml2NameIdFormat {
|
||||
NotConfigured = 0,
|
||||
Unspecified = 1,
|
||||
EmailAddress = 2,
|
||||
X509SubjectName = 3,
|
||||
WindowsDomainQualifiedName = 4,
|
||||
KerberosPrincipalName = 5,
|
||||
EntityIdentifier = 6,
|
||||
Persistent = 7,
|
||||
Transient = 8,
|
||||
}
|
||||
|
||||
export enum Saml2SigningBehavior {
|
||||
IfIdpWantAuthnRequestsSigned = 0,
|
||||
Always = 1,
|
||||
Never = 3,
|
||||
}
|
||||
@@ -3,5 +3,6 @@ export enum StateVersion {
|
||||
Two = 2, // Move to a typed State object
|
||||
Three = 3, // Fix migration of users' premium status
|
||||
Four = 4, // Fix 'Never Lock' option by removing stale data
|
||||
Latest = Four,
|
||||
Five = 5, // New state service implementation
|
||||
Latest = Five,
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export enum TransactionType {
|
||||
Charge = 0,
|
||||
Credit = 1,
|
||||
PromotionalCredit = 2,
|
||||
ReferralCredit = 3,
|
||||
Refund = 4,
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
export enum UriMatchType {
|
||||
Domain = 0,
|
||||
Host = 1,
|
||||
StartsWith = 2,
|
||||
Exact = 3,
|
||||
RegularExpression = 4,
|
||||
Never = 5,
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Account } from "../models/domain/account";
|
||||
|
||||
export class AccountFactory<T extends Account = Account> {
|
||||
private accountConstructor: new (init: Partial<T>) => T;
|
||||
|
||||
constructor(accountConstructor: new (init: Partial<T>) => T) {
|
||||
this.accountConstructor = accountConstructor;
|
||||
}
|
||||
|
||||
create(args: Partial<T>) {
|
||||
return new this.accountConstructor(args);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { GlobalState } from "../models/domain/globalState";
|
||||
|
||||
export class GlobalStateFactory<T extends GlobalState = GlobalState> {
|
||||
private globalStateConstructor: new (init: Partial<T>) => T;
|
||||
|
||||
constructor(globalStateConstructor: new (init: Partial<T>) => T) {
|
||||
this.globalStateConstructor = globalStateConstructor;
|
||||
}
|
||||
|
||||
create(args?: Partial<T>) {
|
||||
return new this.globalStateConstructor(args);
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Account } from "../models/domain/account";
|
||||
import { GlobalState } from "../models/domain/globalState";
|
||||
|
||||
import { AccountFactory } from "./accountFactory";
|
||||
import { GlobalStateFactory } from "./globalStateFactory";
|
||||
|
||||
export class StateFactory<
|
||||
TGlobal extends GlobalState = GlobalState,
|
||||
TAccount extends Account = Account,
|
||||
> {
|
||||
private globalStateFactory: GlobalStateFactory<TGlobal>;
|
||||
private accountFactory: AccountFactory<TAccount>;
|
||||
|
||||
constructor(
|
||||
globalStateConstructor: new (init: Partial<TGlobal>) => TGlobal,
|
||||
accountConstructor: new (init: Partial<TAccount>) => TAccount,
|
||||
) {
|
||||
this.globalStateFactory = new GlobalStateFactory(globalStateConstructor);
|
||||
this.accountFactory = new AccountFactory(accountConstructor);
|
||||
}
|
||||
|
||||
createGlobal(args: Partial<TGlobal>): TGlobal {
|
||||
return this.globalStateFactory.create(args);
|
||||
}
|
||||
|
||||
createAccount(args: Partial<TAccount>): TAccount {
|
||||
return this.accountFactory.create(args);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
import { ITreeNodeObject, TreeNode } from "../models/domain/treeNode";
|
||||
|
||||
export class ServiceUtils {
|
||||
static nestedTraverse(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
partIndex: number,
|
||||
parts: string[],
|
||||
obj: ITreeNodeObject,
|
||||
parent: ITreeNodeObject,
|
||||
delimiter: string,
|
||||
) {
|
||||
if (parts.length <= partIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const end = partIndex === parts.length - 1;
|
||||
const partName = parts[partIndex];
|
||||
|
||||
for (let i = 0; i < nodeTree.length; i++) {
|
||||
if (nodeTree[i].node.name !== parts[partIndex]) {
|
||||
continue;
|
||||
}
|
||||
if (end && nodeTree[i].node.id !== obj.id) {
|
||||
// Another node with the same name.
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
return;
|
||||
}
|
||||
ServiceUtils.nestedTraverse(
|
||||
nodeTree[i].children,
|
||||
partIndex + 1,
|
||||
parts,
|
||||
obj,
|
||||
nodeTree[i].node,
|
||||
delimiter,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nodeTree.filter((n) => n.node.name === partName).length === 0) {
|
||||
if (end) {
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
return;
|
||||
}
|
||||
const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1];
|
||||
ServiceUtils.nestedTraverse(
|
||||
nodeTree,
|
||||
0,
|
||||
[newPartName, ...parts.slice(partIndex + 2)],
|
||||
obj,
|
||||
parent,
|
||||
delimiter,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static getTreeNodeObject(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
id: string,
|
||||
): TreeNode<ITreeNodeObject> {
|
||||
for (let i = 0; i < nodeTree.length; i++) {
|
||||
if (nodeTree[i].node.id === id) {
|
||||
return nodeTree[i];
|
||||
} else if (nodeTree[i].children != null) {
|
||||
const node = ServiceUtils.getTreeNodeObject(nodeTree[i].children, id);
|
||||
if (node !== null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/**
|
||||
* Use as a Decorator on async functions, it will limit how many times the function can be
|
||||
* in-flight at a time.
|
||||
*
|
||||
* Calls beyond the limit will be queued, and run when one of the active calls finishes
|
||||
*/
|
||||
export function throttle(limit: number, throttleKey: (args: any[]) => string) {
|
||||
return <T>(
|
||||
target: any,
|
||||
propertyKey: string | symbol,
|
||||
descriptor: TypedPropertyDescriptor<(...args: any[]) => Promise<T>>,
|
||||
) => {
|
||||
const originalMethod: () => Promise<T> = descriptor.value;
|
||||
const allThrottles = new Map<any, Map<string, (() => void)[]>>();
|
||||
|
||||
const getThrottles = (obj: any) => {
|
||||
let throttles = allThrottles.get(obj);
|
||||
if (throttles != null) {
|
||||
return throttles;
|
||||
}
|
||||
throttles = new Map<string, (() => void)[]>();
|
||||
allThrottles.set(obj, throttles);
|
||||
return throttles;
|
||||
};
|
||||
|
||||
return {
|
||||
value: function (...args: any[]) {
|
||||
const throttles = getThrottles(this);
|
||||
const argsThrottleKey = throttleKey(args);
|
||||
let queue = throttles.get(argsThrottleKey);
|
||||
if (queue == null) {
|
||||
queue = [];
|
||||
throttles.set(argsThrottleKey, queue);
|
||||
}
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const exec = () => {
|
||||
const onFinally = () => {
|
||||
queue.splice(queue.indexOf(exec), 1);
|
||||
if (queue.length >= limit) {
|
||||
queue[limit - 1]();
|
||||
} else if (queue.length === 0) {
|
||||
throttles.delete(argsThrottleKey);
|
||||
if (throttles.size === 0) {
|
||||
allThrottles.delete(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
originalMethod
|
||||
.apply(this, args)
|
||||
.then((val: any) => {
|
||||
onFinally();
|
||||
return val;
|
||||
})
|
||||
.catch((err: any) => {
|
||||
onFinally();
|
||||
throw err;
|
||||
})
|
||||
.then(resolve, reject);
|
||||
};
|
||||
queue.push(exec);
|
||||
if (queue.length <= limit) {
|
||||
exec();
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,55 +0,0 @@
|
||||
import { BaseResponse } from "../response/baseResponse";
|
||||
|
||||
export class PermissionsApi extends BaseResponse {
|
||||
accessEventLogs: boolean;
|
||||
accessImportExport: boolean;
|
||||
accessReports: boolean;
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `createNewCollections`, `editAnyCollection`, and
|
||||
* `deleteAnyCollection`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
manageAllCollections: boolean;
|
||||
createNewCollections: boolean;
|
||||
editAnyCollection: boolean;
|
||||
deleteAnyCollection: boolean;
|
||||
/**
|
||||
* @deprecated Sep 29 2021: This permission has been split out to `editAssignedCollections` and
|
||||
* `deleteAssignedCollections`. It exists here for backwards compatibility with Server versions <= 1.43.0
|
||||
*/
|
||||
manageAssignedCollections: boolean;
|
||||
editAssignedCollections: boolean;
|
||||
deleteAssignedCollections: boolean;
|
||||
manageCiphers: boolean;
|
||||
manageGroups: boolean;
|
||||
manageSso: boolean;
|
||||
managePolicies: boolean;
|
||||
manageUsers: boolean;
|
||||
manageResetPassword: boolean;
|
||||
|
||||
constructor(data: any = null) {
|
||||
super(data);
|
||||
if (data == null) {
|
||||
return this;
|
||||
}
|
||||
this.accessEventLogs = this.getResponseProperty("AccessEventLogs");
|
||||
this.accessImportExport = this.getResponseProperty("AccessImportExport");
|
||||
this.accessReports = this.getResponseProperty("AccessReports");
|
||||
|
||||
// For backwards compatibility with Server <= 1.43.0
|
||||
this.manageAllCollections = this.getResponseProperty("ManageAllCollections");
|
||||
this.manageAssignedCollections = this.getResponseProperty("ManageAssignedCollections");
|
||||
|
||||
this.createNewCollections = this.getResponseProperty("CreateNewCollections");
|
||||
this.editAnyCollection = this.getResponseProperty("EditAnyCollection");
|
||||
this.deleteAnyCollection = this.getResponseProperty("DeleteAnyCollection");
|
||||
this.editAssignedCollections = this.getResponseProperty("EditAssignedCollections");
|
||||
this.deleteAssignedCollections = this.getResponseProperty("DeleteAssignedCollections");
|
||||
|
||||
this.manageCiphers = this.getResponseProperty("ManageCiphers");
|
||||
this.manageGroups = this.getResponseProperty("ManageGroups");
|
||||
this.manageSso = this.getResponseProperty("ManageSso");
|
||||
this.managePolicies = this.getResponseProperty("ManagePolicies");
|
||||
this.manageUsers = this.getResponseProperty("ManageUsers");
|
||||
this.manageResetPassword = this.getResponseProperty("ManageResetPassword");
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
import {
|
||||
OpenIdConnectRedirectBehavior,
|
||||
Saml2BindingType,
|
||||
Saml2NameIdFormat,
|
||||
Saml2SigningBehavior,
|
||||
SsoType,
|
||||
} from "../../enums/ssoEnums";
|
||||
import { BaseResponse } from "../response/baseResponse";
|
||||
import { SsoConfigView } from "../view/ssoConfigView";
|
||||
|
||||
export class SsoConfigApi extends BaseResponse {
|
||||
static fromView(view: SsoConfigView, api = new SsoConfigApi()) {
|
||||
api.configType = view.configType;
|
||||
|
||||
api.keyConnectorEnabled = view.keyConnectorEnabled;
|
||||
api.keyConnectorUrl = view.keyConnectorUrl;
|
||||
|
||||
if (api.configType === SsoType.OpenIdConnect) {
|
||||
api.authority = view.openId.authority;
|
||||
api.clientId = view.openId.clientId;
|
||||
api.clientSecret = view.openId.clientSecret;
|
||||
api.metadataAddress = view.openId.metadataAddress;
|
||||
api.redirectBehavior = view.openId.redirectBehavior;
|
||||
api.getClaimsFromUserInfoEndpoint = view.openId.getClaimsFromUserInfoEndpoint;
|
||||
api.additionalScopes = view.openId.additionalScopes;
|
||||
api.additionalUserIdClaimTypes = view.openId.additionalUserIdClaimTypes;
|
||||
api.additionalEmailClaimTypes = view.openId.additionalEmailClaimTypes;
|
||||
api.additionalNameClaimTypes = view.openId.additionalNameClaimTypes;
|
||||
api.acrValues = view.openId.acrValues;
|
||||
api.expectedReturnAcrValue = view.openId.expectedReturnAcrValue;
|
||||
} else if (api.configType === SsoType.Saml2) {
|
||||
api.spNameIdFormat = view.saml.spNameIdFormat;
|
||||
api.spOutboundSigningAlgorithm = view.saml.spOutboundSigningAlgorithm;
|
||||
api.spSigningBehavior = view.saml.spSigningBehavior;
|
||||
api.spMinIncomingSigningAlgorithm = view.saml.spMinIncomingSigningAlgorithm;
|
||||
api.spWantAssertionsSigned = view.saml.spWantAssertionsSigned;
|
||||
api.spValidateCertificates = view.saml.spValidateCertificates;
|
||||
|
||||
api.idpEntityId = view.saml.idpEntityId;
|
||||
api.idpBindingType = view.saml.idpBindingType;
|
||||
api.idpSingleSignOnServiceUrl = view.saml.idpSingleSignOnServiceUrl;
|
||||
api.idpSingleLogoutServiceUrl = view.saml.idpSingleLogoutServiceUrl;
|
||||
api.idpX509PublicCert = view.saml.idpX509PublicCert;
|
||||
api.idpOutboundSigningAlgorithm = view.saml.idpOutboundSigningAlgorithm;
|
||||
api.idpAllowUnsolicitedAuthnResponse = view.saml.idpAllowUnsolicitedAuthnResponse;
|
||||
api.idpWantAuthnRequestsSigned = view.saml.idpWantAuthnRequestsSigned;
|
||||
|
||||
// Value is inverted in the api model (disable instead of allow)
|
||||
api.idpDisableOutboundLogoutRequests = !view.saml.idpAllowOutboundLogoutRequests;
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
configType: SsoType;
|
||||
|
||||
keyConnectorEnabled: boolean;
|
||||
keyConnectorUrl: string;
|
||||
|
||||
// OpenId
|
||||
authority: string;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
metadataAddress: string;
|
||||
redirectBehavior: OpenIdConnectRedirectBehavior;
|
||||
getClaimsFromUserInfoEndpoint: boolean;
|
||||
additionalScopes: string;
|
||||
additionalUserIdClaimTypes: string;
|
||||
additionalEmailClaimTypes: string;
|
||||
additionalNameClaimTypes: string;
|
||||
acrValues: string;
|
||||
expectedReturnAcrValue: string;
|
||||
|
||||
// SAML
|
||||
spNameIdFormat: Saml2NameIdFormat;
|
||||
spOutboundSigningAlgorithm: string;
|
||||
spSigningBehavior: Saml2SigningBehavior;
|
||||
spMinIncomingSigningAlgorithm: boolean;
|
||||
spWantAssertionsSigned: boolean;
|
||||
spValidateCertificates: boolean;
|
||||
|
||||
idpEntityId: string;
|
||||
idpBindingType: Saml2BindingType;
|
||||
idpSingleSignOnServiceUrl: string;
|
||||
idpSingleLogoutServiceUrl: string;
|
||||
idpX509PublicCert: string;
|
||||
idpOutboundSigningAlgorithm: string;
|
||||
idpAllowUnsolicitedAuthnResponse: boolean;
|
||||
idpDisableOutboundLogoutRequests: boolean;
|
||||
idpWantAuthnRequestsSigned: boolean;
|
||||
|
||||
constructor(data: any = null) {
|
||||
super(data);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.configType = this.getResponseProperty("ConfigType");
|
||||
|
||||
this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled");
|
||||
this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl");
|
||||
|
||||
this.authority = this.getResponseProperty("Authority");
|
||||
this.clientId = this.getResponseProperty("ClientId");
|
||||
this.clientSecret = this.getResponseProperty("ClientSecret");
|
||||
this.metadataAddress = this.getResponseProperty("MetadataAddress");
|
||||
this.redirectBehavior = this.getResponseProperty("RedirectBehavior");
|
||||
this.getClaimsFromUserInfoEndpoint = this.getResponseProperty("GetClaimsFromUserInfoEndpoint");
|
||||
this.additionalScopes = this.getResponseProperty("AdditionalScopes");
|
||||
this.additionalUserIdClaimTypes = this.getResponseProperty("AdditionalUserIdClaimTypes");
|
||||
this.additionalEmailClaimTypes = this.getResponseProperty("AdditionalEmailClaimTypes");
|
||||
this.additionalNameClaimTypes = this.getResponseProperty("AdditionalNameClaimTypes");
|
||||
this.acrValues = this.getResponseProperty("AcrValues");
|
||||
this.expectedReturnAcrValue = this.getResponseProperty("ExpectedReturnAcrValue");
|
||||
|
||||
this.spNameIdFormat = this.getResponseProperty("SpNameIdFormat");
|
||||
this.spOutboundSigningAlgorithm = this.getResponseProperty("SpOutboundSigningAlgorithm");
|
||||
this.spSigningBehavior = this.getResponseProperty("SpSigningBehavior");
|
||||
this.spMinIncomingSigningAlgorithm = this.getResponseProperty("SpMinIncomingSigningAlgorithm");
|
||||
this.spWantAssertionsSigned = this.getResponseProperty("SpWantAssertionsSigned");
|
||||
this.spValidateCertificates = this.getResponseProperty("SpValidateCertificates");
|
||||
|
||||
this.idpEntityId = this.getResponseProperty("IdpEntityId");
|
||||
this.idpBindingType = this.getResponseProperty("IdpBindingType");
|
||||
this.idpSingleSignOnServiceUrl = this.getResponseProperty("IdpSingleSignOnServiceUrl");
|
||||
this.idpSingleLogoutServiceUrl = this.getResponseProperty("IdpSingleLogoutServiceUrl");
|
||||
this.idpX509PublicCert = this.getResponseProperty("IdpX509PublicCert");
|
||||
this.idpOutboundSigningAlgorithm = this.getResponseProperty("IdpOutboundSigningAlgorithm");
|
||||
this.idpAllowUnsolicitedAuthnResponse = this.getResponseProperty(
|
||||
"IdpAllowUnsolicitedAuthnResponse",
|
||||
);
|
||||
this.idpDisableOutboundLogoutRequests = this.getResponseProperty(
|
||||
"IdpDisableOutboundLogoutRequests",
|
||||
);
|
||||
this.idpWantAuthnRequestsSigned = this.getResponseProperty("IdpWantAuthnRequestsSigned");
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { ProductType } from "../../enums/productType";
|
||||
import { PermissionsApi } from "../api/permissionsApi";
|
||||
import { ProfileOrganizationResponse } from "../response/profileOrganizationResponse";
|
||||
|
||||
export class OrganizationData {
|
||||
id: string;
|
||||
name: string;
|
||||
status: OrganizationUserStatusType;
|
||||
type: OrganizationUserType;
|
||||
enabled: boolean;
|
||||
usePolicies: boolean;
|
||||
useGroups: boolean;
|
||||
useDirectory: boolean;
|
||||
useEvents: boolean;
|
||||
useTotp: boolean;
|
||||
use2fa: boolean;
|
||||
useApi: boolean;
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
maxCollections: number;
|
||||
maxStorageGb?: number;
|
||||
ssoBound: boolean;
|
||||
identifier: string;
|
||||
permissions: PermissionsApi;
|
||||
resetPasswordEnrolled: boolean;
|
||||
userId: string;
|
||||
hasPublicAndPrivateKeys: boolean;
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
isProviderUser: boolean;
|
||||
familySponsorshipFriendlyName: string;
|
||||
familySponsorshipAvailable: boolean;
|
||||
planProductType: ProductType;
|
||||
keyConnectorEnabled: boolean;
|
||||
keyConnectorUrl: string;
|
||||
|
||||
constructor(response: ProfileOrganizationResponse) {
|
||||
this.id = response.id;
|
||||
this.name = response.name;
|
||||
this.status = response.status;
|
||||
this.type = response.type;
|
||||
this.enabled = response.enabled;
|
||||
this.usePolicies = response.usePolicies;
|
||||
this.useGroups = response.useGroups;
|
||||
this.useDirectory = response.useDirectory;
|
||||
this.useEvents = response.useEvents;
|
||||
this.useTotp = response.useTotp;
|
||||
this.use2fa = response.use2fa;
|
||||
this.useApi = response.useApi;
|
||||
this.useSso = response.useSso;
|
||||
this.useKeyConnector = response.useKeyConnector;
|
||||
this.useResetPassword = response.useResetPassword;
|
||||
this.selfHost = response.selfHost;
|
||||
this.usersGetPremium = response.usersGetPremium;
|
||||
this.seats = response.seats;
|
||||
this.maxCollections = response.maxCollections;
|
||||
this.maxStorageGb = response.maxStorageGb;
|
||||
this.ssoBound = response.ssoBound;
|
||||
this.identifier = response.identifier;
|
||||
this.permissions = response.permissions;
|
||||
this.resetPasswordEnrolled = response.resetPasswordEnrolled;
|
||||
this.userId = response.userId;
|
||||
this.hasPublicAndPrivateKeys = response.hasPublicAndPrivateKeys;
|
||||
this.providerId = response.providerId;
|
||||
this.providerName = response.providerName;
|
||||
this.familySponsorshipFriendlyName = response.familySponsorshipFriendlyName;
|
||||
this.familySponsorshipAvailable = response.familySponsorshipAvailable;
|
||||
this.planProductType = response.planProductType;
|
||||
this.keyConnectorEnabled = response.keyConnectorEnabled;
|
||||
this.keyConnectorUrl = response.keyConnectorUrl;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import { ProviderUserStatusType } from "../../enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "../../enums/providerUserType";
|
||||
import { ProfileProviderResponse } from "../response/profileProviderResponse";
|
||||
|
||||
export class ProviderData {
|
||||
id: string;
|
||||
name: string;
|
||||
status: ProviderUserStatusType;
|
||||
type: ProviderUserType;
|
||||
enabled: boolean;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
|
||||
constructor(response: ProfileProviderResponse) {
|
||||
this.id = response.id;
|
||||
this.name = response.name;
|
||||
this.status = response.status;
|
||||
this.type = response.type;
|
||||
this.enabled = response.enabled;
|
||||
this.userId = response.userId;
|
||||
this.useEvents = response.useEvents;
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
import { AuthenticationStatus } from "../../enums/authenticationStatus";
|
||||
import { KdfType } from "../../enums/kdfType";
|
||||
import { UriMatchType } from "../../enums/uriMatchType";
|
||||
import { OrganizationData } from "../data/organizationData";
|
||||
import { ProviderData } from "../data/providerData";
|
||||
|
||||
import { EncString } from "./encString";
|
||||
import { EnvironmentUrls } from "./environmentUrls";
|
||||
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||
|
||||
export class EncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: TEncrypted;
|
||||
decrypted?: TDecrypted;
|
||||
}
|
||||
|
||||
export class DataEncryptionPair<TEncrypted, TDecrypted> {
|
||||
encrypted?: { [id: string]: TEncrypted };
|
||||
decrypted?: TDecrypted[];
|
||||
}
|
||||
|
||||
export class AccountData {
|
||||
ciphers?: any = new DataEncryptionPair<any, any>();
|
||||
folders?: DataEncryptionPair<any, any> = new DataEncryptionPair<any, any>();
|
||||
localData?: any;
|
||||
sends?: any = new DataEncryptionPair<any, any>();
|
||||
collections?: DataEncryptionPair<any, any> = new DataEncryptionPair<any, any>();
|
||||
policies?: DataEncryptionPair<any, any> = new DataEncryptionPair<any, any>();
|
||||
passwordGenerationHistory?: EncryptionPair<any[], any[]> = new EncryptionPair<any[], any[]>();
|
||||
addEditCipherInfo?: any;
|
||||
eventCollection?: any[];
|
||||
organizations?: { [id: string]: OrganizationData };
|
||||
providers?: { [id: string]: ProviderData };
|
||||
}
|
||||
|
||||
export class AccountKeys {
|
||||
cryptoMasterKey?: SymmetricCryptoKey;
|
||||
cryptoMasterKeyAuto?: string;
|
||||
cryptoMasterKeyB64?: string;
|
||||
cryptoMasterKeyBiometric?: string;
|
||||
cryptoSymmetricKey?: EncryptionPair<string, SymmetricCryptoKey> = new EncryptionPair<
|
||||
string,
|
||||
SymmetricCryptoKey
|
||||
>();
|
||||
organizationKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<
|
||||
any,
|
||||
Map<string, SymmetricCryptoKey>
|
||||
>();
|
||||
providerKeys?: EncryptionPair<any, Map<string, SymmetricCryptoKey>> = new EncryptionPair<
|
||||
any,
|
||||
Map<string, SymmetricCryptoKey>
|
||||
>();
|
||||
privateKey?: EncryptionPair<string, ArrayBuffer> = new EncryptionPair<string, ArrayBuffer>();
|
||||
legacyEtmKey?: SymmetricCryptoKey;
|
||||
publicKey?: ArrayBuffer;
|
||||
apiKeyClientSecret?: string;
|
||||
}
|
||||
|
||||
export class AccountProfile {
|
||||
apiKeyClientId?: string;
|
||||
authenticationStatus?: AuthenticationStatus;
|
||||
convertAccountToKeyConnector?: boolean;
|
||||
email?: string;
|
||||
emailVerified?: boolean;
|
||||
entityId?: string;
|
||||
entityType?: string;
|
||||
everBeenUnlocked?: boolean;
|
||||
forcePasswordReset?: boolean;
|
||||
hasPremiumPersonally?: boolean;
|
||||
lastSync?: string;
|
||||
userId?: string;
|
||||
usesKeyConnector?: boolean;
|
||||
keyHash?: string;
|
||||
kdfIterations?: number;
|
||||
kdfType?: KdfType;
|
||||
}
|
||||
|
||||
export class AccountSettings {
|
||||
autoConfirmFingerPrints?: boolean;
|
||||
autoFillOnPageLoadDefault?: boolean;
|
||||
biometricLocked?: boolean;
|
||||
biometricUnlock?: boolean;
|
||||
clearClipboard?: number;
|
||||
collapsedGroupings?: string[];
|
||||
defaultUriMatch?: UriMatchType;
|
||||
disableAddLoginNotification?: boolean;
|
||||
disableAutoBiometricsPrompt?: boolean;
|
||||
disableAutoTotpCopy?: boolean;
|
||||
disableBadgeCounter?: boolean;
|
||||
disableChangedPasswordNotification?: boolean;
|
||||
disableContextMenuItem?: boolean;
|
||||
disableGa?: boolean;
|
||||
dontShowCardsCurrentTab?: boolean;
|
||||
dontShowIdentitiesCurrentTab?: boolean;
|
||||
enableAlwaysOnTop?: boolean;
|
||||
enableAutoFillOnPageLoad?: boolean;
|
||||
enableBiometric?: boolean;
|
||||
enableFullWidth?: boolean;
|
||||
enableGravitars?: boolean;
|
||||
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
|
||||
equivalentDomains?: any;
|
||||
minimizeOnCopyToClipboard?: boolean;
|
||||
neverDomains?: { [id: string]: any };
|
||||
passwordGenerationOptions?: any;
|
||||
usernameGenerationOptions?: any;
|
||||
generatorOptions?: any;
|
||||
pinProtected?: EncryptionPair<string, EncString> = new EncryptionPair<string, EncString>();
|
||||
protectedPin?: string;
|
||||
settings?: any; // TODO: Merge whatever is going on here into the AccountSettings model properly
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string = "lock";
|
||||
}
|
||||
|
||||
export class AccountTokens {
|
||||
accessToken?: string;
|
||||
decodedToken?: any;
|
||||
refreshToken?: string;
|
||||
securityStamp?: string;
|
||||
}
|
||||
|
||||
export class Account {
|
||||
data?: AccountData = new AccountData();
|
||||
keys?: AccountKeys = new AccountKeys();
|
||||
profile?: AccountProfile = new AccountProfile();
|
||||
settings?: AccountSettings = new AccountSettings();
|
||||
tokens?: AccountTokens = new AccountTokens();
|
||||
|
||||
constructor(init: Partial<Account>) {
|
||||
Object.assign(this, {
|
||||
data: {
|
||||
...new AccountData(),
|
||||
...init?.data,
|
||||
},
|
||||
keys: {
|
||||
...new AccountKeys(),
|
||||
...init?.keys,
|
||||
},
|
||||
profile: {
|
||||
...new AccountProfile(),
|
||||
...init?.profile,
|
||||
},
|
||||
settings: {
|
||||
...new AccountSettings(),
|
||||
...init?.settings,
|
||||
},
|
||||
tokens: {
|
||||
...new AccountTokens(),
|
||||
...init?.tokens,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import { View } from "../view/view";
|
||||
|
||||
import { EncString } from "./encString";
|
||||
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||
|
||||
export default class Domain {
|
||||
protected buildDomainModel<D extends Domain>(
|
||||
domain: D,
|
||||
dataObj: any,
|
||||
map: any,
|
||||
notEncList: any[] = [],
|
||||
) {
|
||||
for (const prop in map) {
|
||||
// eslint-disable-next-line
|
||||
if (!map.hasOwnProperty(prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const objProp = dataObj[map[prop] || prop];
|
||||
if (notEncList.indexOf(prop) > -1) {
|
||||
(domain as any)[prop] = objProp ? objProp : null;
|
||||
} else {
|
||||
(domain as any)[prop] = objProp ? new EncString(objProp) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected buildDataModel<D extends Domain>(
|
||||
domain: D,
|
||||
dataObj: any,
|
||||
map: any,
|
||||
notEncStringList: any[] = [],
|
||||
) {
|
||||
for (const prop in map) {
|
||||
// eslint-disable-next-line
|
||||
if (!map.hasOwnProperty(prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const objProp = (domain as any)[map[prop] || prop];
|
||||
if (notEncStringList.indexOf(prop) > -1) {
|
||||
(dataObj as any)[prop] = objProp != null ? objProp : null;
|
||||
} else {
|
||||
(dataObj as any)[prop] = objProp != null ? (objProp as EncString).encryptedString : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async decryptObj<T extends View>(
|
||||
viewModel: T,
|
||||
map: any,
|
||||
orgId: string,
|
||||
key: SymmetricCryptoKey = null,
|
||||
): Promise<T> {
|
||||
const promises = [];
|
||||
const self: any = this;
|
||||
|
||||
for (const prop in map) {
|
||||
// eslint-disable-next-line
|
||||
if (!map.hasOwnProperty(prop)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
(function (theProp) {
|
||||
const p = Promise.resolve()
|
||||
.then(() => {
|
||||
const mapProp = map[theProp] || theProp;
|
||||
if (self[mapProp]) {
|
||||
return self[mapProp].decrypt(orgId, key);
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.then((val: any) => {
|
||||
(viewModel as any)[theProp] = val;
|
||||
});
|
||||
promises.push(p);
|
||||
})(prop);
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
return viewModel;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export class EncArrayBuffer {
|
||||
constructor(public buffer: ArrayBuffer) {}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
import { CryptoService } from "../../abstractions/crypto.service";
|
||||
import { EncryptionType } from "../../enums/encryptionType";
|
||||
import { Utils } from "../../misc/utils";
|
||||
|
||||
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||
|
||||
export class EncString {
|
||||
encryptedString?: string;
|
||||
encryptionType?: EncryptionType;
|
||||
decryptedValue?: string;
|
||||
data?: string;
|
||||
iv?: string;
|
||||
mac?: string;
|
||||
|
||||
constructor(
|
||||
encryptedStringOrType: string | EncryptionType,
|
||||
data?: string,
|
||||
iv?: string,
|
||||
mac?: string,
|
||||
) {
|
||||
if (data != null) {
|
||||
// data and header
|
||||
const encType = encryptedStringOrType as EncryptionType;
|
||||
|
||||
if (iv != null) {
|
||||
this.encryptedString = encType + "." + iv + "|" + data;
|
||||
} else {
|
||||
this.encryptedString = encType + "." + data;
|
||||
}
|
||||
|
||||
// mac
|
||||
if (mac != null) {
|
||||
this.encryptedString += "|" + mac;
|
||||
}
|
||||
|
||||
this.encryptionType = encType;
|
||||
this.data = data;
|
||||
this.iv = iv;
|
||||
this.mac = mac;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.encryptedString = encryptedStringOrType as string;
|
||||
if (!this.encryptedString) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headerPieces = this.encryptedString.split(".");
|
||||
let encPieces: string[] = null;
|
||||
|
||||
if (headerPieces.length === 2) {
|
||||
try {
|
||||
this.encryptionType = parseInt(headerPieces[0], null);
|
||||
encPieces = headerPieces[1].split("|");
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
encPieces = this.encryptedString.split("|");
|
||||
this.encryptionType =
|
||||
encPieces.length === 3
|
||||
? EncryptionType.AesCbc128_HmacSha256_B64
|
||||
: EncryptionType.AesCbc256_B64;
|
||||
}
|
||||
|
||||
switch (this.encryptionType) {
|
||||
case EncryptionType.AesCbc128_HmacSha256_B64:
|
||||
case EncryptionType.AesCbc256_HmacSha256_B64:
|
||||
if (encPieces.length !== 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iv = encPieces[0];
|
||||
this.data = encPieces[1];
|
||||
this.mac = encPieces[2];
|
||||
break;
|
||||
case EncryptionType.AesCbc256_B64:
|
||||
if (encPieces.length !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.iv = encPieces[0];
|
||||
this.data = encPieces[1];
|
||||
break;
|
||||
case EncryptionType.Rsa2048_OaepSha256_B64:
|
||||
case EncryptionType.Rsa2048_OaepSha1_B64:
|
||||
if (encPieces.length !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.data = encPieces[0];
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async decrypt(orgId: string, key: SymmetricCryptoKey = null): Promise<string> {
|
||||
if (this.decryptedValue != null) {
|
||||
return this.decryptedValue;
|
||||
}
|
||||
|
||||
let cryptoService: CryptoService;
|
||||
const containerService = (Utils.global as any).bitwardenContainerService;
|
||||
if (containerService) {
|
||||
cryptoService = containerService.getCryptoService();
|
||||
} else {
|
||||
throw new Error("global bitwardenContainerService not initialized.");
|
||||
}
|
||||
|
||||
try {
|
||||
if (key == null) {
|
||||
key = await cryptoService.getOrgKey(orgId);
|
||||
}
|
||||
this.decryptedValue = await cryptoService.decryptToUtf8(this, key);
|
||||
} catch {
|
||||
this.decryptedValue = "[error: cannot decrypt]";
|
||||
}
|
||||
return this.decryptedValue;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { SymmetricCryptoKey } from "./symmetricCryptoKey";
|
||||
|
||||
export class EncryptedObject {
|
||||
iv: ArrayBuffer;
|
||||
data: ArrayBuffer;
|
||||
mac: ArrayBuffer;
|
||||
key: SymmetricCryptoKey;
|
||||
}
|
||||
@@ -2,9 +2,5 @@ export class EnvironmentUrls {
|
||||
base: string = null;
|
||||
api: string = null;
|
||||
identity: string = null;
|
||||
icons: string = null;
|
||||
notifications: string = null;
|
||||
events: string = null;
|
||||
webVault: string = null;
|
||||
keyConnector: string = null;
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { StateVersion } from "../../enums/stateVersion";
|
||||
import { ThemeType } from "../../enums/themeType";
|
||||
|
||||
import { EnvironmentUrls } from "./environmentUrls";
|
||||
import { WindowState } from "./windowState";
|
||||
|
||||
export class GlobalState {
|
||||
enableAlwaysOnTop?: boolean;
|
||||
installedVersion?: string;
|
||||
locale?: string = "en";
|
||||
organizationInvitation?: any;
|
||||
ssoCodeVerifier?: string;
|
||||
ssoOrganizationIdentifier?: string;
|
||||
ssoState?: string;
|
||||
rememberedEmail?: string;
|
||||
theme?: ThemeType = ThemeType.System;
|
||||
window?: WindowState = new WindowState();
|
||||
twoFactorToken?: string;
|
||||
disableFavicon?: boolean;
|
||||
biometricAwaitingAcceptance?: boolean;
|
||||
biometricFingerprintValidated?: boolean;
|
||||
vaultTimeout?: number;
|
||||
vaultTimeoutAction?: string;
|
||||
loginRedirect?: any;
|
||||
mainWindowSize?: number;
|
||||
enableBiometrics?: boolean;
|
||||
biometricText?: string;
|
||||
noAutoPromptBiometrics?: boolean;
|
||||
noAutoPromptBiometricsText?: string;
|
||||
stateVersion: StateVersion = StateVersion.One;
|
||||
environmentUrls: EnvironmentUrls = new EnvironmentUrls();
|
||||
enableTray?: boolean;
|
||||
enableMinimizeToTray?: boolean;
|
||||
enableCloseToTray?: boolean;
|
||||
enableStartToTray?: boolean;
|
||||
openAtLogin?: boolean;
|
||||
alwaysShowDock?: boolean;
|
||||
enableBrowserIntegration?: boolean;
|
||||
enableBrowserIntegrationFingerprint?: boolean;
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { ProductType } from "../../enums/productType";
|
||||
import { PermissionsApi } from "../api/permissionsApi";
|
||||
import { OrganizationData } from "../data/organizationData";
|
||||
|
||||
export class Organization {
|
||||
id: string;
|
||||
name: string;
|
||||
status: OrganizationUserStatusType;
|
||||
type: OrganizationUserType;
|
||||
enabled: boolean;
|
||||
usePolicies: boolean;
|
||||
useGroups: boolean;
|
||||
useDirectory: boolean;
|
||||
useEvents: boolean;
|
||||
useTotp: boolean;
|
||||
use2fa: boolean;
|
||||
useApi: boolean;
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
maxCollections: number;
|
||||
maxStorageGb?: number;
|
||||
ssoBound: boolean;
|
||||
identifier: string;
|
||||
permissions: PermissionsApi;
|
||||
resetPasswordEnrolled: boolean;
|
||||
userId: string;
|
||||
hasPublicAndPrivateKeys: boolean;
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
isProviderUser: boolean;
|
||||
familySponsorshipFriendlyName: string;
|
||||
familySponsorshipAvailable: boolean;
|
||||
planProductType: ProductType;
|
||||
keyConnectorEnabled: boolean;
|
||||
keyConnectorUrl: string;
|
||||
|
||||
constructor(obj?: OrganizationData) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = obj.id;
|
||||
this.name = obj.name;
|
||||
this.status = obj.status;
|
||||
this.type = obj.type;
|
||||
this.enabled = obj.enabled;
|
||||
this.usePolicies = obj.usePolicies;
|
||||
this.useGroups = obj.useGroups;
|
||||
this.useDirectory = obj.useDirectory;
|
||||
this.useEvents = obj.useEvents;
|
||||
this.useTotp = obj.useTotp;
|
||||
this.use2fa = obj.use2fa;
|
||||
this.useApi = obj.useApi;
|
||||
this.useSso = obj.useSso;
|
||||
this.useKeyConnector = obj.useKeyConnector;
|
||||
this.useResetPassword = obj.useResetPassword;
|
||||
this.selfHost = obj.selfHost;
|
||||
this.usersGetPremium = obj.usersGetPremium;
|
||||
this.seats = obj.seats;
|
||||
this.maxCollections = obj.maxCollections;
|
||||
this.maxStorageGb = obj.maxStorageGb;
|
||||
this.ssoBound = obj.ssoBound;
|
||||
this.identifier = obj.identifier;
|
||||
this.permissions = obj.permissions;
|
||||
this.resetPasswordEnrolled = obj.resetPasswordEnrolled;
|
||||
this.userId = obj.userId;
|
||||
this.hasPublicAndPrivateKeys = obj.hasPublicAndPrivateKeys;
|
||||
this.providerId = obj.providerId;
|
||||
this.providerName = obj.providerName;
|
||||
this.isProviderUser = obj.isProviderUser;
|
||||
this.familySponsorshipFriendlyName = obj.familySponsorshipFriendlyName;
|
||||
this.familySponsorshipAvailable = obj.familySponsorshipAvailable;
|
||||
this.planProductType = obj.planProductType;
|
||||
this.keyConnectorEnabled = obj.keyConnectorEnabled;
|
||||
this.keyConnectorUrl = obj.keyConnectorUrl;
|
||||
}
|
||||
|
||||
get canAccess() {
|
||||
if (this.type === OrganizationUserType.Owner) {
|
||||
return true;
|
||||
}
|
||||
return this.enabled && this.status === OrganizationUserStatusType.Confirmed;
|
||||
}
|
||||
|
||||
get isManager() {
|
||||
return (
|
||||
this.type === OrganizationUserType.Manager ||
|
||||
this.type === OrganizationUserType.Owner ||
|
||||
this.type === OrganizationUserType.Admin
|
||||
);
|
||||
}
|
||||
|
||||
get isAdmin() {
|
||||
return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin;
|
||||
}
|
||||
|
||||
get isOwner() {
|
||||
return this.type === OrganizationUserType.Owner || this.isProviderUser;
|
||||
}
|
||||
|
||||
get canAccessEventLogs() {
|
||||
return this.isAdmin || this.permissions.accessEventLogs;
|
||||
}
|
||||
|
||||
get canAccessImportExport() {
|
||||
return this.isAdmin || this.permissions.accessImportExport;
|
||||
}
|
||||
|
||||
get canAccessReports() {
|
||||
return this.isAdmin || this.permissions.accessReports;
|
||||
}
|
||||
|
||||
get canCreateNewCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.createNewCollections ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
}
|
||||
|
||||
get canEditAnyCollection() {
|
||||
return (
|
||||
this.isAdmin || (this.permissions.editAnyCollection ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
}
|
||||
|
||||
get canDeleteAnyCollection() {
|
||||
return (
|
||||
this.isAdmin ||
|
||||
(this.permissions.deleteAnyCollection ?? this.permissions.manageAllCollections)
|
||||
);
|
||||
}
|
||||
|
||||
get canViewAllCollections() {
|
||||
return this.canCreateNewCollections || this.canEditAnyCollection || this.canDeleteAnyCollection;
|
||||
}
|
||||
|
||||
get canEditAssignedCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.editAssignedCollections ?? this.permissions.manageAssignedCollections)
|
||||
);
|
||||
}
|
||||
|
||||
get canDeleteAssignedCollections() {
|
||||
return (
|
||||
this.isManager ||
|
||||
(this.permissions.deleteAssignedCollections ?? this.permissions.manageAssignedCollections)
|
||||
);
|
||||
}
|
||||
|
||||
get canViewAssignedCollections() {
|
||||
return this.canDeleteAssignedCollections || this.canEditAssignedCollections;
|
||||
}
|
||||
|
||||
get canManageGroups() {
|
||||
return this.isAdmin || this.permissions.manageGroups;
|
||||
}
|
||||
|
||||
get canManageSso() {
|
||||
return this.isAdmin || this.permissions.manageSso;
|
||||
}
|
||||
|
||||
get canManagePolicies() {
|
||||
return this.isAdmin || this.permissions.managePolicies;
|
||||
}
|
||||
|
||||
get canManageUsers() {
|
||||
return this.isAdmin || this.permissions.manageUsers;
|
||||
}
|
||||
|
||||
get canManageUsersPassword() {
|
||||
return this.isAdmin || this.permissions.manageResetPassword;
|
||||
}
|
||||
|
||||
get isExemptFromPolicies() {
|
||||
return this.canManagePolicies;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { ProviderUserStatusType } from "../../enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "../../enums/providerUserType";
|
||||
import { ProviderData } from "../data/providerData";
|
||||
|
||||
export class Provider {
|
||||
id: string;
|
||||
name: string;
|
||||
status: ProviderUserStatusType;
|
||||
type: ProviderUserType;
|
||||
enabled: boolean;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
|
||||
constructor(obj?: ProviderData) {
|
||||
if (obj == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = obj.id;
|
||||
this.name = obj.name;
|
||||
this.status = obj.status;
|
||||
this.type = obj.type;
|
||||
this.enabled = obj.enabled;
|
||||
this.userId = obj.userId;
|
||||
this.useEvents = obj.useEvents;
|
||||
}
|
||||
|
||||
get canAccess() {
|
||||
if (this.isProviderAdmin) {
|
||||
return true;
|
||||
}
|
||||
return this.enabled && this.status === ProviderUserStatusType.Confirmed;
|
||||
}
|
||||
|
||||
get canCreateOrganizations() {
|
||||
return this.enabled && this.isProviderAdmin;
|
||||
}
|
||||
|
||||
get canManageUsers() {
|
||||
return this.isProviderAdmin;
|
||||
}
|
||||
|
||||
get canAccessEventLogs() {
|
||||
return this.isProviderAdmin;
|
||||
}
|
||||
|
||||
get isProviderAdmin() {
|
||||
return this.type === ProviderUserType.ProviderAdmin;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Account } from "./account";
|
||||
import { GlobalState } from "./globalState";
|
||||
|
||||
export class State<
|
||||
TGlobalState extends GlobalState = GlobalState,
|
||||
TAccount extends Account = Account,
|
||||
> {
|
||||
accounts: { [userId: string]: TAccount } = {};
|
||||
globals: TGlobalState;
|
||||
activeUserId: string;
|
||||
authenticatedAccounts: string[] = [];
|
||||
accountActivity: { [userId: string]: number } = {};
|
||||
|
||||
constructor(globals: TGlobalState) {
|
||||
this.globals = globals;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
export class TreeNode<T extends ITreeNodeObject> {
|
||||
parent: T;
|
||||
node: T;
|
||||
children: TreeNode<T>[] = [];
|
||||
|
||||
constructor(node: T, name: string, parent: T) {
|
||||
this.parent = parent;
|
||||
this.node = node;
|
||||
this.node.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ITreeNodeObject {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class ApiKeyResponse extends BaseResponse {
|
||||
apiKey: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.apiKey = this.getResponseProperty("ApiKey");
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { PaymentMethodType } from "../../enums/paymentMethodType";
|
||||
import { TransactionType } from "../../enums/transactionType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class BillingResponse extends BaseResponse {
|
||||
balance: number;
|
||||
paymentSource: BillingSourceResponse;
|
||||
invoices: BillingInvoiceResponse[] = [];
|
||||
transactions: BillingTransactionResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.balance = this.getResponseProperty("Balance");
|
||||
const paymentSource = this.getResponseProperty("PaymentSource");
|
||||
const transactions = this.getResponseProperty("Transactions");
|
||||
const invoices = this.getResponseProperty("Invoices");
|
||||
this.paymentSource = paymentSource == null ? null : new BillingSourceResponse(paymentSource);
|
||||
if (transactions != null) {
|
||||
this.transactions = transactions.map((t: any) => new BillingTransactionResponse(t));
|
||||
}
|
||||
if (invoices != null) {
|
||||
this.invoices = invoices.map((i: any) => new BillingInvoiceResponse(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingSourceResponse extends BaseResponse {
|
||||
type: PaymentMethodType;
|
||||
cardBrand: string;
|
||||
description: string;
|
||||
needsVerification: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.cardBrand = this.getResponseProperty("CardBrand");
|
||||
this.description = this.getResponseProperty("Description");
|
||||
this.needsVerification = this.getResponseProperty("NeedsVerification");
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingInvoiceResponse extends BaseResponse {
|
||||
url: string;
|
||||
pdfUrl: string;
|
||||
number: string;
|
||||
paid: boolean;
|
||||
date: string;
|
||||
amount: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.url = this.getResponseProperty("Url");
|
||||
this.pdfUrl = this.getResponseProperty("PdfUrl");
|
||||
this.number = this.getResponseProperty("Number");
|
||||
this.paid = this.getResponseProperty("Paid");
|
||||
this.date = this.getResponseProperty("Date");
|
||||
this.amount = this.getResponseProperty("Amount");
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingTransactionResponse extends BaseResponse {
|
||||
createdDate: string;
|
||||
amount: number;
|
||||
refunded: boolean;
|
||||
partiallyRefunded: boolean;
|
||||
refundedAmount: number;
|
||||
type: TransactionType;
|
||||
paymentMethodType: PaymentMethodType;
|
||||
details: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.createdDate = this.getResponseProperty("CreatedDate");
|
||||
this.amount = this.getResponseProperty("Amount");
|
||||
this.refunded = this.getResponseProperty("Refunded");
|
||||
this.partiallyRefunded = this.getResponseProperty("PartiallyRefunded");
|
||||
this.refundedAmount = this.getResponseProperty("RefundedAmount");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.paymentMethodType = this.getResponseProperty("PaymentMethodType");
|
||||
this.details = this.getResponseProperty("Details");
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class BreachAccountResponse extends BaseResponse {
|
||||
addedDate: string;
|
||||
breachDate: string;
|
||||
dataClasses: string[];
|
||||
description: string;
|
||||
domain: string;
|
||||
isActive: boolean;
|
||||
isVerified: boolean;
|
||||
logoPath: string;
|
||||
modifiedDate: string;
|
||||
name: string;
|
||||
pwnCount: number;
|
||||
title: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.addedDate = this.getResponseProperty("AddedDate");
|
||||
this.breachDate = this.getResponseProperty("BreachDate");
|
||||
this.dataClasses = this.getResponseProperty("DataClasses");
|
||||
this.description = this.getResponseProperty("Description");
|
||||
this.domain = this.getResponseProperty("Domain");
|
||||
this.isActive = this.getResponseProperty("IsActive");
|
||||
this.isVerified = this.getResponseProperty("IsVerified");
|
||||
this.logoPath = this.getResponseProperty("LogoPath");
|
||||
this.modifiedDate = this.getResponseProperty("ModifiedDate");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.pwnCount = this.getResponseProperty("PwnCount");
|
||||
this.title = this.getResponseProperty("Title");
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { DeviceType } from "../../enums/deviceType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class DeviceResponse extends BaseResponse {
|
||||
id: string;
|
||||
name: number;
|
||||
identifier: string;
|
||||
type: DeviceType;
|
||||
creationDate: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.identifier = this.getResponseProperty("Identifier");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.creationDate = this.getResponseProperty("CreationDate");
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { GlobalDomainResponse } from "./globalDomainResponse";
|
||||
|
||||
export class DomainsResponse extends BaseResponse {
|
||||
equivalentDomains: string[][];
|
||||
globalEquivalentDomains: GlobalDomainResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.equivalentDomains = this.getResponseProperty("EquivalentDomains");
|
||||
const globalEquivalentDomains = this.getResponseProperty("GlobalEquivalentDomains");
|
||||
if (globalEquivalentDomains != null) {
|
||||
this.globalEquivalentDomains = globalEquivalentDomains.map(
|
||||
(d: any) => new GlobalDomainResponse(d),
|
||||
);
|
||||
} else {
|
||||
this.globalEquivalentDomains = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class GlobalDomainResponse extends BaseResponse {
|
||||
type: number;
|
||||
domains: string[];
|
||||
excluded: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.domains = this.getResponseProperty("Domains");
|
||||
this.excluded = this.getResponseProperty("Excluded");
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse";
|
||||
|
||||
export class GroupResponse extends BaseResponse {
|
||||
id: string;
|
||||
organizationId: string;
|
||||
name: string;
|
||||
accessAll: boolean;
|
||||
externalId: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.accessAll = this.getResponseProperty("AccessAll");
|
||||
this.externalId = this.getResponseProperty("ExternalId");
|
||||
}
|
||||
}
|
||||
|
||||
export class GroupDetailsResponse extends GroupResponse {
|
||||
collections: SelectionReadOnlyResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
const collections = this.getResponseProperty("Collections");
|
||||
if (collections != null) {
|
||||
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class KeyConnectorUserKeyResponse extends BaseResponse {
|
||||
key: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.key = this.getResponseProperty("Key");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class KeysResponse extends BaseResponse {
|
||||
privateKey: string;
|
||||
publicKey: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.privateKey = this.getResponseProperty("PrivateKey");
|
||||
this.publicKey = this.getResponseProperty("PublicKey");
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class ListResponse<T> extends BaseResponse {
|
||||
data: T[];
|
||||
continuationToken: string;
|
||||
|
||||
constructor(response: any, t: new (dataResponse: any) => T) {
|
||||
super(response);
|
||||
const data = this.getResponseProperty("Data");
|
||||
this.data = data == null ? [] : data.map((dr: any) => new t(dr));
|
||||
this.continuationToken = this.getResponseProperty("ContinuationToken");
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import { NotificationType } from "../../enums/notificationType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class NotificationResponse extends BaseResponse {
|
||||
contextId: string;
|
||||
type: NotificationType;
|
||||
payload: any;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.contextId = this.getResponseProperty("ContextId");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
|
||||
const payload = this.getResponseProperty("Payload");
|
||||
switch (this.type) {
|
||||
case NotificationType.SyncCipherCreate:
|
||||
case NotificationType.SyncCipherDelete:
|
||||
case NotificationType.SyncCipherUpdate:
|
||||
case NotificationType.SyncLoginDelete:
|
||||
this.payload = new SyncCipherNotification(payload);
|
||||
break;
|
||||
case NotificationType.SyncFolderCreate:
|
||||
case NotificationType.SyncFolderDelete:
|
||||
case NotificationType.SyncFolderUpdate:
|
||||
this.payload = new SyncFolderNotification(payload);
|
||||
break;
|
||||
case NotificationType.SyncVault:
|
||||
case NotificationType.SyncCiphers:
|
||||
case NotificationType.SyncOrgKeys:
|
||||
case NotificationType.SyncSettings:
|
||||
case NotificationType.LogOut:
|
||||
this.payload = new UserNotification(payload);
|
||||
break;
|
||||
case NotificationType.SyncSendCreate:
|
||||
case NotificationType.SyncSendUpdate:
|
||||
case NotificationType.SyncSendDelete:
|
||||
this.payload = new SyncSendNotification(payload);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncCipherNotification extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
organizationId: string;
|
||||
collectionIds: string[];
|
||||
revisionDate: Date;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||
this.collectionIds = this.getResponseProperty("CollectionIds");
|
||||
this.revisionDate = new Date(this.getResponseProperty("RevisionDate"));
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncFolderNotification extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
revisionDate: Date;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.revisionDate = new Date(this.getResponseProperty("RevisionDate"));
|
||||
}
|
||||
}
|
||||
|
||||
export class UserNotification extends BaseResponse {
|
||||
userId: string;
|
||||
date: Date;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.date = new Date(this.getResponseProperty("Date"));
|
||||
}
|
||||
}
|
||||
|
||||
export class SyncSendNotification extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
revisionDate: Date;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.revisionDate = new Date(this.getResponseProperty("RevisionDate"));
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import { SsoConfigApi } from "../../api/ssoConfigApi";
|
||||
import { BaseResponse } from "../baseResponse";
|
||||
|
||||
export class OrganizationSsoResponse extends BaseResponse {
|
||||
enabled: boolean;
|
||||
data: SsoConfigApi;
|
||||
urls: SsoUrls;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.data =
|
||||
this.getResponseProperty("Data") != null
|
||||
? new SsoConfigApi(this.getResponseProperty("Data"))
|
||||
: null;
|
||||
this.urls = new SsoUrls(this.getResponseProperty("Urls"));
|
||||
}
|
||||
}
|
||||
|
||||
class SsoUrls extends BaseResponse {
|
||||
callbackPath: string;
|
||||
signedOutCallbackPath: string;
|
||||
spEntityId: string;
|
||||
spMetadataUrl: string;
|
||||
spAcsUrl: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.callbackPath = this.getResponseProperty("CallbackPath");
|
||||
this.signedOutCallbackPath = this.getResponseProperty("SignedOutCallbackPath");
|
||||
this.spEntityId = this.getResponseProperty("SpEntityId");
|
||||
this.spMetadataUrl = this.getResponseProperty("SpMetadataUrl");
|
||||
this.spAcsUrl = this.getResponseProperty("SpAcsUrl");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class OrganizationAutoEnrollStatusResponse extends BaseResponse {
|
||||
id: string;
|
||||
resetPasswordEnabled: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.resetPasswordEnabled = this.getResponseProperty("ResetPasswordEnabled");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { KeysResponse } from "./keysResponse";
|
||||
|
||||
export class OrganizationKeysResponse extends KeysResponse {
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
import { PlanType } from "../../enums/planType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { PlanResponse } from "./planResponse";
|
||||
|
||||
export class OrganizationResponse extends BaseResponse {
|
||||
id: string;
|
||||
identifier: string;
|
||||
name: string;
|
||||
businessName: string;
|
||||
businessAddress1: string;
|
||||
businessAddress2: string;
|
||||
businessAddress3: string;
|
||||
businessCountry: string;
|
||||
businessTaxNumber: string;
|
||||
billingEmail: string;
|
||||
plan: PlanResponse;
|
||||
planType: PlanType;
|
||||
seats: number;
|
||||
maxAutoscaleSeats: number;
|
||||
maxCollections: number;
|
||||
maxStorageGb: number;
|
||||
useGroups: boolean;
|
||||
useDirectory: boolean;
|
||||
useEvents: boolean;
|
||||
useTotp: boolean;
|
||||
use2fa: boolean;
|
||||
useApi: boolean;
|
||||
useResetPassword: boolean;
|
||||
hasPublicAndPrivateKeys: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.identifier = this.getResponseProperty("Identifier");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.businessName = this.getResponseProperty("BusinessName");
|
||||
this.businessAddress1 = this.getResponseProperty("BusinessAddress1");
|
||||
this.businessAddress2 = this.getResponseProperty("BusinessAddress2");
|
||||
this.businessAddress3 = this.getResponseProperty("BusinessAddress3");
|
||||
this.businessCountry = this.getResponseProperty("BusinessCountry");
|
||||
this.businessTaxNumber = this.getResponseProperty("BusinessTaxNumber");
|
||||
this.billingEmail = this.getResponseProperty("BillingEmail");
|
||||
const plan = this.getResponseProperty("Plan");
|
||||
this.plan = plan == null ? null : new PlanResponse(plan);
|
||||
this.planType = this.getResponseProperty("PlanType");
|
||||
this.seats = this.getResponseProperty("Seats");
|
||||
this.maxAutoscaleSeats = this.getResponseProperty("MaxAutoscaleSeats");
|
||||
this.maxCollections = this.getResponseProperty("MaxCollections");
|
||||
this.maxStorageGb = this.getResponseProperty("MaxStorageGb");
|
||||
this.useGroups = this.getResponseProperty("UseGroups");
|
||||
this.useDirectory = this.getResponseProperty("UseDirectory");
|
||||
this.useEvents = this.getResponseProperty("UseEvents");
|
||||
this.useTotp = this.getResponseProperty("UseTotp");
|
||||
this.use2fa = this.getResponseProperty("Use2fa");
|
||||
this.useApi = this.getResponseProperty("UseApi");
|
||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||
this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys");
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { OrganizationResponse } from "./organizationResponse";
|
||||
import {
|
||||
BillingSubscriptionResponse,
|
||||
BillingSubscriptionUpcomingInvoiceResponse,
|
||||
} from "./subscriptionResponse";
|
||||
|
||||
export class OrganizationSubscriptionResponse extends OrganizationResponse {
|
||||
storageName: string;
|
||||
storageGb: number;
|
||||
subscription: BillingSubscriptionResponse;
|
||||
upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse;
|
||||
expiration: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.storageName = this.getResponseProperty("StorageName");
|
||||
this.storageGb = this.getResponseProperty("StorageGb");
|
||||
const subscription = this.getResponseProperty("Subscription");
|
||||
this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription);
|
||||
const upcomingInvoice = this.getResponseProperty("UpcomingInvoice");
|
||||
this.upcomingInvoice =
|
||||
upcomingInvoice == null
|
||||
? null
|
||||
: new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice);
|
||||
this.expiration = this.getResponseProperty("Expiration");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class OrganizationUserBulkPublicKeyResponse extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
key: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class OrganizationUserBulkResponse extends BaseResponse {
|
||||
id: string;
|
||||
error: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.error = this.getResponseProperty("Error");
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { KdfType } from "../../enums/kdfType";
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { PermissionsApi } from "../api/permissionsApi";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { SelectionReadOnlyResponse } from "./selectionReadOnlyResponse";
|
||||
|
||||
export class OrganizationUserResponse extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: OrganizationUserType;
|
||||
status: OrganizationUserStatusType;
|
||||
accessAll: boolean;
|
||||
permissions: PermissionsApi;
|
||||
resetPasswordEnrolled: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("Permissions"));
|
||||
this.accessAll = this.getResponseProperty("AccessAll");
|
||||
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
||||
}
|
||||
}
|
||||
|
||||
export class OrganizationUserUserDetailsResponse extends OrganizationUserResponse {
|
||||
name: string;
|
||||
email: string;
|
||||
twoFactorEnabled: boolean;
|
||||
usesKeyConnector: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.email = this.getResponseProperty("Email");
|
||||
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
|
||||
this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false;
|
||||
}
|
||||
}
|
||||
|
||||
export class OrganizationUserDetailsResponse extends OrganizationUserResponse {
|
||||
collections: SelectionReadOnlyResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
const collections = this.getResponseProperty("Collections");
|
||||
if (collections != null) {
|
||||
this.collections = collections.map((c: any) => new SelectionReadOnlyResponse(c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class OrganizationUserResetPasswordDetailsReponse extends BaseResponse {
|
||||
kdf: KdfType;
|
||||
kdfIterations: number;
|
||||
resetPasswordKey: string;
|
||||
encryptedPrivateKey: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.kdf = this.getResponseProperty("Kdf");
|
||||
this.kdfIterations = this.getResponseProperty("KdfIterations");
|
||||
this.resetPasswordKey = this.getResponseProperty("ResetPasswordKey");
|
||||
this.encryptedPrivateKey = this.getResponseProperty("EncryptedPrivateKey");
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { ProfileResponse } from "./profileResponse";
|
||||
|
||||
export class PaymentResponse extends BaseResponse {
|
||||
userProfile: ProfileResponse;
|
||||
paymentIntentClientSecret: string;
|
||||
success: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
const userProfile = this.getResponseProperty("UserProfile");
|
||||
if (userProfile != null) {
|
||||
this.userProfile = new ProfileResponse(userProfile);
|
||||
}
|
||||
this.paymentIntentClientSecret = this.getResponseProperty("PaymentIntentClientSecret");
|
||||
this.success = this.getResponseProperty("Success");
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import { PlanType } from "../../enums/planType";
|
||||
import { ProductType } from "../../enums/productType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class PlanResponse extends BaseResponse {
|
||||
type: PlanType;
|
||||
product: ProductType;
|
||||
name: string;
|
||||
isAnnual: boolean;
|
||||
nameLocalizationKey: string;
|
||||
descriptionLocalizationKey: string;
|
||||
canBeUsedByBusiness: boolean;
|
||||
baseSeats: number;
|
||||
baseStorageGb: number;
|
||||
maxCollections: number;
|
||||
maxUsers: number;
|
||||
|
||||
hasAdditionalSeatsOption: boolean;
|
||||
maxAdditionalSeats: number;
|
||||
hasAdditionalStorageOption: boolean;
|
||||
maxAdditionalStorage: number;
|
||||
hasPremiumAccessOption: boolean;
|
||||
trialPeriodDays: number;
|
||||
|
||||
hasSelfHost: boolean;
|
||||
hasPolicies: boolean;
|
||||
hasGroups: boolean;
|
||||
hasDirectory: boolean;
|
||||
hasEvents: boolean;
|
||||
hasTotp: boolean;
|
||||
has2fa: boolean;
|
||||
hasApi: boolean;
|
||||
hasSso: boolean;
|
||||
hasResetPassword: boolean;
|
||||
usersGetPremium: boolean;
|
||||
|
||||
upgradeSortOrder: number;
|
||||
displaySortOrder: number;
|
||||
legacyYear: number;
|
||||
disabled: boolean;
|
||||
|
||||
stripePlanId: string;
|
||||
stripeSeatPlanId: string;
|
||||
stripeStoragePlanId: string;
|
||||
stripePremiumAccessPlanId: string;
|
||||
basePrice: number;
|
||||
seatPrice: number;
|
||||
additionalStoragePricePerGb: number;
|
||||
premiumAccessOptionPrice: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.product = this.getResponseProperty("Product");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.isAnnual = this.getResponseProperty("IsAnnual");
|
||||
this.nameLocalizationKey = this.getResponseProperty("NameLocalizationKey");
|
||||
this.descriptionLocalizationKey = this.getResponseProperty("DescriptionLocalizationKey");
|
||||
this.canBeUsedByBusiness = this.getResponseProperty("CanBeUsedByBusiness");
|
||||
this.baseSeats = this.getResponseProperty("BaseSeats");
|
||||
this.baseStorageGb = this.getResponseProperty("BaseStorageGb");
|
||||
this.maxCollections = this.getResponseProperty("MaxCollections");
|
||||
this.maxUsers = this.getResponseProperty("MaxUsers");
|
||||
this.hasAdditionalSeatsOption = this.getResponseProperty("HasAdditionalSeatsOption");
|
||||
this.maxAdditionalSeats = this.getResponseProperty("MaxAdditionalSeats");
|
||||
this.hasAdditionalStorageOption = this.getResponseProperty("HasAdditionalStorageOption");
|
||||
this.maxAdditionalStorage = this.getResponseProperty("MaxAdditionalStorage");
|
||||
this.hasPremiumAccessOption = this.getResponseProperty("HasPremiumAccessOption");
|
||||
this.trialPeriodDays = this.getResponseProperty("TrialPeriodDays");
|
||||
this.hasSelfHost = this.getResponseProperty("HasSelfHost");
|
||||
this.hasPolicies = this.getResponseProperty("HasPolicies");
|
||||
this.hasGroups = this.getResponseProperty("HasGroups");
|
||||
this.hasDirectory = this.getResponseProperty("HasDirectory");
|
||||
this.hasEvents = this.getResponseProperty("HasEvents");
|
||||
this.hasTotp = this.getResponseProperty("HasTotp");
|
||||
this.has2fa = this.getResponseProperty("Has2fa");
|
||||
this.hasApi = this.getResponseProperty("HasApi");
|
||||
this.hasSso = this.getResponseProperty("HasSso");
|
||||
this.hasResetPassword = this.getResponseProperty("HasResetPassword");
|
||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||
this.upgradeSortOrder = this.getResponseProperty("UpgradeSortOrder");
|
||||
this.displaySortOrder = this.getResponseProperty("SortOrder");
|
||||
this.legacyYear = this.getResponseProperty("LegacyYear");
|
||||
this.disabled = this.getResponseProperty("Disabled");
|
||||
this.stripePlanId = this.getResponseProperty("StripePlanId");
|
||||
this.stripeSeatPlanId = this.getResponseProperty("StripeSeatPlanId");
|
||||
this.stripeStoragePlanId = this.getResponseProperty("StripeStoragePlanId");
|
||||
this.stripePremiumAccessPlanId = this.getResponseProperty("StripePremiumAccessPlanId");
|
||||
this.basePrice = this.getResponseProperty("BasePrice");
|
||||
this.seatPrice = this.getResponseProperty("SeatPrice");
|
||||
this.additionalStoragePricePerGb = this.getResponseProperty("AdditionalStoragePricePerGb");
|
||||
this.premiumAccessOptionPrice = this.getResponseProperty("PremiumAccessOptionPrice");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { KdfType } from "../../enums/kdfType";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class PreloginResponse extends BaseResponse {
|
||||
kdf: KdfType;
|
||||
kdfIterations: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.kdf = this.getResponseProperty("Kdf");
|
||||
this.kdfIterations = this.getResponseProperty("KdfIterations");
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import { OrganizationUserStatusType } from "../../enums/organizationUserStatusType";
|
||||
import { OrganizationUserType } from "../../enums/organizationUserType";
|
||||
import { ProductType } from "../../enums/productType";
|
||||
import { PermissionsApi } from "../api/permissionsApi";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class ProfileOrganizationResponse extends BaseResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
usePolicies: boolean;
|
||||
useGroups: boolean;
|
||||
useDirectory: boolean;
|
||||
useEvents: boolean;
|
||||
useTotp: boolean;
|
||||
use2fa: boolean;
|
||||
useApi: boolean;
|
||||
useSso: boolean;
|
||||
useKeyConnector: boolean;
|
||||
useResetPassword: boolean;
|
||||
selfHost: boolean;
|
||||
usersGetPremium: boolean;
|
||||
seats: number;
|
||||
maxCollections: number;
|
||||
maxStorageGb?: number;
|
||||
key: string;
|
||||
hasPublicAndPrivateKeys: boolean;
|
||||
status: OrganizationUserStatusType;
|
||||
type: OrganizationUserType;
|
||||
enabled: boolean;
|
||||
ssoBound: boolean;
|
||||
identifier: string;
|
||||
permissions: PermissionsApi;
|
||||
resetPasswordEnrolled: boolean;
|
||||
userId: string;
|
||||
providerId: string;
|
||||
providerName: string;
|
||||
familySponsorshipFriendlyName: string;
|
||||
familySponsorshipAvailable: boolean;
|
||||
planProductType: ProductType;
|
||||
keyConnectorEnabled: boolean;
|
||||
keyConnectorUrl: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.usePolicies = this.getResponseProperty("UsePolicies");
|
||||
this.useGroups = this.getResponseProperty("UseGroups");
|
||||
this.useDirectory = this.getResponseProperty("UseDirectory");
|
||||
this.useEvents = this.getResponseProperty("UseEvents");
|
||||
this.useTotp = this.getResponseProperty("UseTotp");
|
||||
this.use2fa = this.getResponseProperty("Use2fa");
|
||||
this.useApi = this.getResponseProperty("UseApi");
|
||||
this.useSso = this.getResponseProperty("UseSso");
|
||||
this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false;
|
||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||
this.selfHost = this.getResponseProperty("SelfHost");
|
||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||
this.seats = this.getResponseProperty("Seats");
|
||||
this.maxCollections = this.getResponseProperty("MaxCollections");
|
||||
this.maxStorageGb = this.getResponseProperty("MaxStorageGb");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
this.hasPublicAndPrivateKeys = this.getResponseProperty("HasPublicAndPrivateKeys");
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.ssoBound = this.getResponseProperty("SsoBound");
|
||||
this.identifier = this.getResponseProperty("Identifier");
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
||||
this.resetPasswordEnrolled = this.getResponseProperty("ResetPasswordEnrolled");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.providerId = this.getResponseProperty("ProviderId");
|
||||
this.providerName = this.getResponseProperty("ProviderName");
|
||||
this.familySponsorshipFriendlyName = this.getResponseProperty("FamilySponsorshipFriendlyName");
|
||||
this.familySponsorshipAvailable = this.getResponseProperty("FamilySponsorshipAvailable");
|
||||
this.planProductType = this.getResponseProperty("PlanProductType");
|
||||
this.keyConnectorEnabled = this.getResponseProperty("KeyConnectorEnabled") ?? false;
|
||||
this.keyConnectorUrl = this.getResponseProperty("KeyConnectorUrl");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { ProfileOrganizationResponse } from "./profileOrganizationResponse";
|
||||
|
||||
export class ProfileProviderOrganizationResponse extends ProfileOrganizationResponse {
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.keyConnectorEnabled = false;
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { ProviderUserStatusType } from "../../enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "../../enums/providerUserType";
|
||||
import { PermissionsApi } from "../api/permissionsApi";
|
||||
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class ProfileProviderResponse extends BaseResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
key: string;
|
||||
status: ProviderUserStatusType;
|
||||
type: ProviderUserType;
|
||||
enabled: boolean;
|
||||
permissions: PermissionsApi;
|
||||
userId: string;
|
||||
useEvents: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("permissions"));
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.useEvents = this.getResponseProperty("UseEvents");
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
import { ProfileOrganizationResponse } from "./profileOrganizationResponse";
|
||||
import { ProfileProviderOrganizationResponse } from "./profileProviderOrganizationResponse";
|
||||
import { ProfileProviderResponse } from "./profileProviderResponse";
|
||||
|
||||
export class ProfileResponse extends BaseResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
masterPasswordHint: string;
|
||||
premium: boolean;
|
||||
culture: string;
|
||||
twoFactorEnabled: boolean;
|
||||
key: string;
|
||||
privateKey: string;
|
||||
securityStamp: string;
|
||||
forcePasswordReset: boolean;
|
||||
usesKeyConnector: boolean;
|
||||
organizations: ProfileOrganizationResponse[] = [];
|
||||
providers: ProfileProviderResponse[] = [];
|
||||
providerOrganizations: ProfileProviderOrganizationResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.email = this.getResponseProperty("Email");
|
||||
this.emailVerified = this.getResponseProperty("EmailVerified");
|
||||
this.masterPasswordHint = this.getResponseProperty("MasterPasswordHint");
|
||||
this.premium = this.getResponseProperty("Premium");
|
||||
this.culture = this.getResponseProperty("Culture");
|
||||
this.twoFactorEnabled = this.getResponseProperty("TwoFactorEnabled");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
this.privateKey = this.getResponseProperty("PrivateKey");
|
||||
this.securityStamp = this.getResponseProperty("SecurityStamp");
|
||||
this.forcePasswordReset = this.getResponseProperty("ForcePasswordReset") ?? false;
|
||||
this.usesKeyConnector = this.getResponseProperty("UsesKeyConnector") ?? false;
|
||||
|
||||
const organizations = this.getResponseProperty("Organizations");
|
||||
if (organizations != null) {
|
||||
this.organizations = organizations.map((o: any) => new ProfileOrganizationResponse(o));
|
||||
}
|
||||
const providers = this.getResponseProperty("Providers");
|
||||
if (providers != null) {
|
||||
this.providers = providers.map((o: any) => new ProfileProviderResponse(o));
|
||||
}
|
||||
const providerOrganizations = this.getResponseProperty("ProviderOrganizations");
|
||||
if (providerOrganizations != null) {
|
||||
this.providerOrganizations = providerOrganizations.map(
|
||||
(o: any) => new ProfileProviderOrganizationResponse(o),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { BaseResponse } from "../baseResponse";
|
||||
|
||||
export class ProviderOrganizationResponse extends BaseResponse {
|
||||
id: string;
|
||||
providerId: string;
|
||||
organizationId: string;
|
||||
key: string;
|
||||
settings: string;
|
||||
creationDate: string;
|
||||
revisionDate: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.providerId = this.getResponseProperty("ProviderId");
|
||||
this.organizationId = this.getResponseProperty("OrganizationId");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
this.settings = this.getResponseProperty("Settings");
|
||||
this.creationDate = this.getResponseProperty("CreationDate");
|
||||
this.revisionDate = this.getResponseProperty("RevisionDate");
|
||||
}
|
||||
}
|
||||
|
||||
export class ProviderOrganizationOrganizationDetailsResponse extends ProviderOrganizationResponse {
|
||||
organizationName: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.organizationName = this.getResponseProperty("OrganizationName");
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { BaseResponse } from "../baseResponse";
|
||||
|
||||
export class ProviderResponse extends BaseResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
businessName: string;
|
||||
billingEmail: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.businessName = this.getResponseProperty("BusinessName");
|
||||
this.billingEmail = this.getResponseProperty("BillingEmail");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import { OrganizationUserBulkPublicKeyResponse } from "../organizationUserBulkPublicKeyResponse";
|
||||
|
||||
export class ProviderUserBulkPublicKeyResponse extends OrganizationUserBulkPublicKeyResponse {}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "../baseResponse";
|
||||
|
||||
export class ProviderUserBulkResponse extends BaseResponse {
|
||||
id: string;
|
||||
error: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.error = this.getResponseProperty("Error");
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { ProviderUserStatusType } from "../../../enums/providerUserStatusType";
|
||||
import { ProviderUserType } from "../../../enums/providerUserType";
|
||||
import { PermissionsApi } from "../../api/permissionsApi";
|
||||
import { BaseResponse } from "../baseResponse";
|
||||
|
||||
export class ProviderUserResponse extends BaseResponse {
|
||||
id: string;
|
||||
userId: string;
|
||||
type: ProviderUserType;
|
||||
status: ProviderUserStatusType;
|
||||
permissions: PermissionsApi;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.userId = this.getResponseProperty("UserId");
|
||||
this.type = this.getResponseProperty("Type");
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.permissions = new PermissionsApi(this.getResponseProperty("Permissions"));
|
||||
}
|
||||
}
|
||||
|
||||
export class ProviderUserUserDetailsResponse extends ProviderUserResponse {
|
||||
name: string;
|
||||
email: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.email = this.getResponseProperty("Email");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class SelectionReadOnlyResponse extends BaseResponse {
|
||||
id: string;
|
||||
readOnly: boolean;
|
||||
hidePasswords: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.readOnly = this.getResponseProperty("ReadOnly");
|
||||
this.hidePasswords = this.getResponseProperty("HidePasswords");
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class SubscriptionResponse extends BaseResponse {
|
||||
storageName: string;
|
||||
storageGb: number;
|
||||
maxStorageGb: number;
|
||||
subscription: BillingSubscriptionResponse;
|
||||
upcomingInvoice: BillingSubscriptionUpcomingInvoiceResponse;
|
||||
license: any;
|
||||
expiration: string;
|
||||
usingInAppPurchase: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.storageName = this.getResponseProperty("StorageName");
|
||||
this.storageGb = this.getResponseProperty("StorageGb");
|
||||
this.maxStorageGb = this.getResponseProperty("MaxStorageGb");
|
||||
this.license = this.getResponseProperty("License");
|
||||
this.expiration = this.getResponseProperty("Expiration");
|
||||
this.usingInAppPurchase = this.getResponseProperty("UsingInAppPurchase");
|
||||
const subscription = this.getResponseProperty("Subscription");
|
||||
const upcomingInvoice = this.getResponseProperty("UpcomingInvoice");
|
||||
this.subscription = subscription == null ? null : new BillingSubscriptionResponse(subscription);
|
||||
this.upcomingInvoice =
|
||||
upcomingInvoice == null
|
||||
? null
|
||||
: new BillingSubscriptionUpcomingInvoiceResponse(upcomingInvoice);
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingSubscriptionResponse extends BaseResponse {
|
||||
trialStartDate: string;
|
||||
trialEndDate: string;
|
||||
periodStartDate: string;
|
||||
periodEndDate: string;
|
||||
cancelledDate: string;
|
||||
cancelAtEndDate: boolean;
|
||||
status: string;
|
||||
cancelled: boolean;
|
||||
items: BillingSubscriptionItemResponse[] = [];
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.trialEndDate = this.getResponseProperty("TrialStartDate");
|
||||
this.trialEndDate = this.getResponseProperty("TrialEndDate");
|
||||
this.periodStartDate = this.getResponseProperty("PeriodStartDate");
|
||||
this.periodEndDate = this.getResponseProperty("PeriodEndDate");
|
||||
this.cancelledDate = this.getResponseProperty("CancelledDate");
|
||||
this.cancelAtEndDate = this.getResponseProperty("CancelAtEndDate");
|
||||
this.status = this.getResponseProperty("Status");
|
||||
this.cancelled = this.getResponseProperty("Cancelled");
|
||||
const items = this.getResponseProperty("Items");
|
||||
if (items != null) {
|
||||
this.items = items.map((i: any) => new BillingSubscriptionItemResponse(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingSubscriptionItemResponse extends BaseResponse {
|
||||
name: string;
|
||||
amount: number;
|
||||
quantity: number;
|
||||
interval: string;
|
||||
sponsoredSubscriptionItem: boolean;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.name = this.getResponseProperty("Name");
|
||||
this.amount = this.getResponseProperty("Amount");
|
||||
this.quantity = this.getResponseProperty("Quantity");
|
||||
this.interval = this.getResponseProperty("Interval");
|
||||
this.sponsoredSubscriptionItem = this.getResponseProperty("SponsoredSubscriptionItem");
|
||||
}
|
||||
}
|
||||
|
||||
export class BillingSubscriptionUpcomingInvoiceResponse extends BaseResponse {
|
||||
date: string;
|
||||
amount: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.date = this.getResponseProperty("Date");
|
||||
this.amount = this.getResponseProperty("Amount");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class TaxInfoResponse extends BaseResponse {
|
||||
taxId: string;
|
||||
taxIdType: string;
|
||||
line1: string;
|
||||
line2: string;
|
||||
city: string;
|
||||
state: string;
|
||||
country: string;
|
||||
postalCode: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.taxId = this.getResponseProperty("TaxIdNumber");
|
||||
this.taxIdType = this.getResponseProperty("TaxIdType");
|
||||
this.line1 = this.getResponseProperty("Line1");
|
||||
this.line2 = this.getResponseProperty("Line2");
|
||||
this.city = this.getResponseProperty("City");
|
||||
this.state = this.getResponseProperty("State");
|
||||
this.postalCode = this.getResponseProperty("PostalCode");
|
||||
this.country = this.getResponseProperty("Country");
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class TaxRateResponse extends BaseResponse {
|
||||
id: string;
|
||||
country: string;
|
||||
state: string;
|
||||
postalCode: string;
|
||||
rate: number;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.id = this.getResponseProperty("Id");
|
||||
this.country = this.getResponseProperty("Country");
|
||||
this.state = this.getResponseProperty("State");
|
||||
this.postalCode = this.getResponseProperty("PostalCode");
|
||||
this.rate = this.getResponseProperty("Rate");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class TwoFactorAuthenticatorResponse extends BaseResponse {
|
||||
enabled: boolean;
|
||||
key: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.key = this.getResponseProperty("Key");
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { BaseResponse } from "./baseResponse";
|
||||
|
||||
export class TwoFactorDuoResponse extends BaseResponse {
|
||||
enabled: boolean;
|
||||
host: string;
|
||||
secretKey: string;
|
||||
integrationKey: string;
|
||||
|
||||
constructor(response: any) {
|
||||
super(response);
|
||||
this.enabled = this.getResponseProperty("Enabled");
|
||||
this.host = this.getResponseProperty("Host");
|
||||
this.secretKey = this.getResponseProperty("SecretKey");
|
||||
this.integrationKey = this.getResponseProperty("IntegrationKey");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user