create(); $code = HostEnrolment::issue($host); expect($code)->toHaveLength(32); $stored = DB::table('hosts')->where('id', $host->id)->value('enrolment_code_hash'); expect($stored)->not->toBe($code) ->and($stored)->not->toContain($code); }); it('resolves a valid code to its host and consumes it', function () { $host = Host::factory()->create(); $code = HostEnrolment::issue($host); expect(HostEnrolment::claim($code)?->id)->toBe($host->id); // Verbraucht. Ein zweiter Lauf braucht einen neuen Code aus der Konsole — // eine halb installierte Maschine wird neu aufgesetzt, nicht nachgebessert. expect(HostEnrolment::claim($code))->toBeNull(); }); /** * Der Fortschritt kommt VOR der Registrierung und darf den Code deshalb nicht * verbrauchen — sonst könnte der Host sich nach seiner ersten Meldung nie mehr * registrieren. */ it('resolves a code without consuming it, for the progress reports', function () { $host = Host::factory()->create(); $code = HostEnrolment::issue($host); expect(HostEnrolment::resolve($code)?->id)->toBe($host->id) ->and(HostEnrolment::resolve($code)?->id)->toBe($host->id); // Und danach ist er immer noch einlösbar. expect(HostEnrolment::claim($code)?->id)->toBe($host->id); }); it('refuses a code past its expiry', function () { $host = Host::factory()->create(); $code = HostEnrolment::issue($host); $this->travel(25)->hours(); expect(HostEnrolment::claim($code))->toBeNull(); }); it('refuses a code that was never issued', function () { expect(HostEnrolment::claim(str_repeat('a', 32)))->toBeNull(); }); /** * Der Hub muss den Peer kennen, BEVOR der Host im Tunnel ist — sonst käme er * nie hinein und müsste seinen Schlüssel über einen öffentlichen Weg melden. * Genau das vermeidet dieser Entwurf. */ it('allocates a tunnel address and admits the peer up front', function () { $host = Host::factory()->create(['wg_ip' => null, 'wg_pubkey' => null]); HostEnrolment::issue($host); $host->refresh(); expect($host->wg_ip)->not->toBeNull() ->and($host->wg_pubkey)->not->toBeNull(); // `peers()` ist nach dem öffentlichen Schlüssel indiziert, die Werte sind // Momentaufnahmen — geprüft wird deshalb gegen die Schlüssel. expect(array_keys(app(WireguardHub::class)->peers()))->toContain($host->wg_pubkey); }); /** * Der private Schlüssel steht in der kopierten Befehlszeile und sonst nirgends. * Ihn zu speichern hieße, ein Geheimnis aufzubewahren, das nach Task 9 des * Skripts ohnehin wertlos ist — und bis dahin eine Stelle mehr, an der es liegt. */ it('returns the private key with the code and never stores it', function () { $host = Host::factory()->create(); $enrolment = HostEnrolment::issueWithKeys($host); expect($enrolment['private_key'])->not->toBeEmpty(); $row = (array) DB::table('hosts')->where('id', $host->id)->first(); foreach ($row as $value) { expect((string) $value)->not->toContain($enrolment['private_key']); } }); /** * Ein zweiter Code macht den ersten wertlos. Sonst hätte eine Maschine, die im * Rettungssystem hängengeblieben ist, weiter einen gültigen Ausweis für einen * Host, den der Betreiber gerade neu übernimmt. */ it('invalidates the previous code when a new one is issued', function () { $host = Host::factory()->create(); $first = HostEnrolment::issue($host); $second = HostEnrolment::issue($host); expect(HostEnrolment::claim($first))->toBeNull() ->and(HostEnrolment::claim($second)?->id)->toBe($host->id); });