diff --git a/lib/core/remote_registry.dart b/lib/core/remote_registry.dart index 9df9091..8f4038a 100644 --- a/lib/core/remote_registry.dart +++ b/lib/core/remote_registry.dart @@ -75,5 +75,5 @@ class RemoteRegistry { /// 문: 비밀 값이 있으면 마스터 확인을 지나야 한다 (못 지나면 연결 전에 [DavLockedException]). 비밀 값이 없으면 묻지 않는다 Future requireSecretGate(bool hasSecret) async { - if (hasSecret && !await SecretGate.pass()) throw DavLockedException(tr(secretGateMessage)); + if (!await SecretGate.passIf(hasSecret)) throw DavLockedException(tr(secretGateMessage)); } diff --git a/lib/core/secret_gate.dart b/lib/core/secret_gate.dart index 893e00f..49b463e 100644 --- a/lib/core/secret_gate.dart +++ b/lib/core/secret_gate.dart @@ -6,6 +6,10 @@ class SecretGate { /// 써도 되면 true static Future pass({bool force = false}) => check?.call(force) ?? Future.value(true); + + /// 비밀 값이 있을 때만 문을 지나야 한다 ([hasSecret] 가 false 면 묻지 않고 true). 저장된 비밀 값을 쓰기 · 보이기 · 고치기 전에 부른다. + /// 원격 등록 · 플레이어 · 그림 AI · 서버 · 서비스 설정이 함께 쓴다 (관리자 10/24 - 같은 꼴의 복사를 한 곳으로) + static Future passIf(bool hasSecret, {bool force = false}) => hasSecret ? pass(force: force) : Future.value(true); } /// 마스터 비밀번호를 넣지 않아 막혔을 때의 안내 diff --git a/lib/platform/common/media_kit_full_player.dart b/lib/platform/common/media_kit_full_player.dart index dc7a64f..0125074 100644 --- a/lib/platform/common/media_kit_full_player.dart +++ b/lib/platform/common/media_kit_full_player.dart @@ -148,7 +148,7 @@ class MediaKitFullPlayer implements MediaPlayer { if (dav == null) return mk.Media(f); // 124: 저장된 비밀번호로 여는 것이면 (마스터 비밀번호를 정해 두었으면 묻는다) // 사용자가 직접 튼 것이니 한 번 취소했어도 다시 묻는다 - if (dav.headers.isNotEmpty && !await SecretGate.pass(force: true)) throw Exception(tr(secretGateMessage)); + if (!await SecretGate.passIf(dav.headers.isNotEmpty, force: true)) throw Exception(tr(secretGateMessage)); return mk.Media(dav.url, httpHeaders: dav.headers); } diff --git a/lib/services/image_ai.dart b/lib/services/image_ai.dart index 6e9d268..013b273 100644 --- a/lib/services/image_ai.dart +++ b/lib/services/image_ai.dart @@ -140,7 +140,7 @@ class RemoteImageEngine implements ImageAiEngine { Future> generate(ImageGenRequest r, {required String outDir, required String baseName, AiProgress? onProgress}) async { _cancel = false; // 124: 저장된 API 키를 쓰기 전에 (마스터 비밀번호를 정해 두었으면) - if (service.apiKey.isNotEmpty && !await SecretGate.pass(force: true)) throw ImageAiException(tr(secretGateMessage)); + if (!await SecretGate.passIf(service.apiKey.isNotEmpty, force: true)) throw ImageAiException(tr(secretGateMessage)); final seed = resolveSeed(r.seed); onProgress?.call(null, trf('{0} 에 보내는 중', [service.label])); final List> images = switch (service.kind) { diff --git a/lib/ui/ai_settings.dart b/lib/ui/ai_settings.dart index d661216..7e048e8 100644 --- a/lib/ui/ai_settings.dart +++ b/lib/ui/ai_settings.dart @@ -468,7 +468,7 @@ String _kindName(String kind) => switch (kind) { /// 서비스 추가 · 고치기. 저장된 API 키가 칸에 채워지므로 고칠 때는 먼저 마스터 비밀번호 (127 과 같음) Future editAiService(BuildContext context, AppController c, {AiService? old}) async { - if (old != null && old.apiKey.isNotEmpty && !await SecretGate.pass(force: true)) return; + if (old != null && !await SecretGate.passIf(old.apiKey.isNotEmpty, force: true)) return; if (!context.mounted) return; final name = TextEditingController(text: old?.name ?? ''); final url = TextEditingController(text: old?.url ?? 'http://'); diff --git a/lib/ui/ftp_settings.dart b/lib/ui/ftp_settings.dart index 30aa782..a3a4987 100644 --- a/lib/ui/ftp_settings.dart +++ b/lib/ui/ftp_settings.dart @@ -128,7 +128,7 @@ Future trustFtpCert(AppController c, String id, FtpCertException e) => /// FTP 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null) Future editFtpServer(BuildContext context, AppController c, {FtpServer? old}) async { - if (old != null && old.password.isNotEmpty && !await SecretGate.pass(force: true)) return null; + if (old != null && !await SecretGate.passIf(old.password.isNotEmpty, force: true)) return null; if (!context.mounted) return null; final name = TextEditingController(text: old?.name ?? ''); final host = TextEditingController(text: old?.host ?? ''); diff --git a/lib/ui/sftp_settings.dart b/lib/ui/sftp_settings.dart index d7d8309..3962697 100644 --- a/lib/ui/sftp_settings.dart +++ b/lib/ui/sftp_settings.dart @@ -144,7 +144,7 @@ Future trustSftpHostKey(AppController c, String id, SftpHostKeyException e /// SFTP 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null) Future editSftpServer(BuildContext context, AppController c, {SftpServer? old}) async { // 저장된 비밀번호 · 키가 칸에 채워지므로 먼저 마스터 비밀번호 (정해 두었으면) - if (old != null && (old.password.isNotEmpty || old.privateKey.isNotEmpty) && !await SecretGate.pass(force: true)) return null; + if (old != null && !await SecretGate.passIf(old.password.isNotEmpty || old.privateKey.isNotEmpty, force: true)) return null; if (!context.mounted) return null; final name = TextEditingController(text: old?.name ?? ''); final host = TextEditingController(text: old?.host ?? ''); diff --git a/lib/ui/smb_settings.dart b/lib/ui/smb_settings.dart index 623aa2b..0ae4bf0 100644 --- a/lib/ui/smb_settings.dart +++ b/lib/ui/smb_settings.dart @@ -113,7 +113,7 @@ Future deleteSmbServer(BuildContext context, AppController c, SmbServer s) /// SMB 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null). [from]: 경로 입력에 넣은 smb:// · \\서버\공유 (채워 둠) Future editSmbServer(BuildContext context, AppController c, {SmbServer? old, String? from}) async { - if (old != null && old.password.isNotEmpty && !await SecretGate.pass(force: true)) return null; + if (old != null && !await SecretGate.passIf(old.password.isNotEmpty, force: true)) return null; if (!context.mounted) return null; final pre = from == null ? null : parseSmbAddress(from); final name = TextEditingController(text: old?.name ?? ''); diff --git a/lib/ui/webdav_settings.dart b/lib/ui/webdav_settings.dart index b53f6a1..d346b9d 100644 --- a/lib/ui/webdav_settings.dart +++ b/lib/ui/webdav_settings.dart @@ -99,7 +99,7 @@ List davServerUsers(AppSettings s, String id) { /// WebDAV 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null). Future editDavServer(BuildContext context, AppController c, {DavServer? old}) async { // 127: 저장된 비밀번호가 칸에 채워지므로 (눈 버튼으로 보임) 먼저 마스터 비밀번호 (정해 두었으면) - if (old != null && old.password.isNotEmpty && !await SecretGate.pass(force: true)) return null; + if (old != null && !await SecretGate.passIf(old.password.isNotEmpty, force: true)) return null; if (!context.mounted) return null; final name = TextEditingController(text: old?.name ?? ''); final url = TextEditingController(text: old?.url ?? 'https://'); diff --git a/test/remote_registry_contract_test.dart b/test/remote_registry_contract_test.dart index 46b35d8..c5943f7 100644 --- a/test/remote_registry_contract_test.dart +++ b/test/remote_registry_contract_test.dart @@ -96,4 +96,19 @@ void main() { } expect(asked, 0); }); + + test('SecretGate.passIf: 비밀 값이 있을 때만 묻고 그 답을 따름 · 없으면 묻지 않고 지나감 · force 를 넘김', () async { + final asked = []; + var answer = false; + SecretGate.check = (force) async { + asked.add(force); + return answer; + }; + expect(await SecretGate.passIf(false), isTrue); + expect(asked, isEmpty); + expect(await SecretGate.passIf(true), isFalse); + answer = true; + expect(await SecretGate.passIf(true, force: true), isTrue); + expect(asked, [false, true]); + }); }