SecretGate.passIf: "비밀 값이 있으면 문을 지나야 함" 을 한 곳에서 (관리자 10/24)
- SecretGate.passIf(hasSecret, force): 비밀 값이 없으면 묻지 않고 지나감, 있으면 pass 의 답 - 모은 곳 (같은 꼴의 복사 9곳): 원격 등록 requireSecretGate · 플레이어 (WebDAV 머리) · 그림 AI (API 키) · WebDAV · FTP · SFTP · SMB 설정 · AI 서비스 설정 (저장된 비밀 값이 있는 것을 고치기 전). 오류 종류 · 닫기는 그대로 - 그대로 둔 것 (같은 꼴 아님): 사용자가 직접 누른 일 앞에서 다시 묻는 8곳 (탐색기 · 폴더 고르기 · 다시 시도 · 보관함 · 자막 찾기 등), OpenSubtitles (비밀 값 조건 없이 늘 문 - 따로 보고) - 약속 시험 한 줄 (remote_registry_contract: 묻는지 · 답 · force) 사용자가 보게 될 동작: 바뀌는 것 없음 확인: analyze 0 · 단위 1498 ×2 · 통합 6/6 ×2 · base10 214장 0px Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>modular
parent
c27eb9b59e
commit
e93b0b0be6
|
|
@ -75,5 +75,5 @@ class RemoteRegistry<S, C extends RemoteClient> {
|
|||
|
||||
/// 문: 비밀 값이 있으면 마스터 확인을 지나야 한다 (못 지나면 연결 전에 [DavLockedException]). 비밀 값이 없으면 묻지 않는다
|
||||
Future<void> requireSecretGate(bool hasSecret) async {
|
||||
if (hasSecret && !await SecretGate.pass()) throw DavLockedException(tr(secretGateMessage));
|
||||
if (!await SecretGate.passIf(hasSecret)) throw DavLockedException(tr(secretGateMessage));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ class SecretGate {
|
|||
|
||||
/// 써도 되면 true
|
||||
static Future<bool> pass({bool force = false}) => check?.call(force) ?? Future.value(true);
|
||||
|
||||
/// 비밀 값이 있을 때만 문을 지나야 한다 ([hasSecret] 가 false 면 묻지 않고 true). 저장된 비밀 값을 쓰기 · 보이기 · 고치기 전에 부른다.
|
||||
/// 원격 등록 · 플레이어 · 그림 AI · 서버 · 서비스 설정이 함께 쓴다 (관리자 10/24 - 같은 꼴의 복사를 한 곳으로)
|
||||
static Future<bool> passIf(bool hasSecret, {bool force = false}) => hasSecret ? pass(force: force) : Future.value(true);
|
||||
}
|
||||
|
||||
/// 마스터 비밀번호를 넣지 않아 막혔을 때의 안내
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class RemoteImageEngine implements ImageAiEngine {
|
|||
Future<List<GeneratedImage>> 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<List<int>> images = switch (service.kind) {
|
||||
|
|
|
|||
|
|
@ -468,7 +468,7 @@ String _kindName(String kind) => switch (kind) {
|
|||
|
||||
/// 서비스 추가 · 고치기. 저장된 API 키가 칸에 채워지므로 고칠 때는 먼저 마스터 비밀번호 (127 과 같음)
|
||||
Future<void> 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://');
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ Future<void> trustFtpCert(AppController c, String id, FtpCertException e) =>
|
|||
|
||||
/// FTP 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null)
|
||||
Future<FtpServer?> 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 ?? '');
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ Future<void> trustSftpHostKey(AppController c, String id, SftpHostKeyException e
|
|||
/// SFTP 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null)
|
||||
Future<SftpServer?> 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 ?? '');
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ Future<bool> deleteSmbServer(BuildContext context, AppController c, SmbServer s)
|
|||
|
||||
/// SMB 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null). [from]: 경로 입력에 넣은 smb:// · \\서버\공유 (채워 둠)
|
||||
Future<SmbServer?> 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 ?? '');
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ List<String> davServerUsers(AppSettings s, String id) {
|
|||
/// WebDAV 서버 추가 · 고치기 창. 저장한 서버를 돌려준다 (취소면 null).
|
||||
Future<DavServer?> 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://');
|
||||
|
|
|
|||
|
|
@ -96,4 +96,19 @@ void main() {
|
|||
}
|
||||
expect(asked, 0);
|
||||
});
|
||||
|
||||
test('SecretGate.passIf: 비밀 값이 있을 때만 묻고 그 답을 따름 · 없으면 묻지 않고 지나감 · force 를 넘김', () async {
|
||||
final asked = <bool>[];
|
||||
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]);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue