Code

Some backport from trunk
[gosa.git] / gosa-core / plugins / personal / password / class_password.inc
index d7d323c49f3269f0afa18d736fec2f931b8f7f2b..df056069a208217c2a050b75408712b1fa1031b0 100644 (file)
@@ -29,19 +29,13 @@ class password extends plugin
     var $proposal = "";
     var $proposalEnabled = FALSE;
     var $proposalSelected = FALSE;
+    var $proposalInitialized = FALSE;
 
     var $forcedHash = NULL;
 
     function password(&$config, $dn= NULL, $parent= NULL)
     {
         plugin::plugin($config, $dn, $parent);
-
-        // Try to generate a password proposal, if this is successfull 
-        //  then preselect the proposal usage. 
-        $this->refreshProposal();
-        if($this->proposal != ""){
-            $this->proposalSelected = TRUE;
-        }
     }
 
     
@@ -64,6 +58,17 @@ class password extends plugin
         $smarty = get_smarty();
         $ui = get_userinfo();
 
+
+        // Try to generate a password proposal, if this is successfull 
+        //  then preselect the proposal usage. 
+        if(!$this->proposalInitialized){
+            $this->refreshProposal();
+            if($this->proposal != ""){
+                $this->proposalSelected = TRUE;
+            }
+            $this->proposalInitialized = TRUE;
+        }
+
         /* Get acls */
         $password_ACLS = $ui->get_permissions($ui->dn,"users/password");
         $smarty->assign("ChangeACL" ,  $password_ACLS);
@@ -100,54 +105,47 @@ class password extends plugin
             }
 
 
-            /* Should we check different characters in new password */
+            // Get configuration flags for further input checks.
             $check_differ = $this->config->get_cfg_value("core","passwordMinDiffer") != "";
             $differ       = $this->config->get_cfg_value("core","passwordMinDiffer");
-
-            /* Enable length check ? */
             $check_length = $this->config->get_cfg_value("core","passwordMinLength") != "";
             $length       = $this->config->get_cfg_value("core","passwordMinLength");
 
-            /* Call external password quality hook ?*/
-            $check_hook   = $this->config->get_cfg_value("core","passwordHook") != "";
-
-            /* Prepare password hook */
-            $cmd = $this->config->get_cfg_value("core","passwordHook");
-            $cmd = preg_replace("/%current_password/",escapeshellarg(get_post('current_password')), $cmd);
-            $cmd = preg_replace("/%new_password/",escapeshellarg(get_post('new_password')), $cmd);
-            $cmd = preg_replace("/%uid/",escapeshellarg($ui->username), $cmd);
-            $cmd = preg_replace("/%dn/",escapeshellarg($ui->dn), $cmd);
-            if($check_hook){
-                exec($cmd,$resarr);
-                $check_hook_output = "";
-                if(count($resarr) > 0) {
-                    $check_hook_output= join('\n', $resarr);
-                }
-            }
+            // Once an error has occured it is stored here.
+            $message = array();
 
-            /* Check given values */    
+            // Call the check hook
+            $attrs = array();
+            $attrs['current_password'] = ($current_password);
+            $attrs['new_password'] = ($new_password);
+
+            // Perform GOsa password policy checks 
             if(empty($current_password)){
-                msg_dialog::display(_("Password change"),
-                        _("You need to specify your current password in order to proceed."),WARNING_DIALOG);
-            }elseif ($new_password  != $repeated_password){
-                msg_dialog::display(_("Password change"),
-                        _("The passwords you've entered as 'New password' and 'Repeated new password' do not match."),WARNING_DIALOG);
-            } elseif ($new_password == ""){
-                msg_dialog::display(_("Password change"),
-                        _("The password you've entered as 'New password' is empty."),WARNING_DIALOG);
+                $message[] = _("You need to specify your current password in order to proceed.");
+            }elseif($new_password  != $repeated_password){
+                $message[] = _("The passwords you've entered as 'New password' and 'Repeated new password' do not match.");
+            }elseif($new_password == ""){
+                $message[] = _("The password you've entered as 'New password' is empty.");
             }elseif($check_differ && (substr($current_password, 0, $differ) == substr($new_password, 0, $differ))){
-                msg_dialog::display(_("Password change"),
-                        _("The password used as new and current are too similar."),WARNING_DIALOG);
+                $message[] = _("The password used as new and current are too similar.");
             }elseif($check_length && (strlen($new_password) < $length)){
-                msg_dialog::display(_("Password change"),
-                        _("The password used as new is to short."),WARNING_DIALOG);
+                $message[] = _("The password used as new is to short.");
             }elseif(!passwordMethod::is_harmless($new_password)){
-                msg_dialog::display(_("Password change"),
-                        _("The password contains possibly problematic Unicode characters!"),WARNING_DIALOG);
-            }elseif($check_hook && $check_hook_output != ""){
-                msg_dialog::display(_("Password change"),
-                        sprintf(_("External password changer reported a problem: %s."),$check_hook_output),WARNING_DIALOG);
-            }else{
+                $message[] = _("The password contains possibly problematic Unicode characters!");
+            }
+
+            // Call external check hook to validate the password change
+            if(!count($message)){
+                $checkRes = $this->callCheckHook($this->config,$this->dn,$attrs);
+                if(count($checkRes)){
+                    $message[] = sprintf(_("Check-hook reported a problem: %s. Password change canceled!"),implode($checkRes));
+                }
+            }
+
+            // Some errors/warning occured, display them and abort password change.
+            if(count($message)){
+                msg_dialog::displayChecks($message);
+            }else{ 
 
                 /* Try to connect via current password */
                 $tldap = new LDAP(
@@ -161,35 +159,22 @@ class password extends plugin
                 if (!$tldap->success()){
                     msg_dialog::display(_("Password change"),
                             _("The password you've entered as your current password doesn't match the real one."),WARNING_DIALOG);
+                }elseif (!preg_match("/w/i",$password_ACLS)){
+                    msg_dialog::display(_("Password change"),
+                            _("You have no permission to change your password."),WARNING_DIALOG);
+                }elseif(!change_password($ui->dn, $new_password,FALSE, $this->forcedHash, $current_password, $message)){
+                    msg_dialog::display(_("Password change"),
+                            $message,WARNING_DIALOG);
                 }else{
-
-                    /* Check GOsa permissions */
-                    if (!preg_match("/w/i",$password_ACLS)){
-                        msg_dialog::display(_("Password change"),
-                                _("You have no permission to change your password."),WARNING_DIALOG);
-                    }else{
-                        $this->change_password($ui->dn, $new_password, $this->forcedHash);
-                        gosa_log ("User/password has been changed");
-                        $ui->password= $new_password;
-                        session::set('ui',$ui);
-#$this->handle_post_events("modify",array("userPassword" => $new_password));
-                        return($smarty->fetch(get_template_path("changed.tpl", TRUE)));
-                    }
+                    $ui->password= $new_password;
+                    session::set('ui',$ui);
+                    return($smarty->fetch(get_template_path("changed.tpl", TRUE)));
                 }
             }
         }
         return($smarty->fetch(get_template_path("password.tpl", TRUE)));
     } 
 
