sync
[Pman.Base] / Pman / Login.php
1 <?php
2
3 require_once 'Pman.php';
4
5 /***
6
7 * Auth wrapper..
8
9 * User class must provide the following features.
10
11 * logout()
12 * isAuth() 
13 * getAuthUser();
14 * authUserArray() 
15 * active()  -- is user active. // or set prior to checking..
16 * authUserName(n) - sets the value prior to a find(true)
17 * checkPassword($_REQUEST['password'])) {
18 * login();
19 * lang(val) - to set the language..
20 */
21
22
23
24 class Pman_Login extends Pman
25
26     
27     var $masterTemplate = 'login.html';
28     
29     function getAuth() // everyone allowed in here..
30     {
31         parent::getAuth(); // load company..
32         return true;
33         
34     }
35     /**
36      * Accepts:
37      * logout =
38      * 
39      * 
40      */
41     function get($v, $opts=array()) 
42     {
43         $this->initErrorHandling();
44          
45          //DB_DataObject::DebugLevel(1);
46         if (!empty($_REQUEST['logout'])) {
47            return $this->logout();
48         }
49         
50         // general query...
51         if (!empty($_REQUEST['getAuthUser'])) {
52             //DB_Dataobject::debugLevel(5);
53             $this->sendAuthUserDetails();
54             exit;
55            
56         }
57         
58         // might be an idea to disable this?!?
59         if (!empty($_REQUEST['username'])) {
60             $this->post();
61         }
62         
63         
64         if (!empty($_REQUEST['switch'])) {
65             $this->switchUser($_REQUEST['switch']);
66         }
67         
68         if (!empty($_REQUEST['loginPublic'])) {
69             $this->switchPublicUser($_REQUEST['loginPublic']);
70         }
71         
72         
73         $this->jerr("INVALID REQUEST");
74         exit;
75     }
76     
77     
78     function logout()
79     {
80         $ff = class_exists('HTML_FlexyFramework2') ?  HTML_FlexyFramework2::get()  :  HTML_FlexyFramework::get();
81         //DB_DAtaObject::debugLevel(1);
82         $u = $this->getAuthUser();
83         //print_r($u);
84         if ($u) {
85             
86             $this->addEvent('LOGOUT');
87             $e = DB_DataObject::factory('Events');
88           
89             
90             $u->logout();
91             session_regenerate_id(true);
92             session_commit(); 
93
94             if(!empty($ff->Pman['local_autoauth']) && !empty($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST'] == 'localhost') {
95                 $this->jerr("you are using local autoauth!?");                
96             }
97             //echo '<PRE>';print_R($_SESSION);
98             $this->jok("Logged out - user ");
99         }
100         // log it..
101         
102         //$_SESSION['Pman_I18N'] = array(); << 
103         session_regenerate_id(true);
104         session_commit();
105         
106         $this->jok("Logged out - no user");
107         
108     }
109     
110     function sendAuthUserDetails()
111     {
112         // remove for normal use - it's a secuirty hole!
113         //DB_DataObject::debugLevel(1);
114         if (!empty($_REQUEST['_debug'])) {
115            // DB_DataObject::debugLevel(1);
116         }
117         // 
118         $ff = HTML_FlexyFramework::get();
119         $tbl = empty($ff->Pman['authTable']) ? 'core_person' : $ff->Pman['authTable'];
120         
121         $u = DB_DataObject::factory($tbl);
122         if (!$u->isAuth()) {
123              
124             $this->jok(array('id' => 0)); // not logged in..
125             exit;
126         }
127         //die("got here?");
128         $au = $u->getAuthUser();
129         
130          // might occur on shared systems.
131         $ff= HTML_FlexyFramework::get();
132         
133         if (!empty($ff->Pman['auth_comptype'])  && $au->id > 0 &&
134                 ($ff->Pman['auth_comptype'] != $au->company()->comptype)) {
135             $au->logout();
136             $this->jerr("Login not permited to outside companies - please reload");
137         }
138         
139         //$au = $u->getAuthUser();
140         
141         $aur = $au ?  $au->authUserArray() : array();
142         
143         /** -- these need modulizing somehow! **/
144         
145         
146         
147         // basically calls Pman_MODULE_Login::sendAuthUserDetails($aur) on all the modules
148         //echo '<PRE>'; print_r($this->modules());
149         // technically each module should only add properties to an array named after that module..
150         
151         foreach($this->modules() as $m) {
152             if (empty($m)) {
153                 continue;
154             }
155             if (!file_exists($this->rootDir.'/Pman/'.$m.'/Login.php')) {
156                 continue;
157             }
158             $cls = 'Pman_'.$m.'_Login';
159             require_once 'Pman/'.$m.'/Login.php';
160             $x = new $cls;
161             $x->authUser = $au;
162             $aur = $x->sendAuthUserDetails($aur);
163         }
164         
165                  
166 //        
167 //        echo '<PRE>';print_r($aur);
168 //        exit;
169         $this->jok($aur);
170         exit;
171         
172             
173     }
174
175     function switchUser($id)
176     {
177         $tbl = empty($ff->Pman['authTable']) ? 'core_person' : $ff->Pman['authTable'];
178         $u = DB_DataObject::factory($tbl);
179         if (!$u->isAuth()) {
180             $this->err("not logged in");
181         }
182         
183         $au = $u->getAuthUser();
184         
185         // first check they have perms to do this..
186         if (!$au|| ($au->company()->comptype != 'OWNER') || !$this->hasPerm('Core.Person', 'E')) {
187             $this->jerr("User switching not permitted");
188         }
189                 
190         $u = DB_DataObject::factory($tbl);
191         $u->get($id);
192         if (!$u->active()) {
193             $this->jerr('Account disabled');
194         }
195         $u->login();
196             // we might need this later..
197         $this->addEvent("SWITCH-USER", false, $au->name . ' TO ' . $u->name);
198         $this->jok("SWITCH");
199         
200     }
201     
202     function switchPublicUser($id)
203     {
204         $tbl = empty($ff->Pman['authTable']) ? 'core_person' : $ff->Pman['authTable'];
205         
206         $u = DB_DataObject::factory($tbl);
207         $u->get($id);
208         
209         if (!$u->active()) {
210             $this->jerr('Account disabled');
211         }
212         
213         if(!$u->loginPublic()){
214             $this->jerr('Switch fail');
215         }
216         
217         $this->jok('OK');
218     }
219     
220     
221     var $domObj = false;
222     function post($v)
223     {
224         
225         //DB_DataObject::debugLevel(1);
226         if (!empty($_REQUEST['getAuthUser'])) {
227             $this->sendAuthUserDetails();
228             exit;
229         }
230         
231         if (!empty($_REQUEST['logout'])) {
232            return $this->logout();
233         }
234         
235         if (!empty($_REQUEST['passwordRequest'])) { //|| (strpos($_REQUEST['username'], '@') < 1)) {
236             
237             return $this->passwordRequest($_REQUEST['passwordRequest']);
238             
239         }
240         
241         if (!empty($_REQUEST['changePassword'])) {
242             return $this->changePassword($_REQUEST);
243         }
244         
245         // login attempt..
246         
247         $ff = HTML_FlexyFramework::get();
248         $tbl = empty($ff->Pman['authTable']) ? 'core_person' : $ff->Pman['authTable'];
249         
250        
251         $u = DB_DataObject::factory($tbl);
252         
253         
254         
255         // ratelimit
256         if (!empty($_SERVER['REMOTE_ADDR'])) {
257             //DB_DataObject::DebugLevel(1);
258             $e = DB_DataObject::Factory('Events');
259             $e->action = 'LOGIN-BAD';
260             $e->ipaddr = $_SERVER['REMOTE_ADDR'];
261             $e->whereAdd('event_when > NOW() - INTERVAL 10 MINUTE');
262             if ($e->count() > 5) {
263                 $this->jerror('LOGIN-RATE', "Login failures are rate limited - please try later");
264             }
265         }
266         
267         //$u->active = 1;
268         
269         // empty username = not really a hacking attempt.
270         
271         if (empty($_REQUEST['username'])) { //|| (strpos($_REQUEST['username'], '@') < 1)) {
272             $this->jerror('LOGIN-EMPTY', 'You typed the wrong Username or Password (0)');
273             exit;
274         }
275         
276         $u->authUserName($_REQUEST['username']);
277         
278         if ($u->count() > 1 || !$u->find(true)) {
279             $this->jerror('LOGIN-BAD','You typed the wrong Username or Password  (1)');
280             exit;
281         }
282         
283         if (!$u->active()) {
284             $this->jerror('LOGIN-BAD','Account disabled');
285         }
286         
287         if(!empty($u->oath_key) && empty($_REQUEST['oath_password'])){
288             $this->jerror('LOGIN-BAD','Your account require Two-Factor Authentication');
289         }
290         
291         // check if config allows non-owner passwords.
292         // auth_company = "OWNER" // auth_company = "CLIENT" or blank for all?
293         // perhaps it should support arrays..
294         $ff= HTML_FlexyFramework::get();
295         if (!empty($ff->Pman['auth_comptype']) && $ff->Pman['auth_comptype'] != $u->company()->comptype) {
296             //print_r($u->company());
297             $this->jerror('LOGIN-BADUSER', "Login not permited to outside companies"); // serious failure
298         }
299         
300         
301         // note we trim \x10 -- line break - as it was injected the front end
302         // may have an old bug on safari/chrome that added that character in certian wierd scenarios..
303         if (!$u->checkPassword(trim($_REQUEST['password'],"\x10"))) {
304             $this->jerror('LOGIN-BAD', 'You typed the wrong Username or Password  (2)'); // - " . htmlspecialchars(print_r($_POST,true))."'");
305             exit;
306         }
307         
308         if(!empty($u->oath_key) && !$u->checkTwoFactorAuthentication(trim($_REQUEST['oath_password'],"\x10"))){
309             $this->jerror('LOGIN-BAD', 'You typed the wrong Username or Password  (3)');
310             exit;
311         }
312         
313         $u->login();
314         // we might need this later..
315         $this->addEvent("LOGIN", false, session_id());
316         if (!empty($_REQUEST['lang'])) {
317             $u->lang($_REQUEST['lang']);
318         }
319          // log it..
320
321         $this->sendAuthUserDetails();
322         exit;
323          
324         
325     }
326     
327     function passwordRequest($n) 
328     {
329         $u = DB_DataObject::factory('core_person');
330         //$u->company_id = $this->company->id;
331         
332         $u->whereAdd('LENGTH(passwd) > 1');
333         $u->email = $n;
334         $u->active = 1;
335         if ($u->count() > 1 || !$u->find(true)) {
336             $this->jerr('invalid User (1)');
337         }
338         // got a avlid user..
339         if (!strlen($u->passwd)) {
340             $this->jerr('invalid User (2)');
341         }
342         // check to see if we have sent a request before..
343         
344         if ($u->no_reset_sent > 3) {
345             $this->jerr('We have issued to many resets - please contact the Administrator');
346         }
347         
348         
349         
350         
351         // sort out sender.
352         $cm = DB_DataObject::factory('core_email');
353         if (!$cm->get('name', 'ADMIN_PASSWORD_RESET')) {
354             $this->jerr("no template ADMIN_PASSWORD_RESET exists - please run importer ");
355             
356         }
357         /*
358         
359         $g = DB_DAtaObject::factory('Groups');
360         if (!$g->get('name', 'system-email-from')) {
361             $this->jerr("no group 'system-email-from' exists in the system");
362         }
363         $from_ar = $g->members();
364         if (count($from_ar) != 1) {
365             $this->jerr(count($from_ar) ? "To many members in the 'system-email-from' group " :
366                        "'system-email-from' group  does not have any members");
367         }
368         */
369         
370         
371         
372         // bcc..
373         $g = DB_DAtaObject::factory('core_group');
374         if (!$g->get('name', 'bcc-email')) {
375             $this->jerr("no group 'bcc-email' exists in the system");
376         }
377         $bcc = $g->members('email');
378         if (!count($bcc)) {
379             $this->jerr( "'bcc-email' group  does not have any members");
380         }
381         
382         
383         
384         $this->authFrom = time();
385         $this->authKey = $u->genPassKey($this->authFrom);
386         //$this->authKey = md5($u->email . $this->authFrom . $u->passwd);
387         $this->person = $u;
388         $this->bcc = $bcc;
389         $this->rcpts = $u->getEmailFrom();
390         
391         $ret = $cm->send($this);
392         //$this->jerr(print_r($r->toData(),true));
393         
394         if (is_object($ret)) {
395             $this->addEvent('SYSERR',false, $ret->getMessage());
396             $this->jerr($ret->getMessage());
397         }
398         $this->addEvent('PASSREQ',$u, $u->email);
399         $uu = clone($u);
400         $uu->no_reset_sent++;
401         $uu->update($u);
402         $this->jok("done");
403         
404     }
405     
406     function changePassword($r)
407     {
408         
409         $au = $this->getAuthUser();
410         if ($au) {
411             $uu = clone($au);
412             $au->setPassword($r['passwd1']);
413             $au->update($uu);
414             $this->addEvent("CHANGEPASS", $au);
415             $this->jok($au);
416         }
417         // not logged in -> need to validate 
418         if (empty($r['passwordReset'])) {
419             $this->jerr("invalid request");
420         }
421         // same code as reset pasword
422        
423         $bits = explode('/', $r['passwordReset']);
424         //print_R($bits);
425       
426         $res= $this->resetPassword(@$bits[0],@$bits[1],@$bits[2]);
427           
428         if ($res !== false) {
429             $this->jerr($res);
430         }
431         // key is correct.. let's change password...
432         
433         $u = DB_DataObject::factory('core_person');
434         
435         //$u->company_id = $this->company->id;
436         $u->whereAdd('LENGTH(passwd) > 1');
437         $u->active = 1;
438         if (!$u->get($bits[0])) {
439            $this->jerr("invalid id"); // should not happen!!!!
440         }
441         $uu = clone($u);
442         $u->setPassword($r['passwd1']);
443         $u->update($uu);
444         $u->login();
445         $this->addEvent("CHANGEPASS", $u);
446         $this->jok($u);
447     }
448     
449     
450     
451 }
452