LDAP Authentifizierung

Fragen zur Installation von CONTENIDO 4.9? Probleme bei der Konfiguration? Hinweise oder Fragen zur Entwicklung des Systemes oder zur Sicherheit?
Antworten
langwebdesign
Beiträge: 60
Registriert: So 23. Okt 2005, 14:11
Wohnort: Titting
Kontaktdaten:

LDAP Authentifizierung

Beitrag von langwebdesign » Do 6. Dez 2018, 22:42

Hallo Zusammen,

nachdem ich nirgends eine LDAP Authentifizierung für Frontend User finden konnte, habe ich mich selbst dran gemacht.

Hier ein erster Entwurf, ich habe dazu einfach die validateCredentials() in contenido/classes/auth/class.auth.handler.frontend.php editiert.

Code: Alles auswählen

    public function validateCredentials() {

        $client = cRegistry::getClientId();
        $serverFQDN = getEffectiveSetting('ldap', 'serverFQDN', "domaincontroller.domain.local");
        $userDomain = getEffectiveSetting('ldap', 'userDomain', "domain");
        $userSearchPath = getEffectiveSetting('ldap', 'userSearchPath', "dc=domain,dc=local");
        
        $adServer = "ldap://".$serverFQDN;
        $ldap = ldap_connect($adServer);
        $username = conHtmlentities(stripslashes(trim($_POST['username'])));
        $password = $_POST['password'];
        $ldaprdn = $userDomain."\\". $username;

        ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
        ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);

        $bind = @ldap_bind($ldap, $ldaprdn, $password);
    
        if ($bind) {
            $filter="(sAMAccountName=".$username.")";
            $result = ldap_search($ldap, $userSearchPath, $filter);

            ldap_sort($ldap,$result,"sn");
            $info = ldap_get_entries($ldap, $result);
            for ($i=0; $i<$info["count"]; $i++)
            {
                if($info['count'] > 1){
                    return false;    
                }
                $userDn = $info[$i]["distinguishedname"][0];
                $lastname = $info[$i]["sn"][0];
                $firstname = $info[$i]["givenname"][0];
                $memberof = $info[$i]["memberof"];
            }
            @ldap_close($ldap);
        
            // create user on logon
            // find user if exists
            $feUser = new cApiFrontendUser($idfrontenduser);
            $frontendUserColl = new cApiFrontendUserCollection();
            $frontendUserColl->select("idclient = ".$client." AND username = '".$username."'");

            if($frontenduser = $frontendUserColl->next()) {
                $idfrontenduser = $frontenduser->get("idfrontenduser");
                // remove user from all groups, see con_frontendgroupmembers
                $feUser->loadByPrimaryKey($idfrontenduser);
                if ($feUser->isLoaded() === true) {
                    $feGroups = $feUser->getGroupsForUser();
                    if(count($feGroups)>0){
                        $groupmembers = new cApiFrontendGroupMemberCollection();
                        foreach ($feGroups as $idfrontendgroup) {
                            $groupmembers->remove($idfrontendgroup, $idfrontenduser);
                        }
                    }
                }
            } else {
                // create user
                $feusers = new cApiFrontendUserCollection();
                $feUser = $feusers->create($username);
            }
            
            // add user to groups based on AD group memberships
            //convert from DN to plain groupname
            array_walk($memberof,
                function (&$item, $key) {
                    $parts = explode(",", $item);
                    $item = substr($parts[0], 3);
                }
            );
            // get all contenido groups
            $feGroupsCol = new cApiFrontendGroupCollection();
            $feGroupsCol->select("idclient = ".$client, "", "groupname ASC");
            
            // match groups
            while (($fegroup = $feGroupsCol->next()) !== false) {
                $groupname = $fegroup->get("groupname");
                $idfegroup = $fegroup->get("idfrontendgroup");
                $key = array_search($groupname, $memberof);
                // add user to contenido group
                if($key)
                {
                    $groupmembers = new cApiFrontendGroupMemberCollection();
                    $groupmembers->create($idfegroup, $idfrontenduser);
                }
            }
      
            // ldap user only for frontend access
            $this->auth['perm'] = "frontend";
            return $idfrontenduser;
        }else {
            return false;    
        }
    }
