1 <?php
2 /*
3 * This code is part of GOsa (http://www.gosa-project.org)
4 * Copyright (C) 2003-2008 GONICUS GmbH
5 *
6 * ID: $$Id$$
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
23 class password extends plugin
24 {
25 /* Definitions */
26 var $plHeadline = "Change password";
27 var $plDescription = "Change user password";
29 var $proposal = "";
30 var $proposalEnabled = FALSE;
31 var $proposalSelected = FALSE;
33 var $forcedHash = NULL;
35 function password(&$config, $dn= NULL, $parent= NULL)
36 {
37 plugin::plugin($config, $dn, $parent);
39 // Try to generate a password proposal, if this is successfull
40 // then preselect the proposal usage.
41 $this->refreshProposal();
42 if($this->proposal != ""){
43 $this->proposalSelected = TRUE;
44 }
45 }
48 function forceHash($hash)
49 {
50 $this->forcedHash = $hash;
51 }
54 function refreshProposal()
55 {
56 $this->proposal = passwordMethod::getPasswordProposal($this->config);
57 $this->proposalEnabled = (!empty($this->proposal));
58 }
61 function execute()
62 {
63 plugin::execute();
64 $smarty = get_smarty();
65 $ui = get_userinfo();
67 /* Get acls */
68 $password_ACLS = $ui->get_permissions($ui->dn,"users/password");
69 $smarty->assign("ChangeACL" , $password_ACLS);
70 $smarty->assign("NotAllowed" , !preg_match("/w/i",$password_ACLS));
72 /* Display expiration template */
73 $smarty->assign("passwordExpired", FALSE);
74 if ($this->config->boolValueIsTrue("core","handleExpiredAccounts")){
75 $expired= ldap_expired_account($this->config, $ui->dn, $ui->username);
76 $smarty->assign("passwordExpired", $expired & POSIX_FORCE_PASSWORD_CHANGE);
77 if($expired == POSIX_DISALLOW_PASSWORD_CHANGE){
78 return($smarty->fetch(get_template_path("nochange.tpl", TRUE)));
79 }
80 }
82 // Refresh proposal if requested
83 if(isset($_POST['refreshProposal'])) $this->refreshProposal();
84 if(isset($_POST['proposalSelected'])) $this->proposalSelected = get_post('proposalSelected') == 1;
85 $smarty->assign("proposal" , set_post($this->proposal));
86 $smarty->assign("proposalEnabled" , $this->proposalEnabled);
87 $smarty->assign("proposalSelected" , $this->proposalSelected);
89 /* Pwd change requested */
90 if (isset($_POST['password_finish'])){
92 if($this->proposalSelected){
93 $current_password = get_post('current_password');
94 $new_password = $this->proposal;
95 $repeated_password = $this->proposal;
96 }else{
97 $current_password = get_post('current_password');
98 $new_password = get_post('new_password');
99 $repeated_password = get_post('repeated_password');
100 }
103 // Get configuration flags for further input checks.
104 $check_differ = $this->config->get_cfg_value("core","passwordMinDiffer") != "";
105 $differ = $this->config->get_cfg_value("core","passwordMinDiffer");
106 $check_length = $this->config->get_cfg_value("core","passwordMinLength") != "";
107 $length = $this->config->get_cfg_value("core","passwordMinLength");
109 // Once an error has occured it is stored here.
110 $message = array();
112 // Call the check hook
113 $attrs = array();
114 $attrs['current_password'] = ($current_password);
115 $attrs['new_password'] = ($new_password);
117 // Perform GOsa password policy checks
118 if(empty($current_password)){
119 $message[] = _("You need to specify your current password in order to proceed.");
120 }elseif($new_password != $repeated_password){
121 $message[] = _("The passwords you've entered as 'New password' and 'Repeated new password' do not match.");
122 }elseif($new_password == ""){
123 $message[] = _("The password you've entered as 'New password' is empty.");
124 }elseif($check_differ && (substr($current_password, 0, $differ) == substr($new_password, 0, $differ))){
125 $message[] = _("The password used as new and current are too similar.");
126 }elseif($check_length && (strlen($new_password) < $length)){
127 $message[] = _("The password used as new is to short.");
128 }elseif(!passwordMethod::is_harmless($new_password)){
129 $message[] = _("The password contains possibly problematic Unicode characters!");
130 }
132 // Call external check hook to validate the password change
133 if(!count($message)){
134 $checkRes = $this->callCheckHook($this->config,$this->dn,$attrs);
135 if(count($checkRes)){
136 $message[] = sprintf(_("Check-hook reported a problem: %s. Password change canceled!"),implode($checkRes));
137 }
138 }
140 // Some errors/warning occured, display them and abort password change.
141 if(count($message)){
142 msg_dialog::displayChecks($message);
143 }else{
145 /* Try to connect via current password */
146 $tldap = new LDAP(
147 $ui->dn,
148 $current_password,
149 $this->config->current['SERVER'],
150 $this->config->get_cfg_value("core","ldapFollowReferrals") == "true",
151 $this->config->get_cfg_value("core","ldapTLS") == "true");
153 /* connection Successfull ? */
154 if (!$tldap->success()){
155 msg_dialog::display(_("Password change"),
156 _("The password you've entered as your current password doesn't match the real one."),WARNING_DIALOG);
157 }elseif (!preg_match("/w/i",$password_ACLS)){
158 msg_dialog::display(_("Password change"),
159 _("You have no permission to change your password."),WARNING_DIALOG);
160 }elseif(!change_password($ui->dn, $new_password,FALSE, $this->forcedHash, $current_password, $message)){
161 msg_dialog::display(_("Password change"),
162 $message,WARNING_DIALOG);
163 }else{
164 $ui->password= $new_password;
165 session::set('ui',$ui);
166 return($smarty->fetch(get_template_path("changed.tpl", TRUE)));
167 }
168 }
169 }
170 return($smarty->fetch(get_template_path("password.tpl", TRUE)));
171 }
173 function remove_from_parent()
174 {
175 $this->handle_post_events("remove");
176 }
178 function save()
179 {
180 }
182 static function callCheckHook($config,$dn,$attrs = array())
183 {
184 $command = $config->configRegistry->getPropertyValue("password","check");
185 if (!empty($command)){
187 // Build up ldif to send to the check hook
188 $ldif= "dn: $dn\n";
189 foreach ($attrs as $name => $value){
190 $ldif.= "{$name}: {$value}\n";
191 }
192 $ldif.= "\n";
194 /* Feed "ldif" into hook and retrieve result*/
195 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
196 $fh= proc_open($command, $descriptorspec, $pipes);
197 if (is_resource($fh)) {
198 fwrite ($pipes[0], $ldif);
199 fclose($pipes[0]);
200 $arr= stream_get_contents($pipes[1]);
201 $err = stream_get_contents($pipes[2]);
202 fclose($pipes[1]);
203 fclose($pipes[2]);
204 $returnCode = proc_close($fh);
206 if($returnCode !== 0){
207 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
208 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$err);
209 $message= msgPool::cmdexecfailed($err,$command, "password");
210 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
211 }else{
212 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$arr);
213 return(preg_split("/\n/", $arr,0,PREG_SPLIT_NO_EMPTY));
214 }
215 }
216 }
217 return(array());
218 }
220 static function plInfo()
221 {
222 return (array(
223 "plDescription" => _("User password"),
224 "plSelfModify" => TRUE,
225 "plDepends" => array("user"),
226 "plPriority" => 10,
227 "plSection" => array("personal" => _("My account")),
228 "plCategory" => array("users"),
229 "plOptions" => array(),
231 "plProvidedAcls" => array())
232 );
233 }
235 }
236 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
237 ?>