W naszej funkcji skorzystamy z paczki dbms_ldap, dostępnej w bazie Oracle. Poniżej opis najistotniejszych fragmentów kodu funkcji.
Aby móc naszą funkcję wykorzystać w Custom Authentication powinna ona przyjmować dwa parametry typu varchar2 p_username i p_password oraz zwracać typ boolean
function my_ldap_auth( p_username in varchar2, p_password in varchar2 ) return booleanZakładamy, że pełna nazwa naszej domeny to my.domain.com, krótka nazwa to my, kontroler domeny stoi pod adresem 192.168.0.1, a konta użytkowników znajdują się w OU Users
ldap_host varchar2(256) := '192.168.0.1'; -- address of domain controller ldap_port varchar2(256) := '389'; -- default port of domain controller ldap_base varchar2(256) := 'OU=Users,DC=my,DC=domain,DC=com'; -- OU with Users l_dn_prefix varchar2(100) := 'my\'; -- short domain name l_dn_suffix varchar2(100) := '@my.domain.com'; -- long domain nameSprawdzamy, czy użytkownik podał hasło i odrzucamy uwierzytelnianie w przeciwnym wypadku
if p_password is null or p_password = '' then return false; end if;Otwieramy połączenie do naszego kontrolera domeny
dbms_ldap.use_exception := true; l_session := dbms_ldap.init( hostname =>ldap_host, portnum => ldap_port );Uwaga! Użytkownik uwierzytelniając się w Active Directory może użyć trzech różnych z naszego punktu widzenia loginów: UserPrincipalName, UserPrincipalName@FullDomainName oraz ShortDomainName\SamAccountName, czyli np. pawel, pawel@my.domain.com oraz my\pawp. Taką wartość będzie również posiadał item APP_USER. Musimy zatem pamiętać, aby po uwierzytelnieniu znaleźć unikalny identyfikator użytkownika w aplikacji.
Ze względów organizacyjnych w naszym systemie dopuściliśmy logowanie przy pomocy SamAccountName, czyli pawp zamiast my\pawp (dodatkowo wszystkie SamAccountName były inne niż UserPrincipalName i były 4 znakowe).
Funkcja dbms_ldap.simple_bind_s zwraca wartość 0, gdy znajdzie użytkownika o podanym Distinguished Name i poprawnym haśle. Natomiast zwraca również 0 w przypadku, gdy nie podamy hasła w ogóle! Dlatego w jednym z poprzednich kroków sprawdzaliśmy, czy hasło nie jest puste.
Kod obejmujący powyższe przypadki wygląda następująco
if length(p_username)=4 then retval := dbms_ldap.SIMPLE_BIND_S( ld => l_session , dn => l_dn_prefix||p_username , passwd => p_password ); else if instr(p_username,'@') = 0 then retval := dbms_ldap.SIMPLE_BIND_S( ld => l_session , dn => p_username|| l_dn_suffix , passwd => p_password ); else retval := dbms_ldap.SIMPLE_BIND_S( ld => l_session , dn => p_username , passwd => p_password ); end if; end if;Gdy uwierzytelnienie się powiodło, będziemy zwracać wartość true
l_authed := case when retval = 0 then true else false end;Odłączamy się od kontrolera domeny
retval := dbms_ldap.unbind_s( ld => l_session );Ostatecznie cała funkcja wygląda w następujący sposób: Kliknij tutaj, aby rozwinąć:
Zostaje jeszcze utworzyć Custom Authentication, gdzie jako funkcję uwierzytelniającą należy podać naszą funkcję my_ldap_auth.
Nie zapominajmy jeszcze o utworzeniu stosownych ACL, aby nasza baza Oracle mogła połączyć się z kontrolerem domeny: Kliknij tutaj, aby rozwinąć:
Bardzo fajny artykuł. Drobna uwaga pusty string jest tożsamy z null zatem nie trzeba dwa razy sprawdzać hasła.
OdpowiedzUsuńDziękuję za cenną uwagę. Osobiście kilka razy przejechałem się na warunkach z nullami, dlatego wolę dmuchać na zimne. Osobny warunek dla null i osobny dla pustych stringów czy zer. Pozdrawiam.
UsuńAutoryzacja z pomocą LDAP to jeszcze nie jest problem, gorzej jest kiedy najdzie nas ochota na autentykację na podstawie grup LDAP. Napisałem sobie dwa pluginy które to jakoś rozwiązują:
OdpowiedzUsuńhttps://github.com/gisprogrammer/Oracle-APEX-Authentication-and-Authorization-plugins