Wdrażanie DDD: użytkownicy i uprawnienia

16

Pracuję nad małą aplikacją, próbując zrozumieć zasady projektowania opartego na domenie. Jeśli się powiedzie, może to być projekt pilotażowy dla większego projektu. Staram się podążać za książką „Implementing Domain-Driven Design” (autor: Vaughn Vernon) i próbuję wdrożyć podobne, proste forum dyskusyjne. Sprawdziłem również próbki IDDD na github. Mam pewne trudności z przyjęciem Tożsamości i dostępu do mojej sprawy. Pozwól, że podam kilka podstawowych informacji:

  • (Mam nadzieję) rozumiem uzasadnienie oddzielenia logiki użytkowników i uprawnień: jest to domena wspierająca i inny kontekst ograniczony.
  • W domenie podstawowej nie ma użytkowników, tylko autorzy, moderatorzy itp. Są oni tworzeni, docierając do kontekstu tożsamości i dostępu za pomocą usługi, a następnie tłumacząc otrzymane obiekty użytkownika na i moderatora.
  • Operacje w domenie są wywoływane z powiązaną rolą jako parametrem: np .:

    ModeratePost( ..., moderator);

  • Metoda obiektu domeny sprawdza, czy dana instancja Moderator nie ma wartości Null (instancja Moderator będzie pusta, jeśli użytkownik zapytany z kontekstu Tożsamość i Dostęp nie ma roli Moderator).

  • W jednym przypadku dokonuje dodatkowej kontroli przed zmianą posta:

    if (forum.IsModeratedby(moderator))

Moje pytania to:

  • W tym drugim przypadku obawy dotyczące bezpieczeństwa nie są ponownie mieszane w domenie podstawowej? Wcześniej książki stwierdzają „z kim można opublikować temat lub na jakich warunkach jest to dozwolone. Forum musi tylko wiedzieć, że autor robi to teraz”.

  • Implementacja oparta na rolach w książce jest dość prosta: gdy Moderator jest domeną podstawową, próbuje przekonwertować bieżącego ID użytkownika na instancję Moderatora lub Autor, gdy tego potrzebuje. Usługa odpowie odpowiednią instancją lub wartością null, jeśli użytkownik nie ma wymaganej roli. Nie widzę jednak, jak mogę dostosować to do bardziej złożonego modelu bezpieczeństwa; nasz obecny projekt, dla którego pilotuję, ma dość złożony model z grupami, listami kontroli dostępu itp.

Nawet w przypadku reguł, które nie są zbyt skomplikowane, takich jak: „Post powinien być edytowany tylko przez jego właściciela lub redaktora”, to podejście wydaje się załamać, a przynajmniej nie widzę właściwego sposobu na jego wdrożenie.

Pytanie o kontekst Tożsamość i dostęp do instancji OwnerOrEditor nie wydaje się właściwe, a ja kończyłbym coraz większą liczbą klas związanych z bezpieczeństwem w domenie podstawowej. Ponadto musiałbym przekazać nie tylko identyfikator użytkownika, ale identyfikator chronionego zasobu (identyfikator wpisu, forum itp.) Do kontekstu bezpieczeństwa, który prawdopodobnie nie powinien dbać o te rzeczy (czy to prawda? )

Wyciągając uprawnienia do podstawowej domeny i sprawdzając je w metodach obiektów domeny lub w usługach, skończyłem na pierwszym: mieszanie problemów związanych z bezpieczeństwem z domeną.

Czytałem gdzieś (i zgadzam się z tym), że te rzeczy związane z uprawnieniami nie powinny być częścią domeny podstawowej, chyba że bezpieczeństwo i uprawnienia są samą domeną rdzeniową. Czy prosta reguła, taka jak podana powyżej, uzasadnia uczynienie bezpieczeństwa częścią podstawowej domeny?