Die Funktionsweiße kurz beschrieben:
  • Benutzer und Passwort werden per LDAP geprüft - ldap_bind()
  • Benutzerdetails und Gruppenmitgliedschaften werden geladen
  • Sollte der Benutzer aus LDAP im Contenido vorhanden sein werden die Contenido Gruppenmitgliedschaften entfernt
  • Fehlende Benutzer werden angelegt
  • Die Contenido Gruppen werden mit den LDAP Gruppen verglichen, bei Übereinstimmung wird der Benutzer zur Contenido Gruppe hinzugefügt
  • Die Contenido Gruppen müssen dazu manuell mit dem gleichen Gruppennamen angelegt und Berechtigungen zugewiesen werden
  • Es muss nichts weiter an Contenido angepasst werden
  • Manuell angelegte Frontend User funktionieren nicht mehr
Das Updaten der Gruppenmitgliedschaften könnte nur alle X Tage durchgeführt werden, indem man z.B. das Benutzerfeld valid_to setzt und auswerted.

Eleganter wäre es natürlich eine eigene Klasse zu schreiben und zu registrieren um bei Updates keine Probleme zu bekommen.
Wenn jemand Ahnung hat wie man das hinbekommt, bin ich offen für Vorschläge.

Außerdem würde ich beim Benutzer gern noch ein paar infos aus LDAP ins Contenido speichern, Name, Abteilung usw.
Wie würdet ihr das machen ohne am core rumzuspielen, oder kann ich die frontendusers Tabelle einfach erweitern ohne Probleme zu bekommen?

Feedback und Verbesserungen erwünscht.
Stephan

emergence
Beiträge: 10599
Registriert: Mo 28. Jul 2003, 12:49
Wohnort: Austria
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von emergence » Fr 7. Dez 2018, 15:57

hmm...

du könntest um zusätzliche infos zu speichern die properties verwenden... so wie es die frontenduser plugins machen...
dann brauchst du die tabelle nicht modifizieren...
*** make your own tools (wishlist :: thx)

Faar
Beiträge: 1345
Registriert: Sa 8. Sep 2007, 16:23
Wohnort: Brandenburg
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von Faar » Fr 7. Dez 2018, 16:29

langwebdesign hat geschrieben:
Do 6. Dez 2018, 22:42
Eleganter wäre es natürlich eine eigene Klasse zu schreiben und zu registrieren um bei Updates keine Probleme zu bekommen.
Wenn jemand Ahnung hat wie man das hinbekommt, bin ich offen für Vorschläge.
Ich bin kein OOP-Spezialist, aber...
Du kannst die Klasse kopieren, deine Änderungen rein setzen und als cMyAuthHandlerFrontend bezeichnen.
Dann speicherst Du sie vielleicht im Frontendbereich und bindest sie im Modul mit cInlcude() ein.
Es könnte dann zu Konflikten kommen, weil zweimal Objekte gleichen Namens vorhanden wären.
Das weiß ich jetzt nicht genau, ob das ginge.

Oder schreibst eigene Methoden (Funktionen) in eine eigene Klasse und erweiterst die Klasse:

Code: Alles auswählen

