1:   2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18:  19:  20:  21:  22:  23:  24:  25:  26:  27:  28:  29:  30:  31:  32:  33:  34:  35:  36:  37:  38:  39:  40:  41:  42:  43:  44:  45:  46:  47:  48:  49:  50:  51:  52:  53:  54:  55:  56:  57:  58:  59:  60:  61:  62:  63:  64:  65:  66:  67:  68:  69:  70:  71:  72:  73:  74:  75:  76:  77:  78:  79:  80:  81:  82:  83:  84:  85:  86:  87:  88:  89:  90:  91:  92:  93:  94:  95:  96:  97:  98:  99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 
<?php
    declare(strict_types=1);
    /**
     *  +------------------------------------------------------------+
     *  | apnscp                                                     |
     *  +------------------------------------------------------------+
     *  | Copyright (c) Apis Networks                                |
     *  +------------------------------------------------------------+
     *  | Licensed under Artistic License 2.0                        |
     *  +------------------------------------------------------------+
     *  | Author: Matt Saladna (msaladna@apisnetworks.com)           |
     *  +------------------------------------------------------------+
     */

    use Opcenter\Terminal;

    /**
     * Provides common functionality associated with SSH
     *
     * @package core
     */
    class Ssh_Module extends Module_Skeleton implements \Opcenter\Contracts\Hookable
    {
        const PAM_SVC_NAME = 'ssh';
        const DEPENDENCY_MAP = [
            'siteinfo',
            'ipinfo',
            'ipinfo6',
            'users',
            'auth'
        ];

        /**
         * {{{ void __construct(void)
         *
         * @ignore
         */

        public function __construct()
        {
            parent::__construct();
            $this->exportedFunctions = array(
                '*'       => PRIVILEGE_SITE,
                'enabled' => PRIVILEGE_SITE | PRIVILEGE_USER
            );
        }

        public function deny_user(string $user): bool
        {
            return (new Util_Pam($this->getAuthContext()))->remove($user, self::PAM_SVC_NAME);
        }

        public function permit_user(string $user): bool
        {
            if ($this->auth_is_demo()) {
                return error('SSH disabled for demo account');
            }

            return (new Util_Pam($this->getAuthContext()))->add($user, self::PAM_SVC_NAME);
        }

        public function _edit_user(string $userold, string $usernew, array $oldpwd)
        {
            if ($userold === $usernew) {
                return;
            }

            if (!$this->enabled() || !$this->user_enabled($userold)) {
                return true;
            }
            // @TODO nuke active ssh sessions?
            $pam = new Util_Pam($this->getAuthContext());
            $pam->remove($userold, self::PAM_SVC_NAME);
            $pam->add($usernew, self::PAM_SVC_NAME);

            return true;
        }

        public function enabled()
        {
            $check = (bool)$this->getServiceValue('ssh', 'enabled');
            if ($this->permission_level & PRIVILEGE_USER) {
                $check = $check && $this->user_enabled($this->username);
            }

            return $check;
        }

        public function port_range(): array
        {
            if (!$this->getServiceValue('ssh', 'enabled') || !SSH_USER_DAEMONS) {
                return array();
            }

            return Terminal::formatPortRange(
                $this->getServiceValue('ssh', 'port_index', [])
            );
        }

        public function user_enabled($user)
        {
            if (!$this->getConfig('ssh', 'enabled')) {
                return warn('ssh not enabled on account');
            }

            return (new Util_Pam($this->getAuthContext()))->check($user, self::PAM_SVC_NAME);
        }

        public function _housekeeping()
        {
            if (SSH_EMBED_TERMINAL && !APNSCPD_HEADLESS) {
                dlog('Loading terminal...');
                Service_Terminal::autostart();
            } else {
                Service_Terminal::stop();
            }
        }

        public function _create()
        {
            // stupid thor...
            $conf = $this->getAuthContext()->getAccount()->new;
            $admin = $conf['siteinfo']['admin_user'];
            $pam = new Util_Pam($this->getAuthContext());
            if ($this->auth_is_demo() && $pam->check($admin, self::PAM_SVC_NAME)) {
                $pam->remove($admin, self::PAM_SVC_NAME);
            }
        }

        public function _verify_conf(\Opcenter\Service\ConfigurationContext $ctx): bool
        {
            return true;
        }

        public function _delete()
        {
            // TODO: Implement _delete() method.
        }

        public function _edit()
        {
            // TODO: Implement _edit() method.
        }

        public function _create_user(string $user)
        {
            // TODO: Implement _create_user() method.
        }

        public function _delete_user(string $user)
        {
            // TODO: Implement _delete_user() method.
        }


    }