@mudrichenkoevgeny/web-platform-sdk-core-security 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +110 -110
  3. package/dist/di/security-component.d.ts +56 -0
  4. package/dist/di/security-component.d.ts.map +1 -0
  5. package/dist/di/security-component.test.d.ts +2 -0
  6. package/dist/di/security-component.test.d.ts.map +1 -0
  7. package/dist/error/model/security-error.d.ts +117 -0
  8. package/dist/error/model/security-error.d.ts.map +1 -0
  9. package/dist/error/model/security-error.test.d.ts +2 -0
  10. package/dist/error/model/security-error.test.d.ts.map +1 -0
  11. package/dist/error/parser/security-error-parser.d.ts +37 -0
  12. package/dist/error/parser/security-error-parser.d.ts.map +1 -0
  13. package/dist/error/parser/security-error-parser.test.d.ts +2 -0
  14. package/dist/error/parser/security-error-parser.test.d.ts.map +1 -0
  15. package/dist/index.d.ts +20 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +12255 -5155
  18. package/dist/integration/SecuritySettingsIntegration.test.d.ts +2 -0
  19. package/dist/integration/SecuritySettingsIntegration.test.d.ts.map +1 -0
  20. package/dist/mock/di/security-component-mock.d.ts +9 -0
  21. package/dist/mock/di/security-component-mock.d.ts.map +1 -0
  22. package/dist/mock/network/open-security-settings-api-mock.d.ts +25 -0
  23. package/dist/mock/network/open-security-settings-api-mock.d.ts.map +1 -0
  24. package/dist/mock/repository/open-security-settings-repository-mock.d.ts +28 -0
  25. package/dist/mock/repository/open-security-settings-repository-mock.d.ts.map +1 -0
  26. package/dist/mock/storage/open-security-settings-storage-mock.d.ts +31 -0
  27. package/dist/mock/storage/open-security-settings-storage-mock.d.ts.map +1 -0
  28. package/dist/mock/usecase/validate-password-use-case-mock.d.ts +22 -0
  29. package/dist/mock/usecase/validate-password-use-case-mock.d.ts.map +1 -0
  30. package/dist/network/api/security-settings/fetch-open-security-settings-api.d.ts +22 -0
  31. package/dist/network/api/security-settings/fetch-open-security-settings-api.d.ts.map +1 -0
  32. package/dist/network/api/security-settings/fetch-open-security-settings-api.test.d.ts +2 -0
  33. package/dist/network/api/security-settings/fetch-open-security-settings-api.test.d.ts.map +1 -0
  34. package/dist/network/api/security-settings/open-security-settings-api.d.ts +14 -0
  35. package/dist/network/api/security-settings/open-security-settings-api.d.ts.map +1 -0
  36. package/dist/network/api/security-settings/security-settings-dto-mapping.test.d.ts +2 -0
  37. package/dist/network/api/security-settings/security-settings-dto-mapping.test.d.ts.map +1 -0
  38. package/dist/network/contract/security-web-socket-event-types.d.ts +2 -0
  39. package/dist/network/contract/security-web-socket-event-types.d.ts.map +1 -0
  40. package/dist/network/websocket/message-handler/security-web-socket-message-handler.d.ts +15 -0
  41. package/dist/network/websocket/message-handler/security-web-socket-message-handler.d.ts.map +1 -0
  42. package/dist/network/websocket/message-handler/security-web-socket-message-handler.test.d.ts +2 -0
  43. package/dist/network/websocket/message-handler/security-web-socket-message-handler.test.d.ts.map +1 -0
  44. package/dist/repository/open-security-settings-repository-impl.d.ts +57 -0
  45. package/dist/repository/open-security-settings-repository-impl.d.ts.map +1 -0
  46. package/dist/repository/open-security-settings-repository-impl.test.d.ts +2 -0
  47. package/dist/repository/open-security-settings-repository-impl.test.d.ts.map +1 -0
  48. package/dist/repository/open-security-settings-repository.d.ts +35 -0
  49. package/dist/repository/open-security-settings-repository.d.ts.map +1 -0
  50. package/dist/storage/security-settings/encrypted-open-security-settings-storage.d.ts +32 -0
  51. package/dist/storage/security-settings/encrypted-open-security-settings-storage.d.ts.map +1 -0
  52. package/dist/storage/security-settings/encrypted-open-security-settings-storage.test.d.ts +2 -0
  53. package/dist/storage/security-settings/encrypted-open-security-settings-storage.test.d.ts.map +1 -0
  54. package/dist/storage/security-settings/open-security-settings-storage.d.ts +23 -0
  55. package/dist/storage/security-settings/open-security-settings-storage.d.ts.map +1 -0
  56. package/dist/usecase/observe-open-security-settings-use-case.d.ts +19 -0
  57. package/dist/usecase/observe-open-security-settings-use-case.d.ts.map +1 -0
  58. package/dist/usecase/observe-open-security-settings-use-case.test.d.ts +2 -0
  59. package/dist/usecase/observe-open-security-settings-use-case.test.d.ts.map +1 -0
  60. package/dist/usecase/refresh-open-security-settings-use-case.d.ts +22 -0
  61. package/dist/usecase/refresh-open-security-settings-use-case.d.ts.map +1 -0
  62. package/dist/usecase/refresh-open-security-settings-use-case.test.d.ts +2 -0
  63. package/dist/usecase/refresh-open-security-settings-use-case.test.d.ts.map +1 -0
  64. package/dist/usecase/validate-password-use-case.d.ts +26 -0
  65. package/dist/usecase/validate-password-use-case.d.ts.map +1 -0
  66. package/dist/usecase/validate-password-use-case.test.d.ts +2 -0
  67. package/dist/usecase/validate-password-use-case.test.d.ts.map +1 -0
  68. package/package.json +16 -7
  69. package/src/di/security-component.test.ts +0 -33
  70. package/src/di/security-component.ts +0 -86
  71. package/src/domain/model/open-security-settings.ts +0 -79
  72. package/src/domain/model/password-policy-validator.test.ts +0 -96
  73. package/src/domain/model/password-policy-validator.ts +0 -63
  74. package/src/error/model/security-error.test.ts +0 -65
  75. package/src/error/model/security-error.ts +0 -182
  76. package/src/error/naming/client-security-error-codes.ts +0 -21
  77. package/src/error/parser/security-error-parser.test.ts +0 -87
  78. package/src/error/parser/security-error-parser.ts +0 -138
  79. package/src/index.ts +0 -24
  80. package/src/locales/en/strings.ts +0 -67
  81. package/src/locales/index.ts +0 -2
  82. package/src/locales/ru/strings.ts +0 -35
  83. package/src/mock/di/security-component-mock.ts +0 -35
  84. package/src/mock/domain/model/open-security-settings-mock.ts +0 -52
  85. package/src/mock/network/model/open-security-settings-payload-mock.ts +0 -28
  86. package/src/mock/network/open-security-settings-api-mock.ts +0 -38
  87. package/src/mock/repository/open-security-settings-repository-mock.ts +0 -62
  88. package/src/mock/storage/open-security-settings-storage-mock.ts +0 -45
  89. package/src/mock/usecase/validate-password-use-case-mock.ts +0 -31
  90. package/src/network/contract/security-web-socket-event-types.ts +0 -7
  91. package/src/network/security-settings/fetch-open-security-settings-api.test.ts +0 -51
  92. package/src/network/security-settings/fetch-open-security-settings-api.ts +0 -30
  93. package/src/network/security-settings/open-security-settings-api.ts +0 -15
  94. package/src/network/websocket/message-handler/security-web-socket-message-handler.test.ts +0 -19
  95. package/src/network/websocket/message-handler/security-web-socket-message-handler.ts +0 -19
  96. package/src/repository/open-security-settings-repository-impl.test.ts +0 -112
  97. package/src/repository/open-security-settings-repository-impl.ts +0 -151
  98. package/src/repository/open-security-settings-repository.ts +0 -39
  99. package/src/storage/security-settings/encrypted-open-security-settings-storage.test.ts +0 -71
  100. package/src/storage/security-settings/encrypted-open-security-settings-storage.ts +0 -65
  101. package/src/storage/security-settings/open-security-settings-storage.ts +0 -25
  102. package/src/usecase/refresh-open-security-settings-use-case.ts +0 -26
  103. package/src/usecase/security-use-cases.test.ts +0 -67
  104. package/src/usecase/validate-password-use-case.ts +0 -76
  105. package/tsconfig.json +0 -16
  106. package/vite.config.ts +0 -50
@@ -1,51 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import {
3
- HttpClient,
4
- DeviceInfoProviderMock,
5
- isSuccess
6
- } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
7
- import { FetchOpenSecuritySettingsApi } from '@/network/security-settings/fetch-open-security-settings-api'
8
- import type { OpenSecuritySettingsPayload } from '@mudrichenkoevgeny/shared-foundation'
9
-
10
- describe('FetchOpenSecuritySettingsApi', () => {
11
- const samplePayload: OpenSecuritySettingsPayload = {
12
- open_password_policy: {
13
- min_length: 8,
14
- require_letter: true,
15
- require_upper_case: true,
16
- require_lower_case: true,
17
- require_digit: true,
18
- require_special_char: false
19
- },
20
- otp_confirmation: {
21
- retry_after_seconds: 60,
22
- number_of_symbols: 6,
23
- expiration_seconds: 300
24
- }
25
- }
26
-
27
- it('fetches open security settings successfully', async () => {
28
- const mockResponse = new Response(JSON.stringify(samplePayload), { status: 200 })
29
- const customFetch = vi.fn().mockResolvedValue(mockResponse)
30
- const deviceInfoProvider = new DeviceInfoProviderMock()
31
-
32
- const httpClient = new HttpClient({
33
- baseUrl: 'https://api.example.com',
34
- deviceInfoProvider,
35
- customFetch
36
- })
37
-
38
- const api = new FetchOpenSecuritySettingsApi(httpClient)
39
- const result = await api.getSecuritySettings()
40
-
41
- expect(isSuccess(result)).toBe(true)
42
- if (isSuccess(result)) {
43
- expect(result.data).toEqual(samplePayload)
44
- }
45
-
46
- expect(customFetch).toHaveBeenCalledWith(
47
- 'https://api.example.com/security/settings',
48
- expect.any(Object)
49
- )
50
- })
51
- })
@@ -1,30 +0,0 @@
1
- import { OpenSecuritySettingsRoutes } from '@mudrichenkoevgeny/shared-foundation'
2
- import type { OpenSecuritySettingsPayload } from '@mudrichenkoevgeny/shared-foundation'
3
- import { callResult } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
4
- import type { HttpClient, AppResult, AppError } from "@mudrichenkoevgeny/web-platform-sdk-core-common";
5
- import type { OpenSecuritySettingsApi } from '@/network/security-settings/open-security-settings-api'
6
- /**
7
- * Fetch HTTP client implementation of {@link OpenSecuritySettingsApi}.
8
- */
9
- export class FetchOpenSecuritySettingsApi implements OpenSecuritySettingsApi {
10
- /**
11
- * Constructs a new {@link FetchOpenSecuritySettingsApi}.
12
- *
13
- * @param httpClient - Configured SDK HTTP client instance
14
- */
15
- public constructor(private readonly httpClient: HttpClient) {}
16
-
17
- /**
18
- * Executes HTTP GET request to retrieve open security settings payload.
19
- *
20
- * @returns AppResult wrapping {@link OpenSecuritySettingsPayload}
21
- */
22
- public async getSecuritySettings(): Promise<AppResult<OpenSecuritySettingsPayload, AppError>> {
23
- return callResult(() =>
24
- this.httpClient.request<OpenSecuritySettingsPayload>(
25
- OpenSecuritySettingsRoutes.GET_OPEN_SECURITY_SETTINGS,
26
- { method: 'GET' }
27
- )
28
- )
29
- }
30
- }
@@ -1,15 +0,0 @@
1
- import type { OpenSecuritySettingsPayload } from '@mudrichenkoevgeny/shared-foundation'
2
- import type { AppError } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
3
- import type { AppResult } from "@mudrichenkoevgeny/web-platform-sdk-core-common";
4
-
5
- /**
6
- * Direct HTTP REST API client interface for fetching open security settings.
7
- */
8
- export interface OpenSecuritySettingsApi {
9
- /**
10
- * Fetches active open security settings payload from the server.
11
- *
12
- * @returns AppResult containing {@link OpenSecuritySettingsPayload} on success or {@link AppError} on failure
13
- */
14
- getSecuritySettings(): Promise<AppResult<OpenSecuritySettingsPayload, AppError>>
15
- }
@@ -1,19 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import type { SocketFrame } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
3
- import { SecurityWebSocketMessageHandler } from '@/network/websocket/message-handler/security-web-socket-message-handler'
4
-
5
- describe('SecurityWebSocketMessageHandler', () => {
6
- it('returns NotHandled for unhandled frames', () => {
7
- const handler = new SecurityWebSocketMessageHandler()
8
- const frame: SocketFrame = {
9
- id: 'frame-1',
10
- type: 'UNKNOWN_TYPE',
11
- payload: null,
12
- metadata: {},
13
- timestamp: Date.now()
14
- }
15
-
16
- const result = handler.handle(frame)
17
- expect(result.kind).toBe('NotHandled')
18
- })
19
- })
@@ -1,19 +0,0 @@
1
- import {
2
- webSocketMessageHandlerResultNotHandled
3
- } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
4
- import type { WebSocketMessageHandler, WebSocketMessageHandlerResult, SocketFrame } from "@mudrichenkoevgeny/web-platform-sdk-core-common";
5
-
6
- /**
7
- * WebSocket message handler for security domain frame types.
8
- */
9
- export class SecurityWebSocketMessageHandler implements WebSocketMessageHandler {
10
- /**
11
- * Processes incoming security WebSocket message frame.
12
- *
13
- * @param frame - Incoming socket frame
14
- * @returns {@link WebSocketMessageHandlerResult}
15
- */
16
- public handle(_frame: SocketFrame): WebSocketMessageHandlerResult {
17
- return webSocketMessageHandlerResultNotHandled()
18
- }
19
- }
@@ -1,112 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest'
2
- import { WebSocketServiceMock, EncryptedSettingsMock } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
3
- import type { AppResult, AppError } from "@mudrichenkoevgeny/web-platform-sdk-core-common";
4
- import type { OpenSecuritySettingsPayload } from '@mudrichenkoevgeny/shared-foundation'
5
- import { OpenSecuritySettingsRepositoryImpl } from '@/repository/open-security-settings-repository-impl'
6
- import type { OpenSecuritySettingsApi } from '@/network/security-settings/open-security-settings-api'
7
- import { EncryptedOpenSecuritySettingsStorage } from '@/storage/security-settings/encrypted-open-security-settings-storage'
8
- import { toOpenSecuritySettings } from '@/domain/model/open-security-settings'
9
- import { SecurityWebSocketEventTypes } from '@/network/contract/security-web-socket-event-types'
10
- describe('OpenSecuritySettingsRepositoryImpl', () => {
11
- const samplePayload: OpenSecuritySettingsPayload = {
12
- open_password_policy: {
13
- min_length: 8,
14
- require_letter: true,
15
- require_upper_case: true,
16
- require_lower_case: true,
17
- require_digit: true,
18
- require_special_char: false
19
- },
20
- otp_confirmation: {
21
- retry_after_seconds: 60,
22
- number_of_symbols: 6,
23
- expiration_seconds: 300
24
- }
25
- }
26
-
27
- const sampleSettings = toOpenSecuritySettings(samplePayload)
28
-
29
- class MockApi implements OpenSecuritySettingsApi {
30
- public getSecuritySettingsCallCount = 0
31
- public async getSecuritySettings(): Promise<AppResult<OpenSecuritySettingsPayload, AppError>> {
32
- this.getSecuritySettingsCallCount++
33
- return { success: true, data: samplePayload }
34
- }
35
- }
36
-
37
- it('loads from network if cache is empty', async () => {
38
- const api = new MockApi()
39
- const storage = new EncryptedOpenSecuritySettingsStorage(new EncryptedSettingsMock())
40
- const ws = new WebSocketServiceMock()
41
-
42
- const repo = new OpenSecuritySettingsRepositoryImpl(api, storage, ws)
43
- const result = await repo.getOpenSecuritySettings()
44
-
45
- expect(result.success).toBe(true)
46
- if (result.success) {
47
- expect(result.data).toEqual(sampleSettings)
48
- }
49
- expect(api.getSecuritySettingsCallCount).toBe(1)
50
- })
51
-
52
- it('loads from storage without calling network if cache exists', async () => {
53
- const api = new MockApi()
54
- const storage = new EncryptedOpenSecuritySettingsStorage(new EncryptedSettingsMock())
55
- await storage.updateOpenSecuritySettings(sampleSettings)
56
- const ws = new WebSocketServiceMock()
57
-
58
- const repo = new OpenSecuritySettingsRepositoryImpl(api, storage, ws)
59
- const result = await repo.getOpenSecuritySettings()
60
-
61
- expect(result.success).toBe(true)
62
- expect(api.getSecuritySettingsCallCount).toBe(0)
63
- })
64
-
65
- it('forces network refresh when refreshOpenSecuritySettings is called', async () => {
66
- const api = new MockApi()
67
- const storage = new EncryptedOpenSecuritySettingsStorage(new EncryptedSettingsMock())
68
- await storage.updateOpenSecuritySettings(sampleSettings)
69
- const ws = new WebSocketServiceMock()
70
-
71
- const repo = new OpenSecuritySettingsRepositoryImpl(api, storage, ws)
72
- await repo.refreshOpenSecuritySettings()
73
-
74
- expect(api.getSecuritySettingsCallCount).toBe(1)
75
- })
76
-
77
- it('updates state and storage when WebSocket update is received', async () => {
78
- const api = new MockApi()
79
- const storage = new EncryptedOpenSecuritySettingsStorage(new EncryptedSettingsMock())
80
- const ws = new WebSocketServiceMock()
81
-
82
- const repo = new OpenSecuritySettingsRepositoryImpl(api, storage, ws)
83
-
84
- const observer = vi.fn()
85
- repo.observeOpenSecuritySettings(observer)
86
-
87
- // Wait for potential preload to finish so observer stabilizes
88
- await repo.getOpenSecuritySettings()
89
-
90
- const wsPayload: OpenSecuritySettingsPayload = {
91
- open_password_policy: { ...samplePayload.open_password_policy, min_length: 12 },
92
- otp_confirmation: samplePayload.otp_confirmation
93
- }
94
-
95
- ws.emitFrameLocally({
96
- id: 'frame1',
97
- type: SecurityWebSocketEventTypes.SECURITY_SETTINGS_UPDATED,
98
- payload: wsPayload,
99
- timestamp: Date.now()
100
- })
101
-
102
- const updatedSettings = await storage.getOpenSecuritySettings()
103
- expect(updatedSettings?.openPasswordPolicy.minLength).toBe(12)
104
-
105
- const currentResult = await repo.getOpenSecuritySettings()
106
- if (currentResult.success) {
107
- expect(currentResult.data.openPasswordPolicy.minLength).toBe(12)
108
- }
109
-
110
- expect(observer).toHaveBeenCalled()
111
- })
112
- })
@@ -1,151 +0,0 @@
1
- import { mapSuccess } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
2
- import type { AppResult, AppError, WebSocketService, SocketFrame } from "@mudrichenkoevgeny/web-platform-sdk-core-common";
3
- import { openSecuritySettingsPayloadSchema } from '@mudrichenkoevgeny/shared-foundation'
4
- import { toOpenSecuritySettings } from '@/domain/model/open-security-settings'
5
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
6
- import type { OpenSecuritySettingsRepository } from '@/repository/open-security-settings-repository'
7
- import type { OpenSecuritySettingsObserver } from '@/repository/open-security-settings-repository'
8
- import type { OpenSecuritySettingsApi } from '@/network/security-settings/open-security-settings-api'
9
- import type { OpenSecuritySettingsStorage } from '@/storage/security-settings/open-security-settings-storage'
10
- import { SecurityWebSocketEventTypes } from '@/network/contract/security-web-socket-event-types'
11
- /**
12
- * Implementation of {@link OpenSecuritySettingsRepository} handling API fetch, encrypted storage, and WebSocket updates.
13
- */
14
- export class OpenSecuritySettingsRepositoryImpl implements OpenSecuritySettingsRepository {
15
- private inMemorySettings: OpenSecuritySettings | null = null
16
- private readonly observers = new Set<OpenSecuritySettingsObserver>()
17
- private initializationPromise: Promise<void> | null = null
18
-
19
- /**
20
- * Constructs a new {@link OpenSecuritySettingsRepositoryImpl}.
21
- *
22
- * @param api - REST API client for open security settings
23
- * @param storage - Encrypted storage backend for settings persistence
24
- * @param webSocketService - WebSocket client for receiving real-time setting updates
25
- * @param logger - Optional diagnostic logger
26
- */
27
- public constructor(
28
- private readonly api: OpenSecuritySettingsApi,
29
- private readonly storage: OpenSecuritySettingsStorage,
30
- private readonly webSocketService: WebSocketService,
31
- private readonly logger?: (msg: string) => void
32
- ) {
33
- this.startWebSocketObservation()
34
- this.initializationPromise = this.preloadCache()
35
- }
36
-
37
- /**
38
- * Retrieves security settings from memory, storage, or network.
39
- *
40
- * @returns Resolved security settings result
41
- */
42
- public async getOpenSecuritySettings(): Promise<AppResult<OpenSecuritySettings, AppError>> {
43
- if (this.inMemorySettings) {
44
- return { success: true, data: this.inMemorySettings }
45
- }
46
-
47
- if (this.initializationPromise) {
48
- await this.initializationPromise
49
- if (this.inMemorySettings) {
50
- return { success: true, data: this.inMemorySettings }
51
- }
52
- }
53
-
54
- return this.refreshOpenSecuritySettingsInternal()
55
- }
56
-
57
- /**
58
- * Forces a refresh from the network and persists result.
59
- *
60
- * @returns Updated security settings result
61
- */
62
- public async refreshOpenSecuritySettings(): Promise<AppResult<OpenSecuritySettings, AppError>> {
63
- return this.refreshOpenSecuritySettingsInternal()
64
- }
65
-
66
- /**
67
- * Overwrites the active security settings (e.g. from WebSocket update).
68
- *
69
- * @param securitySettings - New active settings
70
- */
71
- public async updateOpenSecuritySettings(securitySettings: OpenSecuritySettings): Promise<void> {
72
- await this.applySettingsUpdate(securitySettings)
73
- }
74
-
75
- /**
76
- * Subscribes to updates of the security settings.
77
- *
78
- * @param observer - Callback
79
- * @returns Unsubscribe function
80
- */
81
- public observeOpenSecuritySettings(observer: OpenSecuritySettingsObserver): () => void {
82
- this.observers.add(observer)
83
- observer(this.inMemorySettings)
84
- return () => {
85
- this.observers.delete(observer)
86
- }
87
- }
88
-
89
- private async preloadCache(): Promise<void> {
90
- try {
91
- const cached = await this.storage.getOpenSecuritySettings()
92
- if (cached && !this.inMemorySettings) {
93
- this.inMemorySettings = cached
94
- this.notifyObservers(cached)
95
- }
96
- } catch (e) {
97
- this.logger?.(`OpenSecuritySettingsRepositoryImpl: Preload failed - ${String(e)}`)
98
- } finally {
99
- this.initializationPromise = null
100
- }
101
- }
102
-
103
- private async refreshOpenSecuritySettingsInternal(): Promise<AppResult<OpenSecuritySettings, AppError>> {
104
- const response = await this.api.getSecuritySettings()
105
- return mapSuccess(response, (payload) => {
106
- const settings = toOpenSecuritySettings(payload)
107
- void this.applySettingsUpdate(settings)
108
- return settings
109
- })
110
- }
111
-
112
- private async applySettingsUpdate(settings: OpenSecuritySettings): Promise<void> {
113
- this.inMemorySettings = settings
114
- this.notifyObservers(settings)
115
- try {
116
- await this.storage.updateOpenSecuritySettings(settings)
117
- } catch (e) {
118
- this.logger?.(`OpenSecuritySettingsRepositoryImpl: Storage update failed - ${String(e)}`)
119
- }
120
- }
121
-
122
- private notifyObservers(settings: OpenSecuritySettings | null): void {
123
- for (const observer of this.observers) {
124
- try {
125
- observer(settings)
126
- } catch (e) {
127
- this.logger?.(`OpenSecuritySettingsRepositoryImpl: Observer threw error - ${String(e)}`)
128
- }
129
- }
130
- }
131
-
132
- private startWebSocketObservation(): void {
133
- this.webSocketService.observeEvents((frame: SocketFrame) => {
134
- if (frame.type === SecurityWebSocketEventTypes.SECURITY_SETTINGS_UPDATED) {
135
- try {
136
- if (frame.payload) {
137
- const validationResult = openSecuritySettingsPayloadSchema.safeParse(frame.payload)
138
- if (validationResult.success) {
139
- const settings = toOpenSecuritySettings(validationResult.data)
140
- void this.applySettingsUpdate(settings)
141
- } else {
142
- this.logger?.('OpenSecuritySettingsRepositoryImpl: Invalid WS payload schema')
143
- }
144
- }
145
- } catch (e) {
146
- this.logger?.(`OpenSecuritySettingsRepositoryImpl: WS payload parsing failed - ${String(e)}`)
147
- }
148
- }
149
- })
150
- }
151
- }
@@ -1,39 +0,0 @@
1
- import type { AppResult, AppError } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
2
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
3
-
4
- /** Observer callback for security settings changes. */
5
- export type OpenSecuritySettingsObserver = (settings: OpenSecuritySettings | null) => void
6
-
7
- /**
8
- * Repository interface for retrieving, updating, and observing open security settings.
9
- */
10
- export interface OpenSecuritySettingsRepository {
11
- /**
12
- * Retrieves in-memory or persisted settings. If unavailable, fetches from network.
13
- *
14
- * @returns AppResult containing current settings or error
15
- */
16
- getOpenSecuritySettings(): Promise<AppResult<OpenSecuritySettings, AppError>>
17
-
18
- /**
19
- * Forces a network refresh of the open security settings and persists them.
20
- *
21
- * @returns AppResult containing updated settings or error
22
- */
23
- refreshOpenSecuritySettings(): Promise<AppResult<OpenSecuritySettings, AppError>>
24
-
25
- /**
26
- * Applies an external update to the settings and persists them.
27
- *
28
- * @param securitySettings - New settings snapshot
29
- */
30
- updateOpenSecuritySettings(securitySettings: OpenSecuritySettings): Promise<void>
31
-
32
- /**
33
- * Registers an observer for live updates to the security settings.
34
- *
35
- * @param observer - Callback receiving updated settings or null
36
- * @returns Unsubscribe function
37
- */
38
- observeOpenSecuritySettings(observer: OpenSecuritySettingsObserver): () => void
39
- }
@@ -1,71 +0,0 @@
1
- import { describe, it, expect } from 'vitest'
2
- import { EncryptedSettingsMock } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
3
- import { EncryptedOpenSecuritySettingsStorage } from '@/storage/security-settings/encrypted-open-security-settings-storage'
4
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
5
-
6
- describe('EncryptedOpenSecuritySettingsStorage', () => {
7
- const sampleSettings: OpenSecuritySettings = {
8
- openPasswordPolicy: {
9
- minLength: 8,
10
- requireLetter: true,
11
- requireUpperCase: true,
12
- requireLowerCase: true,
13
- requireDigit: true,
14
- requireSpecialChar: false
15
- },
16
- otpConfirmation: {
17
- retryAfterSeconds: 60,
18
- numberOfSymbols: 6,
19
- expirationSeconds: 300
20
- }
21
- }
22
-
23
- it('returns null when settings are not present in storage', async () => {
24
- const encryptedSettings = new EncryptedSettingsMock()
25
- const storage = new EncryptedOpenSecuritySettingsStorage(encryptedSettings)
26
-
27
- const result = await storage.getOpenSecuritySettings()
28
- expect(result).toBeNull()
29
- })
30
-
31
- it('updates and retrieves settings correctly', async () => {
32
- const encryptedSettings = new EncryptedSettingsMock()
33
- const storage = new EncryptedOpenSecuritySettingsStorage(encryptedSettings)
34
-
35
- await storage.updateOpenSecuritySettings(sampleSettings)
36
- const result = await storage.getOpenSecuritySettings()
37
-
38
- expect(result).toEqual(sampleSettings)
39
- })
40
-
41
- it('clears persisted settings', async () => {
42
- const encryptedSettings = new EncryptedSettingsMock()
43
- const storage = new EncryptedOpenSecuritySettingsStorage(encryptedSettings)
44
-
45
- await storage.updateOpenSecuritySettings(sampleSettings)
46
- await storage.clearOpenSecuritySettings()
47
-
48
- const result = await storage.getOpenSecuritySettings()
49
- expect(result).toBeNull()
50
- })
51
-
52
- it('handles invalid stored JSON gracefully and cleans up entry', async () => {
53
- const encryptedSettings = new EncryptedSettingsMock()
54
- await encryptedSettings.put('security_settings', 'invalid-json-{')
55
- const storage = new EncryptedOpenSecuritySettingsStorage(encryptedSettings)
56
-
57
- const result = await storage.getOpenSecuritySettings()
58
- expect(result).toBeNull()
59
- expect(await encryptedSettings.get('security_settings')).toBeNull()
60
- })
61
-
62
- it('handles schema-mismatched JSON gracefully and cleans up entry', async () => {
63
- const encryptedSettings = new EncryptedSettingsMock()
64
- await encryptedSettings.put('security_settings', JSON.stringify({ unknown_field: 123 }))
65
- const storage = new EncryptedOpenSecuritySettingsStorage(encryptedSettings)
66
-
67
- const result = await storage.getOpenSecuritySettings()
68
- expect(result).toBeNull()
69
- expect(await encryptedSettings.get('security_settings')).toBeNull()
70
- })
71
- })
@@ -1,65 +0,0 @@
1
- import { openSecuritySettingsPayloadSchema } from '@mudrichenkoevgeny/shared-foundation'
2
- import type { EncryptedSettings } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
3
- import type { OpenSecuritySettingsStorage } from '@/storage/security-settings/open-security-settings-storage'
4
- import {
5
- toOpenSecuritySettings,
6
- toOpenSecuritySettingsPayload
7
- } from '@/domain/model/open-security-settings'
8
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
9
-
10
- const KEY_SECURITY_SETTINGS = 'security_settings'
11
-
12
- /**
13
- * Storage implementation of {@link OpenSecuritySettingsStorage} backed by {@link EncryptedSettings}.
14
- */
15
- export class EncryptedOpenSecuritySettingsStorage implements OpenSecuritySettingsStorage {
16
- /**
17
- * Constructs a new {@link EncryptedOpenSecuritySettingsStorage}.
18
- *
19
- * @param encryptedSettings - Encrypted settings key-value store instance
20
- */
21
- public constructor(private readonly encryptedSettings: EncryptedSettings) {}
22
-
23
- /**
24
- * Retrieves and deserializes encrypted open security settings.
25
- *
26
- * @returns Deserialized settings or null if unreadable or absent
27
- */
28
- public async getOpenSecuritySettings(): Promise<OpenSecuritySettings | null> {
29
- const rawData = await this.encryptedSettings.get(KEY_SECURITY_SETTINGS)
30
- if (!rawData) {
31
- return null
32
- }
33
-
34
- try {
35
- const parsedJson = JSON.parse(rawData)
36
- const validationResult = openSecuritySettingsPayloadSchema.safeParse(parsedJson)
37
- if (!validationResult.success) {
38
- await this.encryptedSettings.remove(KEY_SECURITY_SETTINGS)
39
- return null
40
- }
41
- return toOpenSecuritySettings(validationResult.data)
42
- } catch {
43
- await this.encryptedSettings.remove(KEY_SECURITY_SETTINGS)
44
- return null
45
- }
46
- }
47
-
48
- /**
49
- * Serializes and persists open security settings.
50
- *
51
- * @param securitySettings - Security settings snapshot to store
52
- */
53
- public async updateOpenSecuritySettings(securitySettings: OpenSecuritySettings): Promise<void> {
54
- const payload = toOpenSecuritySettingsPayload(securitySettings)
55
- const jsonString = JSON.stringify(payload)
56
- await this.encryptedSettings.put(KEY_SECURITY_SETTINGS, jsonString)
57
- }
58
-
59
- /**
60
- * Removes persisted open security settings from storage.
61
- */
62
- public async clearOpenSecuritySettings(): Promise<void> {
63
- await this.encryptedSettings.remove(KEY_SECURITY_SETTINGS)
64
- }
65
- }
@@ -1,25 +0,0 @@
1
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
2
-
3
- /**
4
- * Persistence port for {@link OpenSecuritySettings} storage implementations.
5
- */
6
- export interface OpenSecuritySettingsStorage {
7
- /**
8
- * Retrieves the stored {@link OpenSecuritySettings}, or null if none are persisted yet.
9
- *
10
- * @returns Promise resolving to stored settings or null
11
- */
12
- getOpenSecuritySettings(): Promise<OpenSecuritySettings | null>
13
-
14
- /**
15
- * Persists a new snapshot of {@link OpenSecuritySettings}.
16
- *
17
- * @param securitySettings - Snapshot to serialize and persist
18
- */
19
- updateOpenSecuritySettings(securitySettings: OpenSecuritySettings): Promise<void>
20
-
21
- /**
22
- * Removes persisted open security settings from storage, if present.
23
- */
24
- clearOpenSecuritySettings(): Promise<void>
25
- }
@@ -1,26 +0,0 @@
1
- import type { AppResult, AppError } from '@mudrichenkoevgeny/web-platform-sdk-core-common'
2
- import type { OpenSecuritySettings } from '@/domain/model/open-security-settings'
3
- import type { OpenSecuritySettingsRepository } from '@/repository/open-security-settings-repository'
4
-
5
- /**
6
- * Use case that delegates to {@link OpenSecuritySettingsRepository.refreshOpenSecuritySettings}.
7
- */
8
- export class RefreshOpenSecuritySettingsUseCase {
9
- /**
10
- * Constructs a new {@link RefreshOpenSecuritySettingsUseCase}.
11
- *
12
- * @param openSecuritySettingsRepository - Repository performing the network refresh and persistence
13
- */
14
- public constructor(
15
- private readonly openSecuritySettingsRepository: OpenSecuritySettingsRepository
16
- ) {}
17
-
18
- /**
19
- * Executes the refresh operation.
20
- *
21
- * @returns Promise resolving to AppResult with refreshed settings or error
22
- */
23
- public async execute(): Promise<AppResult<OpenSecuritySettings, AppError>> {
24
- return this.openSecuritySettingsRepository.refreshOpenSecuritySettings()
25
- }
26
- }