class cMyAuthHandlerFrontend extends cAuthHandlerFrontend { 
public function DinGens() { 
   do your code 
} 
}
Diese eigenen Funktionen können auf Inhalte und Funktionen der Contenido Klasse zugreifen. Das bietet einige Möglichkeiten.
Außerdem würde ich beim Benutzer gern noch ein paar infos aus LDAP ins Contenido speichern, Name, Abteilung usw.
Wie würdet ihr das machen ohne am core rumzuspielen, oder kann ich die frontendusers Tabelle einfach erweitern ohne Probleme zu bekommen?
Die Frontenduser-Tabelle ist relativ harmlos aber statt die original Tabelle zu ändern, habe ich es so gemacht, dass ich die UserID nahm und diese als Referenz in eine neue Tabelle übernommen habe und dort dann meine eigenen Daten hinein geschrieben.
Dann kann man da einfach mit SQL auswählen: SELECT * FROM table WHERE userid='3'
Natürlich muss irgendein Modul dafür sorgen, dass Contenido UserID mit der in der eigenen Tabelle überein stimmt.
Das ist schon einiges mehr an Programmierung darum herum als es hier vereinfacht dargestellt wird.
Vermutlich wirst Du dich auch hiermit rumschlagen müssen: https://api.contenido.org/latest/class- ... ction.html
Fliegt der Bauer übers Dach, ist der Wind weißgott nicht schwach.

langwebdesign
Beiträge: 60
Registriert: So 23. Okt 2005, 14:11
Wohnort: Titting
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von langwebdesign » Fr 7. Dez 2018, 16:47

emergence hat geschrieben:
Fr 7. Dez 2018, 15:57
du könntest um zusätzliche infos zu speichern die properties verwenden... so wie es die frontenduser plugins machen...
Welche Plugins meinst du?
Kannst du da einen link posten?

xmurrix
Beiträge: 2646
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von xmurrix » Fr 7. Dez 2018, 16:52

Faar hat geschrieben:
Fr 7. Dez 2018, 16:29
...Oder schreibst eigene Methoden (Funktionen) in eine eigene Klasse und erweiterst die Klasse:

Code: Alles auswählen

class cMyAuthHandlerFrontend extends cAuthHandlerFrontend { 
public function DinGens() { 
   do your code 
} 
}
...
Das mit der eigenen Klasse ist eine sehr gute Idee, wir bräuchten noch in CONTENIDO eine Möglichkeit, auch die eigene Klasse anstatt die vordefinierte zu verwenden. Überall, wo die Anwendung, die Session, die Authentifizierung und die Berechtigungen initialisiert, müsste dann die eigene Klasse angegeben werden.

Also anstatt

Code: Alles auswählen

    cRegistry::bootstrap(array(
        'sess' => 'cFrontendSession',
        'auth' => 'cAuthHandlerFrontend',
        'perm' => 'cPermission'
    ));
folgendermaßen

Code: Alles auswählen

    cRegistry::bootstrap(array(
        'sess' => $cfg['bootstrap']['sess_class_name'],
        'auth' => $cfg['bootstrap']['auth_class_name'],
        'perm' => $cfg['bootstrap']['perm_class_name']
    ));
Dann bräuchten wir neue Konfigurationen, in der man die Namen der zu verwendenden Klassen definiert

Code: Alles auswählen

$cfg['bootstrap']['sess_class_name'] = 'cFrontendSession';
$cfg['bootstrap']['auth_class_name'] = 'cAuthHandlerFrontend';
$cfg['bootstrap']['perm_class_name'] = 'cPermission';
Zwei Sachen sind hier zu berücksichtigen. Wenn man seine eigenen Klassen dafür implementiert, muss man sich sehr sicher sein, was man da macht, denn man ersetzt hierbei den Schutzmechanismus des Systems gegen einen eigenen. Ist der nicht sicher genug, kann es fatale Folgen haben.
Das andere wäre die Bereitstellung der Klasses. Das System bekommt nur die Klassennamen, d. h. es weis nicht, woher es die dazu nötige Klassen-Dateien laden soll. Die Dateien der eigenen Klassen müssten im CONTENIDO Autoloader-Mechanismus registriert werden, damit sie gefunden werden können.

Gruß
xmurrix
CONTENIDO downloads: CONTENIDO 4.9.12
CONTENIDO links: Documentation, API documentation

langwebdesign
Beiträge: 60
Registriert: So 23. Okt 2005, 14:11
Wohnort: Titting
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von langwebdesign » Fr 7. Dez 2018, 16:59