-    function change_password($dn, $pwd, $hash)
-    {
-        if(!$hash){
-            change_password ($dn, $pwd);
-        }else{
-            change_password ($dn, $pwd,0, $hash);
-        }
-    }
-
     function remove_from_parent()
     {
         $this->handle_post_events("remove");
@@ -199,17 +184,93 @@ class password extends plugin
     {
     }
 
+    static function callCheckHook($config,$dn,$attrs = array())
+    {
+        $command = $config->configRegistry->getPropertyValue("password","check");
+        if (!empty($command)){
+
+            // Build up ldif to send to the check hook
+            $ldif= "dn: $dn\n";
+            foreach ($attrs as $name => $value){
+                $ldif.= "{$name}: {$value}\n";
+            }
+            $ldif.= "\n";
+
+            /* Feed "ldif" into hook and retrieve result*/
+            $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
+            $fh= proc_open($command, $descriptorspec, $pipes);
+            if (is_resource($fh)) {
+                fwrite ($pipes[0], $ldif);
+                fclose($pipes[0]);
+                $arr= stream_get_contents($pipes[1]);
+                $err = stream_get_contents($pipes[2]);
+                fclose($pipes[1]);
+                fclose($pipes[2]);
+                $returnCode = proc_close($fh);
+
+                if($returnCode !== 0){
+                    @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
+                    @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$err);
+                    $message= msgPool::cmdexecfailed($err,$command, "password");
+                    msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
+                }else{
+                    @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$arr);
+                    return(preg_split("/\n/", $arr,0,PREG_SPLIT_NO_EMPTY));
+                }
+            }
+        }
+        return(array());
+    }
+
     static function plInfo()
     {
         return (array(
-                    "plDescription"     => _("User password"),
-                    "plSelfModify"      => TRUE,
-                    "plDepends"         => array("user"),
-                    "plPriority"        => 10,
+                    "plDescription" => _("User password"),
+                    "plSelfModify"  => TRUE,
+                    "plDepends"     => array("user"),
+                    "plPriority"    => 10,
                     "plSection"     => array("personal" => _("My account")),
                     "plCategory"    => array("users"),
-                    "plOptions"         => array(),
+                    "plOptions"     => array(),
 
+                    "plProperties" => array(
+                        array(
+                            "name"          => "PRELOCK",
+                            "type"          => "command",
+                            "default"       => "",
+                            "description"   => _("Script to be called before a password gets locked."),
+                            "check"         => "gosaProperty::isCommand",
+                            "migrate"       => "",
+                            "group"         => "password",
+                            "mandatory"     => FALSE),
+                        array(
+                            "name"          => "POSTLOCK",
+                            "type"          => "command",
+                            "default"       => "",
+                            "description"   => _("Script to be called after a password gets locked."),
+                            "check"         => "gosaProperty::isCommand",
+                            "migrate"       => "",
+                            "group"         => "password",
+                            "mandatory"     => FALSE),
+                        array(
+                            "name"          => "PREUNLOCK",
+                            "type"          => "command",
+                            "default"       => "",
+                            "description"   => _("Script to be called before a password gets unlocked."),
+                            "check"         => "gosaProperty::isCommand",
+                            "migrate"       => "",
+                            "group"         => "password",
+                            "mandatory"     => FALSE),
+                        array(
+                            "name"          => "POSTUNLOCK",
+                            "type"          => "command",
+                            "default"       => "",
+                            "description"   => _("Script to be called after a password gets unlocked."),
+                            "check"         => "gosaProperty::isCommand",
+                            "migrate"       => "",
+                            "group"         => "password",
+                            "mandatory"     => FALSE)
+                        ),
                     "plProvidedAcls"  => array())
                );
     }