Bezpieczeństwo w PASOE cz. IV – LDAP, domeny i CP
W poprzednim artykule wyszliśmy poza technologię Progress Software i dzięki Apache Directory Studio przeprowadziliśmy uwierzytelnienie użytkowników korzystających z restowej aplikacji PASOE.
Pora wykonać następny krok (naprzód!) i uwzględnić w całym procesie token – obiekt CLIENT-PRINCIPAL. Obiekt ten został wprowadzony w OE10, wraz z pojawieniem się technologii Auditing. Przypomnę, że chodziło o rejestrowanie zmian w systemie bazodanowym i niezbędnym było posiadanie informacji, kto te zmiany wprowadza. Domyślnie, auditing korzysta z wartości user-id użytkownika, który jest zdefiniowany w bazie.
Jednakże w kontekście aplikacji bardziej odpowiedni jest identyfikator użytkownika sesji OpenEdge. Dzięki takiemu identyfikatorowi – tokenowi, aplikacja nie musi sprawdzać za każdym razem gdy jest to konieczne, kto jest aktualnie zalogowany. Wszystkie potrzebne informacje są bezpiecznie przechowywane w obiekcie sesji CLIENT-PRINCIPAL. Obiekt ten może zawierać nie tylko dane użytkownika, ale dodatkowe właściwości jak np. role potrzebne w procesie autoryzacji.
CLIENT-PRINCIPAL (CP) jest szczególnie przydatny w aplikacjach rozproszonych (AppServer, PASOE) ponieważ może zarządzać kontrolą dostępu dla wielu procesów aplikacji.
Nie będę omawiał wszystkich szczegółów związanych z CP, gdyż jest to temat zbyt obszerny. Bardzo ważne jest to, że do “zapieczętowania” (seal) obiektu i późniejszego jego użycia w sesji, użytkownik musi być zdefiniowany w tzw. domenie i musi być znany klucz (Access Code) do domeny. Czyli: definiujemy domeny w bazie (nazwa domeny, klucz dostępu), a następnie definiujemy użytkownika w danej domenie. OK, to powinno nam na razie wystarczyć. Na poniższym rysunku widać narzędzie Database Administration i okno do definiowania domen ( opcja: Admin -> Security -> Domain Maintenance -> Domains…).
Teraz uwaga! użytkownicy nie muszą znajdować się w bazie, mogą być zdefiniowani w systemie LDAP, co zaraz pokażę. Definicje domen w bazie będą jednak potrzebne. Później wyjaśnię dlaczego.
Teraz jest dobry moment żeby podłączyć PASOE do serwera bazy danych.
Proszę zwrócić uwagę na dwie domeny: adminusers i webusers (opis: LDAP), które zdefiniowałem na potrzeby tego artykułu. Kodem dostępu do domen jest odpowiednio: adminpa, webpa.
Tworzymy prosty tekstowy plik zawierający pary wartości: nazwa domeny, kod dostępu. Zobaczmy taki przykładowy plik ldapasreg.csv.
Posłuży on jako parametr wejściowy do utworzenia zaszyfrowanego pliku rejestru, który może służyć do zapieczętowania tokena. W oknie znakowym podajemy komendę:
gendomreg ldapasreg.csv ldapasreg.bin.
Wygenerowany plik ldapasreg.bin kopiujemy do podkatalogu PASOE conf.
Teraz zabieramy się za modyfikację struktury w Apache Directory Studio. Po uruchomieniu instancji serwera i otwarciu połączenia dodajemy dwie grupy: OED:adminusers, OED:webusers. Jak łatwo się domyślić, to są nasze domeny. W LDAP nie ma obiektów typu domena, więc definiujemy grupy, a żeby odróżnić je od zwykłych grup dodajmy np. przedrostek. Użyłem przedrostka OED:, ponieważ oznacza OpenEdge Domain, ale można użyć dowolnego jaki nam pasuje.
W grupie OED:adminusers dodałem użytkownika Piotr Adminowy (czyli admin1), a w domenie OED:webusers użytkownika Marek Webowy (user1). Tym samym należą oni do dwóch grup (drugą jest PSCUser).
Przechodzimy do pliku: [working directory]\[instancja pasoe]\webapps\ROOT\WEB-INF\oeablSecurity.properties i dodajemy ustawienia:
## OEClientPrincipalFilter.registryFile=ABLDomainRegistry.keystore OEClientPrincipalFilter.registryFile=ldapasreg.bin ... ## Used to obtain an OE Domain name from the authenticated user's list of ## roles if the user did not supply one (overrides the default domain property) OEClientPrincipalFilter.domainRoleFilter=ROLE_OED:(.*)
Pierwszy parametr określa binarny plik, który utworzyliśmy wcześniej, drugi to prefix określający domenę. Z poprzedniego artykułu wiemy, że dla określenie roli, do nazwy grupy użytkownika zostanie dodany prefix ROLE_ (parametr ldap.authpopulator.rolePrefix). Natomiast dla określenia domeny, zostanie zidentyfikowany ciąg znaków ROLE_OED: i wszystko co nastąpi po nim, to nazwa domeny. Czyli dla użytkownika admin1 będzie to adminusers, a dla user1 webusers.
Ponieważ w pliku binarnym istnieje taka domena i kod dostępu, uwierzytelnienie powinno się udać.
Zanim przejdę dalej trzeba odpowiedzieć na jedno ważne pytanie: dlaczego informacje o domenach znajdują się w trzech miejscach: bazie danych, pliku bin, serwerze LDAP? Aby to zrozumieć musimy przypomniec sobie, że PASOE to połączenie dwóch technologii: ABL i Java (Tomcat). W Spring Security (Java) tworzony jest token, który Tomcat może zapieczętować dzięki informacjom w pliku bin. Token ten jest transformowany do CLIENT-PRINCIPAL ABL, dzięki czemu możemy korzystać z metod ABL aby np. modyfikować role, zarządzać stanem CP itd. CP nie korzysta z pliku bin, dane o domenach pobiera z bazy. Tutaj w bazie nie trzeba definiować użytkowników w domenach, są oni zdefiniowani tylko na serwerze LDAP.
Ponieważ możemy teraz korzystać z obiektu CP, dodajmy dwie procedury w konfiguracji naszej instancji PASOE, dla otwarcia sesji i rozpoczęciu żądania. Całe procedury można pobrać tutaj.
Pierwsza procedura wczytuje parametry domen z bazy.
cpStartup.p
// Loading domain registry for the session m_lOK = SECURITY-POLICY:LOAD-DOMAINS(1).
pcActivate.p weryfikuje poprawność zapieczętowania CP i wydruk jego atrybutów do logu.
cpActivate.p
// Verify if the Client-Principal is sealed successfully or not. If not, return error. IF lOK THEN DO: // Dump the Client-Principal attributes MESSAGE " ID: '" + hCP:QUALIFIED-USER-ID + "'". MESSAGE " session-id:" hCP:SESSION-ID. MESSAGE " state:" hCP:LOGIN-STATE. MESSAGE " roles: " + hCP:ROLES. MESSAGE " domain: " + hCP:DOMAIN-NAME. cList = hCP:LIST-PROPERTY-NAMES. iListSize = NUM-ENTRIES(cList, ","). IF ( 0 < iListSize ) THEN DO iListPos = 1 TO iListSize: DEFINE VARIABLE cProp AS CHARACTER NO-UNDO. DEFINE VARIABLE cVal AS CHARACTER NO-UNDO. MESSAGE " properties:". cProp = ENTRY(iListPos, cList, ","). cVal = hCP:GET-PROPERTY(cProp). MESSAGE " property:" cProp ", value:" cVal. END. // Set the Client-Principal as client to connect with DB. SET-DB-CLIENT(hCP). END.
Restartujemy teraz PASOE i wywołujemy ekran logowania: http://localhost:[port]/rest/_oepingService/_oeping
Podajemy dane: user1, user1. Dane są uwierzytelnione, ale najciekawsze informacje znajdujemy w logu agenta ldapas.agent.[data].log
Po pierwsze mamy wpis o prawidłowym wczytaniu domen z pliku cpStartup.p
Po drugie możemy odczytać parametry tokena CLIENT-PRINCIPAL wygenerowane przez plik cpActivate.p.
Widać, że pełne id użytkownika zostało uzupełnione o nazwę domeny a także, że użytkownik dostał nową rolę.
To chyba jest najbardziej zaawansowany z artykułów, które do tej pory Wam przedstawiłem. Jeśli ktoś przećwiczy cały proces – gratuluję!
W razie pytań piszcie, jak zwykle, na PUG Poland.