Blog ·
White-labelpanelen: wat de klant ziet
Je naam op een resellerpaneel zetten is de makkelijke helft. De moeilijke helft is bepalen welke schermen voor je klant überhaupt bestaan, en dat afdwingen.
Door Provibr
Verkoop je VPS'en of hosting door, dan willen je klanten uiteindelijk een paneel. Ze willen hun eigen machine om twee uur 's nachts opnieuw kunnen starten in plaats van een ticket te openen, en je wilt dat ook.
Dat wordt twee vragen die op één vraag lijken en het niet zijn. Wiens naam staat er op het paneel, en welke delen van het paneel bestaan er voor die persoon überhaupt. Het eerste is vermerken, en dat is grotendeels presentatie. Het tweede is een grens, en die verkeerd trekken is hoe een klant van jou je eigen leveranciersfactuur zit te lezen.
Vermerken houdt op waar het papierwerk begint
Een paneel opnieuw vermerken betekent de chrome veranderen: het woordmerk in de zijbalk, de paginatitel, het favicon, de naam in het accountblok. Dat is een nuttige hoeveelheid, en daar houden de meeste white-labelfuncties op.
Het interessante deel is de lijst met dingen die nooit de naam van de reseller mogen dragen.
| Scherm | Wiens naam erop staat | Waarom |
|---|---|---|
| De chrome van het paneel, titels, favicon | De reseller | De relatie waar de klant in zit |
| Privacyverklaring | Het platform | Een verwerkingsmededeling beschrijft wie er werkelijk verwerkt |
| Algemene voorwaarden | Het platform | Het is de overeenkomst van het platform, niet die van de reseller |
| Verwerkersovereenkomst | Het platform | Idem, en er staan subverwerkers in genoemd |
| Facturen en proformadocumenten | Het platform | Het is een financieel bewijsstuk van een echte transactie |
| De regel op een bankafschrift | Het platform | Een blijvend bewijsstuk dat niemand achteraf kan toelichten |
De redenering is één zin: een klant van je klant mag nooit gaan geloven dat hij een contract heeft met iemand anders dan met de partij met wie hij het werkelijk heeft. Alles met een juridisch of financieel karakter draagt de naam van de tegenpartij in dat document, en dat is geen ontwerpvoorkeur.
Wat er meer toe doet dan de regel is waar hij leeft. In onze codebase is het een lijst met bestandspaden, en een test loopt vanaf elk daarvan de transitieve importboom af, en faalt op het moment dat er één de vermerkmodule kan bereiken. Hij zoekt in de breedte, zodat de fout de kortste keten noemt, en dat is de keten die je moet doorknippen.
Een controle op rechtstreekse imports is te makkelijk te omzeilen: zet het opzoeken van het merk in een hulpfunctie, importeer die hulpfunctie, klaar. En de bewaking toetst zichzelf twee kanten op. Een genoemd pad dat verdwijnt is een fout in plaats van een stille overslag, en er wordt afzonderlijk vastgesteld dat de paneelindeling de vermerkmodule wél bereikt, want een oplosser die stilletjes niets oplost zou elk bestand groen kleuren.
De reseller logt zelf nooit in op een vermerkt paneel
Er is een tweede regel die minder voor de hand ligt, en die we verkeerd hadden gedaan zonder hem uit te spreken: vermerken geldt uitsluitend op het eigen domein van de klant.
Logt de reseller in op de host van het platform zelf, dan ziet hij het platform en niet zijn eigen merk. Daar leven de facturen, het abonnement en de relatie met ons. Het opzoeken van het merk valt terug op de kale variant op het moment dat een verzoek niet aan een accountdomein gebonden is, dus precies één plek beslist en geen enkel scherm hoeft iets te onthouden.
Het beheerscherm voor het merk toont een voorbeeld van de chrome die de klant ziet, in plaats van zelf vermerkt te zijn. Dat klinkt pedant tot je nagaat wat het alternatief aanleert: een reseller die zijn eigen logo boven zijn eigen factuur ziet staan, concludeert redelijkerwijs dat zijn klanten iets vergelijkbaars zien.
Een wit logo op een witte zijbalk
De achtergrond van ons paneel is wit, dus een logo met een doorzichtige achtergrond en witte inkt is onzichtbaar, en degene die het uploadt komt daar niet achter: hij kijkt ernaar in een ontwerpprogramma op een grijs werkvlak.
We hebben gemeten in plaats van geraden, door elk logo in een headless browser op de werkelijke kleur van het vlak te componeren:
| Logo | Contrast tegen het paneelvlak |
|---|---|
| Donkere inkt, doorzichtige achtergrond | 16,46:1 |
| Witte inkt, doorzichtige achtergrond | 1,00:1 |
| Witte inkt op een eigen dekkende plaat | 17,74:1 |
| Lichtgrijze inkt | 1,24:1 |
De rij met 1,00:1 betekent niet "moeilijk leesbaar". Elke pixel is gelijk aan de achtergrond. Het logo wordt getekend en er is niets te zien.
Daar kwamen twee beslissingen uit. Er zit bewust geen plaat onder het logo, want dat zou het merk van de klant hertekenen en elk normaal logo een omlijsting geven om één uitzondering te redden. En het uploadscherm waarschuwt in plaats van te weigeren, want een merk mag lichtgrijs zijn.
De meting telt inkt, geen overlap. Een logo met een grote doorzichtige marge valt over het grootste deel van zijn oppervlak samen met de achtergrond en is prima zichtbaar; wat telt is hoeveel ervan getekend wordt en hoe ver die inkt van de kleur van het vlak af ligt.
Afwezigheid is geen recht
Nu de moeilijkere helft. Welke schermen horen er voor een klant van een reseller te bestaan?
De ingeving is om dit als rechten te modelleren: geef de sub-klant een rol, vink minder aan. Dat is de verkeerde vorm, om een reden die pas aan de randen zichtbaar wordt. Rechten beantwoorden "mag dit lid dit doen". De vraag hier is "bestaat dit scherm op dit domein", en die twee vallen uit elkaar op het moment dat een accounteigenaar inlogt om zijn eigen klantpaneel te bekijken. Hij is de eigenaar. Elke rechtencontrole wuift hem door.
Het model is dus een omkering en geen recht. Een vaste lijst zegt welke oppervlakken op een accountdomein afwezig zijn: facturatie, accountinstellingen, tickets, agents, integraties, het activiteitenlog van het account, het topologieoverzicht, het platformbeheer, en de pagina's voor aanmelden en wachtwoord vergeten. Afwezig voor iedereen, de eigenaar inbegrepen.
Drie daarvan hebben een verantwoording nodig, want de reden is niet de voor de hand liggende.
Tickets zijn afwezig omdat een klant van een reseller een supportrelatie heeft met de reseller, en niet met ons. Bestond dat scherm wel, dan zou hij een antwoord lezen dat een bedrijf noemt dat volgens zijn eigen paneel niet bestaat.
Het activiteitenlog is afwezig omdat het het log van het account is en niet dat van de lezer. De eerlijke uitkomst is geen gefilterd log maar "dit scherm bestaat hier niet". Het activiteitentabblad per server blijft staan, want dat is werkelijk afgebakend op een machine die hij heeft.
Het topologieoverzicht bewijst het model. Het draait op de servermodule, en de servermodule is nu juist het enige dat een sub-klant wél heeft. Een rechtenpoort zou hier helemaal niets uitfilteren.
Drie sluitingen, want een pagina is geen grens
Eén controle is niet genoeg, en elk van de drie vangt wat de andere niet kunnen.
De eerste zit in de rechtenfunctie zelf, waar de volgorde van twee regels het hele punt is:
- Bestaat deze module op dit oppervlak? Zo nee, weiger.
- Heeft deze rol volledige toegang? Zo ja, sta toe.
De oppervlaktetoets vooropzetten is wat "bestaat niet" laat winnen van "mag alles", en het is wat de omkering overal laat gelden in plaats van alleen in de schermen waar iemand aan gedacht heeft.
De tweede is een bewaking in elke pagina onder die paden, die een gewone 404 oplevert. Hij neemt bewust geen argument: een aanroep die zijn eigen scherm zou noemen, zou van een tikfout een stilletjes openstaande deur maken.
De derde zit in de reverse proxy, want een pagina kan geen verzoek vangen dat nooit bij een pagina aankomt. Een POST naar een afwezig pad wordt geweigerd voordat er paginacode draait. Navigaties worden doorgelaten, zodat de 404 van de applicatie zelf verschijnt in plaats van een kale proxyfout.
De cookie is een browserregel, geen serverregel
Dit is de bevinding die het meenemen waard is, ook als je niets van het bovenstaande bouwt.
Onze sessiecookies zijn host-only. Geen Domain-attribuut, dus een sessie op het ene klantdomein wordt niet naar een ander gestuurd, en ook niet naar de host van het platform. Dat klopt en het is getest, inclusief het negatieve geval waarin het aanzetten van cookies over subdomeinen heen een Domain laat verschijnen.
Het is ook geen grens. De sub-klant heeft zijn eigen sessietoken in handen en kan het met een HTTP-client op de opdrachtregel sturen waarheen hij wil. Alleen een browser zou geweigerd hebben.
We hebben gemeten wat dat toestond, en het was erger dan verwacht: een sub-klant kon een gloednieuwe organisatie aanmaken met zichzelf als eigenaar, compleet met een eigen proefabonnement, via twee onafhankelijke wegen door de code, bereikbaar vanaf vier verschillende ingangen. Vier organisaties, achteraf nageteld in de database. Op zijn eigen domein werd hetzelfde verzoek door de proxy geweigerd, dus de deur zat aan de ene kant dicht en stond aan de andere kant open.
De reparatie was niet om aan die twee wegen controles toe te voegen. Het was om de grens te leggen op het ene punt waar elk geauthenticeerd verzoek langskomt, en de invariant "een sub-klant wordt nooit eigenaar van een organisatie" in de ene functie die alle aanmaakwegen delen.
Dezelfde ronde vond de algemene versie van het probleem ergens anders. Een API-sleutel draagt een rol maar geen oppervlak. Dat was niet uit te buiten, want het tabblad waar een sleutel wordt aangemaakt bestaat niet op een accountdomein. Maar dat is een verborgen knop en geen grens, en het verschil doet ertoe op de dag dat iemand een tweede manier toevoegt om sleutels aan te maken. De reparatie weigert de principaal in plaats van de sleutel te merken.
Geen kolom zegt "dit is een sub-klant"
Nog één ontwerpnotitie, want het is het soort beslissing dat zich stilletjes terugbetaalt.
Een sub-klant is een gewone gebruikersrij met een gereserveerd e-mailadres van de vorm gebruikersnaam@<organisatie-id>.invalid, plus een gewone lidmaatschapsrij met een smal rechtenprofiel. Er is met opzet geen booleaanse kolom die hem markeert: een tweede bron van waarheid kan uit de pas raken met het adres, en dan zijn twee delen van het systeem het oneens over wie iemand is.
Omdat het organisatie-ID in het adres zit, komt de uniciteit gratis uit de index die er toch al stond, en kan dezelfde gebruikersnaam onder twee resellers bestaan. Het inlogformulier vraagt om een gebruikersnaam en haalt het organisatie-ID uit de host in plaats van uit het formulier, dus niemand logt in op een ander account door een aanduiding te raden.
En zijn adres wordt nooit als geverifieerd gemarkeerd, want niemand kan het verifiëren. Die kolom betekent "dit adres hoort aantoonbaar bij deze persoon", en hem op waar zetten voor een adres dat geen enkele mailbox bereikt zou de leugen naar een minder zichtbare plek verplaatsen.
Wat het niet doet
Opnieuw vermerken dekt de chrome. De teksten in de interface blijven lezen als ons product, in welke taal de bezoeker ook gebruikt, en doen alsof dat anders is zou betekenen dat we het vocabulaire van een reseller namens hem in negen talen vertalen. De lijst met afwezige oppervlakken is een beslissing over de vorm van het product en op zichzelf geen beveiligingsmodel: hij werkt doordat de drie sluitingen erachter werken.
Zo werken accountdomeinen bij Provibr, waar de agent op je eigen hardware draait en uitsluitend uitgaand verbindt.