diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..1c046bd0a9cbbffe3385b58dfc052ce63f48e5a2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +CHANGELOG +========= + +3.0.0 (01/10/2020) +------------------ + +- Adaptation du code pour le passage à ZF3. +- Scission des classes liées aux entités ("Generic", "Group", "People", "Root", "Structure" et "System") en deux classes : + - classe de base avec un setter pour (presque) chaque attribut Ldap + - classe qui hérite de la classe de base avec des fonctions spécifiques à l'entité +- Gestions des attributs "supannActivite", "ucbnSecteurDisciplinaire" et "supannEmpCorps" +- Ajout des getters pour les attributs de la classe "People" : "rid", "sambaSID", "uidNumber", "gidNumber", "loginShell" et "homeDirectory" +- Ajout des setters pour les différents attributs liés aux structures de la classe "People" +- Ajout des attributs cachés "createTimestamp" et "modifyTimestamp" comme DateTimeAttributes +- Ajout des setters pour les attributs de la classe "People" : "supannEtablissement", "supannEtuAnneeInscription", "supannEtuCursusAnnee", "supannEtuDiplome" et "supannEtuEtape" +- Ajout des setters pour les attributs de la classe "People" : "supannEtuInscription", "ucbnSiteLocalisation", "ucbnAnneePostBac", "ucbnCodeEtape", "ucbnEtuComplementInscription", +"ucbnPrivateAddress", "ucbnPrivateAddresseBis", "supannEtuElementPedagogique", "supannEtuRegimeInscription", "supannEtuSecteurDisciplinaire" +et "supannEtuTypeDiplome" +- Gestion de la modification de l'attribut "supannRefId" par étiquette +- Ajout de fonctions de vérification du statut d'une personne dans la classe "People" +- Fonction de récupération de champs date au format Php DateTime +- Mise en place d'une entité "Root" pour gérer la racine de l'annuaire Ldap et d'un service associé +- Ajout des setters pour les attributs de la classe "People" : "supannAutreMail" et "mailForwardingAddress" diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0e6ea14691837d42ed7dca7ba7b2ca30c9940acd --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# UnicaenLdap + + * [Introduction](#introduction) + * [Installation](#installation) + * [Configuration](#configuration) + +## Introduction + +Ce module permet de se connecter à l'annuaire Ldap de l'université et de consulter/modifier ses données. + +## Pré-requis + +L'utilisation de ce module nécessite l'installation de l'extension `ext-ldap` de PHP. + +## Installation + +```bash +$ composer require unicaen/ldap +``` + +## Configuration + +> Récupérer les fichiers de config du module + +```bash +$ cp -n vendor/unicaen/zimbra/config/unicaen-ldap.local.php.dist config/autoload/unicaen-ldap.local.php +``` + +> Adapter le contenu à vos besoins en configurant notamment les paramètres de connexion au serveur Ldap. + +```php +'unicaen-ldap' => [ + 'host' => 'ldap-test.unicaen.fr', + 'port' => 389, + 'version' => 3, + 'baseDn' => "dc=unicaen,dc=fr", // racine de l'annuaire + 'bindRequiresDn' => true, + 'username' => "uid=xxxx,ou=system,dc=unicaen,dc=fr", + 'password' => "xxxx", + 'accountFilterFormat' => "(&(objectClass=supannPerson)(supannAliasLogin=%s))", +] +``` \ No newline at end of file diff --git a/config/module.config.php b/config/module.config.php index 130e11db74c5a7fbffe72445913b92ed755666d3..615aed75d6780e509a69f0d6e977acded45985df 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -14,51 +14,54 @@ use UnicaenLdap\Service\Group as LdapGroupService; use UnicaenLdap\Service\GroupFactory as GroupServiceFactory; use UnicaenLdap\Service\People as LdapPeopleService; use UnicaenLdap\Service\PeopleFactory as PeopleServiceFactory; +use UnicaenLdap\Service\Root as LdapRootService; +use UnicaenLdap\Service\RootFactory as RootServiceFactory; use UnicaenLdap\Service\Structure as LdapStructureService; use UnicaenLdap\Service\StructureFactory as StructureServiceFactory; use UnicaenLdap\Service\System as LdapSystemService; use UnicaenLdap\Service\SystemFactory as SystemServiceFactory; use Zend\ServiceManager\Proxy\LazyServiceFactory; -;; - return array( 'unicaen-ldap' => [ ], 'service_manager' => [ 'factories' => [ - 'Ldap' => LdapFactory::class, - 'LdapOptions' => ModuleOptionsFactory::class, - - 'LdapServiceGeneric' => GenericServiceFactory::class, - 'LdapServiceGroup' => GroupServiceFactory::class, - 'LdapServicePeople' => PeopleServiceFactory::class, - 'LdapServiceStructure' => StructureServiceFactory::class, - 'LdapServiceSystem' => SystemServiceFactory::class, + 'Ldap' => LdapFactory::class, + 'LdapOptions' => ModuleOptionsFactory::class, + 'LdapServiceGeneric' => GenericServiceFactory::class, + 'LdapServiceGroup' => GroupServiceFactory::class, + 'LdapServicePeople' => PeopleServiceFactory::class, + 'LdapServiceRoot' => RootServiceFactory::class, + 'LdapServiceStructure' => StructureServiceFactory::class, + 'LdapServiceSystem' => SystemServiceFactory::class, ], 'aliases' => [ - 'ldap' => 'Ldap', - 'ldapOptions' => 'LdapOptions', - 'ldapServiceGeneric' => 'LdapServiceGeneric', - 'ldapServiceGroup' => 'LdapServiceGroup', - 'ldapServicePeople' => 'LdapServicePeople', - 'ldapServiceStructure' => 'LdapServiceStructure', - 'ldapServiceSystem' => 'LdapServiceSystem', - LdapGenericService::class => 'LdapServiceGeneric', - LdapPeopleService::class => 'LdapServicePeople', - LdapGroupService ::class => 'LdapServiceGroup', - LdapStructureService::class => 'LdapServiceStructure', - LdapSystemService::class => 'LdapServiceSystem', + 'ldap' => 'Ldap', + 'ldapOptions' => 'LdapOptions', + 'ldapServiceGeneric' => 'LdapServiceGeneric', + 'ldapServiceGroup' => 'LdapServiceGroup', + 'ldapServicePeople' => 'LdapServicePeople', + 'ldapServiceRoot' => 'LdapServiceRoot', + 'ldapServiceStructure' => 'LdapServiceStructure', + 'ldapServiceSystem' => 'LdapServiceSystem', + LdapGenericService::class => 'LdapServiceGeneric', + LdapGroupService ::class => 'LdapServiceGroup', + LdapPeopleService::class => 'LdapServicePeople', + LdapRootService::class => 'LdapServiceRoot', + LdapStructureService::class => 'LdapServiceStructure', + LdapSystemService::class => 'LdapServiceSystem', ], 'lazy_services' => [ // Mapping services to their class names is required since the ServiceManager is not a declarative DIC. 'class_map' => [ - 'LdapServicePeople' => LdapPeopleService::class, - 'LdapServiceGeneric' => LdapGenericService::class, - 'LdapServiceGroup' => LdapGroupService::class, - 'LdapServiceStructure' => LdapStructureService::class, - 'LdapServiceSystem' => LdapSystemService::class, + 'LdapServicePeople' => LdapPeopleService::class, + 'LdapServiceGeneric' => LdapGenericService::class, + 'LdapServiceGroup' => LdapGroupService::class, + 'LdapServiceRoot' => LdapRootService::class, + 'LdapServiceStructure' => LdapStructureService::class, + 'LdapServiceSystem' => LdapSystemService::class, ], ], 'delegators' => [ @@ -71,14 +74,15 @@ return array( 'LdapServiceGroup' => [ LazyServiceFactory::class, ], + 'LdapServiceRoot' => [ + LazyServiceFactory::class, + ], 'LdapServiceStructure' => [ LazyServiceFactory::class, ], 'LdapServiceSystem' => [ LazyServiceFactory::class, ], - ], - ], ); diff --git a/config/unicaen-ldap.local.php.dist b/config/unicaen-ldap.local.php.dist index e4091d5a8e837f36f3dbc4e2b93d147fa1198ce5..dd8d54c08dc1722477cf66af54d26956d4591998 100644 --- a/config/unicaen-ldap.local.php.dist +++ b/config/unicaen-ldap.local.php.dist @@ -1,14 +1,14 @@ <?php -return array( - 'unicaen-ldap' => array( - 'host' => 'host.domain.fr', - 'port' => 389, - 'version' => 3, - 'baseDn' => "ou=xxxxxxxxxxx,dc=domain,dc=fr", - 'bindRequiresDn' => true, - 'username' => "uid=xxxxxxxxx,ou=xxxxxxxxxx,dc=domain,dc=fr", - 'password' => "xxxxxxxxxxxx", - 'accountFilterFormat' => "(&(objectClass=posixAccount)(supannAliasLogin=%s))", - ) -); +return [ + 'unicaen-ldap' => [ + 'host' => 'host.domain.fr', + 'port' => 389, + 'version' => 3, + 'baseDn' => "dc=domain,dc=fr", // racine de l'annuaire + 'bindRequiresDn' => true, + 'username' => "uid=xxxxxxxxx,ou=xxxxxxxxxx,dc=domain,dc=fr", + 'password' => "xxxxxxxxxxxx", + 'accountFilterFormat' => "(&(objectClass=posixAccount)(supannAliasLogin=%s))", + ] +]; diff --git a/src/UnicaenLdap/Entity/Base/Generic.php b/src/UnicaenLdap/Entity/Base/Generic.php new file mode 100644 index 0000000000000000000000000000000000000000..e3f8c63fcfead988d951355df5c68c54d33a57fb --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/Generic.php @@ -0,0 +1,189 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Exception; +use Zend\Ldap\Attribute; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère des entités de la branche "generic" de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class Generic extends Entity +{ + /** + * @var string + */ + protected $type = 'Generic'; + + /** + * Liste des classes d'objet nécessaires à la création d'une adresse générique + * + * @var array + */ + protected $objectClass = [ + 'top', + 'inetOrgPerson', + 'organizationalPerson', + 'person', + ]; + + /** + * Liste des attributs autorisés pour une entité "Generic" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'cn', + 'description', + 'mail', + 'sn', + 'supannAliasLogin', + 'ucbnServiceIMAP', + 'userPassword', + ]; + + + /** + * Liste des attributs contenant des dates + * + * @var string[] + */ + protected $dateTimeAttributes = [ + ]; + + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = [ + 'supannALiasLogin', + 'ucbnServiceIMAP', + 'userPassword', + ]; + + + /** + * Attribut Ldap "cn" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setCn($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('cn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "description" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setDescription($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('description', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "mail" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setMail($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_EMAIL)); + $this->appendOrNot('mail', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "sn" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setSn($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('sn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannALiasLogin" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannAliasLogin($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map('strtolower', $value); + $value = array_filter($value, function ($v) { + return preg_match('/^[0-9a-z\-]+$/', $v); + }); + + $this->appendOrNot('supannAliasLogin', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnServiceIMAP" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnServiceIMAP($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatBoolean'], $value); + $this->appendOrNot('ucbnServiceIMAP', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "userPassword" + * + * @param string $value + * @return self + * @throws LdapException + */ + public function setUserPassword(string $value) + { + $this->getNode()->setPasswordAttribute($value, Attribute::PASSWORD_HASH_SHA, 'userPassword'); + + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Base/Group.php b/src/UnicaenLdap/Entity/Base/Group.php new file mode 100644 index 0000000000000000000000000000000000000000..23fafdcf46199c78508d6f309ee1d0352312af48 --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/Group.php @@ -0,0 +1,272 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\People as PeopleEntity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Entity\System as SystemEntity; +use UnicaenLdap\Exception; +use Zend\Ldap\Dn; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère des entités de la branche "groups" de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class Group extends Entity +{ + /** + * Membre par défaut d'un groupe lorsque le groupe est vide + */ + const MEMBER_NOBODY = 'nobody'; + + /** + * @var string + */ + protected $type = 'Group'; + + /** + * Liste des classes d'objet nécessaires à la création d'un groupe + * + * @var string[] + */ + protected $objectClass = [ + 'top', + 'groupOfNames', + 'supannGroupe', + ]; + + /** + * Liste des attributs autorisés pour une entité "Group" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'description', + 'member', + 'owner', + 'supannGroupeDateFin', + 'supannGroupeLecteurDN', + 'supannGroupeAdminDN', + 'supannRefId', + ]; + + /** + * Liste des attributs contenant des dates + * + * @var string[] + */ + protected $dateTimeAttributes = [ + 'supannGroupeDateFin', + ]; + + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = [ + 'supannGroupeDateFin', + ]; + + + /** + * Retourne le DN du membre par défaut + * + * @return string + */ + public function getMemberNobody() + { + return sprintf('uid=%s,%s', + self::MEMBER_NOBODY, + $this->service->getLdapSystemService()->getBranches()[0] + ); + } + + /** + * Attribut Ldap "description" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setDescription($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('description', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "member" + * + * @param array|string|Dn|PeopleEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setMember($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof PeopleEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('member', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "owner" + * + * @param array|string|Dn|PeopleEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setOwner($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof PeopleEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('owner', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannGroupeDateFin" + * + * @param array|string|DateTime|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannGroupeDateFin($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + $val = new DateTime($val, new \DateTimeZone('+0000')); // définition du timezone à +0h + } + return (int)$val->format('U'); + }, $value); + + $this->appendOrNot('supannGroupeDateFin', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannGroupeLecteurDN" + * + * @param array|string|Dn|PeopleEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannGroupeLecteurDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof PeopleEntity) { + return $val->getDn(); + } elseif ($val instanceof SystemEntity) { + return $val->getDn(); + } + else { + return null; + } + }, $value); + + $this->appendOrNot('supannGroupeLecteurDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannGroupeAdminDN" + * + * @param array|string|Dn|PeopleEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannGroupeAdminDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof PeopleEntity) { + return $val->getDn(); + } elseif ($val instanceof SystemEntity) { + return $val->getDn(); + } + else { + return null; + } + }, $value); + + $this->appendOrNot('supannGroupeAdminDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannRefId" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannRefId($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannRefId', $value, $append); + + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Base/People.php b/src/UnicaenLdap/Entity/Base/People.php new file mode 100644 index 0000000000000000000000000000000000000000..fedc45f9966c57e6e29ea0d985c6bff3ad75bb2d --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/People.php @@ -0,0 +1,2059 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use DateTime; +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\People as PeopleEntity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Exception; +use Zend\Ldap\Attribute; +use Zend\Ldap\Dn; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère des entités de la branche "people" de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class People extends Entity +{ + /** + * Constantes pour les champs eduPersonAffiliation et eduPersonPrimaryAffiliation + */ + const STATUS_AFFILIATE = 'affiliate'; + const STATUS_ALUM = 'alum'; + const STATUS_EMERITUS = 'emeritus'; + const STATUS_EMPLOYEE = 'employee'; + const STATUS_FACULTY = 'faculty'; + const STATUS_MEMBER = 'member'; + const STATUS_REGISTERED_READER = 'registered-reader'; + const STATUS_RESEARCHER = 'researcher'; + const STATUS_RETIRED = 'retired'; + const STATUS_STAFF = 'staff'; + const STATUS_STUDENT = 'student'; + const STATUS_TEACHER = 'teacher'; + + /** + * @var string + */ + protected $type = 'People'; + + /** + * Liste des classes d'objet nécessaires à la création d'une entité "People" + * Il est nécessaire d'ajouter la classe 'ucbnEtu' ou 'ucbnEmp' selon le + * statut de la personne. + * + * @var array + */ + protected $objectClass = [ + 'top', + 'inetOrgPerson', + 'organizationalPerson', + 'person', + 'eduPerson', + 'supannPerson', + 'schacPersonalCharacteristics', + 'schacEntryMetadata', + 'ucbnEmp', + 'ucbnEtu', + 'posixAccount', + 'sambaAccount', + 'sambaSamAccount', + ]; + + /** + * Liste des attributs autorisés pour une entité "People" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'cn', + 'dateDeNaissance', + 'dateFinInscription', + 'displayName', + 'eduPersonAffiliation', + 'eduPersonNickname', + 'eduPersonOrgDN', + 'eduPersonOrgUnitDN', + 'eduPersonPrimaryAffiliation', + 'eduPersonPrimaryOrgUnitDN', + 'eduPersonPrincipalName', + 'facsimileTelephoneNumber', + 'gidNumber', + 'givenName', + 'homeDirectory', + 'labeledURI', + 'leoCode', + 'loginShell', + 'mail', + 'mailForwardingAddress', + 'mobile', + 'ntPassword', + 'pager', + 'postalAddress', + 'preferredLanguage', + 'rid', + 'sambaNTPassword', + 'sambaSID', + 'schacDateOfBirth', + 'schacExpiryDate', + 'sexe', + 'sn', + 'supannActivite', + 'supannAffectation', + 'supannAliasLogin', + 'supannAutreMail', + 'supannAutreTelephone', + 'supannCivilite', + 'supannCodeINE', + 'supannEmpCorps', + 'supannEmpId', + 'supannEntiteAffectation', + 'supannEntiteAffectationPrincipale', + 'supannEtablissement', + 'supannEtuAnneeInscription', + 'supannEtuCursusAnnee', + 'supannEtuDiplome', + 'supannEtuElementPedagogique', + 'supannEtuEtape', + 'supannEtuId', + 'supannEtuInscription', + 'supannEtuRegimeInscription', + 'supannEtuSecteurDisciplinaire', + 'supannEtuTypeDiplome', + 'supannListeRouge', + 'supannMailPerso', + 'supannParrainDN', + 'supannRoleGenerique', + 'supannRoleEntite', + 'supannRefId', + 'supannTypeEntiteAffectation', + 'telephoneNumber', + 'title', + 'ucbnAnneePostBac', + 'ucbnCodeEtape', + 'ucbnEtuComplementInscription', + 'ucbnFonctionStructurelle', + 'ucbnOrganisme', + 'ucbnPrivateAddress', + 'ucbnPrivateAddressBis', + 'ucbnSecteurDisciplinaire', + 'ucbnServiceADE', + 'ucbnServicePapercut', + 'ucbnSiteLocalisation', + 'ucbnSousStructure', + 'ucbnSquidHash', + 'ucbnStatus', + 'ucbnStructureRecherche', + 'uidNumber', + 'unicaenMonEtupass', + 'unicaenTermsOfUse', + 'userCertificate', + 'userPassword', + ]; + + /** + * Liste des attributs contenant des dates + * + * @var array + */ + protected $dateTimeAttributes = [ + 'schacExpiryDate', + ]; + + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = [ + 'cn', + 'dateDeNaissance', + 'dateFinInscription', + 'displayName', + 'eduPersonOrgDN', + 'eduPersonPrimaryAffiliation', + 'eduPersonPrimaryOrgUnitDN', + 'eduPersonPrincipalName', + 'gidNumber', + 'givenName', + 'homeDirectory', + 'leoCode', + 'loginShell', + 'ntPassword', + 'preferredLanguage', + 'rid', + 'sambaNTPassword', + 'sambaSID', + 'schacDateOfBirth', + 'schacExpiryDate', + 'sexe', + 'supannAliasLogin', + 'supannCivilite', + 'supannCodeINE', + 'supannEmpId', + 'supannEntiteAffectationPrincipale', + 'supannEtuId', + 'supannListeRouge', + 'telephoneNumber', + 'ucbnServiceADE', + 'ucbnServicePapercut', + 'ucbnSquidHash', + 'uidNumber', + 'userPassword', + ]; + + /** + * Liste des valeurs autorisées pour les attributs "eduPersonAffiliation" et "eduPersonPrimaryAffiliation" + * + * - affiliate : personne qui ne dépend pas de l'établissement (exemple : un partenaire extérieur). La valeur + * "member" est exclue pour ces personnes. + * - alum : ancien étudiant conservant des relations avec l'établissement. + * - emeritus : professeur ayant obtenu l'éméritat dans l'établissement. + * - employee : tout personnel rémunéré par l'établissement, quelque soit son activité. + * - faculty : personnel dont l'activité principale (dans l'établissement) est pédagogique, d'enseignement ou/et de + * recherche. La valeur “member” est positionnée si ce personnel est géré par l'établissement. + * - member : personne inscrite dans la (les) base(s) de gestion des étudiants ou celle(s) des personnels. + * - registered-reader : lecteur de bibliothèque autorisé. + * - researcher : personne assurant une activité de recherche. La valeur “member” est positionnée si ce personnel + * est géré par l'établissement. + * - retired : personne à la retraite conservant des relations avec l'établissement. + * - staff : personnel dont l'activité principale (dans l'établissement) est autre qu'enseignant ou chercheur + * (typiquement BIATOS). + * - student : personne suivant une formation quelconque dans l'établissement. Si la valeur “member” est positionnée, + * la personne est enregistrée dans la base des étudiants. + * - teacher : personnel assurant une activité d'enseignement. + * + * @var array + */ + protected $eduPersonAffiliationAuthorizedValues = [ + self::STATUS_AFFILIATE, + self::STATUS_ALUM, + self::STATUS_EMERITUS, + self::STATUS_EMPLOYEE, + self::STATUS_FACULTY, + self::STATUS_MEMBER, + self::STATUS_REGISTERED_READER, + self::STATUS_RESEARCHER, + self::STATUS_RETIRED, + self::STATUS_STAFF, + self::STATUS_STUDENT, + self::STATUS_TEACHER, + ]; + + /** + * Liste des valeurs autorisées pour l'attribut "ucbnStatus" + * + * @var array + */ + protected $ucbnStatusAuthorizedValues = [ + 'ALUMNI', + 'APPRENANT', + 'AUDITEUR_LIBRE', + 'CONTRACTUEL', + 'ETUDIANT', + 'HEBERGE', + 'INVITE', + 'LECTEUR_SCD', + 'TITULAIRE', + ]; + + /** + * Liste des patterns spécifiques utilisés pour différents attributs + */ + static protected $structure_pattern = '/^(?<code>[\w\-]+);(?<libelle>.+)$/'; + static protected $localisation_pattern = '/^(?<code>[\w\-]+);(?<libelle>.+)$/'; + static protected $etape_pattern = '/^(?<code>.+);(?<libelle>.+)$/'; + static protected $secteur_disciplinaire_pattern = '/^(?<code>\d+);(?<libelle>.+)$/'; + static protected $inscription_pattern = + '/^\[etab=(?<etab>\{[\w\-:]+\}.+)\]' . + '\[anneeinsc=(?<anneeinsc>\d{4})\]' . + '\[regimeinsc=(?<regimeinsc>\{[\w\-:]+\}\w*)\]' . // {SISE}.* ou {INCONNU} + '\[sectdisc=(?<sectdisc>\{[\w\-:]+\}\w*)\]' . // {SISE}.* ou {INCONNU} + '\[typedip=(?<typedip>\{[\w\-:]+\}\w*)\]' . // {SISE}.* ou {INCONNU} + '\[cursusann=(?<cursusann>\{SUPANN\}\w+)\]' . + '\[affect=(?<affect>[\w\-]+)\]' . + '\[diplome=(?<diplome>\{[\w\-:]+\}\w+)\]' . + '\[etape=(?<etape>\{[\w\-:]+\}.+)\]$/'; + static protected $inscription_complement_pattern = + '/^\[anneeinsc=(?<anneeinsc>\d{4})\]' . + '\[etape=(?<etape>\{[\w\-:]+\}.+)\]' . + '\[adistance=(?<adistance>\{[\w\-:]+\}\w{1})\]$/'; + static protected $role_pattern = + '/^\[role=(?<role>\{SUPANN\}[\w\-]+)\]' . + '\[type=(?<type>\{SUPANN\}[\w\-]+)\]' . + '\[code=(?<code>[\w\-]+)\]' . + '\[libelle=(?<libelle>.+)\]$/'; + static protected $role_src_pattern = '/^(?<code>\d{4});(?<libelle>.+)$/'; + + + /** + * Attribut Ldap "sn" + * + * Nom d'un individu + * Doit contenir le nom d'usage. Il est possible d'ajouter le nom de famille en seconde valeur. + * + * Multivalué + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSn($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatName'], $value); + $this->appendOrNot('sn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "givenName" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setGivenName($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatName'], $value); + $this->appendOrNot('givenName', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "displayName" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setDisplayName($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatName'], $value); + $this->appendOrNot('displayName', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "cn" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setCn($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatName'], $value, [true]); + $this->appendOrNot('cn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "dateDeNaissance" + * + * @param array|string|DateTime|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + * @deprecated Deprecated, use {@link self::setSchacDateOfBirth} + * + */ + public function setDateDeNaissance($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + $val = new DateTime($val); + } + return $val->format('Ymd'); + }, $value); + + $this->appendOrNot('dateDeNaissance', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "schacDateOfBirth" + * + * @param array|string|DateTime|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSchacDateOfBirth($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + $val = new DateTime($val); + } + return $val->format('Ymd'); + }, $value); + + $this->appendOrNot('schacDateOfBirth', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "dateFinInscription" + * + * @param array|string|DateTime|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + * @deprecated Deprecated, use {@link self::setSchacExpiryDate} + * + */ + public function setDateFinInscription($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + $val = new DateTime($val); + } + return $val->format('Ymd'); + }, $value); + + $this->appendOrNot('dateFinInscription', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "schacDateOfBirth" + * + * @param array|string|DateTime|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSchacExpiryDate($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + $val = new DateTime($val, new \DateTimeZone('+0000')); // définition du timezone à +0h + } + return (int)$val->format('U'); + }, $value); + + $this->appendOrNot('schacExpiryDate', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "sexe" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSexe($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return in_array($v, ['F', 'M']); + }); + + $this->appendOrNot('sexe', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonAffiliation" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonAffiliation($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return in_array($v, $this->eduPersonAffiliationAuthorizedValues); + }); + + $this->appendOrNot('eduPersonAffiliation', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonPrimaryAffiliation" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonPrimaryAffiliation($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return in_array($v, $this->eduPersonAffiliationAuthorizedValues); + }); + + $this->appendOrNot('eduPersonPrimaryAffiliation', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "postalAddress" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setPostalAddress($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$postal_address_pattern, $v); + }); + + $this->appendOrNot('postalAddress', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "telephoneNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setTelephoneNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('telephoneNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannAutreTelephone" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannAutreTelephone($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('supannAutreTelephone', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "mail" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setMail($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_EMAIL)); + $this->appendOrNot('mail', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannAutreMail" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannAutreMail($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_EMAIL)); + $this->appendOrNot('supannAutreMail', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "mailForwardingAddress" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setMailForwardingAddress($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_EMAIL)); + $this->appendOrNot('mailForwardingAddress', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "mobile" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setMobile($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('mobile', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "pager" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setPager($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('pager', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "preferredLanguage" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setPreferredLanguage($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map('strtolower', $value); + $this->appendOrNot('preferredLanguage', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "leoCode" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setLeoCode($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d{13}$/', $v); + }); + + $this->appendOrNot('leoCode', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannALiasLogin" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannAliasLogin($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map('strtolower', $value); + $value = array_filter($value, function ($v) { + return preg_match('/^[0-9a-z\-]+$/', $v); + }); + + $this->appendOrNot('supannAliasLogin', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEmpId" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannEmpId($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('supannEmpId', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuId" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuId($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('supannEtuId', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuInscription" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuInscription($value = null, $append = false) + { + $value = $this->preFormat($value); + + $supannLabel = $this->getLabel('SUPANN'); + $siseLabel = $this->getLabel('SISE'); + $inconnuLabel = $this->getLabel('INCONNU'); + $value = array_map(function ($v) use ($supannLabel, $siseLabel, $inconnuLabel) { + if (preg_match(self::$inscription_pattern, $v, $matches)) { + /** + * @var string $etab + * @var string $anneeinsc + * @var string $regimeinsc + * @var string $sectdisc + * @var string $typedip + * @var string $cursusann + * @var string $affect + * @var string $diplome + * @var string $etape + */ + foreach (['regimeinsc', 'sectdisc', 'typedip'] as $part) { + $$part = preg_match("/^($siseLabel|$inconnuLabel)\w*$/", $matches[$part]) + ? $matches[$part] + : sprintf('%s%s', $siseLabel, $matches[$part]); + } + foreach (['cursusann'] as $part) { + $$part = preg_match("/^$supannLabel\w+$/", $matches[$part]) + ? $matches[$part] + : sprintf('%s%s', $supannLabel, $matches[$part]); + } + + $prefixe = $this->service->getLdapStructureService()->getCodeStructurePrefixe(); + $affect = (0 !== strpos($matches['affect'], $prefixe)) + ? sprintf('%s%s', $structure_prefixe, $matches['affect']) + : $matches['affect']; + + $prefixe = $this->service->getLdapStructureService()->getCodeModuleEnseignementPrefixe(); + preg_match('/^(?<etiquette>\{[\w\-:]+\})(?<identifiant>.+)$/', $matches['etape'], $parts); + $val = (0 !== strpos($parts['identifiant'], $prefixe)) + ? $prefixe . $parts['identifiant'] + : $parts['identifiant']; + $etape = sprintf('%s%s', $parts['etiquette'], $val); + + return preg_replace( + self::$inscription_pattern, + "[etab=\\1][anneeinsc=\\2][regimeinsc=$regimeinsc][sectdisc=$sectdisc][typedip=$typedip][cursusann=$cursusann][affect=$affect][diplome=\\8][etape=$etape]", + $v, 1); + } else { + return null; + } + + }, $value); + + $this->appendOrNot('supannEtuInscription', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannCodeINE" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannCodeINE($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map('strtoupper', $value); + $value = array_filter($value, function ($v) { + return preg_match('/^[\w]{11}$/', $v); + }); + + $this->appendOrNot('supannCodeINE', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannCivilite" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannCivilite($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return in_array($v, ['Mme', 'M.']); + }); + + $this->appendOrNot('supannCivilite', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannListeRouge" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannListeRouge($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatBoolean'], $value); + $this->appendOrNot('supannListeRouge', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannMailPerso" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannMailPerso($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_EMAIL)); + $this->appendOrNot('supannMailPerso', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannAffectation" + * + * @param array|string|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannAffectation($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return preg_match(self::$structure_pattern, $val) ? $val : null; + } elseif ($val instanceof StructureEntity) { + $val = sprintf('%s;%s', strtr($val->get('supannCodeEntite'), $this->service->getLdapStructureService()->getCodeStructurePrefixe(), ''), $val->get('description')); + return preg_match(self::$structure_pattern, $val) ? $val : null; + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannAffectation', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEntiteAffectation" + * + * @param array|string|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannEntiteAffectation($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + if (0 !== strpos($val, $this->service->getLdapStructureService()->getCodeStructurePrefixe())) { + $val = $this->service->getLdapStructureService()->getCodeStructurePrefixe() . $val; + } + return $val; + } elseif ($val instanceof StructureEntity) { + return $val->get('supannCodeEntite'); + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannEntiteAffectation', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEntiteAffectationPrincipale" + * + * @param array|string|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannEntiteAffectationPrincipale($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + if (0 !== strpos($val, $this->service->getLdapStructureService()->getCodeStructurePrefixe())) { + $val = $this->service->getLdapStructureService()->getCodeStructurePrefixe() . $val; + } + return $val; + } elseif ($val instanceof StructureEntity) { + return $val->get('supannCodeEntite'); + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannEntiteAffectationPrincipale', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannTypeEntiteAffectation" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannTypeEntiteAffectation($value = null, $append = false) + { + $value = $this->preFormat($value); + $supannLabel = $this->getLabel('SUPANN'); + $value = array_map(function ($val) use ($supannLabel) { + if (is_string($val)) { + if (preg_match("/^" . $supannLabel . "[\w\-]+$/", $val)) { + return $val; + } elseif (preg_match("/^[\w\-]+$/", $val)) { + return sprintf('%s%s', $supannLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannTypeEntiteAffectation', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnSiteLocalisation" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnSiteLocalisation($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$localisation_pattern, $v); + }); + + $this->appendOrNot('ucbnSiteLocalisation', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnSousStructure" + * + * @param array|string|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnSousStructure($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return preg_match(self::$structure_pattern, $val) ? $val : null; + } elseif ($val instanceof StructureEntity) { + $val = sprintf('%s;%s', strtr($val->get('supannCodeEntite'), $this->service->getLdapStructureService()->getCodeStructurePrefixe(), ''), $val->get('description')); + return preg_match(self::$structure_pattern, $val) ? $val : null; + } else { + return null; + } + }, $value); + + $this->appendOrNot('ucbnSousStructure', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnStructureRecherche" + * + * @param array|string|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnStructureRecherche($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return preg_match(self::$structure_pattern, $val) ? $val : null; + } elseif ($val instanceof StructureEntity) { + $val = sprintf('%s;%s', strtr($val->get('supannCodeEntite'), $this->service->getLdapStructureService()->getCodeStructurePrefixe(), ''), $val->get('description')); + return preg_match(self::$structure_pattern, $val) ? $val : null; + } else { + return null; + } + }, $value); + + $this->appendOrNot('ucbnStructureRecherche', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonPrincipalName" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonPrincipalName($value = null, $append = false) + { + $universiteDomaine = $this->service->getLdapRootService()->getEtablissementDomain(); + $value = $this->preFormat($value); + $value = array_map(function ($v) use ($universiteDomaine) { + list($identifiant, $domaine) = array_pad(explode('@', strtolower($v)), 2, null); + if ($domaine != $universiteDomaine) { + $v = sprintf('%s@%s', $v, $universiteDomaine); + } + return $v; + }, $value); + $value = array_filter($value, function ($v) use ($universiteDomaine) { + return preg_match('/^[0-9a-z\-]+@' . $universiteDomaine . '$/', $v); + }); + + $this->appendOrNot('eduPersonPrincipalName', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonOrgDN" + * + * @param array|string|Dn|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonOrgDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof StructureEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('eduPersonOrgDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonOrgUnitDN" + * + * @param array|string|Dn|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonOrgUnitDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof StructureEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('eduPersonOrgUnitDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "eduPersonPrimaryOrgUnitDN" + * + * @param array|string|Dn|StructureEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduPersonPrimaryOrgUnitDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof StructureEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('eduPersonPrimaryOrgUnitDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannRoleGenerique" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannRoleGenerique($value = null, $append = false) + { + $value = $this->preFormat($value); + $supannLabel = $this->getLabel('SUPANN'); + $value = array_map(function ($val) use ($supannLabel) { + if (is_string($val)) { + if (preg_match("/^" . $supannLabel . "[\w\-]+$/", $val)) { + return $val; + } elseif (preg_match("/^[\w\-]+$/", $val)) { + return sprintf('%s%s', $supannLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannRoleGenerique', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannRoleEntite" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannRoleEntite($value = null, $append = false) + { + $value = $this->preFormat($value); + + $supannLabel = $this->getLabel('SUPANN'); + $value = array_map(function ($v) use ($supannLabel) { + if (preg_match(self::$role_pattern, $v, $matches)) { + /** + * @var string $role + * @var string $type + * @var string $code + */ + foreach (['role', 'type'] as $part) { + $$part = preg_match("/^" . $supannLabel . "[\w\-]+$/", $matches[$part]) + ? $matches[$part] + : sprintf('%s%s', $supannLabel, $matches[$part]); + } + + $prefixe = $this->service->getLdapStructureService()->getCodeStructurePrefixe(); + $code = (0 !== strpos($matches['code'], $prefixe)) + ? sprintf('%s%s', $prefixe, $matches['code']) + : $matches['code']; + + return preg_replace(self::$role_pattern, "[role=$role][type=$type][code=$code][libelle=\\4]", $v, 1); + } else { + return null; + } + + }, $value); + + $this->appendOrNot('supannRoleEntite', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnAnneePostBac" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setUcbnAnneePostBac($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d{1}$/', $v); + }); + + $this->appendOrNot('ucbnAnneePostBac', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnCodeEtape" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnCodeEtape($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$etape_pattern, $v); + }); + + $this->appendOrNot('ucbnCodeEtape', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnEtuComplementInscription" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnEtuComplementInscription($value = null, $append = false) + { + $value = $this->preFormat($value); + + $value = array_map(function ($v) { + if (preg_match(self::$inscription_complement_pattern, $v, $matches)) { + /** + * @var string $anneeinsc + * @var string $etape + * @var string $adistance + */ + $prefixe = $this->service->getLdapStructureService()->getCodeModuleEnseignementPrefixe(); + preg_match('/^(?<etiquette>\{[\w\-:]+\})(?<identifiant>.+)$/', $matches['etape'], $parts); + $val = (0 !== strpos($parts['identifiant'], $prefixe)) + ? $prefixe . $parts['identifiant'] + : $parts['identifiant']; + $etape = sprintf('%s%s', $parts['etiquette'], $val); + + return preg_replace( + self::$inscription_pattern, + "[anneeinsc=\\1][etape=$etape][adistance=\\3]", + $v, 1); + } else { + return null; + } + + }, $value); + + $this->appendOrNot('ucbnEtuComplementInscription', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnFonctionStructurelle" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnFonctionStructurelle($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$role_src_pattern, $v); + + }); + + $this->appendOrNot('ucbnFonctionStructurelle', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnPrivateAddress" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnPrivateAddress($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$postal_address_pattern, $v); + }); + + $this->appendOrNot('ucbnPrivateAddress', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnPrivateAddressBis" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUcbnPrivateAddressBis($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$postal_address_pattern, $v); + }); + + $this->appendOrNot('ucbnPrivateAddressBis', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannParrainDN" + * + * @param array|string|Dn|PeopleEntity|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannParrainDN($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + return Dn::checkDn($val) ? $val : null; + } elseif ($val instanceof Dn) { + return $val->toString(); + } elseif ($val instanceof PeopleEntity) { + return $val->getDn(); + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannParrainDN', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannRefId" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannRefId($value = null, $append = false) + { + $value = $this->preFormat($value); + + foreach ($value as $v) { + if (preg_match(self::$attribute_with_label_pattern, $v, $matches)) { + if ($append) { + $this->appendOrNot('supannRefId', $v, true); + } else { + $label = $matches['etiquette']; + $identifiant = $matches['identifiant']; + $currentValues = $this->preFormat($this->supannRefId); + array_walk($currentValues, function (&$cv) use ($label, $identifiant) { + if (preg_match("/^".$label."(?<identifiant>.+)$/", $cv, $matches)) { + if($matches['identifiant'] != $identifiant) { + $this->remove('supannRefId', $cv); + $cv = null; + } + } + }); + if(!in_array($v, $currentValues)) { + $currentValues[] = $v; + } + $this->appendOrNot('supannRefId', array_filter($currentValues), false); + } + } + } + + return $this; + } + + /** + * Attribut Ldap "ucbnStatus" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setUcbnStatus($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return in_array($v, $this->ucbnStatusAuthorizedValues); + }); + + $this->appendOrNot('ucbnStatus', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "unicaenTermsOfUse" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setUnicaenTermsOfUse($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('unicaenTermsOfUse', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannActivite" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannActivite($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannActivite', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ucbnSecteurDisciplinaire" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setUcbnSecteurDisciplinaire($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$secteur_disciplinaire_pattern, $v); + + }); + + $this->appendOrNot('ucbnSecteurDisciplinaire', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEmpCorps" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEmpCorps($value = null, $append = false) + { + $value = $this->preFormat($value); + $corpsLabel = $this->getLabel('NCORPS'); + $value = array_map(function ($val) use ($corpsLabel) { + if (is_string($val)) { + if (preg_match("/^$corpsLabel\w+$/", $val)) { + return $val; + } elseif (preg_match("/^\w+$/", $val)) { + return sprintf('%s%s', $corpsLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannEmpCorps', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtablissement" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtablissement($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannEtablissement', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuAnneeInscription" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuAnneeInscription($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d{4}$/', $v); + }); + + $this->appendOrNot('supannEtuAnneeInscription', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuCursusAnnee" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuCursusAnnee($value = null, $append = false) + { + $value = $this->preFormat($value); + $supannLabel = $this->getLabel('SUPANN'); + $value = array_map(function ($val) use ($supannLabel) { + if (is_string($val)) { + if (preg_match("/^$supannLabel\w+$/", $val)) { + return $val; + } elseif (preg_match("/^\w+$/", $val)) { + return sprintf('%s%s', $supannLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannEtuCursusAnnee', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuDiplome" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuDiplome($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannEtuDiplome', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuEtape" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuEtape($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + if (preg_match(self::$attribute_with_label_pattern, $val, $matches)) { + $prefixe = $this->service->getLdapStructureService()->getCodeModuleEnseignementPrefixe(); + $val = (0 !== strpos($matches['identifiant'], $prefixe)) + ? $prefixe . $matches['identifiant'] + : $matches['identifiant']; + return sprintf('%s%s', $matches['etiquette'], $val); + } + return null; + } + return null; + }, $value); + + $this->appendOrNot('supannEtuEtape', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuElementPedagogique" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuElementPedagogique($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + if (preg_match(self::$attribute_with_label_pattern, $val, $matches)) { + $prefixe = $this->service->getLdapStructureService()->getCodeModuleEnseignementPrefixe(); + $val = (0 !== strpos($matches['identifiant'], $prefixe)) + ? $prefixe . $matches['identifiant'] + : $matches['identifiant']; + return sprintf('%s%s', $matches['etiquette'], $val); + } + return null; + } + return null; + }, $value); + + $this->appendOrNot('supannEtuElementPedagogique', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuRegimeInscription" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuRegimeInscription($value = null, $append = false) + { + $value = $this->preFormat($value); + $siseLabel = $this->getLabel('SISE'); + $value = array_map(function ($val) use ($siseLabel) { + if (is_string($val)) { + if (preg_match("/^$siseLabel\w+$/", $val)) { + return $val; + } elseif (preg_match("/^\w+$/", $val)) { + return sprintf('%s%s', $siseLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannEtuRegimeInscription', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuSecteurDisciplinaire" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuSecteurDisciplinaire($value = null, $append = false) + { + $value = $this->preFormat($value); + $siseLabel = $this->getLabel('SISE'); + $value = array_map(function ($val) use ($siseLabel) { + if (is_string($val)) { + if (preg_match("/^$siseLabel\w+$/", $val)) { + return $val; + } elseif (preg_match("/^\w+$/", $val)) { + return sprintf('%s%s', $siseLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannEtuSecteurDisciplinaire', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtuTypeDiplome" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtuTypeDiplome($value = null, $append = false) + { + $value = $this->preFormat($value); + $siseLabel = $this->getLabel('SISE'); + $value = array_map(function ($val) use ($siseLabel) { + if (is_string($val)) { + if (preg_match("/^$siseLabel\w+$/", $val)) { + return $val; + } elseif (preg_match("/^\w+$/", $val)) { + return sprintf('%s%s', $siseLabel, $val); + } + } + return null; + }, $value); + + $this->appendOrNot('supannEtuTypeDiplome', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "rid" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setRid($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d+$/', $v); + }); + $this->appendOrNot('rid', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "sambaSID" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSambaSID($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('sambaSID', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "uidNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setUidNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d+$/', $v); + }); + $this->appendOrNot('uidNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "gidNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setGidNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match('/^\d+$/', $v); + }); + $this->appendOrNot('gidNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "loginShell" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setLoginShell($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('loginShell', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "homeDirectory" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setHomeDirectory($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('homeDirectory', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "userPassword" + * + * @param string $value + * @return self + * @throws LdapException + */ + public function setUserPassword(string $value) + { + $this->getNode()->setPasswordAttribute($value, Attribute::PASSWORD_HASH_SHA, 'userPassword'); + + return $this; + } + + /** + * Attribut Ldap "ntPassword" + * + * @param string $value + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setNtPassword(string $value) + { + $uni = ''; + $value = (string)$value; + for ($i = 0; $i < strlen($value); $i++) { + $a = ord($value{$i}) << 8; + $uni .= sprintf('%X', $a); + } + $nthash = hash("md4", pack('H*', $uni), true); + $this->appendOrNot('ntPassword', strtoupper(bin2hex($nthash)), false); + + return $this; + } + + /** + * Attribut Ldap "sambaNTPassword" + * @param string $value + * @return $this + * @throws Exception + * @throws LdapException + * @see setNtPassword + * + */ + public function setSambaNTPassword(string $value) + { + $uni = ''; + $value = (string)$value; + for ($i = 0; $i < strlen($value); $i++) { + $a = ord($value{$i}) << 8; + $uni .= sprintf('%X', $a); + } + $nthash = hash("md4", pack('H*', $uni), true); + $this->appendOrNot('sambaNTPassword', strtoupper(bin2hex($nthash)), false); + + return $this; + } + + /** + * Attribut Ldap "ucbnSquidHash" + * + * @param string $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setUcbnSquidHash(string $value) + { + $value = "Unicaen:" . md5(sprintf('%s:Unicaen:%s', $this->supannAliasLogin, $value)); + $this->appendOrNot('ucbnSquidHash', $value, false); + + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Base/Root.php b/src/UnicaenLdap/Entity/Base/Root.php new file mode 100644 index 0000000000000000000000000000000000000000..b5b3264181ef407922cecf86044446eec125dcf1 --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/Root.php @@ -0,0 +1,271 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Exception; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère de l'entité racine de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class Root extends Entity +{ + protected $type = 'Root'; + + + /** + * Liste des classes d'objet nécessaires à la création d'une structure + * + * @var string[] + */ + protected $objectClass = [ + 'top', + 'dcObject', + 'organization', + 'eduOrg', + 'supannOrg', + ]; + + /** + * Liste des attributs autorisés pour l'entité "Root" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'description', + 'eduOrgHomePageURI', + 'eduOrgLegalName', + 'eduOrgSuperiorURI', + 'eduOrgWhitePagesURI', + 'facsimileTelephoneNumber', + 'l', + 'o', + 'postalAddress', + 'supannEtablissement', + 'telephoneNumber', + ]; + + /** + * Liste des attributs contenant des dates + * + * @var array + */ + protected $dateTimeAttributes = []; + + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = [ + 'eduOrgLegalName', + ]; + + /** + * Attribut Ldap "description" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setDescription($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('description', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduOrgHomePageURI" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduOrgHomePageURI($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_URL)); + $this->appendOrNot('eduOrgHomePageURI', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduOrgLegalName" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduOrgLegalName($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('eduOrgLegalName', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduOrgSuperiorURI" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduOrgSuperiorURI($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_URL)); + $this->appendOrNot('eduOrgSuperiorURI', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "eduOrgWhitePagesURI" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setEduOrgWhitePagesURI($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter(filter_var_array($value, FILTER_VALIDATE_URL)); + $this->appendOrNot('eduOrgWhitePagesURI', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "facsimileTelephoneNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setFacsimileTelephoneNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('facsimileTelephoneNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "l" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setL($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('l', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "o" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setO($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('o', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "postalAddress" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setPostalAddress($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$postal_address_pattern, $v); + }); + + $this->appendOrNot('postalAddress', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannEtablissement" + * + * @param array|string|null $value + * @param bool $append + * @return $this + * @throws Exception + * @throws LdapException + */ + public function setSupannEtablissement($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannEtablissement', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "telephoneNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setTelephoneNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('telephoneNumber', $value, $append); + + return $this; + } + +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Base/Structure.php b/src/UnicaenLdap/Entity/Base/Structure.php new file mode 100644 index 0000000000000000000000000000000000000000..76007e2a5f22196d10c8fc190291dcf4e39cd7b6 --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/Structure.php @@ -0,0 +1,232 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Exception; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère des entités de la branche "structures" de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class Structure extends Entity +{ + protected $type = 'Structure'; + + /** + * Liste des classes d'objet nécessaires à la création d'une structure + * + * @var string[] + */ + protected $objectClass = [ + 'top', + 'organizationalUnit', + 'supannEntite', + 'ucbnEntite', + ]; + + /** + * Liste des attributs autorisés pour une entité "People" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'description', + 'facsimileTelephoneNumber', + 'ou', + 'postalAddress', + 'supannCodeEntiteParent', + 'supannRefId', + 'supannTypeEntite', + 'telephoneNumber', + ]; + + /** + * Liste des attributs contenant des dates + * + * @var array + */ + protected $dateTimeAttributes = []; + + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = []; + + + /** + * Attribut Ldap "description" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setDescription($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('description', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "ou" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setOu($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('ou', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannCodeEntiteParent" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannCodeEntiteParent($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map(function ($val) { + if (is_string($val)) { + if (0 !== strpos($val, $this->service->getCodeStructurePrefixe())) { + $val = $this->service->getCodeStructurePrefixe() . $val; + } + return $val; + } elseif ($val instanceof StructureEntity) { + return $val->get('supannCodeEntite'); + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannCodeEntiteParent', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "supannTypeEntite" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannTypeEntite($value = null, $append = false) + { + $value = $this->preFormat($value); + $supannLabel = $this->getLabel('SUPANN'); + $value = array_map(function ($val) use ($supannLabel) { + if (is_string($val)) { + return preg_match("/^$supannLabel.+$/", $val) ? $val : sprintf('%s%s', $supannLabel, $val); + } else { + return null; + } + }, $value); + + $this->appendOrNot('supannTypeEntite', array_filter($value), $append); + + return $this; + } + + /** + * Attribut Ldap "telephoneNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setTelephoneNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('telephoneNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "facsimileTelephoneNumber" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setFacsimileTelephoneNumber($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_map([$this, 'formatTel'], $value); + $this->appendOrNot('facsimileTelephoneNumber', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "postalAddress" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setPostalAddress($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$postal_address_pattern, $v); + }); + + $this->appendOrNot('postalAddress', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "supannRefId" + * + * @param array|string|null $value + * @param bool $append + * @return self + * @throws Exception + * @throws LdapException + */ + public function setSupannRefId($value = null, $append = false) + { + $value = $this->preFormat($value); + $value = array_filter($value, function ($v) { + return preg_match(self::$attribute_with_label_pattern, $v); + }); + + $this->appendOrNot('supannRefId', $value, $append); + + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Base/System.php b/src/UnicaenLdap/Entity/Base/System.php new file mode 100644 index 0000000000000000000000000000000000000000..4d282a8045a3496d0778167309e58573b37f8371 --- /dev/null +++ b/src/UnicaenLdap/Entity/Base/System.php @@ -0,0 +1,97 @@ +<?php + +namespace UnicaenLdap\Entity\Base; + +use UnicaenLdap\Entity\Entity; +use Zend\Ldap\Attribute; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe mère des entités de la branche "system" de l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class System extends Entity +{ + protected $type = 'System'; + + /** + * Liste des classes d'objet nécessaires à la création d'un compte système + * + * @var string[] + */ + protected $objectClass = [ + 'top', + 'inetOrgPerson', + 'organizationalPerson', + 'person', + 'supannPerson', + 'ucbnEmp', + ]; + + /** + * Liste des attributs autorisés pour une entité "Generic" + * + * @var array + */ + protected $authorizedAttributes = [ + // Attributes classes + 'objectClass', + // Attributes + 'cn', + 'sn', + 'userPassword', + ]; + + /** + * Liste des attributs contenant des dates + * + * @var string[] + */ + protected $dateTimeAttributes = [ + ]; + + /** + * Attribut Ldap "cn" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setCn($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('cn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "sn" + * + * @param array|string|null $value + * @param bool $append + * @return self + */ + public function setSn($value = null, $append = false) + { + $value = $this->preFormat($value); + $this->appendOrNot('sn', $value, $append); + + return $this; + } + + /** + * Attribut Ldap "userPassword" + * + * @param string $value + * @return self + * @throws LdapException + */ + public function setUserPassword(string $value) + { + $this->getNode()->setPasswordAttribute($value, Attribute::PASSWORD_HASH_SHA, 'userPassword'); + + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Entity.php b/src/UnicaenLdap/Entity/Entity.php index 9899c7f0505d260dcc0ef75b32edad8c9667e876..c1274b38abaf670d5543b487c7aa1f436832cc89 100644 --- a/src/UnicaenLdap/Entity/Entity.php +++ b/src/UnicaenLdap/Entity/Entity.php @@ -5,6 +5,9 @@ namespace UnicaenLdap\Entity; use UnicaenLdap\Node; use UnicaenLdap\Exception; use UnicaenLdap\Service\AbstractService; +use UnicaenLdap\Util; +use Zend\Ldap\Dn; +use Zend\Ldap\Exception\LdapException; /** * Classe mère des entrées de l'annuaire LDAP. @@ -35,40 +38,92 @@ abstract class Entity /** * Liste des classes d'objet nécessaires à la création de l'entité * - * @var string[] + * @var array */ - protected $objectClass = [ + protected $objectClass = []; + /** + * Liste des attributs autorisés pour une entité + * + * @var array + */ + protected $authorizedAttributes = []; + + /** + * Liste des attributs contenant des dates communs à toutes les entités + */ + private $sharedDateTimeAttributes = [ + 'createTimestamp', + 'modifyTimestamp', ]; /** * Liste des attributs contenant des dates * - * @var string[] + * @var array */ - protected $dateTimeAttributes = [ + protected $dateTimeAttributes = []; - ]; + /** + * Liste des attributs monovalués + * + * @var array + */ + protected $monoValuedAttributes = []; + + /** + * Liste des patterns génériques utilisés pour différents attributs + */ + static protected $attribute_with_label_pattern = '/^(?<etiquette>\{[\w\-:]+\})(?<identifiant>.+)$/'; + /** + * Liste des patterns spécifiques utilisés pour différents attributs + */ + static protected $postal_address_pattern = '/^(.*)\$(.*)\$(.*)\$(.*)\$(.*)\$(.*)$/'; + + + /** + * @param string $type type d'entité + * @return array + * @throws Exception + */ public static function getNodeParams($type) { $params = [ - 'Generic' => ['uid', 'UnicaenLdap\\Entity\\Generic'], - 'Group' => ['cn', 'UnicaenLdap\\Entity\\Group'], - 'People' => ['uid', 'UnicaenLdap\\Entity\\People'], + 'Generic' => ['uid', 'UnicaenLdap\\Entity\\Generic'], + 'Group' => ['cn', 'UnicaenLdap\\Entity\\Group'], + 'People' => ['uid', 'UnicaenLdap\\Entity\\People'], + 'Root' => ['dc', 'UnicaenLdap\\Entity\\Root'], 'Structure' => ['supannCodeEntite', 'UnicaenLdap\\Entity\\Structure'], - 'System' => ['uid', 'UnicaenLdap\\Entity\\System'], + 'System' => ['uid', 'UnicaenLdap\\Entity\\System'], ]; if (!isset($params[$type])) throw new Exception('Paramètres de "' . $type . '" non trouvés'); return $params[$type]; } + + /** + * Vérifie le format de l'identifiant Ldap selon le type de l'entité + * + * @param $type + * @param $id + * @return false|int + */ + public static function checkIdFormat($type, $id) + { + switch($type) { + default: + return preg_match('/^[[:alnum:]_-]+$/', $id); + } + } + /** - * Construit une entrée. + * Entity constructor. * - * @param AbstractService $service Service qui gèrera la future entité - * @param Node|Dn|array|string $data Noeud si Node, sinon DN + * @param AbstractService $service Service qui gèrera la future entité + * @param Node|Dn|array|string $data Noeud si Node, sinon DN + * @throws LdapException */ public function __construct(AbstractService $service, $data) { @@ -82,7 +137,7 @@ abstract class Entity } /** - * Retourne le type du service + * Retourne le type de l'entité * * @return string */ @@ -132,98 +187,113 @@ abstract class Entity } /** - * Retourne la clé primaire correspondant à l'entité + * Retourne l'Organizational Unit (OU) de l'entité * * @return string */ - public function getId() + public function getOu() { - list($key) = self::getNodeParams($this->type); + if ($result = $this->getNode()->getDn()->get(1)) { + return $result['ou']; + } - return $this->get($key); + return null; } /** - * Retourne la clé primaire correspondant à l'entité + * Retourne le nom de la clé primaire correspondant à l'entité * - * @return string + * @return mixed + * @throws Exception */ public function getKey() { - return reset(self::getNodeParams($this->type)); + $params = self::getNodeParams($this->type); + return reset($params); } /** - * Retourne la liste des attributs de l'entité + * Retourne la valeur de la clé primaire correspondant à l'entité * - * @return string[] + * @return string + * @throws Exception + * @throws LdapException */ - public function getAttributesList() + public function getId() { - return array_keys($this->getNode()->getAttributes()); + return $this->get($this->getKey()); } /** - * Exporte sous forme de tableau le contenu de l'entité + * Retourne l'attribut "objectClass" * - * @return array + * @return array|int|mixed|null + * @throws LdapException */ - public function toArray() + public function getObjectClass() { - $result = []; - $attrsList = $this->getAttributesList(); - foreach ($attrsList as $attrName) { - $result[$attrName] = $this->get($attrName); - } - - return $result; + return $this->get('objectClass'); } /** - * Mise à jour de l'entité + * Retourne la liste des attributs de l'entité * - * @return self + * @param bool $includeSystemAttributes + * @return string[] */ - public function update() + public function getAttributesList($includeSystemAttributes = true) { - $this->getNode()->update(); - - return $this; + return array_keys($this->getNode()->getAttributes($includeSystemAttributes)); } /** - * Suppression de l'entité + * Retourne un label constitué des différentes clés passées en paramètre + * Format : {$key1:$key2:$key3:...} * - * @return self + * @param string ...$key + * @return string */ - public function delete() + public function getLabel(...$key) { - $this->getNode()->delete(); + $label = ''; + $c = 0; + foreach($key as $k) { + if(is_string($k)) { + $label .= ($c == 0) ? $k : ":$k"; + } + $c++; + } - return $this; + return sprintf('{%s}', $label); } /** - * Insertion de l'entité + * Exporte sous forme de tableau le contenu de l'entité * - * @return self + * @return array + * @throws LdapException */ - public function insert() + public function toArray() { - $this->getNode()->attachLdap($this->service->getLdap()); + $result = []; + $attrsList = $this->getAttributesList(); + foreach ($attrsList as $attrName) { + $result[$attrName] = $this->get($attrName); + } - return $this; + return $result; } /** * Retourne un attribut * * @param string $attrName - * @return mixed + * @return array|int|mixed|null + * @throws LdapException */ public function get($attrName) { - if (in_array($attrName, $this->dateTimeAttributes)) { + if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) { $value = $this->getNode()->getDateTimeAttribute($attrName); } else { $value = $this->getNode()->getAttribute($attrName); @@ -240,13 +310,28 @@ abstract class Entity /** * Affecte une nouvelle valeur à un attribut * + * This is an offline method. + * Node will be updated on calling update(). + * * @param string $attrName - * @param mixed $value + * @param mixed $value * @return self + * @throws Exception + * @throws LdapException */ public function set($attrName, $value) { - if (in_array($attrName, $this->dateTimeAttributes)) { + if(!in_array($attrName, $this->authorizedAttributes)) { + throw new Exception(sprintf("L'attribut Ldap '%s' n'est pas autorisé pour une entité '%s'.", $attrName, get_class($this))); + } + + if(is_array($value) + && count($value) > 1 + && in_array($attrName, $this->monoValuedAttributes)) { + throw new Exception(sprintf("L'attribut Ldap '%s' est monovalué et ne doit contenir qu'une seule valeur.", $attrName)); + } + + if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) { $this->getNode()->setDateTimeAttribute($attrName, $value, true); } else { $this->getNode()->setAttribute($attrName, $value); @@ -259,7 +344,7 @@ abstract class Entity * Retourne <code>true</code> si l'attribut $param possède la valeur $value, <code>false</code> sinon. * * @param string $attrName - * @param mixed $value + * @param mixed $value * @return boolean */ public function has($attrName, $value) @@ -269,27 +354,45 @@ abstract class Entity /** * Ajoute une valeur à un attribut + * Si l'attribut est monovalué, la valeur actuelle est remplacée par la nouvelle valeur + * + * This is an offline method. + * Node will be updated on calling update(). * * @param string $attrName - * @param mixed $value + * @param mixed $value * @return self + * @throws Exception + * @throws LdapException */ public function add($attrName, $value) { - if (in_array($attrName, $this->dateTimeAttributes)) { - $this->getNode()->appendToDateTimeAttribute($attrName, $value); - } else { - $this->getNode()->appendToAttribute($attrName, $value); + if(!in_array($attrName, $this->authorizedAttributes)) { + throw new Exception(sprintf("L'attribut Ldap '%s' n'est pas autorisé pour une entité '%s'.", $attrName, get_class($this))); } - return $this; + if(in_array($attrName, $this->monoValuedAttributes)) { + $this->set($attrName, $value); + } + else { + if (in_array($attrName, array_merge($this->dateTimeAttributes, $this->sharedDateTimeAttributes))) { + $this->getNode()->appendToDateTimeAttribute($attrName, $value); + } else { + $this->getNode()->appendToAttribute($attrName, $value); + } + + return $this; + } } /** * Retire une valeur à un attribut * - * @param type $attrName - * @param type $value + * This is an offline method. + * Node will be updated on calling update(). + * + * @param string $attrName + * @param mixed|array $value * @return self */ public function remove($attrName, $value) @@ -299,11 +402,31 @@ abstract class Entity return $this; } + /** + * Ajoute ou affecte une valeur à un attribut Ldap + * + * This is an offline method. + * Node will be updated on calling update(). + * + * @param string $attrName + * @param mixed $value + * @param bool $append + * @throws Exception + * @throws LdapException + */ + protected function appendOrNot($attrName, $value, $append) + { + (!$append) + ? $this->set($attrName, $value) + : $this->add($attrName, $value); + } + /** * Méthode magique... * * @param string $attrName * @return mixed + * @throws LdapException */ public function __get($attrName) { @@ -314,8 +437,9 @@ abstract class Entity * Méthode magique... * * @param string $attrName - * @param mixed $value + * @param mixed $value * @return self + * @throws LdapException */ public function __set($attrName, $value) { @@ -325,8 +449,8 @@ abstract class Entity /** * Methode magique ... * - * @param string $method Nom de la méthode à appeler - * @param array $arguments Tableau de paramètres + * @param string $method Nom de la méthode à appeler + * @param array $arguments Tableau de paramètres * @return mixed */ public function __call($method, array $arguments) @@ -341,4 +465,175 @@ abstract class Entity } } } + + /** + * Déplacement de l'entité dans un autre OU (Organizational Unit) + * + * This is an offline method. + * Node will be moved on calling update(). + * + * @param string | Dn $newDn + * @throws \Zend\Ldap\Exception\LdapException + */ + public function move(string $newOu) + { + $arrayOu = $this->getService()->getOu(); + + if(empty($arrayOu) || !in_array($newOu, $arrayOu)) { + throw new Exception(sprintf("L'entité ne peut être déplacée dans l'OU '%s'.", $newOu)); + } + + if($newOu == $this->getOu()) { + throw new Exception(sprintf("L'entité est déjà présente dans l'OU '%s'.", $newOu)); + } + + $newDn = sprintf('%s=%s,ou=%s,%s', $this->getKey(), $this->getId(), $newOu, $this->getNode()->getLdap()->getBaseDn()); + + $this->getNode()->move($newDn); + + return $this; + } + + /** + * Renommage de l'entité + * + * This is an offline method. Node will be renamed on calling update(). + * + * @param string | Dn $newDn + * @throws \Zend\Ldap\Exception\LdapException + */ + public function rename($newId) + { + $newDn = sprintf('%s=%s,ou=%s,%s', $this->getKey(), $newId, $this->getOu(), $this->getNode()->getLdap()->getBaseDn()); + + $this->getNode()->move($newDn); + } + + /** + * Suppression de l'entité + * + * This is an offline method. + * Node will be deleted on calling update(). + * + * @return self + */ + public function delete() + { + $this->getNode()->delete(); + + return $this; + } + + /** + * Mise à jour de l'entité + * + * @return self + * @throws LdapException + */ + public function update() + { + $this->getNode()->update(); + + return $this; + } + + /** + * Attache une connexion + * + * @return self + * @throws LdapException + */ + public function attach() + { + $this->getNode()->attachLdap($this->service->getLdap()); + + return $this; + } + + /** + * Formate un nom ou un prénom + * + * @param string $name + * @param bool $removeAccents remplace les caractères avec un signe diacritique + * @return mixed|null|string|string[] + */ + protected function formatName($name, $removeAccents = false) + { + $name = preg_replace('/[[:blank:]]+/', ' ', trim($name)); + + if ($removeAccents) { + $name = Util::removeAccents($name); + } + + return mb_convert_case($name, MB_CASE_TITLE, "UTF-8"); + } + + /** + * Formate un numéro de téléphone (0XXXXXXXXX) au format international (+33 X XX XX XX XX) + * + * @param string $num + * @return string + */ + protected function formatTel($num) + { + if (is_null($num)) { + return null; + } + + $num = preg_replace('/[\.\-\s]+/', '', $num); + + return preg_match("/0\d{9}/", $num) + ? preg_replace("/0(\d{1})(\d{2})(\d{2})(\d{2})(\d{2})/", "+33 $1 $2 $3 $4 $5", $num) + : $num; + } + + /** + * Formate les données sources correspondant à un booléen + * + * @param mixed $value + * @return string + */ + protected function formatBoolean($value) + { + if (!is_bool($value) && $value === null) { + return null; + } + + if (is_string($value)) { + $value = strtolower($value); + } + + switch (true) { + case $value === false; + case $value === 0: + case $value === 'n': + return 'FALSE'; + case $value === true: + case $value === 1: + case $value === 'o': + case $value === 'y': + return 'TRUE'; + default: + return null; + } + } + + /** + * Vérifie une valeur et supprime les valeurs nulles + * + * @param mixed $value + * @return array + */ + protected function preFormat($value) + { + if (is_scalar($value) + || is_null($value) + || is_a($value, 'DateTime')) { + $value = [$value]; + } + + $value = array_filter($value, 'strlen'); // Delete NULL, '' and FALSE values + + return $value; + } } \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Generic.php b/src/UnicaenLdap/Entity/Generic.php index 2f0d585f1451fc51d74504b1c15a841f8d2aa01b..0e27cc1d7e5ca9117c136053df4b4d7292b1f649 100644 --- a/src/UnicaenLdap/Entity/Generic.php +++ b/src/UnicaenLdap/Entity/Generic.php @@ -2,34 +2,12 @@ namespace UnicaenLdap\Entity; +use UnicaenLdap\Entity\Base\Generic as BaseGeneric; + /** - * Classe mère des adresses génériques de l'annuaire LDAP. + * Classe de gestion des entités de la branche "generic" de l'annuaire LDAP. * * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> */ -class Generic extends Entity -{ - protected $type = 'Generic'; - - /** - * Liste des classes d'objet nécessaires à la création d'une adresse générique - * - * @var string[] - */ - protected $objectClass = [ - 'top', - 'inetOrgPerson', - 'organizationalPerson', - 'person', - 'supannPerson', - 'ucbnEmp', - ]; - - /** - * Liste des attributs contenant des dates - * - * @var string[] - */ - protected $dateTimeAttributes = [ - ]; -} \ No newline at end of file +class Generic extends BaseGeneric +{} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Group.php b/src/UnicaenLdap/Entity/Group.php index 84dccb263276f4fabb02cca18f37e2bd8c846243..38b87836f1d49849ce7eb62ca07c20e8fba2a446 100644 --- a/src/UnicaenLdap/Entity/Group.php +++ b/src/UnicaenLdap/Entity/Group.php @@ -3,37 +3,17 @@ namespace UnicaenLdap\Entity; use DateTime; +use UnicaenLdap\Entity\Base\Group as BaseGroup; /** - * Classe mère des groupes de l'annuaire LDAP. + * Classe de gestion des entités de la branche "groups" de l'annuaire LDAP. * * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> */ -class Group extends Entity +class Group extends BaseGroup { - protected $type = 'Group'; - - /** - * Liste des classes d'objet nécessaires à la création d'un groupe - * - * @var string[] - */ - protected $objectClass = [ - 'groupOfNames', - 'supannGroupe', - ]; - /** - * Liste des attributs contenant des dates - * - * @var string[] - */ - protected $dateTimeAttributes = [ - 'supannGroupeDateFin', - ]; - - /** - * Détermine si un groupe est valide ou non, c'est-à-dire si sa date de fin de validité n'est pas antérieure à la + * Détermine si un groupe est valide ou non, c'est-à-dire si sa date de fin de validité est postérieure ou égal à la * date testée * * @param DateTime $dateObservation @@ -64,14 +44,12 @@ class Group extends Entity * * @param string $orderBy Champ de tri (au besoin) * @return People[] + * @throws \Zend\Ldap\Exception\LdapException */ - public function getPeople($orderBy = null) + public function getMembres($orderBy = null) { - /* @var $peopleService \UnicaenLdap\Service\People */ - $peopleService = $this->getService()->getLdapPeopleService(); - - /** @var People[] $result */ - $result = $peopleService->getAllBy($this->get('member'), 'dn', $orderBy); + $peopleService = $this->service->getLdapPeopleService(); + $result = $peopleService->getAllBy($this->member, 'dn', $orderBy); return $result; } diff --git a/src/UnicaenLdap/Entity/People.php b/src/UnicaenLdap/Entity/People.php index d647ee75a3333caa16549fa987b4832aa77a4d6c..2028027dc72aa7680954b6fe8b8e8faf738f1cfa 100644 --- a/src/UnicaenLdap/Entity/People.php +++ b/src/UnicaenLdap/Entity/People.php @@ -3,276 +3,723 @@ namespace UnicaenLdap\Entity; use DateTime; +use UnicaenLdap\Entity\Base\People as BasePeople; +use UnicaenLdap\Entity\Group as GroupEntity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Entity\System as SystemEntity; +use UnicaenLdap\Exception; +use Zend\Ldap\Dn; /** - * Classe mère des people de l'annuaire LDAP. + * Classe de gestion des entités de la branche "people" de l'annuaire LDAP. * * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr> + * @author David SURVILLE <david.surville@unicaen.fr> */ -class People extends Entity +class People extends BasePeople { - static protected $role_pattern = '/^\[role={SUPANN}(.*)\]\[type={SUPANN}(.*)\]\[code=(.*)\]\[libelle=(.*)\]$/'; - /** - * Liste des rôles existants + * Détermine si l'individu est actif ou non * - * @var string[] + * @return boolean */ - public static $roles_list = [ - 'DIRECTEUR' => 'D30', - 'RESPONSABLE' => 'R00', - 'RESP_ADMINISTRATIF' => 'R40', - ]; - - protected $type = 'People'; + public function isPeople() + { + return 'people' == $this->getOu(); + } /** - * Liste des classes d'objet nécessaires à la création d'une personne - * Il est nécessaire d'ajouter la classe 'ucbnEtu' ou 'ucbnEmp' selon le - * statut de la personne. + * Détermine si l'individu est désactivé ou non * - * @var string[] + * @return boolean */ - protected $objectClass = [ - 'top', - 'inetOrgPerson', - 'organizationalPerson', - 'person', - 'eduPerson', - 'supannPerson', - 'sambaAccount', - 'sambaSamAcount', - 'posixAccount', - ]; + public function isDeactivated() + { + return 'deactivated' == $this->getOu(); + } /** - * Liste des attributs contenant des dates + * Détermine si l'individu est bloqué ou non * - * @var string[] + * @return boolean */ - protected $dateTimeAttributes = [ - - ]; + public function isBlocked() + { + return 'blocked' == $this->getOu(); + } /** - * Retourne le nom complet de cet individu LDAP. + * Retourne le nom complet. * - * @param boolean $nomEnMajuscule Mettre le nom de famille en majuscules ? - * @param boolean $avecCivilite Inclure la civilité ? - * @param boolean $prenomDabord Mettre le prénom avant le nom de famille ? + * @param boolean $uppercaseName nom de famille en majuscules + * @param boolean $withCivility inclure la civilité + * @param boolean $firstNameFirst prénom avant le nom de famille * @return string */ - public function getNomComplet($nomEnMajuscule = false, $avecCivilite = false, $prenomDabord = false) + public function getNomComplet($uppercaseName = false, $withCivility = false, $firstNameFirst = false) { - $sn = $this->get('sn'); + $sn = $this->sn; $sn = is_array($sn) ? current($sn) : $sn; if (!$sn) { return ''; } - $nom = $nomEnMajuscule ? strtoupper($sn) : $sn; - $prenom = $this->get('givenname'); - $civilite = $avecCivilite ? ' ' . $this->get('supannCivilite') : null; + $nom = $uppercaseName ? mb_strtoupper($sn, 'UTF-8') : $sn; + $prenom = $this->givenName; + $civilite = $withCivility ? $this->supannCivilite . ' ' : ''; - return ($prenomDabord ? $prenom . ' ' . $nom : $nom . ' ' . $prenom) . $civilite; + return $civilite . ($firstNameFirst ? $prenom . ' ' . $nom : $nom . ' ' . $prenom); } /** - * Détermine si l'individu est actif ou non + * Retourne le nom d'usage * - * @return boolean + * @return string|null */ - public function isPeople() + public function getNomUsage() { - return 'people' == $this->getOu(); + return is_array($this->sn) ? $this->sn[0] : $this->sn; } /** - * Détermine si l'individu est désactivé ou non + * Retourne le nom de famille s'il existe, sinon le nom d'usage * - * @return boolean + * @return string|null */ - public function isDeactivated() + public function getNomFamille() { - return 'deactivated' == $this->getOu(); + return is_array($this->sn) ? $this->sn[1] : $this->sn; } /** - * Détermine si l'individu est bloqué ou non + * Retourne la date de naissance * - * @return boolean + * @return DateTime|false */ - public function isBlocked() + public function getDateNaissance() { - return 'blocked' == $this->getOu(); + return DateTime::createFromFormat('Ymd h:i:s', $this->schacDateOfBirth . ' 00:00:00'); } /** - * Retourne l'Organizational Unit (OU) de l'utilisateur + * Retourne la date d'expiration * - * @return string + * @return DateTime|false */ - public function getOu() + public function getDateExpiration() { - if ($result = $this->getNode()->getDn()->get(1)) { - return $result['ou']; + return DateTime::createFromFormat('Ymd h:i:s', $this->schacExpiryDate . ' 00:00:00'); + } + + /** + * Retourne le responsable de l'entrée Ldap + * + * @return People|SystemEntity|string|null + * @throws Exception + * @throws \Zend\Ldap\Exception\LdapException + */ + public function getParrain() + { + $systemService = $this->service->getLdapSystemService(); + $node = $this->service->getLdap()->getNode($this->supannParrainDN); + $branch = $node->getDn()->get(1); + switch($branch['ou']) { + case strtolower($this->service->getType()): + return $this->service->getBy($node->getDnString(), 'dn'); + case strtolower($systemService->getType()): + return $systemService->getBy($node->getDnString(), 'dn'); + default: + return $this->supannParrainDN; } + } - return null; + /** + * Retourne l'adresse postale professionnelle + * + * @param string $type type de l'adresse : 'professionnelle' ou 'personnelle' + * @return array|null + */ + public function getAdressePostale($type = 'professionnelle') + { + if(!in_array($type, ['professionnelle', 'personnelle'])) { + return null; + } + + $attributes = ($type == 'professionnelle') + ? $this->postalAddress + : [$this->ucbnPrivateAddress, $this->ucbnPrivateAddressBis]; + $value = $this->preFormat($attributes); + $value = array_map(function ($val) { + return explode('$', $val); + }, $value); + + return 1 == count($value) ? $value[0] : $value; } /** - * Retourne la liste des groupes dont l'utilisateur fait partie - * Si le groupe n'est plus valide à la date d'observation, alors il n'est pas retourné dans la liste + * Test si une valeur est renseignée pour le champ "eduPersonAffiliation" * - * @param DateTime $dateObservation - * @param string $orderBy Champ de tri (au besoin) - * @return Group[] + * @param string $status + * @return bool */ - public function getGroups(DateTime $dateObservation = null, $orderBy = null) + protected function whichStatus(string $status): bool { - $groupService = $this->getService()->getLdapGroupService(); + $value = $this->preFormat($this->eduPersonAffiliation); + return in_array($status, $value); + } - return $groupService->filterValids($groupService->getAllBy($this->get('memberOf'), 'dn', $orderBy), $dateObservation); + /** + * Check "affiliate" status + * + * @return bool + */ + public function isAffiliate(): bool + { + return $this->whichStatus(parent::STATUS_AFFILIATE); + } + /** + * Check "alum" status + * + * @return bool + */ + public function isAlum(): bool + { + return $this->whichStatus(parent::STATUS_ALUM); } /** - * Détermine si la personne est étudiante + * Check "emeritus" status * - * @return boolean + * @return bool */ - public function estEtudiant() + public function isEmeritus(): bool { - return 0 === strpos($this->uid, 'e'); + return $this->whichStatus(parent::STATUS_EMERITUS); } /** - * Détermine si la personne est un personnel + * Check "employee" status * - * @return boolean + * @return bool */ - public function estPersonnel() + public function isEmployee(): bool { - return 0 === strpos($this->uid, 'p'); + return $this->whichStatus(parent::STATUS_EMPLOYEE); } /** - * Détermine si la personne est un invité + * Check "faculty" status * - * @return boolean + * @return bool */ - public function estInvite() + public function isFaculty(): bool { - return 0 === strpos($this->uid, 'i'); + return $this->whichStatus(parent::STATUS_FACULTY); } /** - * Retourne les structures auxquelles appartiennent la personne + * Check "member" status * - * @return Entity[]|Structure[] + * @return bool */ - public function getEduPersonOrgUnit() + public function isMember(): bool { - $structureService = $this->getService()->getLdapStructureService(); - $dn = $this->eduPersonOrgUnitDN; - if (empty($dn)) return null; + return $this->whichStatus(parent::STATUS_MEMBER); + } - return $structureService->getAllBy($dn, 'dn'); + /** + * Check "registered reader" status + * + * @return bool + */ + public function isRegisteredReader(): bool + { + return $this->whichStatus(parent::STATUS_REGISTERED_READER); } /** - * Retourne la structure principale à laquelle appartient la personne + * Check "researcher" status * - * @return Entity|Structure - * @throws \UnicaenLdap\Exception + * @return bool */ - public function getEduPersonPrimaryOrgUnit() + public function isResearcher(): bool { - $structureService = $this->getService()->getLdapStructureService(); - $dn = $this->eduPersonPrimaryOrgUnitDN; - if (empty($dn)) return null; + return $this->whichStatus(parent::STATUS_RESEARCHER); + } - return $structureService->getBy($dn, 'dn'); + /** + * Check "retired" status + * + * @return bool + */ + public function isRetired(): bool + { + return $this->whichStatus(parent::STATUS_RETIRED); + } + + /** + * Check "staff" status + * + * @return bool + */ + public function isStaff(): bool + { + return $this->whichStatus(parent::STATUS_STAFF); } /** - * Retourne la structure d'affectation de la personne + * Check "student" status * - * @todo à terminer - * @return Entity|Structure - * @throws \Exception + * @return bool */ - public function getEntiteAffectation() + public function isStudent(): bool { - throw new \Exception('Méthode pas finie'); - $structureService = $this->getService()->getLdapStructureService(); - $codes = $this->getNode()->getAttribute('supannEntiteAffectation'); - var_dump($codes); + return $this->whichStatus(parent::STATUS_STUDENT); + } - return $structureService->getBy($dn, 'dn'); + /** + * Check "teacher" status + * + * @return bool + */ + public function isTeacher(): bool + { + return $this->whichStatus(parent::STATUS_TEACHER); } /** - * Retourne la structure d'affectation de la personne + * Retourne le léocode associé à l'individu * - * @todo à terminer - * @return Structure - * @throws \Exception + * @return string|null */ - public function getEntiteAffectationPrincipale() + public function getLeocarteCode() { - throw new \Exception('Méthode pas finie'); - $structureService = $this->getService()->getLdapStructureService(); + $attributeValues = $this->preFormat($this->supannRefId); + $label = $this->getLabel('LEOCODE'); - $codes = []; - $affectations = $this->getNode()->getAttribute('supannAffectation'); + $value = array_filter($attributeValues, function ($v) use ($label) { + return preg_match("/^$label(?<identifiant>.+)$/", $v); + }); + return !empty($value) + ? str_replace($label, '', array_values($value)[0]) + : null; + } - list($code, $description) = explode(';', $this->supannAffectation); - $code = $this->supannAffectation; - if (empty($dn)) return null; + /** + * Retourne le code de la léocarte associée à l'individu + * + * @return string|null + */ + public function getLeocarteCSN() + { + $attributeValues = $this->preFormat($this->supannRefId); + $label = $this->getLabel('UNICAEN', 'REVERSECSN'); + + $value = array_filter($attributeValues, function ($v) use ($label) { + return preg_match("/^$label(?<identifiant>.+)$/", $v); + }); - return $structureService->getBy($dn, 'dn'); + return !empty($value) + ? str_replace($label, '', array_values($value)[0]) + : null; } /** - * Retourne la structure d'affectation de la personne + * Retourne le numéro d'étudiant * - * @todo à terminer - * @return Structure - * @throws \Exception + * @return string|null */ - public function getAffectationDescription() + public function getEtudiantId() { - throw new \Exception('Méthode pas finie'); - $structureService = $this->getService()->getLdapStructureService(); + return $this->supannEtuId; + } + + /** + * Retourne le numéro d'employé + * + * @return string|null + */ + public function getEmployeId() + { + return $this->supannEmpId; + } + + /** + * Retourne l'identifiant de l'individu dans le référentiel + * + * @return string|null + */ + public function getReferentielId() + { + $attributeValues = $this->preFormat($this->supannRefId); + $label = $this->getLabel('OCTOPUS', 'ID'); + + $value = array_filter($attributeValues, function ($v) use ($label) { + return preg_match("/^$label(?<identifiant>.+)$/", $v); + }); + + return !empty($value) + ? str_replace($label, '', array_values($value)[0]) + : null; + } + + /** + * Établissement auquel appartient la personne + * + * @return StructureEntity|null + */ + public function getEtablissement() + { + $rootService = $this->service->getLdapRootService(); + $structureService = $this->service->getLdapStructureService(); + $dn = $this->eduPersonOrgDN; - list($code, $description) = explode(';', $this->supannAffectation); - $code = $this->supannAffectation; if (empty($dn)) return null; + switch($dn) { + case $rootService->getEtablissementDN(): + return $rootService->getStructureEntity(); + default: + try { + return $structureService->getBy($dn, 'dn'); + } catch (Exception $e) { + return null; + } + } + } + + /** + * Retourne les structures auxquelles appartient la personne + * + * @return StructureEntity|StructureEntity[]|null + */ + public function getStructure() + { + $structureService = $this->service->getLdapStructureService(); + $value = $this->eduPersonOrgUnitDN; + if (empty($value)) return null; + + $value = $structureService->getAllBy($value, 'dn'); + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Retourne la structure principale à laquelle appartient la personne + * + * @return StructureEntity|null + */ + public function getStructurePrincipale() + { + $structureService = $this->service->getLdapStructureService(); + $value = $this->eduPersonPrimaryOrgUnitDN; + if (empty($value)) return null; + + try { + return $structureService->getBy($value, 'dn'); + } catch (Exception $e) { + return null; + } + } + + /** + * Retourne les structures recherche auxquelles appartient la personne + * + * @return StructureEntity|StructureEntity[]|null + */ + public function getStructureRecherche() + { + $structureService = $this->service->getLdapStructureService(); + $structurePrefixe = $structureService->getCodeStructurePrefixe(); + + $value = $this->preFormat($this->ucbnStructureRecherche); + if (empty($value)) return null; + + $value = array_map(function ($v) use ($structurePrefixe) { + preg_match(self::$structure_pattern, $v, $matches); + return $structurePrefixe . $matches['code']; + }, $value); + + $value = $structureService->getAllBy($value, 'supannCodeEntite'); + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Vérifie l'appartenance d'une personne à une structure (structures recherche incluses) + * + * @param string|Dn|StructureEntity $value + * @return bool + * @throws Exception + * @throws \Zend\Ldap\Exception\LdapException + */ + public function isInStructure($value) + { + $structureService = $this->service->getLdapStructureService(); + $structurePrefixe = $structureService->getCodeStructurePrefixe(); + + if (is_string($value)) { + if(Dn::checkDn($value)) { + $value = $structureService->getBy($value, 'dn'); + } + else { + if (0 !== strpos($value, $structurePrefixe)) { + $value = $structurePrefixe . $value; + } + $value = $structureService->get($value); + } + } elseif ($value instanceof Dn) { + $value = $structureService->getBy($value->toString(), 'dn'); + } + + if(!$value instanceof StructureEntity) { + return false; + } + + $structures = $this->preFormat($this->supannEntiteAffectation); + $structuresRecherche = $this->preFormat($this->ucbnStructureRecherche); + + $structuresRecherche = array_map(function ($v) use ($structurePrefixe) { + preg_match(self::$structure_pattern, $v, $matches); + return $structurePrefixe . $matches['code']; + }, $structuresRecherche); + + return in_array($value->getId(), array_merge($structures, $structuresRecherche)); + } + + /** + * Retourne les étapes auxquelles appartient l'étudiant + * + * @return mixed|StructureEntity[]|null + * @throws Exception + */ + public function getInscriptionEtape() + { + $value = $this->preFormat($this->supannEtuEtape); + if (empty($value)) return null; + + $label = $this->getLabel('UAI', $this->service->getLdapRootService()->getEtablissementUAI()); + $structureService = $this->service->getLdapStructureService(); + list($structureKey) = Entity::getNodeParams($structureService->getType()); + $structureBranch = $structureService->getBranches()[0]; + $value = array_map(function ($v) use ($label, $structureKey, $structureBranch) { + return sprintf('%s=%s,%s', + $structureKey, + str_replace($label, '' , $v), + $structureBranch + ); + }, $value); + + $value = $structureService->getAllBy($value, 'dn'); + return 1 == count($value) ? array_shift($value) : $value; + } - return $structureService->getBy($dn, 'dn'); + /** + * Retourne les éléments pédagogiques auxquels appartient l'étudiant + * + * @return mixed|StructureEntity[]|null + * @throws Exception + */ + public function getInscriptionElementPedagogique() + { + $value = $this->preFormat($this->supannEtuElementPedagogique); + if (empty($value)) return null; + + $label = $this->getLabel('UAI', $this->service->getLdapRootService()->getEtablissementUAI()); + $structureService = $this->service->getLdapStructureService(); + list($structureKey) = Entity::getNodeParams($structureService->getType()); + $structureBranch = $structureService->getBranches()[0]; + $value = array_map(function ($v) use ($label, $structureKey, $structureBranch) { + return sprintf('%s=%s,%s', + $structureKey, + str_replace($label, '' , $v), + $structureBranch + ); + }, $value); + + $value = $structureService->getAllBy($value, 'dn'); + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Retourne les inscriptions complètes d'un étudiant + * + * @return array + */ + public function getInscription() + { + $value = $this->preFormat($this->supannEtuInscription); + if (empty($value)) return null; + + $value = array_map(function ($v) { + preg_match(self::$inscription_pattern, $v, $matches); + $affect = $matches['affect']; + $etape = str_replace($this->getLabel('UAI', $this->service->getLdapRootService()->getEtablissementUAI()), '', $matches['etape']); + $replace = [$this->getLabel('SISE'), $this->getLabel('SUPANN'), $this->getLabel('INCONNU')]; + return [ + 'etab' => str_replace($this->getLabel('UAI'), '', $matches['etab']), + 'anneeinsc' => $matches['anneeinsc'], + 'regimeinsc' => str_replace($replace, '', $matches['regimeinsc']), + 'sectdisc' => str_replace($replace, '', $matches['sectdisc']), + 'typedip' => str_replace($replace, '', $matches['typedip']), + 'cursusann' => str_replace($replace, '', $matches['cursusann']), + 'affect' => $this->service->getLdapStructureService()->get($affect) ?: $affect, + 'diplome' => str_replace($replace, '', $matches['diplome']), + 'etape' => $this->service->getLdapStructureService()->get($etape) ?: $etape, + ]; + }, $value); + + return 1 == count($value) ? array_shift($value) : $value; } /** - * Retourne true si l'argument est au format "supannRoleEntite". + * Retourne le complément des inscriptions d'un étudiant + * + * @return array + */ + public function getInscriptionComplement() + { + $value = $this->preFormat($this->ucbnEtuComplementInscription); + if (empty($value)) return null; + + $value = array_map(function ($v) { + preg_match(self::$inscription_complement_pattern, $v, $matches); + $uaiLabel = $this->getLabel('UAI', $this->service->getLdapRootService()->getEtablissementUAI()); + $etape = str_replace($uaiLabel, '', $matches['etape']); + return [ + 'anneeinsc' => $matches['anneeinsc'], + 'etape' => $this->service->getLdapStructureService()->get($etape) ?: $etape, + 'adistance' => str_replace($uaiLabel, '', $matches['adistance']) + ]; + }, $value); + + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Retourne les rôles d'une personne + * + * @return array + */ + public function getRole() + { + $value = $this->preFormat($this->supannRoleEntite); + if (empty($value)) return null; + + $value = array_map(function ($v) { + preg_match(self::$role_pattern, $v, $matches); + $code = $matches['code']; + $replace = [$this->getLabel('SUPANN'), $this->getLabel('INCONNU')]; + return [ + 'role' => str_replace($replace, '', $matches['role']), + 'type' => str_replace($replace, '', $matches['type']), + 'code' => $this->service->getLdapStructureService()->get($code) ?: $code, + 'libelle' => $matches['libelle'], + ]; + }, $value); + + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Vérifie si la personne à une fonction dans un type de structure et une structure * * @param string $role + * @param string $type + * @param string|Dn|StructureEntity|null $value * @return bool + * @throws Exception + * @throws \Zend\Ldap\Exception\LdapException */ - static public function isSupannRoleEntite($string, &$role = null, &$typeStructure = null, &$codeStructure = null, &$libelleRole = null) + public function hasRoleInStructure(string $role, string $type, $structure = null): bool { - if (preg_match(static::$role_pattern, $string, $matches)) { - $role = $matches[1]; - $typeStructure = $matches[2]; - $codeStructure = $matches[3]; - $libelleRole = $matches[4]; + $structureService = $this->service->getLdapStructureService(); + $structurePrefixe = $structureService->getCodeStructurePrefixe(); - return true; + if($structure == null) { + $structure = '.*'; + } + else { + if (is_string($structure)) { + if (Dn::checkDn($structure)) { + $structure = $structureService->getBy($structure, 'dn'); + } else { + if (0 !== strpos($structure, $structurePrefixe)) { + $structure = $structurePrefixe . $structure; + } + $structure = $structureService->get($structure); + } + } elseif ($structure instanceof Dn) { + $structure = $structureService->getBy($structure->toString(), 'dn'); + } + + if (!$structure instanceof StructureEntity) { + return false; + } } - return false; + $structure = $structure instanceof StructureEntity ? $structure->supannCodeEntite : $structure; + $pattern = "/^\[role=\{SUPANN\}$role\]\[type=\{SUPANN\}$type\]\[code=$structure\]\[libelle=.*\]$/"; + + $matches = array_filter($this->supannRoleEntite, function ($v) use ($pattern) { + return preg_match($pattern, $v); + }); + + return !empty($matches); + } + + /** + * Retourne le site d'une personne + * + * @return array|null + */ + public function getSite() + { + $value = $this->preFormat($this->ucbnSiteLocalisation); + if (empty($value)) return null; + + $value = array_map(function ($v) { + preg_match(self::$localisation_pattern, $v, $matches); + $code = $matches['code']; + return [ + 'code' => $matches['code'], + 'libelle' => $matches['libelle'], + ]; + }, $value); + + return 1 == count($value) ? array_shift($value) : $value; + } + + /** + * Retourne la liste des groupes dont l'utilisateur fait partie + * Si le groupe n'est plus valide à la date d'observation, alors il n'est pas retourné dans la liste + * + * @param DateTime $dateObservation + * @param string $orderBy Champ de tri (au besoin) + * @return GroupEntity[]|null + */ + public function getGroups(DateTime $dateObservation = null, $orderBy = null) + { + $groupService = $this->service->getLdapGroupService(); + + return $groupService->filterValids($groupService->getAllBy($this->get('memberOf'), 'dn', $orderBy), $dateObservation); + } + + /** + * Modifie l'ensemble des attributs liés au mot de passe + * + * @param string $value + * @return $this + * @throws \UnicaenLdap\Exception + * @throws \Zend\Ldap\Exception\LdapException + */ + public function setPassword(string $value) + { + parent::setUserPassword($value); + parent::setNtPassword($value); + parent::setSambaNTPassword($value); + parent::setUcbnSquidHash($value); + + return $this; } } \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Root.php b/src/UnicaenLdap/Entity/Root.php new file mode 100644 index 0000000000000000000000000000000000000000..31c1297f41fc93797598c7471981806b8c35f63c --- /dev/null +++ b/src/UnicaenLdap/Entity/Root.php @@ -0,0 +1,15 @@ +<?php + +namespace UnicaenLdap\Entity; + +use UnicaenLdap\Entity\Base\Root as BaseRoot; +use UnicaenLdap\Filter\Filter; +use Zend\Ldap\Exception\LdapException; + +/** + * Classe de gestion de l'entité racine de l'annuaire LDAP. + * + * @author David SURVILLE <david.surville@unicaen.fr> + */ +class Root extends BaseRoot +{} \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/Structure.php b/src/UnicaenLdap/Entity/Structure.php index 1644fc14fe7c57a134a652acde1a91a470224e29..9b5c114f0a73ae49a9ac545650eaea37861fdf85 100644 --- a/src/UnicaenLdap/Entity/Structure.php +++ b/src/UnicaenLdap/Entity/Structure.php @@ -2,38 +2,18 @@ namespace UnicaenLdap\Entity; +use UnicaenLdap\Entity\Base\Structure as BaseStructure; use UnicaenLdap\Filter\Filter; +use Zend\Ldap\Exception\LdapException; /** - * Classe mère des structures de l'annuaire LDAP. + * Classe de gestion des entités de la branche "structures" de l'annuaire LDAP. * * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> + * @author David SURVILLE <david.surville@unicaen.fr> */ -class Structure extends Entity +class Structure extends BaseStructure { - - protected $type = 'Structure'; - - /** - * Liste des classes d'objet nécessaires à la création d'une structure - * - * @var string[] - */ - protected $objectClass = [ - 'top', - 'organizationalUnit', - 'supannEntite', - 'ucbnEntite', - ]; - - /** - * Liste des attributs contenant des dates - * - * @var string[] - */ - protected $dateTimeAttributes = [ - ]; - /** * Retourne la structure parente, si elle existe * @@ -41,7 +21,7 @@ class Structure extends Entity */ public function getParents() { - if (null !== $parentIds = $this->get('supannCodeEntiteParent')) { + if (null !== ($parentIds = $this->get('supannCodeEntiteParent'))) { return $this->service->getAll($parentIds); } @@ -70,17 +50,19 @@ class Structure extends Entity } /** - * Retourne le code Harpège + * Retourne le code dans la base source * * @return string + * @throws LdapException + * @throws \UnicaenLdap\Exception */ - public function getCodeHarpege() + public function getCodeSource() { - $code = $this->get('supannCodeEntite'); - if (0 === strpos($code, 'HS_')) { - return substr($code, 3); - } else { - return null; // Ne retourne rien si le code ne correspond pas à la nomenclature Harpège - } + $code = $this->getId(); + return ( + 0 === strpos($code, $this->service->getCodeStructurePrefixe()) || + 0 === strpos($code, $this->service->getCodeModuleEnseignementPrefixe())) + ? substr($code, 3) + : $code; } } \ No newline at end of file diff --git a/src/UnicaenLdap/Entity/System.php b/src/UnicaenLdap/Entity/System.php index bb21d910c636e25af6df509952155f0eee8f49e6..1e51c1bd2fa250fd6888a0240db8869f4e44f8f2 100644 --- a/src/UnicaenLdap/Entity/System.php +++ b/src/UnicaenLdap/Entity/System.php @@ -2,32 +2,12 @@ namespace UnicaenLdap\Entity; +use UnicaenLdap\Entity\Base\System as BaseSystem; + /** - * Classe mère des utilisateurs système de l'annuaire LDAP. + * Classe de gestion des entités de la branche "system" de l'annuaire LDAP. * * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> */ class System extends Entity -{ - protected $type = 'System'; - - /** - * Liste des classes d'objet nécessaires à la création d'un compte système - * - * @var string[] - */ - protected $objectClass = [ - 'top', - 'inetOrgPerson', - 'organizationalPerson', - 'person', - ]; - - /** - * Liste des attributs contenant des dates - * - * @var string[] - */ - protected $dateTimeAttributes = [ - ]; -} \ No newline at end of file +{} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/AbstractService.php b/src/UnicaenLdap/Service/AbstractService.php index 9a4ed90c3e17a5a7ff95f03c1b224ee91249362b..f2f7175c8e413a54dc9cb11ac7d65291f96a94cc 100644 --- a/src/UnicaenLdap/Service/AbstractService.php +++ b/src/UnicaenLdap/Service/AbstractService.php @@ -8,8 +8,8 @@ use UnicaenLdap\Entity\Entity; use UnicaenLdap\Exception; use UnicaenLdap\Filter\Filter; use UnicaenLdap\Ldap; -use Zend\Ldap\Dn; use Zend\Ldap\Filter\AbstractFilter; +use Zend\Ldap\Dn; use Zend\Stdlib\ErrorHandler; /** @@ -18,17 +18,19 @@ use Zend\Stdlib\ErrorHandler; * @author Laurent Lécluse <laurent.lecluse at unicaen.fr> */ abstract class AbstractService implements + LdapGenericServiceAwareInterface, + LdapGroupServiceAwareInterface, LdapPeopleServiceAwareInterface, + LdapRootServiceAwareInterface, LdapStructureServiceAwareInterface, - LdapGroupServiceAwareInterface, - LdapSystemServiceAwareInterface, - LdapGenericServiceAwareInterface + LdapSystemServiceAwareInterface { + use LdapGenericServiceAwareTrait; + use LdapGroupServiceAwareTrait; use LdapPeopleServiceAwareTrait; + use LdapRootServiceAwareTrait; use LdapStructureServiceAwareTrait; - use LdapGroupServiceAwareTrait; use LdapSystemServiceAwareTrait; - use LdapGenericServiceAwareTrait; /** * Limite de recherche par défaut @@ -46,6 +48,7 @@ abstract class AbstractService implements protected $ldap; /** + * Type de l'entité * * @var string */ @@ -53,28 +56,19 @@ abstract class AbstractService implements /** * Organizational Units - * - * @var string[] + * + * @var array */ - protected $ou = array(); + protected $ou = []; /** + * Compteur utilisé dans la recherche * * @var integer */ protected $count; - /** - * Retourne le type du service - * - * @return string - */ - public function getType() - { - return $this->type; - } - /** * Retourne l'objet d'accès à l'annuaire LDAP. * @@ -89,18 +83,29 @@ abstract class AbstractService implements * Spécifie l'objet d'accès à l'annuaire LDAP. * * @param Ldap $ldap - * @return AbstractService + * @return self */ public function setLdap(Ldap $ldap = null) { $this->ldap = $ldap; + return $this; } + /** + * Retourne le type du service + * + * @return string + */ + public function getType() + { + return $this->type; + } + /** * Retourne la liste des organizational units * - * @return string[] + * @return array */ public function getOu() { @@ -108,52 +113,52 @@ abstract class AbstractService implements } /** - * Redéfinie la liste des organizational units + * Retourne la liste des branches liées à la liste des organizational units * - * @param string[] $ou - * @return AbstractService + * @return array */ - public function setOu(array $ou) + public function getBranches() { - $this->ou = $ou; - return $this; + return array_map(function ($v) { + return sprintf('ou=%s,%s', $v, $this->getLdap()->getBaseDn()); + }, $this->ou); } /** * Retourne la liste de toutes les entités correspondantes - * + * * @param string $orderBy Propriété de référence pour le tri * @return Collection */ - public function getList( $orderBy=null, $limit=self::DEFAULT_LIMIT, $offset=self::DEFAULT_OFFSET ) + public function getList($orderBy = null, $limit = self::DEFAULT_LIMIT, $offset = self::DEFAULT_OFFSET) { list($key) = Entity::getNodeParams($this->type); - return $this->search( "($key=*)", $orderBy, $limit, $offset ); + return $this->search("($key=*)", $orderBy, $limit, $offset); } /** * Recherche une liste d'entités correspondantes * - * @param string|AbstractFilter $filter Valeur de recherche - * @param string $orderBy Attribut de référence pour le tri - * @param integer $limit Nombre maximum d'occurences renvoyées (-1 = infini) - * @param integer $offset Renvoi les entités à partir de $offset uniquement + * @param string|AbstractFilter $filter Valeur de recherche + * @param string $orderBy Attribut de référence pour le tri + * @param integer $limit Nombre maximum d'occurences renvoyées (-1 = infini) + * @param integer $offset Renvoi les entités à partir de $offset uniquement * @return Collection * @throws Exception */ - public function search( $filter, $orderBy=null, $limit=self::DEFAULT_LIMIT, $offset=self::DEFAULT_OFFSET ) + public function search($filter, $orderBy = null, $limit = self::DEFAULT_LIMIT, $offset = self::DEFAULT_OFFSET) { list($key) = Entity::getNodeParams($this->type); if ($limit < 0) $limit = 3999999999; // Limite maximum à 4 milliard... if ($offset < 0) $offset = 0; // Moins de zéro = impossible - list( $resource, $search ) = $this->__searchBegin($filter, $this->ou, array($key,$orderBy)); + list($resource, $search) = $this->__searchBegin($filter, $this->ou, [$key, $orderBy]); ErrorHandler::start(E_WARNING); $this->count = ldap_count_entries($resource, $search); ErrorHandler::stop(); - if ($this->count > 0){ + if ($this->count > 0) { if ($orderBy !== null && is_string($orderBy)) { ErrorHandler::start(E_WARNING); @@ -167,21 +172,21 @@ abstract class AbstractService implements $result = array(); $i = 0; ErrorHandler::start(E_WARNING); - for ($entry=ldap_first_entry($resource,$search); $entry; $entry=ldap_next_entry($resource,$entry)) { - list($value) = ldap_get_values_len($resource,$entry,$key); - if (null !== $value){ + for ($entry = ldap_first_entry($resource, $search); $entry; $entry = ldap_next_entry($resource, $entry)) { + list($value) = ldap_get_values_len($resource, $entry, $key); + if (null !== $value) { $result[] = $value; $i++; } if ($i > $limit + $offset - 1) break; // Pas besoin d'aller plus loin... } ErrorHandler::stop(); - }else{ + } else { $result = array(); } - $this->__searchEnd( $search ); + $this->__searchEnd($search); $this->count = count($result); - return new Collection( $this, array_slice( $result, $offset, $limit ) ); + return new Collection($this, array_slice($result, $offset, $limit)); } /** @@ -191,9 +196,9 @@ abstract class AbstractService implements * @return integer * @throws Exception */ - public function searchCount( $filter ) + public function searchCount($filter) { - list( $resource, $search ) = $this->__searchBegin($filter, $this->ou); + list($resource, $search) = $this->__searchBegin($filter, $this->ou); if ($search === false) { throw new Exception('searching: ' . $filter); @@ -206,29 +211,29 @@ abstract class AbstractService implements /** * Recherche une liste d'entités correspondantes * - * @param string|AbstractFilter $filter Valeur de recherche - * @param array $attributes Liste des attributs à retourner - * @param string $orderBy Champ de référence pour le tri - * @param integer $limit Nombre maximum d'occurences renvoyées (-1 = infini) - * @param integer $offset Renvoi les entités à partir de $offset uniquement + * @param string|AbstractFilter $filter Valeur de recherche + * @param array $attributes Liste des attributs à retourner + * @param string $orderBy Champ de référence pour le tri + * @param integer $limit Nombre maximum d'occurences renvoyées (-1 = infini) + * @param integer $offset Renvoi les entités à partir de $offset uniquement * @return Collection * @throws Exception */ - public function searchAttributes( $filter, array $attributes, $orderBy=null, $limit=self::DEFAULT_LIMIT, $offset=self::DEFAULT_OFFSET ) + public function searchAttributes($filter, array $attributes, $orderBy = null, $limit = self::DEFAULT_LIMIT, $offset = self::DEFAULT_OFFSET) { list($key) = Entity::getNodeParams($this->type); if ($limit < 0) $limit = 3999999999; // Limite maximum à 4 milliard... if ($offset < 0) $offset = 0; // Moins de zéro = impossible $searchAttributes = $attributes; - if (! in_array($key, $searchAttributes)) $searchAttributes[] = $key; - if (null !== $orderBy && ! in_array($orderBy, $searchAttributes)) $searchAttributes[] = $orderBy; - list( $resource, $search ) = $this->__searchBegin($filter, $this->ou, $searchAttributes); + if (!in_array($key, $searchAttributes)) $searchAttributes[] = $key; + if (null !== $orderBy && !in_array($orderBy, $searchAttributes)) $searchAttributes[] = $orderBy; + list($resource, $search) = $this->__searchBegin($filter, $this->ou, $searchAttributes); ErrorHandler::start(E_WARNING); $this->count = ldap_count_entries($resource, $search); ErrorHandler::stop(); - if ($this->count > 0){ + if ($this->count > 0) { if ($orderBy !== null && is_string($orderBy)) { ErrorHandler::start(E_WARNING); $isSorted = ldap_sort($resource, $search, $orderBy); @@ -241,11 +246,11 @@ abstract class AbstractService implements $result = array(); $i = 0; ErrorHandler::start(E_WARNING); - for ($entry=ldap_first_entry($resource,$search); $entry; $entry=ldap_next_entry($resource,$entry)) { - list($id) = ldap_get_values_len($resource,$entry,$key); + for ($entry = ldap_first_entry($resource, $search); $entry; $entry = ldap_next_entry($resource, $entry)) { + list($id) = ldap_get_values_len($resource, $entry, $key); $data = array(); - foreach( $attributes as $attribute ){ - $attrValue = ldap_get_values_len($resource,$entry,$attribute); + foreach ($attributes as $attribute) { + $attrValue = ldap_get_values_len($resource, $entry, $attribute); if (1 == $attrValue['count']) $attrValue = $attrValue[0]; $data[$attribute] = $attrValue; } @@ -254,22 +259,21 @@ abstract class AbstractService implements if ($i > $limit + $offset - 1) break; // Pas besoin d'aller plus loin... } ErrorHandler::stop(); - }else{ + } else { $result = array(); } - $this->__searchEnd( $search ); - return array_slice( $result, $offset, $limit ); + $this->__searchEnd($search); + return array_slice($result, $offset, $limit); } /** - * - * @param string|AbstractFilter $filter Filtre Ldap à appliquer - * @param string[] $ou Liste des organisations dans lesquelles rechercher - * @param string[] $attributes Liste des attributs à retourner + * @param string|AbstractFilter $filter Filtre Ldap à appliquer + * @param string[] $ou Liste des organisations dans lesquelles rechercher + * @param string[] $attributes Liste des attributs à retourner * @return array * @throws Exception */ - private function __searchBegin( $filter, array $ou=null, array $attributes=null ) + private function __searchBegin($filter, array $ou = null, array $attributes = null) { /* Initialisation $basedn et $filter */ if ($filter instanceof AbstractFilter) { @@ -279,15 +283,18 @@ abstract class AbstractService implements if (is_string($ou)) $ou = array($ou); elseif (null === $ou) $ou = $this->ou; - if (1 == count($ou)){ - $basedn = "ou=".$ou[0].",".$this->getLdap()->getBaseDn(); - }else{ + if (0 === count($ou)) { + $basedn = $this->getLdap()->getBaseDn(); + } + elseif (1 === count($ou)) { + $basedn = "ou=" . $ou[0] . "," . $this->getLdap()->getBaseDn(); + } else { $basedn = $this->getLdap()->getBaseDn(); $ouFilter = '(&(|'; - foreach( $ou as $ouItem ){ + foreach ($ou as $ouItem) { $ouFilter .= "(ou:dn:=$ouItem)"; } - $filter = $ouFilter.")$filter)"; + $filter = $ouFilter . ")$filter)"; } $resource = $this->getLdap()->getResource(); if (null === $attributes) $search = ldap_search($resource, $basedn, $filter); @@ -295,7 +302,7 @@ abstract class AbstractService implements if ($search === false) { throw new Exception('searching: ' . $filter); } - return array( $resource, $search ); + return array($resource, $search); } /** @@ -303,7 +310,7 @@ abstract class AbstractService implements * * @param resource $search */ - private function __searchEnd( $search ) + private function __searchEnd($search) { ldap_free_result($search); } @@ -315,7 +322,7 @@ abstract class AbstractService implements * @param resource $entry * @return null|array */ - private function __getEntryAttributes( $resource, $entry ) + private function __getEntryAttributes($resource, $entry) { if (!is_resource($resource)) { return null; @@ -327,7 +334,7 @@ abstract class AbstractService implements $resource, $entry, $berIdentifier ); - $attributes = array(); + $attributes = array(); while ($name) { ErrorHandler::start(E_WARNING); @@ -357,14 +364,18 @@ abstract class AbstractService implements /** * Retourne une entité du type correspondant au service courant * - * * @param string $id - * @return Entity + * @return Entity|null + * @throws Exception */ - public function get( $id ) + public function get($id) { list($key) = Entity::getNodeParams($this->type); - return $this->getBy( $id, $key ); + try { + return $this->getBy($id, $key); + } catch (Exception $e) { + return null; + } } /** @@ -376,42 +387,42 @@ abstract class AbstractService implements * @return Entity * @throws Exception */ - public function getBy( $value, $by ) + public function getBy($value, $by) { - if ('dn' == $by){ + if ('dn' == $by) { $value = Dn::factory($value)->get(0); $by = key($value); $value = current($value); } list($tmp, $classname) = Entity::getNodeParams($this->type); - list( $resource, $search ) = $this->__searchBegin( Filter::equals($by, $value), $this->ou, array('*','+') ); + list($resource, $search) = $this->__searchBegin(Filter::equals($by, $value), $this->ou, array('*', '+')); $count = ldap_count_entries($resource, $search); - switch( $count ){ + switch ($count) { case 0: $this->__searchEnd($search); - throw new Exception( 'Entité de type "'.$this->type.'" ayant "'.$by.'"="'.$value.'" non trouvée'); + throw new Exception('Entité de type "' . $this->type . '" ayant "' . $by . '"="' . $value . '" non trouvée'); case 1: - $entry = ldap_first_entry($resource,$search); + $entry = ldap_first_entry($resource, $search); $attributes = $this->__getEntryAttributes($resource, $entry); - $dn = ldap_get_dn( $resource, $entry ); + $dn = ldap_get_dn($resource, $entry); $attributes['dn'] = $dn; $this->__searchEnd($search); - return new $classname( $this, \UnicaenLdap\Node::fromEntry($dn, $this->getLdap(), $attributes) ); + return new $classname($this, \UnicaenLdap\Node::fromEntry($dn, $this->getLdap(), $attributes)); default: $this->__searchEnd($search); - throw new Exception( 'Plusieurs entités de type "'.$this->type.'" ayant "'.$by.'"="'.$value.'" ont été trouvées'); + throw new Exception('Plusieurs entités de type "' . $this->type . '" ayant "' . $by . '"="' . $value . '" ont été trouvées'); } } /** * Retourne un tableau d'entités de format array[ID] = Entite - * - * @param string[] $ids Tableau d'identifiants + * + * @param string[] $ids Tableau d'identifiants * @param string $orderBy Nom d'attribut à trier * @return Entity[] */ - public function getAll( $ids, $orderBy=null ) + public function getAll($ids, $orderBy = null) { list($key) = Entity::getNodeParams($this->type); return $this->getAllBy($ids, $key, $orderBy); @@ -420,31 +431,36 @@ abstract class AbstractService implements /** * Retourne un tableau d'entités de format array[ID] = Entite * - * @param array $values Tableau des valeurs à rechercher - * @param string $by Nom de champ à rechercher - * @param string $orderBy Nom d'attribut à trier + * @param array $values Tableau des valeurs à rechercher + * @param string $by Nom de champ à rechercher + * @param string $orderBy Nom d'attribut à trier * @return Entity[] */ - public function getAllBy( $values, $by, $orderBy=null ) + public function getAllBy($values, $by, $orderBy = null) { - if (! is_array($values)) $values = array($values); - + if (!is_array($values)) $values = array($values); + $data = array(); $sortedData = array(); - foreach( $values as $val ){ - $valRes = $this->getBy( $val, $by ); - if (! empty($orderBy)){ + foreach ($values as $val) { + try { + $valRes = $this->getBy($val, $by); + } + catch (Exception $e) { + continue; + } + if (!empty($orderBy)) { $sortedData[$valRes->getId()] = $valRes->get($orderBy); } $data[$valRes->getId()] = $valRes; } - if (! empty($orderBy)){ + if (!empty($orderBy)) { asort($sortedData); - foreach( $sortedData as $id => $val ){ + foreach ($sortedData as $id => $val) { $sortedData[$id] = $data[$id]; } return $sortedData; - }else{ + } else { return $data; } } @@ -455,7 +471,7 @@ abstract class AbstractService implements * @param string $id Identifiant de l'entité * @return boolean */ - public function exists( $id ) + public function exists($id) { list($key) = Entity::getNodeParams($this->type); return $this->existsBy($id, $key); @@ -463,20 +479,22 @@ abstract class AbstractService implements /** * Détermine si une entité existe ou non en fonction d'un champ déterminé - * + * * @param mixed $value Valeur de champ à rechercher * @param string $by Nom du champ à tester * @return boolean + * @throws Exception + * @throws \Zend\Ldap\Exception\LdapException */ - public function existsBy( $value, $by ) + public function existsBy($value, $by) { - if ('dn' == $by){ + if ('dn' == $by) { $value = Dn::factory($value)->get(0); $by = key($value); $value = current($value); } - return 1 == $this->searchCount( Filter::equals($by, $value) ); + return 1 == $this->searchCount(Filter::equals($by, $value)); } /** @@ -490,18 +508,22 @@ abstract class AbstractService implements * @return Entity * @throws Exception */ - public function create( $id, $ou=null ) + public function create(string $id, $ou = null) { - list($key, $classname) = Entity::getNodeParams($this->type); - if (empty($ou)){ - if (count($this->ou) > 1){ - throw new Exception('$ou non renseigné alors que le service couvre plusieurs Organizational Units (OU).'); + if(!Entity::checkIdFormat($this->type, $id)) { + throw new Exception(sprintf("Le format de l'identifiant '%s' n'est pas correct.", $id)); + } + + if (empty($ou)) { + if (count($this->ou) > 1) { + throw new Exception('Le paramètre $ou doit être renseigné car le service couvre plusieurs Organizational Units (OU).'); } $ou = $this->ou[0]; } - $dn = $key.'='.$id.',ou='.$ou.','.$this->getLdap()->getBaseDn(); - $classname = 'UnicaenLdap\\Entity\\'.$this->type; - return new $classname( $this, $dn); + + list($key, $classname) = Entity::getNodeParams($this->type); + $dn = sprintf('%s=%s,ou=%s,%s', $key, $id, $ou, $this->getLdap()->getBaseDn()); + return new $classname($this, $dn); } /** diff --git a/src/UnicaenLdap/Service/Generic.php b/src/UnicaenLdap/Service/Generic.php index 273102577c4173e9bf8b68ff7812ed6fd02dcae7..a94458260cd13b69e7fab4b477104b29ee237296 100644 --- a/src/UnicaenLdap/Service/Generic.php +++ b/src/UnicaenLdap/Service/Generic.php @@ -9,7 +9,19 @@ namespace UnicaenLdap\Service; */ class Generic extends AbstractService { + /** + * Type de l'entité + * + * @var string + */ protected $type = 'Generic'; - protected $ou = array('generic'); + /** + * Organizational Units + * + * @var array + */ + protected $ou = [ + 'generic' + ]; } \ No newline at end of file diff --git a/src/UnicaenLdap/Service/GenericFactory.php b/src/UnicaenLdap/Service/GenericFactory.php index a3e2a7d243d4597d4260746d05e0a0b6e7d7820b..b3362da58afcbf34b0f8d074c6a777be18e6a639 100644 --- a/src/UnicaenLdap/Service/GenericFactory.php +++ b/src/UnicaenLdap/Service/GenericFactory.php @@ -27,22 +27,25 @@ class GenericFactory implements FactoryInterface { /** * @var Ldap $ldap + * @var Service\Group $ldapGroupService * @var Service\People $ldapPeopleService + * @var Service\Root $ldapRootService * @var Service\Structure $ldapStructureService - * @var Service\Group $ldapGroupService * @var Service\System $ldapSystemService */ - $ldap = $container->get('ldap'); + $ldap = $container->get('ldap'); + $ldapGroupService = $container->get('ldapServiceGroup'); $ldapPeopleService = $container->get('ldapServicePeople'); + $ldapRootService = $container->get('ldapServiceRoot'); $ldapStructureService = $container->get('ldapServiceStructure'); - $ldapGroupService = $container->get('ldapServiceGroup'); $ldapSystemService = $container->get('ldapServiceSystem'); $service = new Generic(); $service->setLdap($ldap); + $service->setLdapGroupService($ldapGroupService); $service->setLdapPeopleService($ldapPeopleService); + $service->setLdapRootService($ldapRootService); $service->setLdapStructureService($ldapStructureService); - $service->setLdapGroupService($ldapGroupService); $service->setLdapSystemService($ldapSystemService); return $service; diff --git a/src/UnicaenLdap/Service/Group.php b/src/UnicaenLdap/Service/Group.php index 48cb06316f229012b0aea4dc5609ef1b5cd9ecda..5a15593ec81f25cd2c4235052bacb1ab1a058f95 100644 --- a/src/UnicaenLdap/Service/Group.php +++ b/src/UnicaenLdap/Service/Group.php @@ -12,9 +12,21 @@ use UnicaenLdap\Collection; */ class Group extends AbstractService { + /** + * Type de l'entité + * + * @var string + */ protected $type = 'Group'; - protected $ou = array('groups'); + /** + * Organizational Units + * + * @var array + */ + protected $ou = [ + 'groups' + ]; /** * Filtre une liste de groupes pour ne retourner que ceux qui sont encore valides diff --git a/src/UnicaenLdap/Service/GroupFactory.php b/src/UnicaenLdap/Service/GroupFactory.php index 4966a39f1317d9fcb61f043ceb05b6456ca3f801..137503682953c44f866c4656125dc457f53dd461 100644 --- a/src/UnicaenLdap/Service/GroupFactory.php +++ b/src/UnicaenLdap/Service/GroupFactory.php @@ -27,23 +27,26 @@ class GroupFactory implements FactoryInterface { /** * @var Ldap $ldap + * @var Service\Generic $ldapGenericService * @var Service\People $ldapPeopleService + * @var Service\Root $ldapRootService * @var Service\Structure $ldapStructureService * @var Service\System $ldapSystemService - * @var Service\Generic $ldapGenericService */ - $ldap = $container->get('ldap'); + $ldap = $container->get('ldap'); + $ldapGenericService = $container->get('ldapServiceGeneric'); $ldapPeopleService = $container->get('ldapServicePeople'); + $ldapRootService = $container->get('ldapServiceRoot'); $ldapStructureService = $container->get('ldapServiceStructure'); $ldapSystemService = $container->get('ldapServiceSystem'); - $ldapGenericService = $container->get('ldapServiceGeneric'); $service = new Group(); $service->setLdap($ldap); + $service->setLdapGenericService($ldapGenericService); $service->setLdapPeopleService($ldapPeopleService); + $service->setLdapRootService($ldapRootService); $service->setLdapStructureService($ldapStructureService); $service->setLdapSystemService($ldapSystemService); - $service->setLdapGenericService($ldapGenericService); return $service; } diff --git a/src/UnicaenLdap/Service/LdapPeopleServiceAwareInterface.php b/src/UnicaenLdap/Service/LdapPeopleServiceAwareInterface.php index c08423e5ef66489043ab4ed109ae66a495df2b7a..a2f404fcf865b5f2b84443b843974919b6cb6014 100644 --- a/src/UnicaenLdap/Service/LdapPeopleServiceAwareInterface.php +++ b/src/UnicaenLdap/Service/LdapPeopleServiceAwareInterface.php @@ -16,4 +16,4 @@ interface LdapPeopleServiceAwareInterface * @return LdapPeopleService */ public function getLdapPeopleService(); -} +} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/LdapRootServiceAwareInterface.php b/src/UnicaenLdap/Service/LdapRootServiceAwareInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..d086a4c83a2b7794a0835183c68dfe65a14f164e --- /dev/null +++ b/src/UnicaenLdap/Service/LdapRootServiceAwareInterface.php @@ -0,0 +1,19 @@ +<?php + +namespace UnicaenLdap\Service; + +use UnicaenLdap\Service\Root as LdapRootService; + +interface LdapRootServiceAwareInterface +{ + /** + * @param LdapRootService $ldapRootService + * @return mixed + */ + public function setLdapRootService(LdapRootService $ldapRootService); + + /** + * @return LdapRootService + */ + public function getLdapRootService(); +} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/LdapRootServiceAwareTrait.php b/src/UnicaenLdap/Service/LdapRootServiceAwareTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..39945b23986ab9a7a8405d32a18cceea7737a598 --- /dev/null +++ b/src/UnicaenLdap/Service/LdapRootServiceAwareTrait.php @@ -0,0 +1,29 @@ +<?php + +namespace UnicaenLdap\Service; + +use UnicaenLdap\Service\Root as LdapRootService; + +trait LdapRootServiceAwareTrait +{ + /** + * @var LdapRootService + */ + protected $ldapRootService; + + /** + * @param LdapRootService $ldapRootService + */ + public function setLdapRootService(LdapRootService $ldapRootService) + { + $this->ldapRootService = $ldapRootService; + } + + /** + * @return LdapRootService + */ + public function getLdapRootService() + { + return $this->ldapRootService; + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/LdapStructureServiceAwareInterface.php b/src/UnicaenLdap/Service/LdapStructureServiceAwareInterface.php index 1ce386a6dc7f2942de821c437e876b0e85625051..496f863ec170ba9f197870c8f7f72b9434d21f99 100644 --- a/src/UnicaenLdap/Service/LdapStructureServiceAwareInterface.php +++ b/src/UnicaenLdap/Service/LdapStructureServiceAwareInterface.php @@ -16,4 +16,4 @@ interface LdapStructureServiceAwareInterface * @return LdapStructureService */ public function getLdapStructureService(); -} +} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/People.php b/src/UnicaenLdap/Service/People.php index a7c34fc53c7a469d16c1e0d4c4dda556aa9fa3c6..87bd8c96bd0027be6dc451b65ba52b1d2fb43b10 100644 --- a/src/UnicaenLdap/Service/People.php +++ b/src/UnicaenLdap/Service/People.php @@ -9,7 +9,28 @@ namespace UnicaenLdap\Service; */ class People extends AbstractService { + /** + * Type de l'entité + * + * @var string + */ protected $type = 'People'; - protected $ou = array('people', 'deactivated', 'blocked'); + /** + * Organizational Units + * + * @var array + */ + protected $ou = [ + 'people', + 'deactivated', + 'blocked' + ]; + + public function checkPassword($value) + { + /** + * @todo implémenter la fonction de vérification d'un mot de passe + */ + } } \ No newline at end of file diff --git a/src/UnicaenLdap/Service/PeopleFactory.php b/src/UnicaenLdap/Service/PeopleFactory.php index b1de6d368f346bf567d326ace635ef0fa41276aa..ab9149a46a934179543dc23713516984c227f397 100644 --- a/src/UnicaenLdap/Service/PeopleFactory.php +++ b/src/UnicaenLdap/Service/PeopleFactory.php @@ -32,23 +32,26 @@ class PeopleFactory implements FactoryInterface { /** * @var Ldap $ldap - * @var Service\Structure $ldapStructureService + * @var Service\Generic $ldapGenericService * @var Service\Group $ldapGroupService + * @var Service\Root $ldapRootService + * @var Service\Structure $ldapStructureService * @var Service\System $ldapSystemService - * @var Service\Generic $ldapGenericService */ - $ldap = $container->get('ldap'); - $ldapStructureService = $container->get('ldapServiceStructure'); + $ldap = $container->get('ldap'); + $ldapGenericService = $container->get('ldapServiceGeneric'); $ldapGroupService = $container->get('ldapServiceGroup'); + $ldapRootService = $container->get('ldapServiceRoot'); + $ldapStructureService = $container->get('ldapServiceStructure'); $ldapSystemService = $container->get('ldapServiceSystem'); - $ldapGenericService = $container->get('ldapServiceGeneric'); $service = new People(); $service->setLdap($ldap); - $service->setLdapStructureService($ldapStructureService); + $service->setLdapGenericService($ldapGenericService); $service->setLdapGroupService($ldapGroupService); + $service->setLdapRootService($ldapRootService); + $service->setLdapStructureService($ldapStructureService); $service->setLdapSystemService($ldapSystemService); - $service->setLdapGenericService($ldapGenericService); return $service; } diff --git a/src/UnicaenLdap/Service/Root.php b/src/UnicaenLdap/Service/Root.php new file mode 100644 index 0000000000000000000000000000000000000000..3a1a9b1c698d179051ad4cbb505a4b8296ae72f1 --- /dev/null +++ b/src/UnicaenLdap/Service/Root.php @@ -0,0 +1,99 @@ +<?php + +namespace UnicaenLdap\Service; + +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\Root as RootEntity; +use UnicaenLdap\Entity\Structure as StructureEntity; +use UnicaenLdap\Ldap; + +/** + * Classe de service pour la racine l'annuaire LDAP. + * + * @author David Surville <david.surville@unicaen.fr> + */ +class Root extends AbstractService +{ + /** + * Type de l'entité + * + * @var string + */ + protected $type = 'Root'; + + /** + * Distinguished Name de l'établissement + * + * @var string + */ + protected $etablissement_dn = 'dc=unicaen,dc=fr'; + + /** + * Domaine de l'établissement + * + * @var string + */ + protected $etablissement_domain = 'unicaen.fr'; + + /** + * Code UAI de l'établissement + * + * @var string + */ + protected $etablissement_uai = '0141408E'; + + /** + * Retourne le type du service + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * @return string + */ + public function getEtablissementDN() + { + return $this->etablissement_dn; + } + + /** + * @return string + */ + public function getEtablissementDomain() + { + return $this->etablissement_domain; + } + + /** + * @return string + */ + public function getEtablissementUAI() + { + return $this->etablissement_uai; + } + + /** + * Retourne l'entité racine + * + * @return RootEntity + */ + public function getEntity() + { + list($key) = Entity::getNodeParams($this->type); + return $this->getBy($this->etablissement_dn, 'dn'); + } + + /** + * Retourne l'entité dans la branche "structures" liée à l'établissement + * + * @return StructureEntity + */ + public function getStructureEntity() + { + return $this->getLdapStructureService()->getStructureMere(); + } +} \ No newline at end of file diff --git a/src/UnicaenLdap/Service/RootFactory.php b/src/UnicaenLdap/Service/RootFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..a289953b22a0d8b97ce5b32ecd91400bcb3feb95 --- /dev/null +++ b/src/UnicaenLdap/Service/RootFactory.php @@ -0,0 +1,46 @@ +<?php + +namespace UnicaenLdap\Service; + +use Interop\Container\ContainerInterface; +use UnicaenLdap\Ldap; +use UnicaenLdap\Service; +use Zend\ServiceManager\FactoryInterface; +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * + * + * @author Unicaen + */ +class RootFactory implements FactoryInterface +{ + public function createService(ServiceLocatorInterface $serviceLocator) + { + return $this->__invoke($serviceLocator, '?'); + } + + /** + * Create service + * + * @param ContainerInterface $container + * @param string $requestedName + * @param array|null $options + * @return object|System + */ + public function __invoke(ContainerInterface $container, $requestedName, array $options = null) + { + /** + * @var Ldap $ldap + * @var Service\Structure $ldapStructureService + */ + $ldap = $container->get('ldap'); + $ldapStructureService = $container->get('ldapServiceStructure'); + + $service = new Root(); + $service->setLdap($ldap); + $service->setLdapStructureService($ldapStructureService); + + return $service; + } +} diff --git a/src/UnicaenLdap/Service/Structure.php b/src/UnicaenLdap/Service/Structure.php index 37d5bf16dfea813a49e611dff966de435eeb7641..d88345f0f4650acb6a5c4342fc8bc1a1a9ed3319 100644 --- a/src/UnicaenLdap/Service/Structure.php +++ b/src/UnicaenLdap/Service/Structure.php @@ -2,6 +2,9 @@ namespace UnicaenLdap\Service; +use UnicaenLdap\Entity\Entity; +use UnicaenLdap\Entity\Structure as StructureEntity; + /** * Classe regroupant les opérations de recherche de structures dans l'annuaire LDAP. * @@ -9,17 +12,71 @@ namespace UnicaenLdap\Service; */ class Structure extends AbstractService { + /** + * Type de l'entité + * + * @var string + */ protected $type = 'Structure'; - protected $ou = array('structures'); + /** + * Organizational Units + * + * @var array + */ + protected $ou = [ + 'structures' + ]; + + /** + * Préfixe utilisé pour le code des structures + * + * @var string + */ + protected $code_structure_prefixe = 'HS_'; + + /** + * Préfixe utilisé pour le code des modules d'enseignement + * + * @var string + */ + protected $code_module_enseignement_prefixe = 'AE_'; + + /** + * Code de la structure lié à l'établissement + * + * @var string + */ + protected $code_structure_mere = 'UNIV'; + + + /** + * Retourne le préfixe utilisé pour les codes des structures + * + * @return string + */ + public function getCodeStructurePrefixe() + { + return $this->code_structure_prefixe; + } + + /** + * Retourne le préfixe utilisé pour les codes des modules d'enseignement + * + * @return string + */ + public function getCodeModuleEnseignementPrefixe() + { + return $this->code_module_enseignement_prefixe; + } /** * Retourne la structure mère : Université * - * @return \UnicaenLdap\Entity\Structure + * @return StructureEntity */ - public function getUniv() + public function getStructureMere() { - return $this->get('HS_UNIV'); + return $this->get(sprintf('%s%s', $this->code_structure_prefixe, $this->code_structure_mere)); } } \ No newline at end of file diff --git a/src/UnicaenLdap/Service/StructureFactory.php b/src/UnicaenLdap/Service/StructureFactory.php index 7759770f09479d5130bd90bc1edefe42f9dfa54b..5bbf00586ff602612367701290dbe2b20a7a0107 100644 --- a/src/UnicaenLdap/Service/StructureFactory.php +++ b/src/UnicaenLdap/Service/StructureFactory.php @@ -32,23 +32,26 @@ class StructureFactory implements FactoryInterface { /** * @var Ldap $ldap - * @var Service\People $ldapPeopleService + * @var Service\Generic $ldapGenericService * @var Service\Group $ldapGroupService + * @var Service\People $ldapPeopleService + * @var Service\Root $ldapRootService * @var Service\System $ldapSystemService - * @var Service\Generic $ldapGenericService */ - $ldap = $container->get('ldap'); - $ldapPeopleService = $container->get('ldapServicePeople'); + $ldap = $container->get('ldap'); + $ldapGenericService = $container->get('ldapServiceGeneric'); $ldapGroupService = $container->get('ldapServiceGroup'); + $ldapPeopleService = $container->get('ldapServicePeople'); + $ldapRootService = $container->get('ldapServiceRoot'); $ldapSystemService = $container->get('ldapServiceSystem'); - $ldapGenericService = $container->get('ldapServiceGeneric'); $service = new Structure(); $service->setLdap($ldap); - $service->setLdapPeopleService($ldapPeopleService); + $service->setLdapGenericService($ldapGenericService); $service->setLdapGroupService($ldapGroupService); + $service->setLdapPeopleService($ldapPeopleService); + $service->setLdapRootService($ldapRootService); $service->setLdapSystemService($ldapSystemService); - $service->setLdapGenericService($ldapGenericService); return $service; } diff --git a/src/UnicaenLdap/Service/System.php b/src/UnicaenLdap/Service/System.php index 2e5d54760db8737e026fbb49c97a8895e67d78c3..7f72614887c87475fc1b02c94828b21f6eb0341b 100644 --- a/src/UnicaenLdap/Service/System.php +++ b/src/UnicaenLdap/Service/System.php @@ -9,7 +9,19 @@ namespace UnicaenLdap\Service; */ class System extends AbstractService { + /** + * Type de l'entité + * + * @var string + */ protected $type = 'System'; - protected $ou = array('system'); + /** + * Organizational Units + * + * @var array + */ + protected $ou = [ + 'system' + ]; } \ No newline at end of file diff --git a/src/UnicaenLdap/Service/SystemFactory.php b/src/UnicaenLdap/Service/SystemFactory.php index 73953d8d7534440506ccac06b98418db92ddf187..e08a2c254a54d36ab15d8efa42bfc6ecea68c502 100644 --- a/src/UnicaenLdap/Service/SystemFactory.php +++ b/src/UnicaenLdap/Service/SystemFactory.php @@ -32,22 +32,25 @@ class SystemFactory implements FactoryInterface { /** * @var Ldap $ldap - * @var Service\People $ldapPeopleService - * @var Service\Structure $ldapStructureService * @var Service\Group $ldapGroupService * @var Service\Generic $ldapGenericService + * @var Service\People $ldapPeopleService + * @var Service\Root $ldapRootService + * @var Service\Structure $ldapStructureService */ - $ldap = $container->get('ldap'); + $ldap = $container->get('ldap'); + $ldapGenericService = $container->get('ldapServiceGeneric'); + $ldapGroupService = $container->get('ldapServiceGroup'); $ldapPeopleService = $container->get('ldapServicePeople'); + $ldapRootService = $container->get('ldapServiceRoot'); $ldapStructureService = $container->get('ldapServiceStructure'); - $ldapGroupService = $container->get('ldapServiceGroup'); - $ldapGenericService = $container->get('ldapServiceGeneric'); $service = new System(); $service->setLdap($ldap); $service->setLdapPeopleService($ldapPeopleService); $service->setLdapStructureService($ldapStructureService); $service->setLdapGroupService($ldapGroupService); + $service->setLdapRootService($ldapRootService); $service->setLdapGenericService($ldapGenericService); return $service; diff --git a/src/UnicaenLdap/Util.php b/src/UnicaenLdap/Util.php new file mode 100644 index 0000000000000000000000000000000000000000..4791e52b3f4bb21426710352ef0daa0921f4d39f --- /dev/null +++ b/src/UnicaenLdap/Util.php @@ -0,0 +1,113 @@ +<?php + +namespace UnicaenLdap; + +class Util +{ + static public function removeAccents($string) + { + if (!preg_match('/[\x80-\xff]/', $string)) + return $string; + + $chars = [ + // Decompositions for Latin-1 Supplement + chr(195) . chr(128) => 'A', chr(195) . chr(129) => 'A', + chr(195) . chr(130) => 'A', chr(195) . chr(131) => 'A', + chr(195) . chr(132) => 'A', chr(195) . chr(133) => 'A', + chr(195) . chr(135) => 'C', chr(195) . chr(136) => 'E', + chr(195) . chr(137) => 'E', chr(195) . chr(138) => 'E', + chr(195) . chr(139) => 'E', chr(195) . chr(140) => 'I', + chr(195) . chr(141) => 'I', chr(195) . chr(142) => 'I', + chr(195) . chr(143) => 'I', chr(195) . chr(145) => 'N', + chr(195) . chr(146) => 'O', chr(195) . chr(147) => 'O', + chr(195) . chr(148) => 'O', chr(195) . chr(149) => 'O', + chr(195) . chr(150) => 'O', chr(195) . chr(153) => 'U', + chr(195) . chr(154) => 'U', chr(195) . chr(155) => 'U', + chr(195) . chr(156) => 'U', chr(195) . chr(157) => 'Y', + chr(195) . chr(159) => 's', chr(195) . chr(160) => 'a', + chr(195) . chr(161) => 'a', chr(195) . chr(162) => 'a', + chr(195) . chr(163) => 'a', chr(195) . chr(164) => 'a', + chr(195) . chr(165) => 'a', chr(195) . chr(167) => 'c', + chr(195) . chr(168) => 'e', chr(195) . chr(169) => 'e', + chr(195) . chr(170) => 'e', chr(195) . chr(171) => 'e', + chr(195) . chr(172) => 'i', chr(195) . chr(173) => 'i', + chr(195) . chr(174) => 'i', chr(195) . chr(175) => 'i', + chr(195) . chr(177) => 'n', chr(195) . chr(178) => 'o', + chr(195) . chr(179) => 'o', chr(195) . chr(180) => 'o', + chr(195) . chr(181) => 'o', chr(195) . chr(182) => 'o', + chr(195) . chr(182) => 'o', chr(195) . chr(185) => 'u', + chr(195) . chr(186) => 'u', chr(195) . chr(187) => 'u', + chr(195) . chr(188) => 'u', chr(195) . chr(189) => 'y', + chr(195) . chr(191) => 'y', + // Decompositions for Latin Extended-A + chr(196) . chr(128) => 'A', chr(196) . chr(129) => 'a', + chr(196) . chr(130) => 'A', chr(196) . chr(131) => 'a', + chr(196) . chr(132) => 'A', chr(196) . chr(133) => 'a', + chr(196) . chr(134) => 'C', chr(196) . chr(135) => 'c', + chr(196) . chr(136) => 'C', chr(196) . chr(137) => 'c', + chr(196) . chr(138) => 'C', chr(196) . chr(139) => 'c', + chr(196) . chr(140) => 'C', chr(196) . chr(141) => 'c', + chr(196) . chr(142) => 'D', chr(196) . chr(143) => 'd', + chr(196) . chr(144) => 'D', chr(196) . chr(145) => 'd', + chr(196) . chr(146) => 'E', chr(196) . chr(147) => 'e', + chr(196) . chr(148) => 'E', chr(196) . chr(149) => 'e', + chr(196) . chr(150) => 'E', chr(196) . chr(151) => 'e', + chr(196) . chr(152) => 'E', chr(196) . chr(153) => 'e', + chr(196) . chr(154) => 'E', chr(196) . chr(155) => 'e', + chr(196) . chr(156) => 'G', chr(196) . chr(157) => 'g', + chr(196) . chr(158) => 'G', chr(196) . chr(159) => 'g', + chr(196) . chr(160) => 'G', chr(196) . chr(161) => 'g', + chr(196) . chr(162) => 'G', chr(196) . chr(163) => 'g', + chr(196) . chr(164) => 'H', chr(196) . chr(165) => 'h', + chr(196) . chr(166) => 'H', chr(196) . chr(167) => 'h', + chr(196) . chr(168) => 'I', chr(196) . chr(169) => 'i', + chr(196) . chr(170) => 'I', chr(196) . chr(171) => 'i', + chr(196) . chr(172) => 'I', chr(196) . chr(173) => 'i', + chr(196) . chr(174) => 'I', chr(196) . chr(175) => 'i', + chr(196) . chr(176) => 'I', chr(196) . chr(177) => 'i', + chr(196) . chr(178) => 'IJ', chr(196) . chr(179) => 'ij', + chr(196) . chr(180) => 'J', chr(196) . chr(181) => 'j', + chr(196) . chr(182) => 'K', chr(196) . chr(183) => 'k', + chr(196) . chr(184) => 'k', chr(196) . chr(185) => 'L', + chr(196) . chr(186) => 'l', chr(196) . chr(187) => 'L', + chr(196) . chr(188) => 'l', chr(196) . chr(189) => 'L', + chr(196) . chr(190) => 'l', chr(196) . chr(191) => 'L', + chr(197) . chr(128) => 'l', chr(197) . chr(129) => 'L', + chr(197) . chr(130) => 'l', chr(197) . chr(131) => 'N', + chr(197) . chr(132) => 'n', chr(197) . chr(133) => 'N', + chr(197) . chr(134) => 'n', chr(197) . chr(135) => 'N', + chr(197) . chr(136) => 'n', chr(197) . chr(137) => 'N', + chr(197) . chr(138) => 'n', chr(197) . chr(139) => 'N', + chr(197) . chr(140) => 'O', chr(197) . chr(141) => 'o', + chr(197) . chr(142) => 'O', chr(197) . chr(143) => 'o', + chr(197) . chr(144) => 'O', chr(197) . chr(145) => 'o', + chr(197) . chr(146) => 'OE', chr(197) . chr(147) => 'oe', + chr(197) . chr(148) => 'R', chr(197) . chr(149) => 'r', + chr(197) . chr(150) => 'R', chr(197) . chr(151) => 'r', + chr(197) . chr(152) => 'R', chr(197) . chr(153) => 'r', + chr(197) . chr(154) => 'S', chr(197) . chr(155) => 's', + chr(197) . chr(156) => 'S', chr(197) . chr(157) => 's', + chr(197) . chr(158) => 'S', chr(197) . chr(159) => 's', + chr(197) . chr(160) => 'S', chr(197) . chr(161) => 's', + chr(197) . chr(162) => 'T', chr(197) . chr(163) => 't', + chr(197) . chr(164) => 'T', chr(197) . chr(165) => 't', + chr(197) . chr(166) => 'T', chr(197) . chr(167) => 't', + chr(197) . chr(168) => 'U', chr(197) . chr(169) => 'u', + chr(197) . chr(170) => 'U', chr(197) . chr(171) => 'u', + chr(197) . chr(172) => 'U', chr(197) . chr(173) => 'u', + chr(197) . chr(174) => 'U', chr(197) . chr(175) => 'u', + chr(197) . chr(176) => 'U', chr(197) . chr(177) => 'u', + chr(197) . chr(178) => 'U', chr(197) . chr(179) => 'u', + chr(197) . chr(180) => 'W', chr(197) . chr(181) => 'w', + chr(197) . chr(182) => 'Y', chr(197) . chr(183) => 'y', + chr(197) . chr(184) => 'Y', chr(197) . chr(185) => 'Z', + chr(197) . chr(186) => 'z', chr(197) . chr(187) => 'Z', + chr(197) . chr(188) => 'z', chr(197) . chr(189) => 'Z', + chr(197) . chr(190) => 'z', chr(197) . chr(191) => 's' + ]; + + $string = strtr($string, $chars); + + return $string; + } +} \ No newline at end of file