| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 | 
							- <?php
 
- /**
 
-  * Define a Serveur active directory
 
-  * @author Jean CARRUESCO
 
-  * @category Plugin
 
-  * @license MIT
 
-  */
 
- class AdServer extends Entity{
 
- 	public $id;
 
- 	public $ip; //IP (Texte)
 
- 	public $port = 389; //Port (Nombre Entier)
 
- 	public $sslPort = 636; //Port SSL (Nombre Entier)
 
- 	public $protocolVersion = 3; //Version de protocole ldap (Texte)
 
- 	public $domain; //Domaine (Texte)
 
- 	public $userRoot; //Racine des comptes utilisateurs (Texte Long)
 
- 	public $groupRoot; //Racine des groupes utilisateurs (Texte Long)
 
- 	public $readonlyLogin; //Identifiant du compte lecture seule (Texte)
 
- 	public $readonlyPassword; //Mot de passe du compte lecture seule (Mot de passe)
 
- 	public $adminLogin; //Identifiant compte administrateur (Texte)
 
- 	public $adminPassword; //Mot de passe compte administrateur (Mot de passe)
 
- 	public $mapping; //Plan de liaisons des champs (Texte Long)
 
- 	public $authenticationMode = 'samaccountname'; //Authentication mode (Texte)
 
- 	
 
- 	private $datasource; //Ressource AD
 
- 	
 
- 	protected $TABLE_NAME = 'activedirectory_ad_server';
 
- 	public $entityLabel = 'Serveur active directory';
 
- 	public $fields = array(
 
- 		'id' => array('type'=>'key', 'label' => 'Identifiant'),
 
- 		'ip' => array('type'=>'text','label' => 'IP'),
 
- 		'port' => array('type'=>'integer','label' => 'Port'),
 
- 		'sslPort' => array('type'=>'integer','label' => 'Port SSL'),
 
- 		'protocolVersion' => array('type'=>'text','label' => 'Version de protocole ldap'),
 
- 		'domain' => array('type'=>'text','label' => 'Domaine'),
 
- 		'userRoot' => array('type'=>'textarea','label' => 'Racine des comptes utilisateurs'),
 
- 		'groupRoot' => array('type'=>'textarea','label' => 'Racine des groupes utilisateurs'),
 
- 		'readonlyLogin' => array('type'=>'text','label' => 'Identifiant du compte lecture seule'),
 
- 		'readonlyPassword' => array('type'=>'password','label' => 'Mot de passe du compte lecture seule'),
 
- 		'adminLogin' => array('type'=>'text','label' => 'Identifiant compte administrateur'),
 
- 		'adminPassword' => array('type'=>'password','label' => 'Mot de passe compte administrateur'),
 
- 		'mapping' => array('type'=>'textarea','label' => 'Plan de liaisons des champs'),
 
- 		'authenticationMode' => array('type'=>'text','label' => 'Authentication mode'),
 
- 	
 
- 	);
 
- 	//retourne le mapping champ ad -> champ ustilisateur de base 
 
- 	function default_mapping(){
 
- 		$mapping = array(
 
- 			'sn'=>array('label'=>'Nom','field'=>'name','source'=>'active_directory'),
 
- 			'givenname'=>array('label'=>'Prénom','field'=>'firstname','source'=>'active_directory'),
 
- 			'mail'=>array('label'=>'E-mail','field'=>'mail','source'=>'active_directory'),
 
- 			'telephonenumber'=>array('label'=>'Téléphone fixe','field'=>'phone','source'=>'active_directory'),
 
- 			'mobile'=>array('label'=>'Téléphone mobile','field'=>'mobile','source'=>'active_directory'),
 
- 			'title'=>array('label'=>'Fonction','field'=>'function','source'=>'active_directory'),
 
- 			//prend le samaccountname ou le userprincipalname en fct de la conf
 
- 			'samaccountname'=>array('label'=>'samaccountname','field'=>function(&$user,$infos){ 
 
- 				$user->login = str_replace($this->domain,'',mb_strtolower($infos[$this->authenticationMode][0]));
 
- 			},'source'=>'active_directory'),
 
- 			'origin'=>array('label'=>'samaccountname','field'=>'origin','value'=>'active_directory','source'=>'hardcoded'),
 
- 			'department'=>array('label'=>'Nom','field'=>'service','source'=>'active_directory'),
 
- 			'thumbnailphoto'=>array('label'=>'Photo 1','field'=>function(&$user,$infos){ 
 
- 				$user->meta['ldap_avatar'] = base64_encode($infos['thumbnailphoto'][0]);
 
- 			},'source'=>'active_directory'),
 
- 			'jpegphoto'=>array('label'=>'Photo 2','field'=>function(&$user,$infos){ 
 
- 				$user->meta['ldap_avatar'] = base64_encode($infos['jpegphoto'][0]);
 
- 			},'source'=>'active_directory'),
 
- 			'accountexpires'=>array('label'=>'Expiration','field'=>function(&$user,$infos){ 
 
- 				$seconds = (float)($infos['accountexpires'][0] / 10000000);
 
- 				//Convertion d'un timestamp AD en timestamp UNIX
 
- 				$timestamp = round($seconds - (((1970-1601) * 365.242190) * 86400));
 
- 				$user->meta['expiration'] = $timestamp;
 
- 			},'source'=>'active_directory'),
 
- 			//on traite memberof de manière particuliere donc pas de field
 
- 			'memberof'=>array('label'=>'Membre de','field'=>'','source'=>'active_directory'),
 
- 			'manager'=>array('label'=>'Manager','field'=>'manager','source'=>'active_directory'),
 
- 			'userprincipalname'=>array('label'=>'userprincipalname','field'=>'','source'=>'active_directory'),
 
- 			'whencreated'=>array('label'=>'Date création','field'=>function(&$user,$infos){ 
 
- 				if(!isset($infos['whencreated'][0]) || strlen($infos['whencreated'][0])<12 ) return;
 
- 				$created = substr($infos['whencreated'][0],0,8).' '.substr($infos['whencreated'][0],8,2).':'.substr($infos['whencreated'][0],10,2);
 
- 				$user->created = strtotime($created);
 
- 			},'source'=>'active_directory')
 
- 				
 
- 		);
 
- 		return $mapping;
 
- 	}
 
- 	//retourn les mapping champ ad->champ user par défaut + customs
 
- 	function getMapping(){
 
- 		$mapping = array();
 
- 		
 
- 		$jsonmapping = json_decode($this->mapping,true) ;
 
- 		if(is_array($jsonmapping)){
 
- 			foreach(json_decode($this->mapping,true) as $json){
 
- 				if(!isset($json['slug'])) continue;
 
- 				$json['source'] = 'active_directory';
 
- 				$mapping[$json['slug']]= $json;
 
- 			}
 
- 		}
 
- 	
 
- 		$mapping = array_merge(self::default_mapping(),$mapping);
 
- 		return $mapping;
 
- 	}
 
- 	//Gestion des modes d'authentification possibles
 
- 	function authenticationModes($key=null){
 
- 		$items = array(
 
- 			'samaccountname' => array('label'=>'SamAccountName','filter'=>function($server,$login){
 
- 				return "(&(samaccountname=".$login."))(objectClass=user)(objectCategory=person))";
 
- 			}),
 
- 			'userprincipalname' => array('label'=>'UserPrincipalName','filter'=>function($server,$login){
 
- 				return "(&(userprincipalname=".$login.$server->domain.")(objectClass=user)(objectCategory=person))";
 
- 			}),
 
- 		);
 
- 		if(!isset($key)) return $items;
 
- 		return isset($items[$key]) ? $items[$key] : array('label'=>'Non définis','filter'=>function($server,$login){});
 
- 	}
 
- 	//connexion ad
 
- 	function login($options = array()){
 
- 		$options = array_merge(array(
 
- 			'mode' => 'readonly',
 
- 			'ssl' => false
 
- 		),$options);
 
- 		putenv('LDAPTLS_REQCERT=never');
 
- 		$port = $options['ssl'] ? $this->sslPort : $this->port ;
 
- 		$ip = $this->ip;
 
- 		$ip = 'ldap'.($options['ssl']? 's' :'').'://'.$ip;
 
- 		$ip .= ':'.$port;
 
- 		if($ip==null || $port==null || $this->userRoot==null) throw new Exception('Paramètres de connexion manquants',400);
 
- 		$this->datasource = ldap_connect($ip);
 
- 		if(!$this->datasource) throw new Exception('Connexion échouée', 404);	
 
- 		ldap_set_option($this->datasource,LDAP_OPT_PROTOCOL_VERSION,$this->protocolVersion);
 
- 		ldap_set_option($this->datasource, LDAP_OPT_REFERRALS, 0);
 
- 		
 
- 		switch($options['mode']){
 
- 			case 'admin':
 
- 				$login = $this->adminLogin;
 
- 				$password = decrypt($this->adminPassword);
 
- 			break;
 
- 			case 'readonly':
 
- 				$login = $this->readonlyLogin;
 
- 				$password = decrypt($this->readonlyPassword);
 
- 			break;
 
- 			case 'login':
 
- 				$login = $options['login'];
 
- 				$password = $options['password'];
 
- 			break;
 
- 		}
 
- 	
 
- 		try{
 
- 			if(@ldap_bind($this->datasource,$login,$password) == false)
 
- 				throw new Exception(ldap_error($this->datasource), 403);
 
- 			
 
- 		}catch(Exception $e){
 
- 			if(ldap_errno($this->datasource)==49){
 
- 				throw new Exception('Identifiant ou mot de passe incorrect', 403);
 
- 			}else{
 
- 				throw new Exception('Connexion échouée :'.ldap_error($this->datasource), 404);
 
- 			}
 
- 			
 
- 		}
 
- 	}
 
- 	/**
 
- 	 * Recherche des valeurs dans la base de données en fonction du filter
 
- 	 * @param <String> Racine contexte de la recherche
 
- 	 * @param <String> Filtre contenant les éléments a rechercher
 
- 	 * @param <Array> Liste des attributs à récupérer
 
- 	 * @return <Array> tableau contenant les objets correspondants a la recherche
 
- 	 */
 
- 	public function search($dns, $filter=null, $attributes=array(),$limit=0){
 
- 		$searches = ldap_search( array_fill(0, count($dns), $this->datasource) , $dns,$filter,$attributes,0,$limit);
 
- 		$infos = array();
 
- 		foreach($searches as $search){
 
- 			if($search === false) continue;
 
- 	    	$infos = array_merge($infos ,ldap_get_entries($this->datasource, $search));
 
- 		}
 
- 		return $infos;
 
- 	}
 
- 	//Déconnexion ad
 
- 	function logout(){
 
- 		if($this->datasource!=null) @ldap_close($this->datasource);
 
- 	}
 
- 	//Récuperation des utilisateurs
 
- 	function users($filters = array(),$options = array()){
 
- 		require_once(__DIR__.SLASH.'AdFirmRank.class.php');
 
- 		$mapping = $this->getMapping();
 
- 		$options = array_merge(array(
 
- 			'limit' => 0, // limit de users (0= pas de limite)
 
- 			'select' => array_keys($mapping), //Colonnes a cibler
 
- 			'group' => true, // charger les groupes ad
 
- 			'rank' => true, // charger les rank et les firms
 
- 			'manager' => false, // charger les managers en tant qu'objet user
 
- 			'activeOnly' => true //n'afficher que les comptes non expirés
 
- 		),$options);
 
- 		$groups = $options['group'] ? $this->groups() : array();
 
- 		$ranks = array();
 
- 		if($options['rank'] ){
 
- 			foreach(AdFirmRank::loadAll( array(), null,  null,  array('*'), 1)  as $firmRank){
 
- 				if(!isset($ranks[$firmRank->group])) $ranks[$firmRank->group] = array();
 
- 				$ranks[$firmRank->group][] = $firmRank;
 
- 			}
 
- 		}
 
- 		$users = array();
 
- 		$default_filters = array();
 
- 		$filterChain = '';
 
- 		
 
- 		$default_filters['objectClass'] = 'user';
 
- 		$default_filters['objectCategory'] = 'person';
 
- 		$filters = array_merge($default_filters,$filters);
 
- 		if(count($filters)>1) $filterChain ='(&';
 
- 	
 
- 		foreach($filters as $key=>$value)
 
- 			$filterChain .= '('.$key.'='.$value.')';
 
- 		
 
- 		if(count($filters)>1) $filterChain .= ')';
 
- 		$entries = $this->search(explode(PHP_EOL,$this->userRoot),$filterChain,$options['select'],$options['limit']);
 
- 		if(count($entries)==0 || $entries['count']==0)  return $users;
 
- 		if(!isset($entries[0])) return $users;
 
- 		$usersDn = array();
 
- 		for($i=0; $i<$entries['count']; $i++){
 
- 			$entry = $entries[$i];
 
- 			//Vérifie que le compte n'est pas expiré (nb : 0 et 9223372036854775807 sont les deux valeurs possibles pour un n'expire jamais (allez comprendre la logique microsoft...))
 
- 			if($options['activeOnly'] && isset($entry['accountexpires'][0]) && $entry['accountexpires'][0]!=0 && $entry['accountexpires'][0]!=9223372036854775807){
 
- 				//Convertion en seconds
 
- 				$seconds = (float)($entry['accountexpires'][0] / 10000000);
 
- 				//Convertion d'un timestamp AD en timestamp UNIX
 
- 				$timestamp = round($seconds - (((1970-1601) * 365.242190) * 86400));
 
- 			    if($timestamp <= time()) continue;
 
- 		    }
 
- 			$user = self::user_object($entry,$mapping,$options,$groups,$ranks);
 
- 			//on stocke l'id de l'ad ayant provisionné ce user dans les méta pour réutilisations ulterieure (ex save du mdp)
 
- 			$user->meta['activedirectory_server'] = $this->id;
 
- 			$user->meta = json_encode($user->meta);
 
- 			if($options['manager']) $usersDn[$entry['dn']] = $user;
 
- 			$users[$user->login] = $user;
 
- 		}
 
- 		//Chargement des managers
 
- 		if($options['manager']){
 
- 			foreach($users as $key=>$user){
 
- 				if(empty($user->manager)) continue;
 
- 				if(isset($usersDn[$user->manager])){
 
- 					$user->manager = $usersDn[$user->manager];
 
- 				}else{
 
- 					$manager = $this->users(array(
 
- 					'dn' => $user->manager
 
- 					),array(
 
- 						'limit' => 1, // limit de users (0= pas de limite)
 
- 						'group' => true, // charger les groupes ad
 
- 						'rank' => true, // charger les rank et les firms
 
- 						'rights' => false, // charger les rank et les firms
 
- 						'manager' => true, // charger les managers en tant qu'objet user
 
- 						'activeOnly' => true //n'afficher que les comptes non expirés
 
- 					));
 
- 					$user->manager = array_shift($manager);
 
- 				}
 
- 				$users[$key] = $user;
 
- 			}
 
- 		}
 
- 		
 
- 		return $users;
 
- 	}
 
- 	//Remplis et return un objet user en fonction d'une entrée ad
 
- 	static function user_object($entry,$mapping,$options = array(),$groups=array(),$ranks=array()){
 
- 		$user = new User();
 
- 		//récuperation des attributs mappés (défaut et custom)
 
- 		foreach($mapping as $ldap=>$attribute){
 
- 			$field = $attribute['field'];
 
- 			if(empty($field )) continue;
 
- 			//si le champ doit contenir une valeur en dur (et non ldap ex : origin : active_directory)
 
- 			if($attribute['source']=='hardcoded' && !empty($attribute['value'])){
 
- 				$user->$field = $attribute['value'];
 
- 				continue;
 
- 			} 
 
- 			//si aucune correspondance trouvée dans l'entrée ldap on passe au suivant
 
- 			if(empty($entry[$ldap]) || empty($entry[$ldap][0])) continue;
 
- 			
 
- 			//si le field définit est une méthode anonyme
 
- 			if(!is_string($field)){
 
- 				$field($user,$entry);
 
- 			//si le field définit est un simple attribut
 
- 			}else{
 
- 				
 
- 				if(property_exists($user,$field)){
 
- 					$user->$field = $entry[$ldap][0];
 
- 				}else{
 
- 					$user->meta[$field] = $entry[$ldap][0];
 
- 				}
 
- 			}
 
- 		}
 
- 		
 
- 		//Récuperation des groupes direct et indirects
 
- 		$user->groups = array();
 
- 		if(isset($entry['memberof'])){
 
- 			for($i=0; $i<count($entry['memberof'])-1; ++$i){
 
- 				if(empty($groups[$entry['memberof'][$i]])) continue;
 
- 				self::group_recursive_fill($user,$groups,$entry['memberof'][$i],$ranks);
 
- 			}
 
- 		}
 
- 		return $user;
 
- 	}
 
- 	//ajout les groupes a l'utilisateur de manière récursive (un groupe peut être parent d'un autre)
 
- 	static function group_recursive_fill(&$user,$groups,$dn,$ranks){
 
- 		if(!isset($groups[$dn])) return;
 
- 		$groupItem = $groups[$dn];
 
- 		$user->groups[] = $groupItem['label'];
 
- 		if(isset($ranks[$groupItem['label']])){
 
- 			
 
- 			foreach($ranks[$groupItem['label']] as $firmrank){
 
- 				$user->ranks[$firmrank->firm][$firmrank->rank] = $firmrank->join('rank');
 
- 				$user->firms[$firmrank->firm] = $firmrank->join('firm');
 
- 			}
 
- 			
 
- 		}
 
- 		foreach($groupItem['memberof'] as $parentDn){
 
- 			self::group_recursive_fill($user,$groups,$parentDn,$ranks);
 
- 		}
 
- 	}
 
- 	function userChange($login,$changes){
 
- 		switch($this->authenticationMode){
 
- 			case 'samaccountname':
 
- 				$filter = "(".$this->authenticationMode."=".$login.")";
 
- 			break;
 
- 			case 'userprincipalname':
 
- 			default:
 
- 				$filter= "(".$this->authenticationMode."=".$login.$this->domain.")";
 
- 			break;
 
- 		}
 
- 		$filter = '(&'.$filter.'(objectClass=user)(objectCategory=person))';
 
- 		$entries= $this->search(array($this->userRoot),$filter);
 
- 		$cn =  (isset($entries[0])) ? $entries[0]['dn'] : false;
 
- 		if($cn == false) throw new Exception('Impossible de trouver l\'utilisateur sur ce serveur');
 
- 		putenv('LDAPTLS_REQCERT=never');
 
- 		foreach($changes as $adfield=>$value){
 
- 			if($adfield == 'password') continue;
 
- 			if($value != ''){
 
- 				$attributes[$adfield][0] = $value;
 
- 				return ldap_modify($this->datasource,$cn,$attributes);
 
- 			}else{
 
- 				//evite le crash si l'attribut n'existe pas lors d'une supression
 
- 				$attributes[$adfield] ='0';
 
- 				ldap_modify($this->datasource,$cn,$attributes);
 
- 				$attributes[$adfield] = array();
 
- 				return ldap_mod_del($this->datasource,$cn,$attributes);
 
- 			}
 
- 		}
 
- 		if(isset($changes['password'])){
 
- 			if (!ldap_mod_replace($this->datasource, $cn ,  self::encrypt_password($changes['password']))) throw new Exception("Impossible de modifier le mot de passe : ".ldap_error($this->datasource));
 
- 		}
 
- 	}
 
- 	public static function encrypt_password($newPassword){
 
- 		$newPassword = "\"" . $newPassword . "\"";
 
- 		$len = strlen( $newPassword );
 
- 		$newPassw = "";
 
- 		for ( $i = 0; $i < $len; $i++ )
 
- 			$newPassw .= $newPassword[$i]."\000";
 
- 		return array("unicodePwd" => $newPassw);
 
- 	}
 
- 	//Récuperation des groupes ad
 
- 	function groups($filters = array()){
 
- 		$groups = array();
 
- 		$default_filters = array();
 
- 		$filterChain = '';
 
- 		
 
- 		$default_filters['objectClass'] = 'group';
 
- 		$filters = array_merge($default_filters,$filters);
 
- 		if(count($filters)>1) $filterChain ='(&';
 
- 	
 
- 		foreach($filters as $key=>$value)
 
- 			$filterChain .= '('.$key.'='.$value.')';
 
- 		
 
- 		if(count($filters)>1) $filterChain .= ')';
 
- 		
 
- 		$entries = $this->search(explode(PHP_EOL,$this->groupRoot),$filterChain,array('name','memberOf'));
 
- 		
 
- 		for($i=0; $i<$entries['count']; $i++){
 
- 			$info = $entries[$i];
 
- 		
 
- 			$group = array(
 
- 				'label' => $info['name'][0],
 
- 				'dn' => $info['dn'],
 
- 				'memberof' => array()
 
- 			);
 
- 			//on récupere les groupes parents
 
- 			if(isset($info['memberof'])){
 
- 				for($u=0; $u<count($info['memberof'])-1; ++$u){
 
- 					//on ne prend que les groupes parent qui font partie de la racine groupe
 
- 					if(strrpos($info['memberof'][$u],$this->groupRoot)===false) continue;
 
- 					$group['memberof'][] = $info['memberof'][$u];
 
- 				}
 
- 			}
 
- 			$groups[$group['dn']] = $group;
 
- 		}
 
- 		uasort($groups,function($a,$b){
 
- 			return $a['label'] > $b['label'];
 
- 		});
 
- 		return $groups;
 
- 	}
 
- }
 
- ?>
 
 
  |