LittlePilgrim
źródło
Być może znajdziesz tutaj to, czego potrzebujesz: stackoverflow.com/a/23485141/329660 Również fakt, że kontekst kontroli dostępu wie o identyfikatorze zasobu , nie oznacza, że ​​ma on wiedzę o domenie na temat tego, jakim rodzajem jest dany zasób lub czym jest robi.
guillaume31
Dzięki, widziałem ten post wcześniej, moim problemem jest dokładnie to, co napisano na końcu: chciałbym przenieść kontrolę dostępu z mojej podstawowej domeny, ale wydaje mi się, że moja implementacja uderzyła w ścianę. Jednak twoja sugestia dotycząca identyfikatora zasobu ma sens: ponieważ nie używam pojęcia użytkownika lub roli w domenie podstawowej, ale konkretnych ról, być może mógłbym użyć koncepcji zasobu w zabezpieczeniu BC i odwzorować je na powiązane konkretne koncepcja domeny. Warto spróbować, dzięki!
LittlePilgrim
Czy próbki kodu w linku nie odpowiadają przynajmniej „Nie widzę, jak mogę to dostosować do bardziej złożonego modelu bezpieczeństwa” ?
guillaume31
Mój problem nie dotyczy implementacji samego modelu bezpieczeństwa, nie widzę, jak mam odwzorować te bardziej skomplikowane reguły w domenie. Jak powinno się zmienić Użytkownik -> Autor mapowania, jeśli nie jest to prosty model oparty na rolach po stronie bezpieczeństwa? Przekazywanie identyfikatorów zasobów do innego kontekstu może działać, na przykład, HasPermissionToEdit(userId, resourceId)ale nie czuję się dobrze, aby zanieczyszczać logikę domeny tymi wywołaniami. Prawdopodobnie powinienem je sprawdzić w metodach obsługi aplikacji przed wywołaniem logiki domeny?
LittlePilgrim
Oczywiście powinno to być w usługach aplikacyjnych ... Myślałem, że jest to jasne z części kodu jak UserService @AccessControlList[inf3rno]w odpowiedzi, z którą się łączyłem.
guillaume31

Odpowiedzi:

6

Czasami trudno jest odróżnić rzeczywiste reguły kontroli dostępu od niezmienników domeny graniczących z kontrolą dostępu.

W szczególności reguły, które zależą od danych dostępnych tylko w dalszej części logiki domeny, mogą nie być łatwe do wydobycia z domeny. Zwykle kontrola dostępu jest wywoływana przed lub po wykonaniu operacji domeny, ale nie w trakcie.

assert (forum.IsModeratedBy(moderator))Przykład Vaughna Vernona prawdopodobnie powinien być poza domeną, ale nie zawsze jest to wykonalne.

Musiałbym przekazać nie tylko identyfikator użytkownika, ale także identyfikator chronionego zasobu (identyfikator posta, forum itp.) Do kontekstu bezpieczeństwa, który prawdopodobnie nie powinien się tym przejmować (czy to prawda?)

Jeśli istnieje Security BC i chcesz, aby obsługiwał tę logikę, nie musi wiedzieć, czym jest forum, ale:

  • Może po prostu mieć wiedzę na temat takich pojęć, jak „moderowane przez” i odpowiednio przyznawać lub odmawiać praw dostępu.
  • Możesz mieć logikę adaptera, która subskrybuje zdarzenia Core Domain i tłumaczy je na proste pary klucz-wartość (zasób, autoryzowani użytkownicy), aby program Security BC mógł przechowywać i używać.
guillaume31
źródło
Ponieważ obie odpowiedzi są przydatne i mniej więcej wskazują na ten sam kierunek, poparłem je oba. Zaakceptowałem ten, ponieważ @ guillaume31 mniej więcej odpowiedział na moje pytanie dotyczące implementacji Vernona i będę kontynuować moją implementację w oparciu o jego wskazówkę dotyczącą korzystania z zasobów w Security BC.
LittlePilgrim
Muszę powiedzieć, że uważam to za zupełne przeciwieństwo mojej odpowiedzi.
Ewan
1
Być może jestem teraz zbyt zdezorientowany, ale moja interpretacja brzmiała (dla obu odpowiedzi): 1. Trzymaj obawy dotyczące bezpieczeństwa poza domeną i używaj zabezpieczeń BC jako usługi 2. Zadzwoń do usługi przed wywołaniem jakichkolwiek obiektów domeny 3. Usługa wykona mapowanie od użytkowników / acls do moderatorów, autorów itp. moderator = securityService.GetModerator(userId, forumId) 4. Logika domeny zostanie zaimplementowana w tych obiektach, jak w moderator.EditPost () 5. Metody takie jak EditPost nie będą wiedzieć o koncepcjach bezpieczeństwa, nie będzie żadnych dodatkowych kontroli tam
LittlePilgrim
Wciąż szukam odpowiedzi / wskazówek na ten temat, ale odkryłem, że wszelka logika autoryzacji, która opiera się na bieżącym stanie obiektu (na przykład, jeśli jest on obecnie przypisany do moderatora), jest w rzeczywistości logiką biznesową, która należy do twoją domenę, a ponadto, jeśli nie ma jej w domenie, ryzykujesz, że staniesz się nieważny, jeśli model będzie można aktualizować jednocześnie. Na przykład, jeśli zweryfikujesz własność za pomocą zasad, a następnie przejdziesz do aktualizacji tego obiektu - w wielu domenach własność może ulec zmianie i działanie może już nie być prawidłowe.
Jordan
O ile nie masz bardzo złożonego kontekstu współpracy, prawdopodobnie możesz sobie pozwolić na wdrożenie tutaj optymistycznego modelu współbieżności przy użyciu wersjonowania, ale jeśli twoje kontrole nie są wykonywane w obrębie konkretnej instancji agregującej lub przynajmniej w odniesieniu do konkretnej instancji agregującej, wówczas kontrole mogą nie być spójne z rzeczywistym stan obiektu do czasu utrwalenia zmian.
Jordan
5

