'key', 'login' => array('label'=>'Identifiant','type'=>'string'), 'password' => array('label'=>'Mot de passe','type'=>'password'), 'name' => array('label'=>'Nom','type'=>'string'), 'function' => array('label'=>'Fonction','type'=>'string'), 'firstname' => array('label'=>'Prénom','type'=>'text'), 'token' => array('label'=>'Token','type'=>'string'), 'mail' => array('label'=>'E-mail','type'=>'mail'), 'state' => array('label'=>'Etat','type'=>'string'), 'phone' => array('label'=>'Téléphone','type'=>'phone'), 'mobile' => array('label'=>'Téléphone portable','type'=>'phone'), 'manager' => array('label'=>'Manager','type'=>'user'), 'origin' => array('label'=>'Origine','type'=>'text'), 'service' => array('label'=>'Service','type'=>'text'), 'meta' => array('label'=>'Meta informations','type'=>'text') ); function __construct(){ global $myUser, $myFirm, $conf; parent::__construct(); $this->token = self::generateToken(); $this->meta = array(); $this->ranks = array(); $this->firms = array(); } public static function getAll($options = array()){ $users = array(); $options = array_merge(array( 'right' => true, 'manager' => true, 'force' =>false ),$options); if(isset($_SESSION['users_'.($options['right']?'rights':'norights')])) $users = unserialize($_SESSION['users_'.($options['right']?'rights':'norights')]); if(empty($users) || $options['force']){ $users = array(); //récuperation des users non db (plugin ad ...) Plugin::callHook('user_get_all',array(&$users,$options)); $logins = array(); //Récuperation des users db foreach (self::loadAll(array('state'=>User::ACTIVE), array(' name ASC ')) as $baseUser) $users[$baseUser->login] = $baseUser; //récuperation des logins concernés pour requettage foreach($users as $key=>$user){ //chargement des managers pour les origines db if($options['manager'] && empty($user->origin) && !empty($user->manager) && isset($users[$user->manager])){ $users[$key]->manager = $users[$user->manager]; } $logins[] = $user->login; } if($options['right']){ $ranksId = array(); //Ajout des rangs depuis les liens user/firm/rank foreach (UserFirmRank::loadAll(array('user:IN'=>$logins ), null, null, array('*'), 1) as $firmRank) { $rank = $firmRank->join('rank'); $firm = $firmRank->join('firm'); $ranksId[] = $rank->id; $users[$firmRank->user]->firms[$firm->id] = $firm; if(!isset($users[$firmRank->user]->ranks[$firmRank->firm])) $users[$firmRank->user]->ranks[$firmRank->firm] = array(); $users[$firmRank->user]->ranks[$firmRank->firm][$rank->id] = $rank; if(empty($users[$firmRank->user]->superadmin) && $rank->superadmin) $users[$firmRank->user]->superadmin = boolval($rank->superadmin); } //récuperation des droits ciblés sur user et rangs pour les user et rang user concernés $rights = Right::staticQuery('SELECT * FROM {{table}} WHERE (targetScope="user" AND targetUid IN ('.implode(',',array_fill(0,count($logins),'?')).')) OR (targetScope="rank" AND targetUid IN('.implode(',',array_fill(0,count($ranksId),'?')).')) ',array_merge($logins,$ranksId),true); $rightsForRank = array(); foreach($rights as $right): //pour les rangs on remplis un tableau de mapping qu'on redistribuera plus tard if($right->targetScope =='rank'){ if(!isset($rightsForRank[$right->targetUid])) $rightsForRank[$right->targetUid] = array(); $rightsForRank[$right->targetUid][]= $right; //pour les users }else{ //Pour le premier rang qui aborde cette scope on met tous les droits à false if(!isset($users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid])){ $users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid] = array( 'read' => false, 'edit' => false, 'delete' => false, 'recursive' => false, 'configure' => false ); } //Puis on complete uniquement les droits à true sur la scope pour chaques rangs additionnels if($right->read) $users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid]['read'] = true; if($right->edit) $users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid]['edit'] = true; if($right->delete) $users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid]['delete'] = true; if($right->configure) $users[$right->targetUid]->rights[$right->scope][$right->firm][$right->uid]['configure'] = true; } endforeach; //pour chaque user foreach($users as $key=>$user){ //onrecupere les firms foreach($user->ranks as $firmrank){ //on liste les rangs du user pour cette firm foreach($firmrank as $rank){ //si un de ces rangs possede des droits liés if(!isset($rightsForRank[$rank->id])) continue; //pour chaque droit liées on les applique au user foreach($rightsForRank[$rank->id] as $right){ //Pour le premier rang qui aborde cette scope on met tous les droits à false if(!isset($users[$key]->rights[$right->scope][$right->firm][$right->uid])){ $users[$key]->rights[$right->scope][$right->firm][$right->uid] = array( 'read' => false, 'edit' => false, 'delete' => false, 'recursive' => false, 'configure' => false ); } //Puis on complete uniquement les droits à true sur la scope pour chaques rangs additionnels if($right->read) $users[$key]->rights[$right->scope][$right->firm][$right->uid]['read'] = true; if($right->edit) $users[$key]->rights[$right->scope][$right->firm][$right->uid]['edit'] = true; if($right->delete) $users[$key]->rights[$right->scope][$right->firm][$right->uid]['delete'] = true; if($right->configure) $users[$key]->rights[$right->scope][$right->firm][$right->uid]['configure'] = true; } } } } } //sort des users par noms uasort($users, function($a, $b){ return strcmp($a->name, $b->name); }); $_SESSION['users_'.($options['right']?'rights':'norights')] = serialize($users); } return $users; } public function __sleep(){ $fields = $this->toArray(); unset($fields['fullName'], $fields['initials']); return array_merge(array('superadmin','rights','groups','ranks','firms','preferences'),array_keys($fields)); } //Surchage de toArray pour prendre en compte le fullName et les initials régulierement utilisé en toArray public function toArray($decoded=false) { $fields = parent::toArray($decoded); $fields['fullName'] = $this->fullName(); $fields['initials'] = $this->initials(); unset($fields['password']); unset($fields['token']); if(isset($fields['manager']) && is_object($fields['manager'])) $fields['manager'] = $fields['manager']->toArray(); if($decoded){ $fields['fullName'] = html_entity_decode($fields['fullName']); $fields['initials'] = html_entity_decode($fields['initials']); } return $fields; } // $this->rights[$right->scope][$right->uid][$right->firm] public function can($scope,$right,$uid=0,$options = array()){ if($this->superadmin == 1) return true; global $myFirm; $firm = is_object($myFirm) && $myFirm->id !=0 ? $myFirm->id : 0; // Recherche dans les droits ciblé sur la firm courante if(isset($this->rights[$scope][$firm][$uid][$right])) return $this->rights[$scope][$firm][$uid][$right]==1; // Recherche dans les droits non ciblés sur une firm if(isset($this->rights[$scope][0][$uid][$right])) return $this->rights[$scope][0][$uid][$right]==1; if(isset($options['contains']) && $options['contains'] == true){ if(isset($this->rights[$scope])) { $rightArray = array(); if(isset($this->rights[$scope][0])) $rightArray =$this->rights[$scope][0]; if(isset($this->rights[$scope][$myFirm->id])) $rightArray = $this->rights[$scope][$myFirm->id]; foreach($rightArray as $uid=>$rights){ if(!isset($rights[$right]) || !$rights[$right]) continue; return true; } } } return false; } //Lance les exception appropriées en fonction du droit ou des droits spécifiés // ex : User::check_access('document','configure'); public static function check_access($scope,$right,$uid=0){ global $myUser; if(!isset($myUser) || !is_object($myUser) || !$myUser->connected()) throw new Exception("Contrôle d'accès - Vous devez être connecté",401); if(!$myUser->can($scope,$right,$uid)) throw new Exception("Contrôle d'accès - Permissions insuffisantes ('".$scope.".".$right."')",403); } //Vérifie que l'utilisateur courant est lié a l'id du rang spécifié public function hasRank($rankId){ if($this->superadmin) return true; $rankIds = array(); global $myFirm; if(empty($this->ranks) || !isset($this->ranks[$myFirm->id])) return false; foreach ($this->ranks[$myFirm->id] as $rank) $rankIds[$rank->id] = true; return isset($rankIds[$rankId]); } public function preference($key=null, $value=null){ global $myUser; if(!isset($key) && !isset($value)) return $this->preferences; if(isset($key) && !isset($value)) return isset($this->preferences[$key])?$this->preferences[$key]:''; if(isset($key) && isset($value)){ $this->preferences[$key] = $value; $preference = UserPreference::load(array('key'=>$key,'user'=>$this->login)); if(!$preference) $preference = new UserPreference(); $preference->key = $key; $preference->value = $value; $preference->user = $this->login; $preference->save(); if($myUser->login == $this->login) $_SESSION['currentUser'] = serialize($this); } } public function loadRanks(){ //Ajout des rangs depuis les liens user/firm/rank foreach (UserFirmRank::loadAll(array('user'=>$this->login), null, null, array('*'), 1) as $firmRank) { $rank = $firmRank->join('rank'); $firm = $firmRank->join('firm'); $this->firms[$firm->id] = $firm; if(!isset($this->ranks[$firmRank->firm])) $this->ranks[$firmRank->firm] = array(); $this->ranks[$firmRank->firm][$rank->id] = $rank; if(empty($this->superadmin) && $rank->superadmin) $this->superadmin = boolval($rank->superadmin); } } public function loadPreferences(){ $this->preferences = array(); foreach(UserPreference::loadAll(array('user'=>$this->login)) as $line): $this->preferences[$line->key] = $line->value; endforeach; } //@TODO: Ajouter paramètre $firm pour cibler la récup //des droits pour une firm en particulier et plus $myFirm public function loadRights(){ global $myFirm; $this->rights = array(); if($this->superadmin) return; if(!isset($this->ranks)) $this->ranks = array(); if(!isset($myFirm) || !isset($this->ranks[$myFirm->id]) || count($this->ranks[$myFirm->id])==0) return; $ranksId = array(); foreach($this->ranks[$myFirm->id] as $rank){ if(!isset($rank->id) || !is_numeric($rank->id)) continue; $ranksId[] = $rank->id; } if(count($ranksId)==0) return; $rights = Right::staticQuery('SELECT * FROM {{table}} WHERE (targetScope="user" AND targetUid=?) OR (targetScope="rank" AND targetUid IN('.str_repeat('?,', count($ranksId) - 1) . '?'.'))',array_merge(array($this->login), $ranksId),true); foreach($rights as $right): //Pour le premier rang qui aborde cette scope on met tous les droits à false if(!isset($this->rights[$right->scope][$right->firm][$right->uid])){ $this->rights[$right->scope][$right->firm][$right->uid] = array( 'read' => false, 'edit' => false, 'delete' => false, 'recursive' => false, 'configure' => false ); } //Puis on complete uniquement les droits à true sur la scope pour chaques rangs additionnels if($right->read) $this->rights[$right->scope][$right->firm][$right->uid]['read'] = true; if($right->edit) $this->rights[$right->scope][$right->firm][$right->uid]['edit'] = true; if($right->delete) $this->rights[$right->scope][$right->firm][$right->uid]['delete'] = true; if($right->configure) $this->rights[$right->scope][$right->firm][$right->uid]['configure'] = true; endforeach; /* $permissions = Right::staticQuery('SELECT * FROM {{table}} WHERE (targetScope="user" AND targetUid=?) OR (targetScope="rank" AND targetUid IN('.str_repeat('?,', count($ranksId) - 1) . '?'.'))',array_merge(array($this->login), $ranksId),true); $this->rights['permission'] = array(); foreach ($permissions as $permission) { if(!isset($this->rights['permission'][$permission->scope])) $this->rights['permission'][$permission->scope] = array(); if(!isset($this->rights['permission'][$permission->scope][$permission->uid])){ $this->rights['permission'][$permission->scope][$permission->uid] = array( 'read' => false, 'edit' => false, 'delete' => false, 'recursive' => false, 'configure' => false ); } if($permission->read) $this->rights['permission'][$permission->scope][$permission->uid]['read'] = true; if($permission->edit) $this->rights['permission'][$permission->scope][$permission->uid]['edit'] = true; if($permission->delete) $this->rights['permission'][$permission->scope][$permission->uid]['delete'] = true; if($permission->recursive) $this->rights['permission'][$permission->scope][$permission->uid]['recursive'] = true; if($permission->configure) $this->rights['permission'][$permission->scope][$permission->uid]['configure'] = true; } */ } public function getFirms(){ $this->firms = array(); foreach(Firm::staticQuery('SELECT f.* FROM {{table}} f LEFT JOIN '.UserFirmRank::tableName().' uf ON uf.firm=f.id WHERE uf.user=?',array($this->login),true) as $firm): $this->firms[$firm->id] = $firm; endforeach; } //Vérifie que l'utilisateur courant est lié à l'id établissement spécifié //tags : hasFirm public function haveFirm($id){ return in_array($id, array_keys($this->firms)); } public function getAvatar($getPath = false){ $avatar = 'img/default-avatar.png'; if(!$this->check_avatar_path_length()) return $avatar; $files = glob(__ROOT__.FILE_PATH.AVATAR_PATH.self::format_avatar_name($this->login).self::get_avatar_extension_brace(),GLOB_BRACE); if(count($files)>0){ if($getPath) return $files[0]; preg_match("/\.(\w{3,4})$|\?/m", $files[0], $extension); $avatar = 'action.php?action=core_account_avatar_download&user='.urlencode($this->login).'&extension='.$extension[1]; } return $avatar; } public function check_avatar_path_length(){ return strlen($this->login) <= OS_path_max_length() - strlen(__ROOT__.FILE_PATH.AVATAR_PATH) - strlen(User::get_avatar_extension_brace()); } public static function format_avatar_name($text){ return iconv('utf-8','windows-1256//IGNORE', $text); } public static function get_avatar_extension_brace(){ return ".{jpg,png,jpeg,gif}"; } public static function initialization(&$user, $loadRight){ global $myFirm,$conf; //Ajout du check de l'état du user if(!empty($user->state) && $user->state!=User::ACTIVE) return new self; $user->ranks = empty($user->ranks) ? array() : $user->ranks; $user->firms = empty($user->firms) ? array() : $user->firms; if(isset($user->manager) && !empty($user->manager) && !is_object($user->manager)) $user->manager = self::byLogin($user->manager); $user->loadRanks(); $user->loadPreferences(); //Suppression des rangs et de la firm Anonyme if(isset($user->ranks[-1])) unset($user->ranks[-1]); if(isset($user->firms[-1])) unset($user->firms[-1]); //Set des rangs et droits pour le SuperAdmin if($user->superadmin == 1){ foreach(Firm::loadAll() as $firm) { $user->firms[$firm->id] = $firm; foreach (Rank::loadAll() as $rank) $user->ranks[$firm->id][$rank->id] = $rank; } } //Set de l'établissement par défaut if(!empty($user->firms)){ $defaultFirm = !empty($user->preference('default_firm')) ? $user->preferences['default_firm'] : key($user->firms); $myFirm = isset($user->firms[$defaultFirm]) ? $user->firms[$defaultFirm] : reset($user->firms); if(!isset($user->firms[$defaultFirm])) $user->preference('default_firm',$myFirm->id); //Ajout des rang par défaut user connecté if(!empty($conf->get('connected_default_rank'))) { foreach(Rank::loadAll(array('id:IN'=>$conf->get('connected_default_rank'))) as $rank){ foreach ($user->firms as $firm) { if(!isset($user->ranks[$firm->id])) $user->ranks[$firm->id] = array(); $user->ranks[$firm->id][$rank->id] = $rank; } } } } if($loadRight) $user->loadRights(); } public static function check($login, $password, $loadRight=true){ global $myFirm,$conf; //Load from db $user = self::load(array('login'=>$login)); if($user && !password_verify($password, $user->password)) $user = false; //Load from plugins Plugin::callHook("user_login", array(&$user,htmlspecialchars_decode($login),htmlspecialchars_decode($password),$loadRight)); //init user if($user!=false) self::initialization($user, $loadRight); $user = is_object($user) ? $user : new self; return $user; } //Connecte unn utilisateur sans recourir au mot de passe public static function connectLogin($login){ global $myFirm,$conf; //Load from db $user = self::load(array('login'=>$login,'state'=>User::ACTIVE)); //Load from plugins Plugin::callHook("user_login", array(&$user,htmlspecialchars_decode($login),null,true,true,true)); if($user!=false) self::initialization($user, true); $user = is_object($user) ? $user : new self; return $user; } public static function byLogin($login, $loadRight=true, $force=false){ foreach(User::getAll(array('right'=>$loadRight, 'force'=>$force)) as $user){ if($user->login != $login) continue; return $user; } return new User(); } public function lastname(){ return mb_strtoupper(htmlspecialchars_decode(mb_strtolower($this->name))); } public function firstname(){ return mb_ucfirst(htmlspecialchars_decode(mb_strtolower($this->firstname))); } public function fullName(){ $fullName = $this->firstname().' '.$this->lastname(); return trim($fullName) != '' ? $fullName : $this->login; } public function initials(){ $fullname = str_replace(array(' ','\''),'-',implode(' ',array($this->firstname.' '.$this->name))); $fullname = explode('-',$fullname); $result = ''; foreach($fullname as $value) $result.= mb_strtoupper(mb_substr($value,0,1)); return $result; } public function subordinates(){ $subordinates = array(); foreach (User::getAll() as $user) { if(is_object($user->manager) && $user->manager->login == $this->login) $subordinates[] = $user; } return $subordinates; } public static function generate_password($length=16,$iteration=0){ global $conf; if($length<0) $length = 16; $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'. '0123456789`-=~!@#$%^&*()_+,./<>?;:[]{}\|'; //Suppression des caractères interdits en conf de la chaîne de traitement $chars = str_replace(preg_split('//u', htmlspecialchars_decode($conf->get('password_forbidden_char')), null, PREG_SPLIT_NO_EMPTY), '', $chars); $str = ''; $max = strlen($chars) - 1; for ($i=0; $i<$length; $i++) $str .= $chars[mt_rand(0, $max)]; //Génère un password tant que ne correspond pas aux règles définies //(maximum 100 itérations pour éviter boucle infinie) if(!empty(self::check_password_format($str)) && $iteration<100) $str = self::generate_password($length,++$iteration); return $str; } public static function password_formats(){ $formats = array( array('pattern'=>'|[0-9]|i','label'=>'Le mot de passe doit comporter au minimum 1 chiffre (norme ANSSI)'), array('pattern'=>'|[A-Z]|','label'=>'Le mot de passe doit comporter au minimum 1 majuscule (norme ANSSI)'), array('pattern'=>'|[^A-Za-z0-9éèêëàäâïîöôûüù]|i','label'=>'Le mot de passe doit comporter au minimum 1 caractère spécial (norme ANSSI)'), array('pattern'=>'|.{6,}|','label'=>'Le mot de passe doit comporter au minimum 6 caractères'), array('pattern'=>'|.{12,}|','label'=>'Le mot de passe doit comporter au minimum 12 caractères (norme ANSSI)'), ); return $formats; } public static function check_password_format($password){ global $conf; $errors = array(); $formats = array(); foreach (self::password_formats() as $format) { $formats[$format['pattern']] = $format; } $selectedFormats = json_decode($conf->get('password_format'),true); if(is_array($selectedFormats)){ foreach($selectedFormats as $pattern){ if(!isset($formats[$pattern])) continue; $format = $formats[$pattern]; if(!preg_match($pattern, $password)) $errors[] = $format['label']; } } return $errors; } public static function password_encrypt($password){ return password_hash($password, PASSWORD_DEFAULT); } public function connected(){ return !empty($this->login); } public function disconnect($redirect=null){ global $conf; if(isset($this->login)) $this->preference('cookie',''); $url = 'index.php'; if(!is_null($redirect)) $url = base64_decode($redirect); //Permet la redirection vers une url spécifique lors de la déco if(isset($_SESSION['logout_redirect'])) $url = $_SESSION['logout_redirect']; unset($_SESSION['currentUser']); unset($_SESSION['firm']); session_destroy(); unset($_COOKIE[COOKIE_NAME]); setcookie(COOKIE_NAME, null, -1, '/'); return $url; } public function setLogin($login){ $this->login = $login; } public function setName($name){ $this->name = $name; } public function setFirstName($firstname){ $this->firstname = $firstname; } public function setMail($mail){ $this->mail = $mail; } public function setPhone($phone){ $this->phone = $phone; } public function setMobile($mobile){ $this->mobile = $mobile; } public function setFunction($function){ $this->function = $function; } public function setGroups($groups){ $this->groups = $groups; } public static function generateToken(){ return substr(md5(uniqid(rand(), true)),0,10); } public function getGroups(){ return (is_array($this->groups) ? $this->groups : array()); } public function setFirms($firms){ if(empty($firms)) return; $this->firms = $firms; } public function getRanksId($firmId=null){ if(empty($this->ranks)) return array(); $ranks = array(); if(!empty($firmId)) { //On ne rassemble pas les conditions pour ne pas passer dans le else si la sous-condition est fausse et donc garder un array vide if(!empty($this->ranks[$firmId])) $ranks = array_keys($this->ranks[$firmId]); } else { foreach($this->ranks as $fId => $userRanks) $ranks[$fId] = array_keys($userRanks); } return $ranks; } //Retourne un objet manager (User vide si pas de manager) quel que soit le provider d'entré (ad : objet, db: login) public function manager(){ $manager = new User(); if(!isset($this->manager)) return $manager; if(is_object($this->manager)) $manager = $this->manager; if(is_string($this->manager) && !empty($this->manager)) $manager = User::byLogin($this->manager); return is_object($manager) ? $manager: new User(); } }