using System.Net; using System.Net.Http.Json; using Flaro.Api.Endpoints; namespace Flaro.Api.Tests; /// Adminrecht geben und nehmen — und der eine, dem es niemand nimmt. /// /// /// Worum es hier wirklich geht: ein Server ohne jeden Administrator. /// Sobald Administratoren einander das Recht entziehen können, gibt es diesen /// Zustand — und aus ihm führt kein Weg zurück ausser dem an der Datenbank. /// Jeder Test hier prüft eine der Türen, durch die man hineingeraten könnte. /// public class BesitzerTests { [Test] public async Task Wer_einrichtet_ist_Besitzer() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var ich = await alsAdmin.GetFromJsonAsync("/api/v1/users/me"); Assert.Multiple(() => { Assert.That(admin.User.IsOwner, Is.True, "Schon die Anmeldung muss es sagen."); Assert.That(ich!.IsOwner, Is.True); }); } [Test] public async Task Aus_einem_Benutzer_wird_ein_Administrator_und_zurueck() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang"); var vorher = await gast.GetAsync("/api/v1/users"); (await alsAdmin.PutAsJsonAsync($"/api/v1/users/{await KennungVon(alsAdmin, "gast")}/administrator", new RechtRequest(true))).EnsureSuccessStatusCode(); // **Die Rolle wird je Anfrage frisch gelesen**, nicht aus dem Token — // der Gast braucht sich also nicht neu anzumelden. var danach = await gast.GetAsync("/api/v1/users"); (await alsAdmin.PutAsJsonAsync($"/api/v1/users/{await KennungVon(alsAdmin, "gast")}/administrator", new RechtRequest(false))).EnsureSuccessStatusCode(); var wiederZu = await gast.GetAsync("/api/v1/users"); Assert.Multiple(() => { Assert.That(vorher.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); Assert.That(danach.StatusCode, Is.EqualTo(HttpStatusCode.OK), "Das Recht muss sofort gelten, ohne neue Anmeldung."); Assert.That(wiederZu.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); }); } [Test] public async Task Dem_Besitzer_nimmt_niemand_das_Adminrecht() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var zweiter = await server.NeuerBenutzer(alsAdmin, "zweiter", "zweiterpasswortlang"); var zweiteKennung = await KennungVon(alsAdmin, "zweiter"); await alsAdmin.PutAsJsonAsync($"/api/v1/users/{zweiteKennung}/administrator", new RechtRequest(true)); // Auch ein zweiter Administrator kommt an den Besitzer nicht heran. var versuch = await zweiter.PutAsJsonAsync( $"/api/v1/users/{admin.User.Id}/administrator", new RechtRequest(false)); var geloescht = await zweiter.DeleteAsync($"/api/v1/users/{admin.User.Id}"); Assert.Multiple(() => { Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); Assert.That(geloescht.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); }); Assert.That(await versuch.Content.ReadAsStringAsync(), Does.Contain("Besitzer")); } [Test] public async Task Das_eigene_Adminrecht_nimmt_man_sich_nicht_selbst() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var zweiter = await server.NeuerBenutzer(alsAdmin, "zweiter", "zweiterpasswortlang"); var zweiteKennung = await KennungVon(alsAdmin, "zweiter"); await alsAdmin.PutAsJsonAsync($"/api/v1/users/{zweiteKennung}/administrator", new RechtRequest(true)); var selbst = await zweiter.PutAsJsonAsync( $"/api/v1/users/{zweiteKennung}/administrator", new RechtRequest(false)); var selbstWeg = await zweiter.DeleteAsync($"/api/v1/users/{zweiteKennung}"); Assert.Multiple(() => { Assert.That(selbst.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); Assert.That(selbstWeg.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); }); } [Test] public async Task Der_Besitz_geht_nur_vom_Besitzer_und_nur_an_einen_Administrator() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang"); var gastKennung = await KennungVon(alsAdmin, "gast"); // Noch kein Administrator — also noch kein Besitzer. var zuFrueh = await alsAdmin.PostAsync($"/api/v1/users/{gastKennung}/owner", null); await alsAdmin.PutAsJsonAsync($"/api/v1/users/{gastKennung}/administrator", new RechtRequest(true)); // Der Beschenkte kann sich den Besitz nicht selbst nehmen. var selbstGenommen = await gast.PostAsync($"/api/v1/users/{gastKennung}/owner", null); var uebergeben = await alsAdmin.PostAsync($"/api/v1/users/{gastKennung}/owner", null); var alle = await alsAdmin.GetFromJsonAsync>("/api/v1/users"); Assert.Multiple(() => { Assert.That(zuFrueh.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); Assert.That(selbstGenommen.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest)); Assert.That(uebergeben.StatusCode, Is.EqualTo(HttpStatusCode.NoContent)); Assert.That(alle!.Count(u => u.IsOwner), Is.EqualTo(1), "Genau einer, immer."); Assert.That(alle!.Single(u => u.IsOwner).UserName, Is.EqualTo("gast")); }); } /// Ein gelöschtes Konto nimmt mit, was an ihm hing. [Test] public async Task Ein_geloeschtes_Konto_laesst_nichts_zurueck() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang"); var gastKennung = await KennungVon(alsAdmin, "gast"); // Etwas, das an ihm hängt. await gast.PostAsJsonAsync($"/api/v1/items/{Guid.CreateVersion7()}/reports", new MeldungRequest("bild", "Egal.")); (await alsAdmin.DeleteAsync($"/api/v1/users/{gastKennung}")).EnsureSuccessStatusCode(); var alle = await alsAdmin.GetFromJsonAsync>("/api/v1/users"); // Das alte Token gilt nicht mehr: die Geräte hingen am Konto. var mitAltemToken = await gast.GetAsync("/api/v1/users/me"); Assert.Multiple(() => { Assert.That(alle!.Select(u => u.UserName), Does.Not.Contain("gast")); Assert.That(mitAltemToken.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized)); }); } [Test] public async Task Rechte_vergeben_darf_nur_die_Verwaltung() { using var server = new FlaroServer(); var admin = await server.SetUpAndSignIn(); var alsAdmin = server.AlsAngemeldeter(admin.Token); var gast = await server.NeuerBenutzer(alsAdmin, "gast", "gastpasswortlang"); var gastKennung = await KennungVon(alsAdmin, "gast"); var versuch = await gast.PutAsJsonAsync( $"/api/v1/users/{gastKennung}/administrator", new RechtRequest(true)); Assert.That(versuch.StatusCode, Is.EqualTo(HttpStatusCode.Forbidden)); } private static async Task KennungVon(HttpClient alsAdmin, string name) { var alle = await alsAdmin.GetFromJsonAsync>("/api/v1/users"); return alle!.Single(u => u.UserName == name).Id; } }