Uwierzytelnianie i autoryzacja to zły przykład dla DDD.

Żadna z tych rzeczy nie jest częścią Domeny, chyba że Twoja firma tworzy produkty bezpieczeństwa.

Wymaganiem biznesowym lub domeny jest lub powinno być „Wymagam uwierzytelnienia opartego na rolach”

Następnie sprawdź rolę przed wywołaniem funkcji domeny.

Jeśli masz złożone wymagania, takie jak „Mogę edytować własne posty, ale nie inne”, upewnij się, że Twoja domena dzieli funkcję edycji na, EditOwnPost()i EditOthersPost()że masz prostą funkcję do mapowania ról

Możesz także podzielić funkcjonalność na Obiekty Domeny, takie jak Poster.EditPost()i Moderator.EditPost()jest to bardziej podejście OOP, chociaż twój wybór może zależeć od tego, czy twoja metoda jest w Usłudze Domeny, czy Obiektu Domeny.

Bez względu na to, czy zdecydujesz się oddzielić kod, mapowanie ról nastąpi poza domeną. na przykład jeśli masz kontroler webapi:

PostController : ApiController
{
    [Authorize(Roles = "User")]
    public void EditOwnPost(string postId, string newContent)
    {
        this.postDomainService.EditOwnPost(postId, string newContent);
    }

    [Authorize(Roles = "Moderator")]
    public void EditOtherPost(string postId, string newContent)
    {
        this.postDomainService.EditOtherPost(postId, string newContent);
    }
}

Jak widać, chociaż rola mapowanie odbywa się na warstwie hosting, kompleks logika tego, co stanowi edycji własnego lub innych post jest częścią domeny.

Domena rozpoznaje różnicę działań, ale wymóg bezpieczeństwa polega po prostu na tym, że „funkcjonalność może być ograniczona rolami” .

Być może jest to wyraźniejsze w przypadku separacji obiektów domeny, ale zasadniczo sprawdzasz metodę konstruującą obiekt zamiast metody wywołującej metodę usługi. Twój wymóg, jeśli nadal chcesz wyrazić to jako część domeny, stałby się „tylko moderatorzy mogą zbudować obiekt moderatora”

Ewan
źródło
4
Sprawdzanie roli „statycznie” jest trochę uproszczone. Co się stanie, jeśli moderatorowi nie wolno edytować postu innego moderatora? Czy to sprawdzenie nie powinno należeć do domeny?
Réda Housni Alaoui
2
@ RédaHousniAlaoui Zastanawiam się również nad tym. Nie mogę wymyślić innego sposobu na poradzenie sobie z tym, niż włączenie wzmianki o użytkownikach / moderatorach w domenie lub wykonanie logiki w tym ApiController, aby uzyskać rolę autora postu. Żadne z tych nie wydaje się słuszne i jest to dość powszechne w moim doświadczeniu, że pewne jasne wskazówki byłyby niezwykle pomocne.
Jimmy
1
@Erwan, przypadek użycia, o którym mówię, jest dynamiczny. Oparcie zdania „Uwierzytelnianie i autoryzacja to zły przykład dla DDD” na przykładach hello world jest nieuczciwe. DDD jest tutaj, aby uniknąć przypadkowej złożoności i umożliwić zarządzanie złożonością domeny. Uprawnienia dynamiczne nie są przypadkową złożonością ani czymś, co nie dzieje się w prawdziwym życiu.
Réda Housni Alaoui
1
IMHO, problem z twoim rozwiązaniem polega na tym, że nie zadowala klienta. Klient często chce móc dynamicznie zmieniać te relacje. Co więcej, dzieje się tak również wtedy, gdy dostawca zapewnia to samo oprogramowanie dla przedsiębiorstw różnym firmom. Jeśli oprogramowanie jest słabo przystosowalne, sprzedawca ostatecznie umrze.
Réda Housni Alaoui
1
„Ale jest to ogólnie uznawane za„ złą rzecz ”, z czasem twoje ustawienia zabezpieczeń stają się niemożliwe do zarządzania, co w efekcie oznacza, że ​​twoja aplikacja staje się niepewna”. Przy prawidłowym projekcie i testowaniu jest to całkowicie możliwe do zarządzania. Ale z mojego XP, aby stworzyć poprawny projekt, domena musi sprawdzić uprawnienia. Alternatywą jest utopia.
Réda Housni Alaoui