1<?php
2
3/**
4*
5*  User class to work with current user, authentication etc
6*
7*/
8class User extends Common_functions {
9
10
11    /**
12     * Current username
13     *
14     * @var string
15     */
16    public $username;
17
18    /**
19     * flag if user is authenticated
20     *
21     * (default value: false)
22     *
23     * @var bool
24     */
25    protected $authenticated = false;
26
27    /**
28     * timeout flag - is timeout reached
29     *
30     * (default value: false)
31     *
32     * @var bool
33     */
34    protected $timeout = false;
35
36    /**
37     * user details
38     *
39     * (default value: null)
40     *
41     * @var object
42     */
43    public $user = null;
44
45    /**
46     * flag if user is admin
47     *
48     * (default value: false)
49     *
50     * @var bool
51     */
52    protected $isadmin = false;
53
54    /**
55     * limit for IP block - after how many attampts user is blocked
56     *
57     * (default value: 5)
58     *
59     * @var int
60     */
61    public $blocklimit = 5;
62
63    /**
64     * authentication method id for user
65     *
66     * (default value: 1)
67     *
68     * @var int
69     */
70    private $authmethodid = 1;
71
72    /**
73     * authentication method type
74     *
75     * (default value: "local")
76     *
77     * @var string
78     */
79    private $authmethodtype = "local";
80
81    /**
82     * ldap is used flag
83     *
84     * (default value: false)
85     *
86     * @var bool
87     */
88    private $ldap = false;
89
90    /**
91     * Users IP address
92     *
93     * @var mixed
94     */
95    private $ip;
96
97    /**
98     * Set allowed themes
99     *
100     * @var array
101     */
102    public $themes = array("white", "dark");
103
104    /**
105     * (json) parameters for authentication
106     *
107     * @var mixed
108     */
109    protected $authmethodparams;
110
111    /**
112     * Cryptographic functions
113     * @var Crypto
114     */
115    public $Crypto;
116
117
118    /**
119     * __construct function.
120     *
121     * @access public
122     * @param Database_PDO $database
123     * @param bool $api (default: false)
124     */
125    public function __construct (Database_PDO $database, $api = false) {
126        parent::__construct();
127
128        # Save database object
129        $this->Database = $database;
130        # set api
131        $this->api = $api;
132        # initialize Result
133        $this->Result = new Result ();
134
135        # get settings
136        $this->get_settings ();
137
138        # Log object
139        $this->Log = new Logging ($this->Database, $this->settings);
140
141        # initialize Crypto
142        $this->Crypto = new Crypto ();
143
144        # register new session
145        $this->register_session ();
146        # check timeut
147        $this->check_timeout ();
148        # set authenticated flag
149        $this->is_authenticated ();
150        # get users IP address
151        $this->block_get_ip ();
152        # set theme
153        $this->set_user_theme ();
154    }
155
156
157
158
159
160
161
162
163
164
165    /**
166     * @session management functions
167     * ------------------------------
168     */
169
170    /**
171     * registers new session
172     *
173     * @access private
174     * @return void
175     */
176    private function register_session () {
177        // not for api
178        if ($this->api !== true) {
179            if (@$_SESSION===NULL && !isset($_SESSION)) {
180                //set session name
181                $this->set_session_name();
182                //set default params
183                $this->set_session_ini_params ();
184                //register session
185                $this->start_session ();
186            }
187        }
188    }
189
190    /**
191     * Start session - files or use database handler
192     * @method start_session
193     * @return [type]
194     */
195    private function start_session () {
196        // check if database should be set for sessions
197        if (Config::get('session_storage') == "database") {
198            new Session_db ($this->Database);
199        }
200        // local
201        else {
202            session_start ();
203        }
204
205        // Re-set HTTP session cookie with mandatory samesite=Strict attribute.
206        // php native support for samesite is >=php7.3
207
208        $session_name = session_name();
209        $session_id = session_id();
210        $session_lifetime = ini_get('session.cookie_lifetime');
211        $session_use_cookies  = ini_get('session.use_cookies');
212
213        if ($session_use_cookies && is_string($session_id) && strlen($session_id) > 0)
214            setcookie_samesite($session_name, $session_id, $session_lifetime, true);
215    }
216
217    /**
218     * destroys session
219     *
220     * @access public
221     * @return void
222     */
223    public function destroy_session () {
224        session_destroy();
225    }
226
227    /**
228     * sets session name if specified in config file
229     *
230     * @access private
231     * @return void
232     */
233    private function set_session_name () {
234        $sessname = Config::get('phpsessname', 'phpipam');
235        // check old name
236        $old_name = session_name();
237        if ($sessname != $old_name) {
238          // save
239          session_name($sessname);
240        }
241    }
242
243    /**
244     * Default session parameters for phpipam - MAX
245     *
246     *  gc_maxlifetime  : time for server to keep data parameters for (at least 24 hours)
247     *  cookie_lifetime : time for client browser to keep cookies
248     *
249     * @access private
250     * @return void
251     */
252    private function set_session_ini_params () {
253        if(!isset($_SESSION)) {
254            ini_set('session.gc_maxlifetime', 86400);
255            ini_set('session.cookie_lifetime', 86400);
256        }
257    }
258
259    /**
260     * saves parameters to session after authentication succeeds
261     *
262     * @access private
263     * @return void
264     */
265    private function write_session_parameters () {
266        // not for api
267        if ($this->api !== true) {
268            // Avoid session ID fixation attacks
269            session_regenerate_id(true);
270
271            $_SESSION['ipamusername'] = $this->user->username;
272            $_SESSION['ipamlanguage'] = $this->fetch_lang_details ();
273            $_SESSION['lastactive']   = time();
274            // 2fa required ?
275            if (isset($this->twofa) && $this->twofa) {
276                $_SESSION['2fa_required'] = true;
277            }
278        }
279    }
280
281    /**
282     * Update users language
283     *
284     * @access public
285     * @return void
286     */
287    public function update_session_language () {
288        // not for api
289        if ($this->api !== true) {
290            # update user object
291            $this->fetch_user_details ($this->username, true);
292            $_SESSION['ipamlanguage'] = $this->fetch_lang_details ();
293        }
294    }
295
296    /**
297     * Checks if user is authenticated - session is set
298     *
299     * @access public
300     * @return bool
301     */
302    public function is_authenticated () {
303        # if checked for subpages first check if $user is array
304        if(!is_array($this->user)) {
305            if( strlen(@$_SESSION['ipamusername'])>0 ) {
306                # save username
307                $this->username = $_SESSION['ipamusername'];
308                # check for timeout
309                if($this->timeout === true) {
310                    $this->authenticated = false;
311                }
312                else {
313                    # fetch user profile and save it
314                    $this->fetch_user_details ($this->username);
315
316                    $this->authenticated = true;
317                    $this->reset_inactivity_time();
318                    $this->update_activity_time ();
319                    # bind language
320                    $this->set_ui_language();
321                }
322            }
323        }
324
325        # return
326        return $this->authenticated;
327    }
328
329    /**
330     * Check if 2fa is required for user
331     * @method twofa_required
332     * @return bool
333     */
334    public function twofa_required () {
335        return isset($_SESSION['2fa_required']) ? true : false;
336    }
337
338    /**
339     * Checks if current user is admin or not
340     *
341     * @access public
342     * @param bool $die (default: true)
343     * @return string|bool
344     */
345    public function is_admin ($die = true) {
346        if($this->isadmin)      { return true; }
347        else {
348            if($die)            { $this->Result->show("danger", _('Administrator level privileges required'), true); }
349            else                { return false; }
350        }
351    }
352
353    /**
354     * checks if user is authenticated, if not redirects to login page
355     *
356     * @access public
357     * @param bool $redirect (default: true)
358     * @return string|false
359     */
360    public function check_user_session ($redirect = true, $ignore_2fa = false) {
361        # set url
362        $url = $this->createURL();
363
364        # not authenticated
365        if($this->authenticated===false) {
366            # error print for AJAX
367            if(@$_SERVER['HTTP_X_REQUESTED_WITH'] == "XMLHttpRequest") {
368                # for AJAX always check origin
369                $this->check_referrer ();
370                # kill session
371                $this->destroy_session ();
372                # error
373                $this->Result->show("danger", _('Please login first')."!<hr><a class='btn btn-sm btn-default' href='".$url.create_link ("login")."'>"._('Login')."</a>", true, true);
374                die();
375            }
376            # timeout
377            elseif ($this->timeout) {
378                # set redirect cookie
379                $this->set_redirect_cookie ();
380                # redirect
381                if ($redirect)
382                header("Location:".$url.create_link ("login","timeout"));
383                die();
384            }
385            else {
386                # set redirect cookie
387                $this->set_redirect_cookie ();
388                # redirect
389                if ($redirect)
390                header("Location:".$url.create_link ("login"));
391                die();
392            }
393        }
394        # authenticated, do we need to do 2fa ?
395        elseif (isset($_SESSION['2fa_required']) && $ignore_2fa!==true) {
396            header("Location:".$url.create_link ("2fa"));
397            die();
398        }
399        # disabled
400        elseif ($this->user->disabled=="Yes") {
401            header("Location:".$url.create_link ("login"));
402            die();
403        }
404        else {
405            return true;
406        }
407    }
408
409    /**
410     * Sets UI theme for user
411     *
412     * @method set_user_theme
413     * @return void
414     */
415    private function set_user_theme () {
416        // set defaukt theme if field is missing
417        if(!isset($this->settings->theme)) {
418            $this->settings->theme = "dark";
419        }
420        // set user
421        if(is_object($this->user)) {
422            // use default theme from general settings
423            if(!isset($this->user->theme) || @$this->user->theme=="") {
424                $this->user->ui_theme = $this->settings->theme;
425            }
426            else {
427                $this->user->ui_theme = $this->user->theme;
428            }
429            // validate
430            if(!in_array($this->user->ui_theme, $this->themes)) {
431                $this->user->ui_theme = "white";
432            }
433        }
434    }
435
436    /**
437     * Check if users timeout expired
438     *     if yes set timeout flag
439     *
440     * @access private
441     * @return void
442     */
443    private function check_timeout () {
444        //session set
445        if(isset($_SESSION['lastactive'])) {
446            if( strlen($this->settings->inactivityTimeout)>0 && (time()-@$_SESSION['lastactive']) > $this->settings->inactivityTimeout) {
447                $this->timeout = true;
448                unset($_SESSION['lastactive']);
449            }
450        }
451    }
452
453    /**
454     * resets inactivity time after each succesfull login
455     *
456     * @access private
457     * @return void
458     */
459    private function reset_inactivity_time () {
460        if($this->timeout!==true) {
461            $_SESSION['lastactive'] = time();
462        }
463    }
464
465    /**
466     * Saves redirect cookie if session times out
467     *
468     * @access private
469     * @return void
470     */
471    private function set_redirect_cookie () {
472        # save current redirect vaule
473        if( $_SERVER['SCRIPT_URL']=="/login/" ||
474            $_SERVER['SCRIPT_URL']=="logout" ||
475            $_SERVER['SCRIPT_URL']=="?page=login" ||
476            $_SERVER['SCRIPT_URL']=="?page=logout" ||
477            $_SERVER['SCRIPT_URL']=="index.php?page=login" ||
478            $_SERVER['SCRIPT_URL']=="index.php?page=logout" ||
479            $_SERVER['SCRIPT_URL']=="/" ||
480            $_SERVER['SCRIPT_URL']=="%2f")
481        {
482            return;
483        }
484
485        $uri = is_string($_SERVER['HTTP_X_FORWARDED_URI']) ? $_SERVER['HTTP_X_FORWARDED_URI'] : $_SERVER['REQUEST_URI'];
486
487        setcookie_samesite("phpipamredirect", preg_replace('/^\/+/', '/', $uri), 10, true);
488    }
489
490    /**
491     * Sets translation for logged in user
492     *
493     * @access private
494     * @return void
495     */
496    private function set_ui_language () {
497        if(strlen($_SESSION['ipamlanguage'])>0)     {
498            putenv("LC_ALL=$_SESSION[ipamlanguage]");
499            bindtextdomain("phpipam", dirname(__FILE__)."/../locale");    // Specify location of translation tables
500            setlocale(LC_ALL, $_SESSION['ipamlanguage']);        // set language
501            textdomain("phpipam");                                // Choose domain
502        }
503    }
504
505    /**
506     * Checks if system is in maintaneance mode and exits if it is
507     *
508     * @method check_maintaneance_mode
509     * @param  bool    $is_popup (default: false)
510     * @return void
511     */
512    public function check_maintaneance_mode ($is_popup = false) {
513        if($this->settings->maintaneanceMode == "1" && $this->user->username!="Admin") {
514            if($is_popup) {
515                $this->Result->show("warning", "<i class='fa fa-info'></i> "._("System is running in maintenance mode")." !", true, true);
516            }
517            else {
518                $this->Result->show("warning text-center nomargin", "<i class='fa fa-info'></i> "._("System is running in maintenance mode")." !", true);
519            }
520        }
521    }
522
523    /**
524     * Sets maintaneance mode
525     *
526     * @method set_maintaneance_mode
527     * @param  bool $on (default: false)
528     */
529    public function set_maintaneance_mode ($on = false) {
530        # set mode status
531        $maintaneance_mode = $on ? "1" : "0";
532        # execute
533        try { $this->Database->updateObject("settings", array("id"=>1, "maintaneanceMode"=>$maintaneance_mode), "id"); }
534        catch (Exception $e) {}
535    }
536
537    /**
538     * Migrate resolve_subnets from config.php to database
539     * for versions older than 1.31
540     *
541     * @method migrate_resolve_subnets
542     *
543     * @return void
544     */
545    public function migrate_resolve_subnets () {
546        // read config.php
547        $config = Config::get('config');
548
549        // check for array and values
550        if(!isset($config['resolve_subnets']) || !is_array($config['resolve_subnets']) || sizeof($config['resolve_subnets'])==0)
551            return;
552
553        foreach ($config['resolve_subnets'] as $subnetId) {
554            $update = ["id" => $subnetId, "resolveDNS" => 1 ];
555            // update
556            try {
557                $this->Database->updateObject("subnets", $update);
558            } catch (Exception $e) {}
559        }
560        // print that is can be deleted
561        $this->Result->show ("warning", '$config[resolve_subnets] '._('was migrated to database. It can be deleted from config.php'), false);
562    }
563
564
565
566
567
568
569
570
571    /**
572     * @miscalaneous methods
573     * ------------------------------
574     */
575
576    /**
577     * Checks AJAX loaded pages for proper origin
578     *
579     * @access private
580     * @return void
581     */
582    private function check_referrer () {
583        if ( ($_SERVER['HTTP_X_REQUESTED_WITH'] != "XMLHttpRequest") && ($_SERVER['HTTP_ORIGIN'] != $_SERVER['HTTP_HOST'] ) ) {
584            # write log and die
585            $this->Log->write ("referrer_check", _('Page not referred properly'), 0 );
586            $this->Result->show ("danger", _('Page not referred properly'), true);
587        }
588    }
589
590    /**
591     * fetches default language
592     *
593     * @access public
594     * @return object
595     */
596    public function get_default_lang () {
597        try { $lang = $this->Database->findObject("lang","l_id",$this->settings->defaultLang); }
598        catch (Exception $e) { $this->debugging ? : $this->Result->show("danger", _("Database error: ").$e->getMessage()); }
599
600        return $lang;
601    }
602
603    /**
604     * Sets available authentication methods
605     *
606     *    Can be extended by reading set properties from set field options
607     *
608     * @access public
609     * @return array
610     */
611    public function fetch_available_auth_method_types () {
612		return array("AD", "LDAP", "NetIQ", "Radius", "SAML2");
613	}
614
615
616
617
618
619
620
621
622
623
624    /**
625     * @favourite methods
626     * ------------------------------
627     */
628
629    /**
630     * Fetches details for users favourite subnets
631     *
632     * @access public
633     * @return array|false
634     */
635    public function fetch_favourite_subnets () {
636        # none
637        if(strlen($this->user->favourite_subnets)==0) {
638            return false;
639        }
640        # ok
641        else {
642            # store to array
643            $subnets = explode(";", $this->user->favourite_subnets);
644            $subnets = array_filter($subnets);
645
646            if(sizeof($subnets)>0) {
647                // init
648                $fsubnets = array();
649                # fetch details for each subnet
650                foreach($subnets as $id) {
651                    $query = "select `su`.`id` as `subnetId`,`se`.`id` as `sectionId`, `subnet`, `mask`,`isFull`,`su`.`description`,`se`.`description` as `section`, `vlanId`, `isFolder`
652                              from `subnets` as `su`, `sections` as `se` where `su`.`id` = ? and `su`.`sectionId` = `se`.`id` limit 1;";
653
654                    try { $fsubnet = $this->Database->getObjectQuery($query, array($id)); }
655                    catch (Exception $e) {
656                        $this->Result->show("danger", _("Error: ").$e->getMessage());
657                        return false;
658                    }
659
660                    # out array
661                    $fsubnets[] = (array) $fsubnet;
662                }
663                return $fsubnets;
664            } else {
665                return false;
666            }
667        }
668    }
669
670    /**
671     * Edit users favourites
672     *
673     * @access public
674     * @param mixed $action
675     * @param mixed $subnetId
676     * @return bool
677     */
678    public function edit_favourite($action, $subnetId) {
679        # execute
680        if($action=="remove")    { return $this->remove_favourite ($subnetId); }
681        elseif($action=="add")   { return $this->add_favourite ($subnetId); }
682        else                     { return false; }
683    }
684
685    /**
686     * Remove subnet from user favourite subnets
687     *
688     * @access private
689     * @param mixed $subnetId
690     * @return bool
691     */
692    private function remove_favourite ($subnetId) {
693        # set old favourite subnets
694        $old_favourites = explode(";", $this->user->favourite_subnets);
695        # set new
696        $new_favourites = implode(";", array_diff($old_favourites, array($subnetId)));
697        # update
698        try { $this->Database->updateObject("users", array("favourite_subnets"=>$new_favourites, "id"=>$this->user->id), "id"); }
699        catch (Exception $e) {
700            return false;
701        }
702        return true;
703    }
704
705    /**
706     * Add subnet to user favourite subnets
707     *
708     * @access private
709     * @param int $subnetId
710     * @return bool
711     */
712    private function add_favourite ($subnetId) {
713        # set old favourite subnets
714        $old_favourites = explode(";", $this->user->favourite_subnets);
715        $old_favourites = is_array($old_favourites) ? $old_favourites : array();
716        # set new
717        $new_favourites = implode(";",array_merge(array($subnetId), $old_favourites));
718        # update
719        try { $this->Database->updateObject("users", array("favourite_subnets"=>$new_favourites, "id"=>$this->user->id), "id"); }
720        catch (Exception $e) {
721            return false;
722        }
723        return true;
724    }
725
726    /**
727     * Checks if subnet is in users favourite subnets
728     *
729     * @access public
730     * @param int $subnetId
731     * @return boolean
732     */
733    public function is_subnet_favourite ($subnetId) {
734        $this->fetch_favourite_subnets ();
735        # check if in array
736        $subnets = explode(";", $this->user->favourite_subnets);
737        $subnets = array_filter($subnets);
738        # result
739        return in_array($subnetId, $subnets) ? true : false;
740    }
741
742    /**
743     * Checks if folder is favourite - alias for is subnet favourite
744     *
745     * @access public
746     * @param mixed $subnetId
747     * @return bool
748     */
749    public function is_folder_favourite ($subnetId) {
750        return $this->is_subnet_favourite ($subnetId);
751    }
752
753
754
755
756
757
758
759
760
761
762
763    /**
764    * @authentication functions
765    * -------------------------------
766    */
767
768    /**
769     * Main function for authenticating users
770     *
771     *    > tries to fetch user details from database by username
772     *    > sets authentication method and checks validity
773     *    > authenticates
774     *
775     * @access public
776     * @param string $username
777     * @param string $password
778     * @param bool $saml
779     * @return void
780     */
781    public function authenticate ($username, $password, $saml = false) {
782        if(($saml !== false ) && (defined('MAP_SAML_USER')) && (MAP_SAML_USER !== false)) {
783            $username = SAML_USERNAME;
784        }
785        # first we need to check if username exists
786        $this->fetch_user_details ($username);
787        # set method type if set, otherwise presume local auth
788        $this->authmethodid = strlen(@$this->user->authMethod)>0 ? $this->user->authMethod : 1;
789
790        # 2fa
791        if ($this->user->{'2fa'}==1) {
792            $this->twofa = true;
793        }
794
795        # get authentication method details
796        $this->get_auth_method_type ();
797
798        # authenticate based on name of auth method
799        if(!method_exists($this, $this->authmethodtype))    {
800            $this->Log->write ("User login", _('Error: Invalid authentication method'), 2 );
801            $this->Result->show("danger", _("Error: Invalid authentication method"), true);
802        }
803        # disabled
804        elseif ($this->user->disabled=="Yes") {
805            $this->Result->show("danger", _("Your account has been disabled").".", true);
806        }
807        else {
808            # set method name variable
809            $authmethodtype = $this->authmethodtype;
810            if($saml !== false) {
811                $authmethodtype = 'auth_SAML2';
812            }
813            # is auth_SAML and $saml == false throw error
814            if ($authmethodtype=="auth_SAML2" && $saml===false) {
815                $this->Result->show("danger", "Please use <a href='".create_link('saml2')."'>login</a>!", true);
816            }
817            else {
818                # authenticate
819                $this->{$authmethodtype} ($username, $password);
820            }
821        }
822    }
823
824    /**
825     * tries to fetch user datails from database by username if not already existing locally
826     *
827     * @access private
828     * @param string $username
829     * @param bool $force
830     * @return void
831     */
832    private function fetch_user_details ($username, $force = false) {
833        # only if not already active
834        if(!is_object($this->user) || $force) {
835            try { $user = $this->Database->findObject("users", "username", $username); }
836            catch (Exception $e)     { $this->Result->show("danger", _("Error: ").$e->getMessage(), true);}
837
838            # if not result return false
839            $usert = (array) $user;
840
841            # admin?
842            if($user->role == "Administrator")    { $this->isadmin = true; }
843
844            if(sizeof($usert)==0)    { $this->block_ip (); $this->Log->write ("User login", _('Invalid username'), 2, $username ); $this->Result->show("danger", _("Invalid username or password"), true);}
845            else                     { $this->user = $user; }
846
847            // register permissions
848            $this->register_user_module_permissions ();
849        }
850    }
851
852    /**
853     * Fetch all languages from database.
854     *
855     * @access public
856     * @return array
857     */
858    public function fetch_langs () {
859        try { $langs = $this->Database->getObjects("lang", "l_id"); }
860        catch (Exception $e) {
861            $this->Result->show("danger", _("Error: ").$e->getMessage());
862            return false;
863        }
864        # return
865        return $langs;
866    }
867
868    /**
869     * fetches language details from database
870     *
871     * @access private
872     * @return string
873     */
874    private function fetch_lang_details () {
875        // fetch from db
876        try { $lang = $this->Database->findObject("lang", "l_id", $this->user->lang); }
877        catch (Exception $e) {
878            $this->Result->show("danger", _("Error: ").$e->getMessage(), true);
879            return false;
880        }
881        // return code
882        return $lang->l_code;
883    }
884
885    /**
886     * Fetches name and details of authentication method (local, AD, LDAP, ...) from DB and saves them to var
887     *
888     * @access private
889     * @return void
890     */
891    private function get_auth_method_type () {
892        # for older versions - only local is available!
893        if($this->settings->version=="1.1") {
894            $this->authmethodtype = "auth_local";
895        }
896        else {
897            try { $method = $this->Database->getObject("usersAuthMethod", $this->authmethodid); }
898            catch (Exception $e) {
899                $this->Result->show("danger", _("Error: ").$e->getMessage(), true);
900            }
901            # save method name if existing
902            if($method!==false) {
903                $this->authmethodtype   = "auth_".$method->type;
904                $this->authmethodparams = $method->params;
905            }
906        }
907    }
908
909    /**
910     * local user authentication method, authenticates users through local DB entry
911     * we provide user object from DB, and username/password entered by users
912     *
913     * @access private
914     * @param mixed $username
915     * @param mixed $password
916     * @return void
917     */
918    private function auth_local ($username, $password) {
919        # auth ok
920        if(hash_equals($this->user->password, crypt($password, $this->user->password))) {
921            # save to session
922            $this->write_session_parameters ();
923
924            $this->Result->show("success", _("Login successful"));
925            $this->Log->write( "User login", "User ".$this->user->real_name." logged in", 0, $username );
926
927            # write last logintime
928            $this->update_login_time ();
929
930            # remove possible blocked IP
931            $this->block_remove_entry ();
932        }
933        # auth failed
934        else {
935            # add blocked count
936            $this->block_ip ();
937
938            $this->Log->write( "User login", "Invalid username or password", 2, $username );
939
940            # apache
941            if (!empty($_SERVER['PHP_AUTH_USER']) && $this->api!==true) { $this->show_http_login(); }
942            else                                                        { $this->Result->show("danger", _("Invalid username or password"), true); }
943        }
944    }
945
946    /**
947     * HTTP REMOTE_USER authentication, the user is already authenticated
948     * by the web server so just create the session
949     *
950     * @access private
951     * @param mixed $username
952     * @param mixed $password
953     * @return void
954     */
955    public function auth_http ($username, $password) {
956        # save to session
957        $this->write_session_parameters ();
958
959        $this->Result->show("success", _("Login successful"));
960        $this->Log->write( "User login", "User ".$this->user->real_name." logged in", 0, $username );
961
962        # write last logintime
963        $this->update_login_time ();
964
965        # remove possible blocked IP
966        $this->block_remove_entry ();
967    }
968
969    /**
970     * Shows login prompt for apache logins
971     *
972     * @access private
973     * @return void
974     */
975    private function show_http_login () {
976        header('WWW-Authenticate: Basic realm="phpIPAM authentication"');
977        header('HTTP/1.0 401 Unauthorized');
978        echo 'Authentication failed';
979        exit;
980    }
981
982    /**
983     * Connect to a directory given our auth method settings
984     *
985     *Connect using adLDAP
986     *
987     * @access private
988     * @param mixed $authparams
989     * @return adLDAP object
990     */
991    private function directory_connect ($authparams) {
992        # adLDAP script
993        require(dirname(__FILE__) . "/../adLDAP/src/adLDAP.php");
994        $dirparams = Array();
995        $dirparams['base_dn'] = @$authparams['base_dn'];
996        $dirparams['ad_port'] = @$authparams['ad_port'];
997        $dirparams['account_suffix'] = @$authparams['account_suffix'];
998        $dirparams['domain_controllers'] = explode(";", str_replace(" ", "", $authparams['domain_controllers']));
999        // set ssl and tls separate for ldap and AD
1000        if ($this->ldap) {
1001            // set ssl and tls
1002            $dirparams['use_ssl'] = false;
1003            $dirparams['use_tls'] = false;
1004            // Support the pre-1.2 auth settings as well as the current version
1005            // TODO: remove legacy support at some point
1006            if ($authparams['ldap_security'] == 'tls' || $authparams['use_tls'] == 1)         { $dirparams['use_tls'] = true; }
1007            elseif ($authparams['ldap_security'] == 'ssl' || $authparams['use_ssl'] == 1)     { $dirparams['use_ssl'] = true; }
1008            if (isset($authparams['admin_username']) && isset($authparams['admin_password'])) {
1009                $dirparams['admin_username'] = $authparams['adminUsername'];
1010                $dirparams['admin_password'] = $authparams['adminPassword'];
1011            }
1012        }
1013        else {
1014            $dirparams['use_ssl'] = @$authparams['use_ssl'];
1015            $dirparams['use_tls'] = @$authparams['use_tls'];
1016        }
1017        # open connection
1018        try {
1019            # Initialize adLDAP
1020            $dirconn = new adLDAP($dirparams);
1021        } catch (adLDAPException $e) {
1022            $this->Log->write("Directory connection error", "Failed to connect: " . $e->getMessage(), 2, null);
1023            $this->Result->show("danger", _("Error: ") . $e->getMessage(), true);
1024        }
1025        return $dirconn;
1026    }
1027
1028    /**
1029     *    Authenticate against a directory
1030     *
1031     *    Authenticates users against a directory - AD or LDAP
1032     *    Using library > adLDAP - LDAP Authentication with PHP for Active Directory
1033     *    http://adldap.sourceforge.net
1034     *
1035     * @access private
1036     * @param array $authparams
1037     * @param string $username
1038     * @param string $password
1039     * @return void
1040     */
1041    private function directory_authenticate ($authparams, $username, $password) {
1042        // set method
1043        $method = $this->ldap ? "LDAP" : "AD";
1044        // connect
1045        $adldap = $this->directory_connect($authparams);
1046
1047        # authenticate
1048        try {
1049            if ($adldap->authenticate($username, $password)) {
1050                # save to session
1051                $this->write_session_parameters();
1052
1053                $this->Log->write($method . " login", "User " . $this->user->real_name . " logged in via " . $method, 0, $username);
1054                $this->Result->show("success", _($method . " Login successful"));
1055
1056                # write last logintime
1057                $this->update_login_time();
1058                # remove possible blocked IP
1059                $this->block_remove_entry();
1060            } # wrong user/pass by default
1061            else {
1062                # add blocked count
1063                $this->block_ip();
1064                $this->Log->write($method . " login", "User $username failed to authenticate against " . $method, 1, $username);
1065                $this->Result->show("danger", _("Invalid username or password"), true);
1066
1067            }
1068        } catch (adLDAPException $e) {
1069            $this->Log->write("Error", "Something went wrong during auth: " . $e->getMessage(), 2, $username);
1070            $this->Result->show("danger", _("Error: ") . $e->getMessage(), true);
1071        }
1072    }
1073
1074    /**
1075     * AD (Active directory) authentication function
1076     *
1077     *
1078     * @access private
1079     * @param mixed $username
1080     * @param mixed $password
1081     * @return void
1082     */
1083    private function auth_AD ($username, $password) {
1084        // parse settings for LDAP connection and store them to array
1085        $authparams = json_decode($this->authmethodparams, true);
1086        // authenticate
1087        $this->directory_authenticate($authparams, $username, $password);
1088    }
1089
1090    /**
1091     *    LDAP authentication
1092     *    same as AD authentication, only set the LDAP flag to true
1093     *
1094     * @access private
1095     * @param mixed $username
1096     * @param mixed $password
1097     * @return void
1098     */
1099    private function auth_LDAP ($username, $password) {
1100        // parse settings for LDAP connection and store them to array
1101        $authparams = json_decode($this->authmethodparams, true);
1102        $this->ldap = true;                            //set ldap flag
1103
1104        // set uid
1105        if (!empty($authparams['uid_attr'])) { $udn = $authparams['uid_attr'] . '=' . $username; }
1106        else                                 { $udn = 'uid=' . $username; }
1107        // set DN
1108        if (!empty($authparams['users_base_dn'])) { $udn = $udn . "," . $authparams['users_base_dn']; }
1109        else                                      { $udn = $udn . "," . $authparams['base_dn']; }
1110        // authenticate
1111        $this->directory_authenticate($authparams, $udn, $password);
1112    }
1113
1114    /**
1115     * NetIQ authentication
1116     * same as AD authentication, only add cn= before username
1117     *
1118     * @access private
1119     * @param mixed $username
1120     * @param mixed $password
1121     * @return void
1122     */
1123    private function auth_NetIQ ($username, $password) {
1124        $this->auth_AD ("cn=".$username, $password);
1125    }
1126
1127    /**
1128     * Authenticates user on radius server
1129     *
1130     * @access private
1131     * @param mixed $username
1132     * @param mixed $password
1133     * @return void
1134     */
1135    private function auth_radius ($username, $password) {
1136        # decode radius parameters
1137        $params = json_decode($this->authmethodparams);
1138
1139        # check for socket support !
1140        if(!in_array("sockets", get_loaded_extensions())) {
1141            $this->Log->write( "Radius login", "php Socket extension missing", 2 );
1142            $this->Result->show("danger", _("php Socket extension missing"), true);
1143        }
1144
1145        # initialize radius class
1146        require( dirname(__FILE__) . '/class.Radius.php' );
1147        $Radius = new Radius ($params->hostname, $params->secret, $params->suffix, $params->timeout, $params->port);
1148        //debugging
1149        $this->debugging!==true ? : $Radius->SetDebugMode(TRUE);
1150
1151        # authenticate
1152        $auth = $Radius->AccessRequest($username, $password);
1153        # debug?
1154        if($this->debugging) {
1155            print "<pre style='width:700px;margin:auto;margin-top:10px;'>";
1156            print(implode("<br>", $Radius->debug_text));
1157            print "</pre>";
1158        }
1159
1160        # authenticate user
1161        if($auth) {
1162            # save to session
1163            $this->write_session_parameters ();
1164
1165            $this->Log->write( "Radius login", "User ".$this->user->real_name." logged in via radius", 0, $username );
1166            $this->Result->show("success", _("Radius login successful"));
1167
1168            # write last logintime
1169            $this->update_login_time ();
1170            # remove possible blocked IP
1171            $this->block_remove_entry ();
1172        }
1173        else {
1174            # add blocked count
1175            $this->block_ip ();
1176            $this->Log->write( "Radius login", "Failed to authenticate user on radius server", 2, $username );
1177            $this->Result->show("danger", _("Invalid username or password"), true);
1178        }
1179    }
1180
1181    /**
1182     * SAML2 auth
1183     *
1184     * @access private
1185     * @param mixed $username
1186     * @param mixed $password (default: null)
1187     * @return void
1188     */
1189    private function auth_SAML2 ($username, $password = null) {
1190        # save to session
1191        $this->write_session_parameters ();
1192
1193        $this->Log->write( "SAML2 login", "User ".$this->user->real_name." logged in via SAML2", 0, $username );
1194        $this->Result->show("success", _("SAML2 login successful"));
1195
1196        # write last logintime
1197        $this->update_login_time ();
1198        # remove possible blocked IP
1199        $this->block_remove_entry ();
1200    }
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211    /**
1212     *    @crypt functions
1213     *    ------------------------------
1214     */
1215
1216
1217    /**
1218     *    function to crypt user pass, randomly generates salt. Use sha256 if possible, otherwise Blowfish or md5 as fallback
1219     *
1220     *        types:
1221     *            CRYPT_MD5 == 1           (Salt starting with $1$, 12 characters )
1222     *            CRYPT_BLOWFISH == 1        (Salt starting with $2a$. The two digit cost parameter: 09. 22 characters )
1223     *            CRYPT_SHA256 == 1        (Salt starting with $5$rounds=5000$, 16 character salt.)
1224     *            CRYPT_SHA512 == 1        (Salt starting with $6$rounds=5000$, 16 character salt.)
1225     *
1226     * @access public
1227     * @param mixed $input
1228     * @return string
1229     */
1230    public function crypt_user_pass ($input) {
1231        # initialize salt
1232        $salt = "";
1233        # set possible salt characters in array
1234        $salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9));
1235        # loop to create salt
1236        for($i=0; $i < 22; $i++) { $salt .= $salt_chars[array_rand($salt_chars)]; }
1237        # get prefix
1238        $prefix = $this->detect_crypt_type ();
1239        # return crypted variable
1240        return crypt($input, $prefix.$salt);
1241    }
1242
1243    /**
1244     *    this function will detect highest crypt type to use for system
1245     *
1246     * @access public
1247     * @return string
1248     */
1249    private function detect_crypt_type () {
1250        if(CRYPT_SHA512 == 1)        { return '$6$rounds=3000$'; }
1251        elseif(CRYPT_SHA256 == 1)    { return '$5$rounds=3000$'; }
1252        elseif(CRYPT_BLOWFISH == 1)  { return '$2y$'.str_pad(rand(4,31),2,0, STR_PAD_LEFT).'$'; }
1253        elseif(CRYPT_MD5 == 1)       { return '$5$rounds=3000$'; }
1254        else                         { $this->Result->show("danger", _("No crypt types supported"), true); }
1255    }
1256
1257    /**
1258     * Returns crypt type used to encrypt password
1259     *
1260     * @access public
1261     * @return string
1262     */
1263    public function return_crypt_type () {
1264        if(CRYPT_SHA512 == 1)        { return 'CRYPT_SHA512'; }
1265        elseif(CRYPT_SHA256 == 1)    { return 'CRYPT_SHA256'; }
1266        elseif(CRYPT_BLOWFISH == 1)  { return 'CRYPT_BLOWFISH'; }
1267        elseif(CRYPT_MD5 == 1)       { return 'CRYPT_MD5'; }
1268        else                         { return "No crypt types supported"; }
1269    }
1270
1271    /**
1272     * Updates users password
1273     *
1274     * @access public
1275     * @param mixed $password
1276     * @return void
1277     */
1278    public function update_user_pass ($password) {
1279        try { $this->Database->updateObject("users", array("password"=>$this->crypt_user_pass ($password), "passChange"=>"No", "id"=>$this->user->id), "id"); }
1280        catch (Exception $e) { $this->Result->show("danger", $e->getMessage(), true); }
1281
1282        $this->Result->show("success", "Hi, ".$this->user->real_name.", "._("your password was updated").". <a class='btn btn-sm btn-default' href='".create_link("dashboard")."'>Dashboard</a>", false);
1283    }
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294    /**
1295     *    @updating user methods
1296     *    ------------------------------
1297     */
1298
1299    /**
1300     * User self update method
1301     *
1302     * @access public
1303     * @param array|object $post //posted user details
1304     * @return bool
1305     */
1306    public function self_update($post) {
1307        # remove theme
1308        if($post['theme'] == "default") { $post['theme'] = ""; }
1309        # set items to update
1310        $items  = array("real_name"        => escape_input(strip_tags($post['real_name'])),
1311                        "mailNotify"       => $post['mailNotify'] == "Yes" ? "Yes" : "No",
1312                        "mailChangelog"    => $post['mailChangelog'] == "Yes" ? "Yes" : "No",
1313                        "email"            => $this->validate_email($post['email']) ? escape_input($post['email']) : '',
1314                        "lang"             => escape_input(strip_tags($post['lang'])),
1315                        "id"               => $this->user->id,
1316                        //display
1317                        "compressOverride" => escape_input(strip_tags($post['compressOverride'])),
1318                        "hideFreeRange"    => $this->verify_checkbox(@$post['hideFreeRange']),
1319                        "menuType"         => $this->verify_checkbox(@$post['menuType']),
1320                        "menuCompact"      => $this->verify_checkbox(@$post['menuCompact']),
1321                        "theme"            => $post['theme'],
1322                        "2fa"              => $this->verify_checkbox(@$post['2fa']),
1323                        );
1324        if(strlen($post['password1'])>0) {
1325        $items['password'] = $this->crypt_user_pass ($post['password1']);
1326        }
1327
1328        # prepare log file
1329        $log = $this->array_to_log ($post);
1330
1331        # update
1332        try { $this->Database->updateObject("users", $items); }
1333        catch (Exception $e) {
1334            $this->Result->show("danger", _("Error: ").$e->getMessage(), false);
1335            $this->Log->write( "User self update", "User self update failed!<br>".$log, 2 );
1336            return false;
1337        }
1338        # update language
1339        $this->update_session_language ();
1340
1341        # ok, update log table
1342        $this->Log->write( "User self update", "User self update suceeded!", 0 );
1343        return true;
1344    }
1345
1346    /**
1347     * User self update widgets.
1348     *
1349     * @access public
1350     * @param mixed $widgets
1351     * @return bool
1352     */
1353    public function self_update_widgets ($widgets) {
1354        # update
1355        try { $this->Database->updateObject("users", array("widgets"=>$widgets, "id"=>$this->user->id)); }
1356        catch (Exception $e) {
1357            $this->Result->show("danger", _("Error: ").$e->getMessage(), false);
1358            return false;
1359        }
1360        # ok, update log table
1361        return true;
1362    }
1363
1364    /**
1365     * Updates last users login time
1366     *
1367     * @access public
1368     * @return bool
1369     */
1370    public function update_login_time () {
1371        # fix for older versions
1372        if($this->settings->version!="1.1") {
1373            # update
1374            try { $this->Database->updateObject("users", array("lastLogin"=>date("Y-m-d H:i:s"), "id"=>$this->user->id)); }
1375            catch (Exception $e) {
1376                $this->Result->show("danger", _("Error: ").$e->getMessage(), false);
1377                return false;
1378            }
1379        }
1380    }
1381
1382    /**
1383     * Updates last users activity time
1384     *
1385     * @access public
1386     * @return void
1387     */
1388    public function update_activity_time () {
1389        # update
1390        try { $this->Database->updateObject("users", array("lastActivity"=>date("Y-m-d H:i:s"), "id"=>$this->user->id)); }
1391        catch (Exception $e) { }
1392    }
1393
1394
1395
1396
1397
1398
1399
1400
1401    /**
1402     *    @blocking IP functions
1403     *    ------------------------------
1404     */
1405
1406
1407    /**
1408     * sets limit for failed login attempts
1409     *
1410     * @access public
1411     * @param int $limit
1412     * @return none
1413     */
1414    public function set_block_limit ($limit) {
1415        $this->blocklimit = $limit;
1416    }
1417
1418    /**
1419     * checks if IP is blocked and returns count for entries
1420     *
1421     * @access public
1422     * @param none
1423     * @return int|false
1424     */
1425    public function block_check_ip () {
1426        # first purge
1427        $this->purge_blocked_entries ();
1428        $this->block_get_ip ();
1429        # set date and query
1430        $now = date("Y-m-d H:i:s", time() - 5*60);
1431        $query = "select count from `loginAttempts` where `ip` = ? and `datetime` > ?;";
1432        # fetch
1433        try { $cnt = $this->Database->getObjectQuery($query, array($this->ip, $now)); }
1434        catch (Exception $e) { !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); }
1435
1436        # verify
1437        return @$cnt->count>0 ? $cnt->count : false;
1438    }
1439
1440    /**
1441     * adds new IP to block or updates count if already present
1442     *
1443     * @access public
1444     * @return bool
1445     */
1446    public function block_ip () {
1447        # validate IP
1448        if(!filter_var($this->ip, FILTER_VALIDATE_IP))    { return false; }
1449
1450        # first check if already in
1451        if($this->block_check_ip ())         { $this->block_update_count(); }
1452        # if not in add first entry
1453        else                                 { $this->block_add_entry(); }
1454    }
1455
1456    /**
1457     * sets IP address to block
1458     * needed for proxy access to block end user not whole proxy
1459     *
1460     * @access private
1461     * @return void
1462     */
1463    private function block_get_ip () {
1464        # set IP
1465        if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { $this->ip = @$_SERVER['HTTP_X_FORWARDED_FOR']; }
1466        else                                        { $this->ip = @$_SERVER['REMOTE_ADDR']; }
1467    }
1468
1469    /**
1470     * purges login attampts more than 5 minutes old (since last attempt)
1471     *
1472     * @access private
1473     * @return void
1474     */
1475    private function purge_blocked_entries () {
1476        # set date 5 min ago and query
1477        $ago = date("Y-m-d H:i:s", time() - 5*60);
1478        $query = "delete from `loginAttempts` where `datetime` < ?; ";
1479
1480        try { $this->Database->runQuery($query, array($ago)); }
1481        catch (Exception $e) { !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); }
1482    }
1483
1484    /**
1485     * updates existing log attampt count
1486     *
1487     * @access private
1488     * @return void
1489     */
1490    private function block_update_count() {
1491        # query
1492        $query = "update `loginAttempts` set `count`=`count`+1 where `ip` = ?; ";
1493        try { $this->Database->runQuery($query, array($this->ip)); }
1494        catch (Exception $e) { !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); }
1495    }
1496
1497    /**
1498     * adds new IP entry to block with count 1
1499     *
1500     * @access private
1501     * @return void
1502     */
1503    private function block_add_entry() {
1504        try { $this->Database->insertObject("loginAttempts", array("ip"=>$this->ip, "count"=>1)); }
1505        catch (Exception $e) { !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); }
1506    }
1507
1508    /**
1509     * removes blocked IP entry if it exists on successfull login
1510     *
1511     * @access private
1512     * @return void
1513     */
1514    private function block_remove_entry() {
1515        try { $this->Database->deleteRow("loginAttempts", "ip", $this->ip); }
1516        catch (Exception $e) { !$this->debugging ? : $this->Result->show("danger", $e->getMessage(), false); }
1517    }
1518
1519
1520
1521	/* @users and groups -------------------- */
1522
1523    /**
1524     * From json {"2":"2","3":"1"}, get user list + perm
1525     *
1526     * @method get_user_permissions_from_json
1527     * @param  json     $json
1528     * @return array
1529     */
1530    public function get_user_permissions_from_json ($json) {
1531        // Check cache
1532        $cached_item = $this->cache_check('get_user_permissions_from_json', $json);
1533        if(is_object($cached_item)) return $cached_item->result;
1534
1535        $groups = array();
1536        foreach((array) json_decode($json, true) as $group_id => $perm) {
1537            $group_details = $this->groups_parse (array($group_id));
1538
1539            $tmp = array();
1540            $tmp['group_id'] = $group_id;
1541            $tmp['permission'] = $perm;
1542            $tmp['name'] = $group_details[$group_id]['g_name'];
1543            $tmp['desc'] = $group_details[$group_id]['g_desc'];
1544            $tmp['members'] = $group_details[$group_id]['members'];
1545
1546            $groups[] = $tmp;
1547        }
1548        // Cache results to avoid repeat database queries.
1549        $this->cache_write('get_user_permissions_from_json', (object) ["id"=>$json, "result" => $groups]);
1550        return $groups;
1551    }
1552
1553	/**
1554	 * Parse user groups
1555	 *
1556	 *	input:  array of group ids
1557	 *	output: array of groups ( "id"=>array($group) )
1558	 *
1559     * @method groups_parse
1560	 * @param array  $group_ids
1561	 * @return array
1562	 */
1563	private function groups_parse ($group_ids) {
1564		if(sizeof($group_ids)>0) {
1565	    	foreach($group_ids as $g_id) {
1566	    		// group details
1567	    		$group = $this->fetch_object ("userGroups", "g_id", $g_id);
1568	    		$out[$group->g_id] = (array) $group;
1569	    		$out[$group->g_id]['members'] = $this->fetch_multiple_objects("users", "groups", "%\"$g_id\"%", "real_name", true, true, array("username"));
1570	    	}
1571	    }
1572	    # return array of groups
1573	    return isset($out) ? $out : array();
1574	}
1575
1576    /**
1577     * Get user l2domain access permissions
1578     *
1579     * Result can be the following:
1580     *     - 0 : no access
1581     *     - 1 : read-only
1582     *     - 2 ; read-write
1583     *     - 3 : admin
1584     *
1585     * @method get_l2domain_permissions
1586     * @param  object $l2domain
1587     * @return int
1588     */
1589    public function get_l2domain_permissions ($l2domain) {
1590        if ($this->is_admin(false))
1591            return 3;
1592
1593        // Default l2domain is assigned to all sections
1594        if ($l2domain->id == 1) {
1595            $sections_ids = [];
1596            $all_sections = $this->fetch_all_objects("sections");
1597            if (is_array($all_sections)) {
1598                foreach($all_sections as $section){
1599                    $sections_ids[] = $section->id;
1600                }
1601            }
1602            $valid_sections = implode(';', $sections_ids);
1603        } else {
1604            $valid_sections = $l2domain->permissions;
1605        }
1606
1607        $cached_item = $this->cache_check('l2domain_permissions', $valid_sections);
1608        if(is_object($cached_item)) return $cached_item->result;
1609
1610        if (empty($valid_sections)) {
1611            $this->cache_write('l2domain_permissions', (object) ["id"=>$valid_sections, "result" => 0]);
1612            return 0;
1613        }
1614
1615        $max_permission = 0;
1616
1617        $ids = explode(";", $valid_sections);
1618        foreach($ids as $id) {
1619            $section = $this->fetch_object("sections", "id", $id);
1620
1621            if (!is_object($section)) continue;
1622
1623            # Get Section permissions
1624            $sectionP = json_decode($section->permissions, true);
1625
1626            # ok, user has section access, check also for any higher access from subnet
1627            if(!is_array($sectionP)) continue;
1628
1629            # get all user groups
1630            $groups = json_decode($this->user->groups, true);
1631
1632            foreach($sectionP as $sk=>$sp) {
1633                # check each group if user is in it and if so check for permissions for that group
1634                foreach($groups as $uk=>$up) {
1635                    if($uk == $sk) {
1636                        if($sp > $max_permission) { $max_permission = $sp; }
1637                    }
1638                }
1639            }
1640        }
1641
1642        # return result
1643        $this->cache_write('l2domain_permissions', (object) ["id"=>$valid_sections, "result" => $max_permission]);
1644        return $max_permission;
1645    }
1646
1647    /**
1648     * Check if user has l2domain permissions for specific access level
1649     *
1650     * @method check_l2domain_permissions
1651     * @param  object $l2domain
1652     * @param  int $required_level
1653     * @param  bool $die
1654     * @param  bool $popup
1655     * @return bool|void
1656     */
1657    public function check_l2domain_permissions($l2domain, $required_level = 1, $die = true, $popup = false) {
1658        // check if valid
1659        $valid = $this->get_l2domain_permissions($l2domain)>=$required_level;
1660        // return or die ?
1661        if ($die===true && !$valid) {
1662            $this->Result->show ("danger", _("You do not have permissions to access this object"), true, $popup);
1663        }
1664        else {
1665            return $valid;
1666        }
1667    }
1668
1669    /**
1670     * Register use module permissions from json
1671     *
1672     * @method register_user_module_permissions
1673     * @return void
1674     */
1675    private function register_user_module_permissions () {
1676        // decode
1677        $permissions = json_decode($this->user->module_permissions, true);
1678        // check for each module
1679        foreach ($this->get_modules_with_permissions() as $m) {
1680            if (!is_array($permissions)) {
1681                $this->user->{'perm_'.$m} = 0;
1682            }
1683            elseif(array_key_exists($m, $permissions)) {
1684                $this->user->{'perm_'.$m} = $permissions[$m];
1685            }
1686            else {
1687                $this->user->{'perm_'.$m} = 0;
1688            }
1689        }
1690    }
1691
1692    /**
1693     * Get module permissions for user
1694     *
1695     * Result can be the following:
1696     *     - 0 : no access
1697     *     - 1 : read-only
1698     *     - 2 ; read-write
1699     *     - 3 : admin
1700     *
1701     * @method get_module_permissions
1702     * @param  string $module_name
1703     * @return int
1704     */
1705    public function get_module_permissions ($module_name = "") {
1706        if(in_array($module_name, $this->get_modules_with_permissions())) {
1707            // admin
1708            if($this->is_admin(false)) {
1709                return 3;
1710            }
1711            else {
1712                return $this->user->{'perm_'.$module_name};
1713            }
1714        }
1715        else {
1716            return 0;
1717        }
1718    }
1719
1720    /**
1721     * Check if user has module permissions for specific access level
1722     *
1723     * @method check_module_permissions
1724     * @param  string $module_name
1725     * @param  int $required_level
1726     * @param  bool $die
1727     * @param  bool $popup
1728     * @return bool|void
1729     */
1730    public function check_module_permissions ($module_name = "", $required_level = 1, $die = true, $popup = false) {
1731        // check if valid
1732        $valid = $this->get_module_permissions($module_name)>=$required_level;
1733        // return or die ?
1734        if ($die===true && !$valid) {
1735            $this->Result->show ("danger", _("You do not have permissions to access this module"), true, $popup);
1736        }
1737        else {
1738            return $valid;
1739        }
1740    }
1741
1742    /**
1743     * Return array of all modules with permissions
1744     *
1745     * @method get_modules_with_permissions
1746     * @return array
1747     */
1748    public function get_modules_with_permissions () {
1749        return [
1750                "vlan",
1751                "vrf",
1752                "pdns",
1753                "circuits",
1754                "racks",
1755                "nat",
1756                "pstn",
1757                "customers",
1758                "locations",
1759                "devices",
1760                "dhcp",
1761                "routing"
1762            ];
1763    }
1764
1765    /**
1766     * Prints permission badge
1767     *
1768     * @method print_permission_badge
1769     * @param  int $level
1770     * @return string
1771     */
1772    public function print_permission_badge ($level) {
1773        // null level
1774        if(is_null($level)) $level = 0;
1775        // return
1776        return $level=="0" ? "<span class='badge badge1 badge5 alert-danger'>"._($this->parse_permissions ($level))."</span>" : "<span class='badge badge1 badge5 alert-success'>"._($this->parse_permissions ($level))."</span>";
1777    }
1778}