Kann ich den autoloader dazu bringen meine statt der original Datei zu laden
Doku habe ich was für die 4.8 gefunden

https://docs.contenido.org/plugins/serv ... ew/3014874

aber damit muss ich mich erst beschäftigen

xmurrix
Beiträge: 2646
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von xmurrix » Fr 7. Dez 2018, 17:20

...Kann ich den autoloader dazu bringen meine statt der original Datei zu laden...
Ja, du brauchst eine Benutzerdefinierte Autoloader-Datei config.autoloader.local.php in data/config/{environment}/ hinterlegen, in der der Pfad zur deiner Klasse drin steht.

Code: Alles auswählen

<?php
return array(
    'cFrontendSession' => 'contenido/classes/class.session.php', // 'contenido/classes/class.session.php' gegen eigenen Pfad ersetzen!
);
Wichtig ist, dass die Datei innerhalb des contenido-Ordners liegen muss, das kann z. B. in ein Plugin sein.

Wenn du das so machst, kannst du auch den gleichen Klassennamen "cFrontendSession" verwenden, dann braucht man die ganze Konfiguration der Klassennamen, wie zuvor erwähnt, nicht.

Gruß
xmurrix
CONTENIDO downloads: CONTENIDO 4.9.12
CONTENIDO links: Documentation, API documentation

emergence
Beiträge: 10599
Registriert: Mo 28. Jul 2003, 12:49
Wohnort: Austria
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von emergence » Fr 7. Dez 2018, 19:34

langwebdesign hat geschrieben:
Fr 7. Dez 2018, 16:47
emergence hat geschrieben:
Fr 7. Dez 2018, 15:57
du könntest um zusätzliche infos zu speichern die properties verwenden... so wie es die frontenduser plugins machen...
Welche Plugins meinst du?
Kannst du da einen link posten?
hier zb eine beschreibung
https://docs.contenido.org/display/COND ... ntendusers
*** make your own tools (wishlist :: thx)

Oldperl
Beiträge: 3989
Registriert: Do 30. Jun 2005, 22:56
Wohnort: Franken, Bayern
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von Oldperl » Sa 8. Dez 2018, 13:32

Servus,

einfach deine eigene Klasse gleichen Namens per Plugin einbinden. Im Plugin kann man ebenfalls den Autoloader ebenfalls nutzen, wobei Klassen gleichen Namens Core-Klassen ersetzen können. Sollte zwar aus Sicherheitsgründen eigentlich nicht gehen, geht aber bei Contenido.
Nur im Backend geht das nicht mit den Auth-, Session- und Perm-Klassen, zumindest nicht vor dem Login, da dort die Plugins erst nach dem Login geladen werden, im Frontend sollte es aber problemlos funktionieren.

Gruß aus Franken

Ortwin
*NEU* PHP 7.x Community Draft von CONTENIDO 4.9 auf github
CONTENIDO 4.9 Entwickler-Handbuch - Publikation auf medium.com zu meinem angedachten Entwickler-Buch zu CONTENIDO 4.9
ConLite 2.0, alternatives und stabiles Update von Contenido 4.8.x
phpBO Search Advanced - das neue Suchwort-Plugin für CONTENIDO 4.9
Mein Entwickler-Blog

langwebdesign
Beiträge: 60
Registriert: So 23. Okt 2005, 14:11
Wohnort: Titting
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von langwebdesign » Sa 8. Dez 2018, 17:35

Hallo,
xmurrix hat geschrieben:
Fr 7. Dez 2018, 17:20
Ja, du brauchst eine Benutzerdefinierte Autoloader-Datei config.autoloader.local.php in data/config/{environment}/ hinterlegen, in der der Pfad zur deiner Klasse drin steht.
Das hat super funktioniert, Danke.

Code: Alles auswählen

