import 'dart:io'; import 'dart:typed_data'; import 'package:halkyon_content/halkyon_content.dart'; import 'package:halkyon_learn_agent/src/agent_home.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; /// Where the agent keeps its token, keys and data (Halkyon's ADR 0044, /// D50): a folder per instance only its user may enter, files only it may /// read, and nothing read that others could have read or changed. void main() { late Directory root; setUp(() async { root = await Directory.systemTemp.createTemp('agent_home'); addTearDown(() => root.delete(recursive: true)); }); int modeOf(String path) => FileStat.statSync(path).mode & 0x1ff; test( 'each instance has a folder of its own, its user alone entering it', () async { final learn = await AgentHome.open( root, Uri.parse('https://learn.halkyon.eu/'), ); final local = await AgentHome.open( root, Uri.parse('http://localhost:8080'), ); expect(p.basename(learn.folder.path), 'learn.halkyon.eu'); expect(p.basename(local.folder.path), 'localhost_8080'); expect(modeOf(learn.folder.path), 0x1c0); // 0700 }, ); test('the token is kept for its user alone, and read back', () async { final home = await AgentHome.open(root, Uri.parse('https://halkyon.li')); expect(await home.token(), isNull); await home.keepToken('hlk_agent_secret\n'); expect(await home.token(), 'hlk_agent_secret'); expect(modeOf(p.join(home.folder.path, 'token')), 0x180); // 0600 }); test( "a token, keys or folder others may read or change isn't read", () async { final home = await AgentHome.open(root, Uri.parse('https://halkyon.li')); await home.keepToken('secret'); await Process.run('chmod', ['644', p.join(home.folder.path, 'token')]); await expectLater(home.token(), throwsA(isA())); await Process.run('chmod', ['600', p.join(home.folder.path, 'token')]); await Process.run('chmod', ['755', home.folder.path]); await expectLater( AgentHome.open(root, Uri.parse('https://halkyon.li')), throwsA(isA()), ); }, ); test( 'the keys are kept for its user alone, and read back as they were', () async { final home = await AgentHome.open(root, Uri.parse('https://halkyon.li')); expect(await home.keys(), isNull); final keys = UnlockedAccountKeys( encryptionPublicKey: Uint8List.fromList(List.filled(32, 1)), encryptionSecretKey: Uint8List.fromList(List.filled(32, 2)), signingPublicKey: Uint8List.fromList(List.filled(32, 3)), signingSecretKey: Uint8List.fromList(List.filled(64, 4)), ); await home.keepKeys(keys); expect(await home.keys(), keys); expect(modeOf(p.join(home.folder.path, 'keys.json')), 0x180); }, ); test('notes once the register holds its public keys', () async { final home = await AgentHome.open(root, Uri.parse('https://halkyon.li')); expect(await home.keysSet(), isFalse); await home.noteKeysSet(); expect(await home.keysSet(), isTrue); }); test('an instance is reached over HTTPS, or plain HTTP on this machine ' 'alone', () async { await expectLater( AgentHome.open(root, Uri.parse('http://halkyon.li')), throwsA(isA()), ); await expectLater( AgentHome.open(root, Uri.parse('ftp://halkyon.li')), throwsA(isA()), ); }); }