i18ntk 1.10.2 → 2.0.2
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.
- package/LICENSE +1 -1
- package/README.md +141 -1191
- package/main/i18ntk-analyze.js +65 -84
- package/main/i18ntk-backup-class.js +420 -0
- package/main/i18ntk-backup.js +3 -3
- package/main/i18ntk-complete.js +90 -65
- package/main/i18ntk-doctor.js +123 -103
- package/main/i18ntk-fixer.js +61 -725
- package/main/i18ntk-go.js +14 -15
- package/main/i18ntk-init.js +77 -26
- package/main/i18ntk-java.js +27 -32
- package/main/i18ntk-js.js +70 -68
- package/main/i18ntk-manage.js +129 -30
- package/main/i18ntk-php.js +75 -75
- package/main/i18ntk-py.js +55 -56
- package/main/i18ntk-scanner.js +59 -57
- package/main/i18ntk-setup.js +9 -404
- package/main/i18ntk-sizing.js +6 -6
- package/main/i18ntk-summary.js +21 -18
- package/main/i18ntk-ui.js +11 -10
- package/main/i18ntk-usage.js +54 -18
- package/main/i18ntk-validate.js +13 -13
- package/main/manage/commands/AnalyzeCommand.js +1124 -0
- package/main/manage/commands/BackupCommand.js +62 -0
- package/main/manage/commands/CommandRouter.js +295 -0
- package/main/manage/commands/CompleteCommand.js +61 -0
- package/main/manage/commands/DoctorCommand.js +60 -0
- package/main/manage/commands/FixerCommand.js +624 -0
- package/main/manage/commands/InitCommand.js +62 -0
- package/main/manage/commands/ScannerCommand.js +654 -0
- package/main/manage/commands/SizingCommand.js +60 -0
- package/main/manage/commands/SummaryCommand.js +61 -0
- package/main/manage/commands/UsageCommand.js +60 -0
- package/main/manage/commands/ValidateCommand.js +978 -0
- package/main/manage/index-fixed.js +1447 -0
- package/main/manage/index.js +1462 -0
- package/main/manage/managers/DebugMenu.js +140 -0
- package/main/manage/managers/InteractiveMenu.js +177 -0
- package/main/manage/managers/LanguageMenu.js +62 -0
- package/main/manage/managers/SettingsMenu.js +53 -0
- package/main/manage/services/AuthenticationService.js +263 -0
- package/main/manage/services/ConfigurationService-fixed.js +449 -0
- package/main/manage/services/ConfigurationService.js +449 -0
- package/main/manage/services/FileManagementService.js +368 -0
- package/main/manage/services/FrameworkDetectionService.js +458 -0
- package/main/manage/services/InitService.js +1051 -0
- package/main/manage/services/SetupService.js +462 -0
- package/main/manage/services/SummaryService.js +450 -0
- package/main/manage/services/UsageService.js +1502 -0
- package/package.json +32 -29
- package/runtime/enhanced.d.ts +221 -221
- package/runtime/index.d.ts +29 -29
- package/runtime/index.full.d.ts +331 -331
- package/runtime/index.js +7 -6
- package/scripts/build-lite.js +17 -17
- package/scripts/deprecate-versions.js +23 -6
- package/scripts/export-translations.js +5 -5
- package/scripts/fix-all-i18n.js +3 -3
- package/scripts/fix-and-purify-i18n.js +3 -2
- package/scripts/fix-locale-control-chars.js +110 -0
- package/scripts/lint-locales.js +80 -0
- package/scripts/locale-optimizer.js +8 -8
- package/scripts/prepublish.js +21 -21
- package/scripts/security-check.js +117 -117
- package/scripts/sync-translations.js +4 -4
- package/scripts/sync-ui-locales.js +9 -8
- package/scripts/validate-all-translations.js +8 -7
- package/scripts/verify-deprecations.js +157 -161
- package/scripts/verify-translations.js +6 -5
- package/settings/i18ntk-config.json +282 -282
- package/settings/language-config.json +5 -5
- package/settings/settings-cli.js +9 -9
- package/settings/settings-manager.js +18 -18
- package/ui-locales/de.json +2417 -2348
- package/ui-locales/en.json +2415 -2352
- package/ui-locales/es.json +2425 -2353
- package/ui-locales/fr.json +2418 -2348
- package/ui-locales/ja.json +2463 -2361
- package/ui-locales/ru.json +2463 -2359
- package/ui-locales/zh.json +2418 -2351
- package/utils/admin-auth.js +2 -2
- package/utils/admin-cli.js +297 -297
- package/utils/admin-pin.js +9 -9
- package/utils/cli-helper.js +9 -9
- package/utils/config-helper.js +73 -104
- package/utils/config-manager.js +204 -171
- package/utils/config.js +5 -4
- package/utils/env-manager.js +249 -263
- package/utils/framework-detector.js +27 -24
- package/utils/i18n-helper.js +85 -41
- package/utils/init-helper.js +152 -94
- package/utils/json-output.js +98 -98
- package/utils/mini-commander.js +179 -0
- package/utils/missing-key-validator.js +5 -5
- package/utils/plugin-loader.js +40 -29
- package/utils/prompt.js +14 -44
- package/utils/safe-json.js +40 -0
- package/utils/secure-errors.js +3 -3
- package/utils/security-check-improved.js +390 -0
- package/utils/security-config.js +5 -5
- package/utils/security-fixed.js +607 -0
- package/utils/security.js +652 -602
- package/utils/setup-enforcer.js +136 -44
- package/utils/setup-validator.js +33 -32
- package/utils/ultra-performance-optimizer.js +11 -9
- package/utils/watch-locales.js +2 -1
- package/utils/prompt-fixed.js +0 -55
- package/utils/security-check.js +0 -454
package/utils/admin-auth.js
CHANGED
|
@@ -39,7 +39,7 @@ class AdminAuth {
|
|
|
39
39
|
*/
|
|
40
40
|
async initialize() {
|
|
41
41
|
try {
|
|
42
|
-
if (!
|
|
42
|
+
if (!SecurityUtils.safeExistsSync(this.configPath)) {
|
|
43
43
|
// Create default config if it doesn't exist
|
|
44
44
|
const defaultConfig = {
|
|
45
45
|
enabled: false,
|
|
@@ -82,7 +82,7 @@ class AdminAuth {
|
|
|
82
82
|
*/
|
|
83
83
|
async loadConfig() {
|
|
84
84
|
try {
|
|
85
|
-
if (!
|
|
85
|
+
if (!SecurityUtils.safeExistsSync(this.configPath)) {
|
|
86
86
|
return null;
|
|
87
87
|
}
|
|
88
88
|
|
package/utils/admin-cli.js
CHANGED
|
@@ -1,298 +1,298 @@
|
|
|
1
|
-
const i18n = require('./i18n-helper');
|
|
2
|
-
const AdminAuth = require('./admin-auth');
|
|
3
|
-
const SecurityUtils = require('./security');
|
|
4
|
-
const { getGlobalReadline, closeGlobalReadline, askHidden, ask } = require('./cli');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* CLI Helper for Admin Authentication
|
|
8
|
-
*/
|
|
9
|
-
class AdminCLI {
|
|
10
|
-
constructor() {
|
|
11
|
-
this.adminAuth = new AdminAuth();
|
|
12
|
-
this.rl = null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Initialize readline interface
|
|
17
|
-
*/
|
|
18
|
-
initReadline() {
|
|
19
|
-
if (!this.rl) {
|
|
20
|
-
this.rl = getGlobalReadline();
|
|
21
|
-
}
|
|
22
|
-
return this.rl;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Close readline interface
|
|
27
|
-
*/
|
|
28
|
-
closeReadline() {
|
|
29
|
-
if (this.rl) {
|
|
30
|
-
closeGlobalReadline();
|
|
31
|
-
this.rl = null;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Prompt for PIN input (hidden)
|
|
37
|
-
*/
|
|
38
|
-
async promptPin(message = null) {
|
|
39
|
-
if (!message) message = i18n.t('adminCli.enterPin');
|
|
40
|
-
return askHidden(message);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Prompt for yes/no confirmation
|
|
45
|
-
*/
|
|
46
|
-
async promptConfirm(message) {
|
|
47
|
-
return ask(`${message} (y/N): `).then(answer => answer.toLowerCase().startsWith('y'));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Setup admin PIN
|
|
52
|
-
*/
|
|
53
|
-
async setupAdminPin() {
|
|
54
|
-
try {
|
|
55
|
-
console.log(i18n.t('adminCli.setupPinProtectionTitle'));
|
|
56
|
-
console.log(i18n.t('adminCli.setupPinProtectionDescription'));
|
|
57
|
-
|
|
58
|
-
const confirm = await this.promptConfirm(i18n.t('adminCli.enablePinProtectionPrompt'));
|
|
59
|
-
if (!confirm) {
|
|
60
|
-
console.log(i18n.t('adminCli.setupCancelled'));
|
|
61
|
-
this.closeReadline();
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
let pin1, pin2;
|
|
66
|
-
do {
|
|
67
|
-
pin1 = await this.promptPin(i18n.t('adminCli.enterPinPrompt'));
|
|
68
|
-
|
|
69
|
-
if (!/^\d{4,6}$/.test(pin1)) {
|
|
70
|
-
console.log(i18n.t('adminCli.pinFormatError'));
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
pin2 = await this.promptPin(i18n.t('adminCli.confirmPinPrompt'));
|
|
75
|
-
|
|
76
|
-
if (pin1 !== pin2) {
|
|
77
|
-
console.log(i18n.t('adminCli.pinMismatchError'));
|
|
78
|
-
}
|
|
79
|
-
} while (pin1 !== pin2 || !/^\d{4,6}$/.test(pin1));
|
|
80
|
-
|
|
81
|
-
await this.adminAuth.initialize();
|
|
82
|
-
const success = await this.adminAuth.setupPin(pin1);
|
|
83
|
-
|
|
84
|
-
if (success) {
|
|
85
|
-
console.log(i18n.t('adminCli.pinProtectionEnabledSuccess'));
|
|
86
|
-
console.log(i18n.t('adminCli.pinRecoveryWarning'));
|
|
87
|
-
SecurityUtils.logSecurityEvent(
|
|
88
|
-
i18n.t('adminCli.adminPinSetupCli'),
|
|
89
|
-
'info',
|
|
90
|
-
{ message: 'Admin PIN setup completed via CLI' }
|
|
91
|
-
);
|
|
92
|
-
} else {
|
|
93
|
-
console.log(i18n.t('adminCli.setupPinProtectionFailed'));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
this.closeReadline();
|
|
97
|
-
return success;
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error(i18n.t('adminCli.errorSettingUpPin', { message: error.message }));
|
|
100
|
-
this.closeReadline();
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Authenticate admin user
|
|
107
|
-
*/
|
|
108
|
-
async authenticateAdmin(operation = 'administrative operation') {
|
|
109
|
-
try {
|
|
110
|
-
await this.adminAuth.initialize();
|
|
111
|
-
|
|
112
|
-
const authRequired = await this.adminAuth.isAuthRequired();
|
|
113
|
-
if (!authRequired) {
|
|
114
|
-
return true; // No authentication required
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
console.log(i18n.t('adminCli.authRequiredForOperation', { operation }));
|
|
118
|
-
|
|
119
|
-
let attempts = 0;
|
|
120
|
-
const maxAttempts = 3;
|
|
121
|
-
|
|
122
|
-
while (attempts < maxAttempts) {
|
|
123
|
-
const pin = await this.promptPin();
|
|
124
|
-
|
|
125
|
-
if (!/^\d{4,6}$/.test(pin)) {
|
|
126
|
-
console.log(i18n.t('adminCli.invalidPinFormat'));
|
|
127
|
-
attempts++;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const isValid = await this.adminAuth.verifyPin(pin);
|
|
132
|
-
|
|
133
|
-
if (isValid) {
|
|
134
|
-
console.log(i18n.t('adminCli.authenticationSuccess'));
|
|
135
|
-
this.closeReadline();
|
|
136
|
-
return true;
|
|
137
|
-
} else {
|
|
138
|
-
attempts++;
|
|
139
|
-
const remaining = maxAttempts - attempts;
|
|
140
|
-
if (remaining > 0) {
|
|
141
|
-
console.log(i18n.t('adminCli.invalidPinAttemptsRemaining', { remaining }));
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
console.log(i18n.t('adminCli.authenticationFailedAccessDenied'));
|
|
147
|
-
SecurityUtils.logSecurityEvent(
|
|
148
|
-
i18n.t('adminCli.adminAuthFailedCli'),
|
|
149
|
-
'warning',
|
|
150
|
-
{ message: `Admin authentication failed after ${maxAttempts} attempts` }
|
|
151
|
-
);
|
|
152
|
-
this.closeReadline();
|
|
153
|
-
return false;
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.error(i18n.t('adminCli.authenticationError', { message: error.message }));
|
|
156
|
-
this.closeReadline();
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Disable admin authentication
|
|
163
|
-
*/
|
|
164
|
-
async disableAdminAuth() {
|
|
165
|
-
try {
|
|
166
|
-
await this.adminAuth.initialize();
|
|
167
|
-
|
|
168
|
-
const authRequired = await this.adminAuth.isAuthRequired();
|
|
169
|
-
if (!authRequired) {
|
|
170
|
-
console.log(i18n.t('adminCli.pinProtectionNotEnabled'));
|
|
171
|
-
return true;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
console.log(i18n.t('adminCli.disablingPinProtectionTitle'));
|
|
175
|
-
|
|
176
|
-
// Require authentication to disable
|
|
177
|
-
const authenticated = await this.authenticateAdmin('disable admin protection');
|
|
178
|
-
if (!authenticated) {
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const confirm = await this.promptConfirm(i18n.t('adminCli.confirmDisablePinProtection'));
|
|
183
|
-
if (!confirm) {
|
|
184
|
-
console.log(i18n.t('adminCli.operationCancelled'));
|
|
185
|
-
this.closeReadline();
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const success = await this.adminAuth.disableAuth();
|
|
190
|
-
|
|
191
|
-
if (success) {
|
|
192
|
-
console.log(i18n.t('adminCli.pinProtectionDisabledSuccess'));
|
|
193
|
-
SecurityUtils.logSecurityEvent(
|
|
194
|
-
i18n.t('adminCli.adminAuthDisabledCli'),
|
|
195
|
-
'info',
|
|
196
|
-
{ message: 'Admin PIN protection disabled via CLI' }
|
|
197
|
-
);
|
|
198
|
-
} else {
|
|
199
|
-
console.log(i18n.t('adminCli.disablePinProtectionFailed'));
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
this.closeReadline();
|
|
203
|
-
return success;
|
|
204
|
-
} catch (error) {
|
|
205
|
-
console.error(i18n.t('adminCli.errorDisablingPinProtection', { message: error.message }));
|
|
206
|
-
this.closeReadline();
|
|
207
|
-
return false;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Show admin status
|
|
213
|
-
*/
|
|
214
|
-
async showAdminStatus() {
|
|
215
|
-
try {
|
|
216
|
-
await this.adminAuth.initialize();
|
|
217
|
-
|
|
218
|
-
const authRequired = await this.adminAuth.isAuthRequired();
|
|
219
|
-
|
|
220
|
-
console.log(i18n.t('adminCli.adminProtectionStatusTitle'));
|
|
221
|
-
console.log('='.repeat(30));
|
|
222
|
-
|
|
223
|
-
if (authRequired) {
|
|
224
|
-
console.log(i18n.t('adminCli.statusEnabled'));
|
|
225
|
-
console.log(i18n.t('adminCli.protectionDetails'));
|
|
226
|
-
console.log(i18n.t('adminCli.lockoutDetails'));
|
|
227
|
-
} else {
|
|
228
|
-
console.log(i18n.t('adminCli.statusDisabled'));
|
|
229
|
-
console.log(i18n.t('adminCli.noAuthRequired'));
|
|
230
|
-
console.log(i18n.t('adminCli.unprotectedRisk'));
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return authRequired;
|
|
234
|
-
} catch (error) {
|
|
235
|
-
console.error(i18n.t('adminCli.errorCheckingAdminStatus', { message: error.message }));
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Check if operation requires admin authentication
|
|
242
|
-
*/
|
|
243
|
-
static requiresAdminAuth(operation) {
|
|
244
|
-
const adminOperations = [
|
|
245
|
-
'complete',
|
|
246
|
-
'manage',
|
|
247
|
-
'init',
|
|
248
|
-
'bulk-update',
|
|
249
|
-
'delete-language',
|
|
250
|
-
'reset-translations',
|
|
251
|
-
'delete',
|
|
252
|
-
'workflow'
|
|
253
|
-
];
|
|
254
|
-
|
|
255
|
-
return adminOperations.includes(operation);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Static method to check if operation requires auth (alias)
|
|
260
|
-
*/
|
|
261
|
-
static requiresAuth(operation) {
|
|
262
|
-
return AdminCLI.requiresAdminAuth(operation);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Static method to authenticate
|
|
267
|
-
*/
|
|
268
|
-
static async authenticate(operation = 'administrative operation') {
|
|
269
|
-
const cli = new AdminCLI();
|
|
270
|
-
return await cli.authenticateAdmin(operation);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Static method to setup admin
|
|
275
|
-
*/
|
|
276
|
-
static async setupAdmin() {
|
|
277
|
-
const cli = new AdminCLI();
|
|
278
|
-
return await cli.setupAdminPin();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Static method to disable admin
|
|
283
|
-
*/
|
|
284
|
-
static async disableAdmin() {
|
|
285
|
-
const cli = new AdminCLI();
|
|
286
|
-
return await cli.disableAdminAuth();
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Static method to show status
|
|
291
|
-
*/
|
|
292
|
-
static async showStatus() {
|
|
293
|
-
const cli = new AdminCLI();
|
|
294
|
-
return await cli.showAdminStatus();
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
1
|
+
const i18n = require('./i18n-helper');
|
|
2
|
+
const AdminAuth = require('./admin-auth');
|
|
3
|
+
const SecurityUtils = require('./security');
|
|
4
|
+
const { getGlobalReadline, closeGlobalReadline, askHidden, ask } = require('./cli');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CLI Helper for Admin Authentication
|
|
8
|
+
*/
|
|
9
|
+
class AdminCLI {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.adminAuth = new AdminAuth();
|
|
12
|
+
this.rl = null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialize readline interface
|
|
17
|
+
*/
|
|
18
|
+
initReadline() {
|
|
19
|
+
if (!this.rl) {
|
|
20
|
+
this.rl = getGlobalReadline();
|
|
21
|
+
}
|
|
22
|
+
return this.rl;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Close readline interface
|
|
27
|
+
*/
|
|
28
|
+
closeReadline() {
|
|
29
|
+
if (this.rl) {
|
|
30
|
+
closeGlobalReadline();
|
|
31
|
+
this.rl = null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Prompt for PIN input (hidden)
|
|
37
|
+
*/
|
|
38
|
+
async promptPin(message = null) {
|
|
39
|
+
if (!message) message = i18n.t('adminCli.enterPin');
|
|
40
|
+
return askHidden(message);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Prompt for yes/no confirmation
|
|
45
|
+
*/
|
|
46
|
+
async promptConfirm(message) {
|
|
47
|
+
return ask(`${message} (y/N): `).then(answer => answer.toLowerCase().startsWith('y'));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Setup admin PIN
|
|
52
|
+
*/
|
|
53
|
+
async setupAdminPin() {
|
|
54
|
+
try {
|
|
55
|
+
console.log(i18n.t('adminCli.setupPinProtectionTitle'));
|
|
56
|
+
console.log(i18n.t('adminCli.setupPinProtectionDescription'));
|
|
57
|
+
|
|
58
|
+
const confirm = await this.promptConfirm(i18n.t('adminCli.enablePinProtectionPrompt'));
|
|
59
|
+
if (!confirm) {
|
|
60
|
+
console.log(i18n.t('adminCli.setupCancelled'));
|
|
61
|
+
this.closeReadline();
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let pin1, pin2;
|
|
66
|
+
do {
|
|
67
|
+
pin1 = await this.promptPin(i18n.t('adminCli.enterPinPrompt'));
|
|
68
|
+
|
|
69
|
+
if (!/^\d{4,6}$/.test(pin1)) {
|
|
70
|
+
console.log(i18n.t('adminCli.pinFormatError'));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pin2 = await this.promptPin(i18n.t('adminCli.confirmPinPrompt'));
|
|
75
|
+
|
|
76
|
+
if (pin1 !== pin2) {
|
|
77
|
+
console.log(i18n.t('adminCli.pinMismatchError'));
|
|
78
|
+
}
|
|
79
|
+
} while (pin1 !== pin2 || !/^\d{4,6}$/.test(pin1));
|
|
80
|
+
|
|
81
|
+
await this.adminAuth.initialize();
|
|
82
|
+
const success = await this.adminAuth.setupPin(pin1);
|
|
83
|
+
|
|
84
|
+
if (success) {
|
|
85
|
+
console.log(i18n.t('adminCli.pinProtectionEnabledSuccess'));
|
|
86
|
+
console.log(i18n.t('adminCli.pinRecoveryWarning'));
|
|
87
|
+
SecurityUtils.logSecurityEvent(
|
|
88
|
+
i18n.t('adminCli.adminPinSetupCli'),
|
|
89
|
+
'info',
|
|
90
|
+
{ message: 'Admin PIN setup completed via CLI' }
|
|
91
|
+
);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(i18n.t('adminCli.setupPinProtectionFailed'));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.closeReadline();
|
|
97
|
+
return success;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(i18n.t('adminCli.errorSettingUpPin', { message: error.message }));
|
|
100
|
+
this.closeReadline();
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Authenticate admin user
|
|
107
|
+
*/
|
|
108
|
+
async authenticateAdmin(operation = 'administrative operation') {
|
|
109
|
+
try {
|
|
110
|
+
await this.adminAuth.initialize();
|
|
111
|
+
|
|
112
|
+
const authRequired = await this.adminAuth.isAuthRequired();
|
|
113
|
+
if (!authRequired) {
|
|
114
|
+
return true; // No authentication required
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(i18n.t('adminCli.authRequiredForOperation', { operation }));
|
|
118
|
+
|
|
119
|
+
let attempts = 0;
|
|
120
|
+
const maxAttempts = 3;
|
|
121
|
+
|
|
122
|
+
while (attempts < maxAttempts) {
|
|
123
|
+
const pin = await this.promptPin();
|
|
124
|
+
|
|
125
|
+
if (!/^\d{4,6}$/.test(pin)) {
|
|
126
|
+
console.log(i18n.t('adminCli.invalidPinFormat'));
|
|
127
|
+
attempts++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const isValid = await this.adminAuth.verifyPin(pin);
|
|
132
|
+
|
|
133
|
+
if (isValid) {
|
|
134
|
+
console.log(i18n.t('adminCli.authenticationSuccess'));
|
|
135
|
+
this.closeReadline();
|
|
136
|
+
return true;
|
|
137
|
+
} else {
|
|
138
|
+
attempts++;
|
|
139
|
+
const remaining = maxAttempts - attempts;
|
|
140
|
+
if (remaining > 0) {
|
|
141
|
+
console.log(i18n.t('adminCli.invalidPinAttemptsRemaining', { remaining }));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(i18n.t('adminCli.authenticationFailedAccessDenied'));
|
|
147
|
+
SecurityUtils.logSecurityEvent(
|
|
148
|
+
i18n.t('adminCli.adminAuthFailedCli'),
|
|
149
|
+
'warning',
|
|
150
|
+
{ message: `Admin authentication failed after ${maxAttempts} attempts` }
|
|
151
|
+
);
|
|
152
|
+
this.closeReadline();
|
|
153
|
+
return false;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error(i18n.t('adminCli.authenticationError', { message: error.message }));
|
|
156
|
+
this.closeReadline();
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Disable admin authentication
|
|
163
|
+
*/
|
|
164
|
+
async disableAdminAuth() {
|
|
165
|
+
try {
|
|
166
|
+
await this.adminAuth.initialize();
|
|
167
|
+
|
|
168
|
+
const authRequired = await this.adminAuth.isAuthRequired();
|
|
169
|
+
if (!authRequired) {
|
|
170
|
+
console.log(i18n.t('adminCli.pinProtectionNotEnabled'));
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(i18n.t('adminCli.disablingPinProtectionTitle'));
|
|
175
|
+
|
|
176
|
+
// Require authentication to disable
|
|
177
|
+
const authenticated = await this.authenticateAdmin('disable admin protection');
|
|
178
|
+
if (!authenticated) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const confirm = await this.promptConfirm(i18n.t('adminCli.confirmDisablePinProtection'));
|
|
183
|
+
if (!confirm) {
|
|
184
|
+
console.log(i18n.t('adminCli.operationCancelled'));
|
|
185
|
+
this.closeReadline();
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const success = await this.adminAuth.disableAuth();
|
|
190
|
+
|
|
191
|
+
if (success) {
|
|
192
|
+
console.log(i18n.t('adminCli.pinProtectionDisabledSuccess'));
|
|
193
|
+
SecurityUtils.logSecurityEvent(
|
|
194
|
+
i18n.t('adminCli.adminAuthDisabledCli'),
|
|
195
|
+
'info',
|
|
196
|
+
{ message: 'Admin PIN protection disabled via CLI' }
|
|
197
|
+
);
|
|
198
|
+
} else {
|
|
199
|
+
console.log(i18n.t('adminCli.disablePinProtectionFailed'));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
this.closeReadline();
|
|
203
|
+
return success;
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(i18n.t('adminCli.errorDisablingPinProtection', { message: error.message }));
|
|
206
|
+
this.closeReadline();
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Show admin status
|
|
213
|
+
*/
|
|
214
|
+
async showAdminStatus() {
|
|
215
|
+
try {
|
|
216
|
+
await this.adminAuth.initialize();
|
|
217
|
+
|
|
218
|
+
const authRequired = await this.adminAuth.isAuthRequired();
|
|
219
|
+
|
|
220
|
+
console.log(i18n.t('adminCli.adminProtectionStatusTitle'));
|
|
221
|
+
console.log('='.repeat(30));
|
|
222
|
+
|
|
223
|
+
if (authRequired) {
|
|
224
|
+
console.log(i18n.t('adminCli.statusEnabled'));
|
|
225
|
+
console.log(i18n.t('adminCli.protectionDetails'));
|
|
226
|
+
console.log(i18n.t('adminCli.lockoutDetails'));
|
|
227
|
+
} else {
|
|
228
|
+
console.log(i18n.t('adminCli.statusDisabled'));
|
|
229
|
+
console.log(i18n.t('adminCli.noAuthRequired'));
|
|
230
|
+
console.log(i18n.t('adminCli.unprotectedRisk'));
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return authRequired;
|
|
234
|
+
} catch (error) {
|
|
235
|
+
console.error(i18n.t('adminCli.errorCheckingAdminStatus', { message: error.message }));
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Check if operation requires admin authentication
|
|
242
|
+
*/
|
|
243
|
+
static requiresAdminAuth(operation) {
|
|
244
|
+
const adminOperations = [
|
|
245
|
+
'complete',
|
|
246
|
+
'manage',
|
|
247
|
+
'init',
|
|
248
|
+
'bulk-update',
|
|
249
|
+
'delete-language',
|
|
250
|
+
'reset-translations',
|
|
251
|
+
'delete',
|
|
252
|
+
'workflow'
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
return adminOperations.includes(operation);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Static method to check if operation requires auth (alias)
|
|
260
|
+
*/
|
|
261
|
+
static requiresAuth(operation) {
|
|
262
|
+
return AdminCLI.requiresAdminAuth(operation);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Static method to authenticate
|
|
267
|
+
*/
|
|
268
|
+
static async authenticate(operation = 'administrative operation') {
|
|
269
|
+
const cli = new AdminCLI();
|
|
270
|
+
return await cli.authenticateAdmin(operation);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Static method to setup admin
|
|
275
|
+
*/
|
|
276
|
+
static async setupAdmin() {
|
|
277
|
+
const cli = new AdminCLI();
|
|
278
|
+
return await cli.setupAdminPin();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Static method to disable admin
|
|
283
|
+
*/
|
|
284
|
+
static async disableAdmin() {
|
|
285
|
+
const cli = new AdminCLI();
|
|
286
|
+
return await cli.disableAdminAuth();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Static method to show status
|
|
291
|
+
*/
|
|
292
|
+
static async showStatus() {
|
|
293
|
+
const cli = new AdminCLI();
|
|
294
|
+
return await cli.showAdminStatus();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
298
|
module.exports = AdminCLI;
|
package/utils/admin-pin.js
CHANGED
|
@@ -215,11 +215,11 @@ class AdminPinManager {
|
|
|
215
215
|
|
|
216
216
|
// Ensure settings directory exists
|
|
217
217
|
const settingsDir = path.dirname(this.pinFile);
|
|
218
|
-
if (!
|
|
218
|
+
if (!SecurityUtils.safeExistsSync(settingsDir)) {
|
|
219
219
|
fs.mkdirSync(settingsDir, { recursive: true });
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
SecurityUtils.safeWriteFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
|
|
223
223
|
|
|
224
224
|
const i18n = getI18n();
|
|
225
225
|
console.log(i18n.t('adminPin.setup_success'));
|
|
@@ -254,7 +254,7 @@ class AdminPinManager {
|
|
|
254
254
|
* Check if PIN is set
|
|
255
255
|
*/
|
|
256
256
|
isPinSet() {
|
|
257
|
-
return
|
|
257
|
+
return SecurityUtils.safeExistsSync(this.pinFile);
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
/**
|
|
@@ -373,7 +373,7 @@ class AdminPinManager {
|
|
|
373
373
|
const shouldCloseRL = !externalRl && !hadGlobal;
|
|
374
374
|
|
|
375
375
|
try {
|
|
376
|
-
const pinData = JSON.parse(
|
|
376
|
+
const pinData = JSON.parse(SecurityUtils.safeReadFileSync(this.pinFile, path.dirname(this.pinFile), 'utf8'));
|
|
377
377
|
|
|
378
378
|
if (pinData.locked) {
|
|
379
379
|
const i18n = getI18n();
|
|
@@ -405,7 +405,7 @@ class AdminPinManager {
|
|
|
405
405
|
if (this.constantTimeCompare(computedHashHex, pinData.hash)) {
|
|
406
406
|
// Reset attempts on successful login
|
|
407
407
|
pinData.attempts = 0;
|
|
408
|
-
|
|
408
|
+
SecurityUtils.safeWriteFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
|
|
409
409
|
|
|
410
410
|
const i18n = getI18n();
|
|
411
411
|
console.log(i18n.t('adminPin.access_granted'));
|
|
@@ -419,11 +419,11 @@ class AdminPinManager {
|
|
|
419
419
|
setTimeout(() => {
|
|
420
420
|
pinData.locked = false;
|
|
421
421
|
pinData.attempts = 0;
|
|
422
|
-
|
|
422
|
+
SecurityUtils.safeWriteFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
|
|
423
423
|
}, 5 * 60 * 1000); // 5 minutes
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
SecurityUtils.safeWriteFileSync(this.pinFile, JSON.stringify(pinData, null, 2));
|
|
427
427
|
|
|
428
428
|
const i18n = getI18n();
|
|
429
429
|
console.log(i18n.t('adminPin.incorrect_pin', { attempts: 3 - pinData.attempts }));
|
|
@@ -492,7 +492,7 @@ class AdminPinManager {
|
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
try {
|
|
495
|
-
const pinData = JSON.parse(
|
|
495
|
+
const pinData = JSON.parse(SecurityUtils.safeReadFileSync(this.pinFile, path.dirname(this.pinFile), 'utf8'));
|
|
496
496
|
const key = Buffer.from(pinData.key, 'hex');
|
|
497
497
|
const decryptedPin = this.decryptPin(pinData.encrypted, key);
|
|
498
498
|
|
|
@@ -510,7 +510,7 @@ class AdminPinManager {
|
|
|
510
510
|
* Reset PIN
|
|
511
511
|
*/
|
|
512
512
|
async resetPin() {
|
|
513
|
-
if (
|
|
513
|
+
if (SecurityUtils.safeExistsSync(this.pinFile)) {
|
|
514
514
|
fs.unlinkSync(this.pinFile);
|
|
515
515
|
}
|
|
516
516
|
return await this.setupPin();
|