<?php
return array(
    'cAuthHandlerFrontend' => 'contenido/plugins/frontend_ldap/classes/class.auth.handler.frontend.ldap.php'
);
?>
Die Idee mit dem Plugin von Ortwin gefällt mir noch besser, hab das mit diesem code in der config.plugin.php versucht (aus AMR kopiert...):

Code: Alles auswählen

$pluginClassPath = 'contenido/plugins/frontend_ldap/classes/';
cAutoload::addClassmapConfig(array(
    'cAuthHandlerFrontend' => $pluginClassPath.'class.auth.handler.frontend.ldap.php'
));
unset($pluginClassPath);
Aber damit lassen sich die Core Klassen anscheinend nicht überschreiben.
Siehe auch den Kommentar am Ende der Docu unter https://docs.contenido.org/display/CONDEVE/cAutoload

Kann ich die config.autoloader.local.php irgendwie mit meinem Plugin ausliefern und installieren lassen?

Stephan

xmurrix
Beiträge: 2646
Registriert: Do 21. Okt 2004, 11:08
Wohnort: Augsburg
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von xmurrix » So 9. Dez 2018, 10:44

Hallo langwebdesign,
langwebdesign hat geschrieben:
Sa 8. Dez 2018, 17:35
...Aber damit lassen sich die Core Klassen anscheinend nicht überschreiben.
Siehe auch den Kommentar am Ende der Docu unter https://docs.contenido.org/display/CONDEVE/cAutoload ...
man kann in Plugins zwar alle Core-Dateien überschreiben, wenn man aber vor hat, dies zur Laufzeit zu machen, ist das für viele Core-Dateien zu spät, da Plugins nach dem Initialisieren des CMS ausgefüht werden. Idealerweise erreicht man das, in dem man seine Klassen in der config.autoloader.local.php setzt, diese Konfiguration greift von Anfang an, also auch während der Initialisierung des CMS.
langwebdesign hat geschrieben:
Sa 8. Dez 2018, 17:35
...Kann ich die config.autoloader.local.php irgendwie mit meinem Plugin ausliefern und installieren lassen?...
Das Plugin-System bietet momentan keine Schnittstelle, über die man bei der Installation benutzerdefinierte Funktionen ausführen kann.

Falls die config.plugin.php schon während des Installationsprozesses eingebunden wird, könntest du deine Funktion darin integrieren, also deine Änderungen an der Datei "config.autoloader.local.php" machen, wenn das Plugin gerade installiert wird. Zum einen hast du aber das Problem, PHP-Code zu generieren oder vorhandenen PHP-Code zu ändern, zum anderen ist die config.plugin.php eigentlich nur dafür gedacht, die Konfiguration des Plugins oder Systems zu setzen.

Gruß
xmurrix
CONTENIDO downloads: CONTENIDO 4.9.12
CONTENIDO links: Documentation, API documentation

Oldperl
Beiträge: 3989
Registriert: Do 30. Jun 2005, 22:56
Wohnort: Franken, Bayern
Kontaktdaten:

Re: LDAP Authentifizierung

Beitrag von Oldperl » So 9. Dez 2018, 19:44

Servus,

ja stimmt, die Core-Klassen im bootstrap können nur in den lokalen Konfigurationsdateien überschrieben werden, und das sowohl im Front- als auch im Backend. Der Aufruf und die Initialisierung erfolgt bereits vor der Einbindung von Plugins, so wie das auch sein sollte, um ein Hacken per Plugin zu vermeiden.

Gruß aus Franken

Ortwin
*NEU* PHP 7.x Community Draft von CONTENIDO 4.9 auf github
CONTENIDO 4.9 Entwickler-Handbuch - Publikation auf medium.com zu meinem angedachten Entwickler-Buch zu CONTENIDO 4.9
ConLite 2.0, alternatives und stabiles Update von Contenido 4.8.x
phpBO Search Advanced - das neue Suchwort-Plugin für CONTENIDO 4.9
Mein Entwickler-Blog

Antworten