xref: /plugin/twofactor/Manager.php (revision 6c996db8dff158c7317a04e1185060c59b19ad7d)
1fca58076SAndreas Gohr<?php
2fca58076SAndreas Gohr
3fca58076SAndreas Gohrnamespace dokuwiki\plugin\twofactor;
4fca58076SAndreas Gohr
5a386a536SAndreas Gohruse dokuwiki\Extension\Plugin;
6a386a536SAndreas Gohr
7fca58076SAndreas Gohr/**
8fca58076SAndreas Gohr * Manages the child plugins etc.
9fca58076SAndreas Gohr */
10a386a536SAndreas Gohrclass Manager extends Plugin
118b7620a8SAndreas Gohr{
128b7620a8SAndreas Gohr    /** @var Manager */
138b7620a8SAndreas Gohr    protected static $instance;
148b7620a8SAndreas Gohr
158b7620a8SAndreas Gohr    /** @var bool */
168b7620a8SAndreas Gohr    protected $ready = false;
178b7620a8SAndreas Gohr
188b7620a8SAndreas Gohr    /** @var string[] */
198b7620a8SAndreas Gohr    protected $classes = [];
208b7620a8SAndreas Gohr
218b7620a8SAndreas Gohr    /** @var Provider[] */
228b7620a8SAndreas Gohr    protected $providers;
23fca58076SAndreas Gohr
24fca58076SAndreas Gohr    /**
258b7620a8SAndreas Gohr     * Constructor
26fca58076SAndreas Gohr     */
278b7620a8SAndreas Gohr    protected function __construct()
288b7620a8SAndreas Gohr    {
298b7620a8SAndreas Gohr        $this->classes = $this->getProviderClasses();
308b7620a8SAndreas Gohr
318b7620a8SAndreas Gohr        $attribute = plugin_load('helper', 'attribute');
328b7620a8SAndreas Gohr        if ($attribute === null) {
338b7620a8SAndreas Gohr            msg('The attribute plugin is not available, 2fa disabled', -1);
348b7620a8SAndreas Gohr        }
358b7620a8SAndreas Gohr
368b7620a8SAndreas Gohr        if (!count($this->classes)) {
378b7620a8SAndreas Gohr            msg('No suitable 2fa providers found, 2fa disabled', -1);
388b7620a8SAndreas Gohr            return;
398b7620a8SAndreas Gohr        }
408b7620a8SAndreas Gohr
418b7620a8SAndreas Gohr        $this->ready = true;
428b7620a8SAndreas Gohr    }
438b7620a8SAndreas Gohr
448b7620a8SAndreas Gohr    /**
458b7620a8SAndreas Gohr     * Get the instance of this singleton
468b7620a8SAndreas Gohr     *
478b7620a8SAndreas Gohr     * @return Manager
488b7620a8SAndreas Gohr     */
498b7620a8SAndreas Gohr    public static function getInstance()
508b7620a8SAndreas Gohr    {
518b7620a8SAndreas Gohr        if (self::$instance === null) {
528b7620a8SAndreas Gohr            self::$instance = new Manager();
538b7620a8SAndreas Gohr        }
548b7620a8SAndreas Gohr        return self::$instance;
558b7620a8SAndreas Gohr    }
568b7620a8SAndreas Gohr
578b7620a8SAndreas Gohr    /**
588b7620a8SAndreas Gohr     * Is the plugin ready to be used?
598b7620a8SAndreas Gohr     *
608b7620a8SAndreas Gohr     * @return bool
618b7620a8SAndreas Gohr     */
628b7620a8SAndreas Gohr    public function isReady()
638b7620a8SAndreas Gohr    {
648b7620a8SAndreas Gohr        return $this->ready;
658b7620a8SAndreas Gohr    }
668b7620a8SAndreas Gohr
678b7620a8SAndreas Gohr    /**
68b6119621SAndreas Gohr     * Is a 2fa login required?
69b6119621SAndreas Gohr     *
70b6119621SAndreas Gohr     * @return bool
71a386a536SAndreas Gohr     */
72a386a536SAndreas Gohr    public function isRequired()
73a386a536SAndreas Gohr    {
74a386a536SAndreas Gohr        $set = $this->getConf('optinout');
75a386a536SAndreas Gohr        if ($set === 'mandatory') {
76a386a536SAndreas Gohr            return true;
77a386a536SAndreas Gohr        }
78a386a536SAndreas Gohr        // FIXME handle other options:
79a386a536SAndreas Gohr        // when optout, return true unless user opted out
80a386a536SAndreas Gohr
81a386a536SAndreas Gohr        return false;
82a386a536SAndreas Gohr    }
83a386a536SAndreas Gohr
84a386a536SAndreas Gohr    /**
85a386a536SAndreas Gohr     * Convenience method to get current user
86a386a536SAndreas Gohr     *
87a386a536SAndreas Gohr     * @return string
88a386a536SAndreas Gohr     */
89a386a536SAndreas Gohr    public function getUser()
90a386a536SAndreas Gohr    {
91a386a536SAndreas Gohr        global $INPUT;
92b6119621SAndreas Gohr        $user = $INPUT->server->str('REMOTE_USER');
93b6119621SAndreas Gohr        if (!$user) {
94b6119621SAndreas Gohr            throw new \RuntimeException('2fa user specifics used before user available');
95b6119621SAndreas Gohr        }
96b6119621SAndreas Gohr        return $user;
97a386a536SAndreas Gohr    }
98a386a536SAndreas Gohr
99a386a536SAndreas Gohr    /**
1008b7620a8SAndreas Gohr     * Get all available providers
1018b7620a8SAndreas Gohr     *
1028b7620a8SAndreas Gohr     * @return Provider[]
1038b7620a8SAndreas Gohr     */
1048b7620a8SAndreas Gohr    public function getAllProviders()
1058b7620a8SAndreas Gohr    {
106a386a536SAndreas Gohr        $user = $this->getUser();
1078b7620a8SAndreas Gohr
1088b7620a8SAndreas Gohr        if ($this->providers === null) {
1098b7620a8SAndreas Gohr            $this->providers = [];
110a386a536SAndreas Gohr            foreach ($this->classes as $plugin => $class) {
111a386a536SAndreas Gohr                $this->providers[$plugin] = new $class($user);
1128b7620a8SAndreas Gohr            }
1138b7620a8SAndreas Gohr        }
1148b7620a8SAndreas Gohr
1158b7620a8SAndreas Gohr        return $this->providers;
1168b7620a8SAndreas Gohr    }
1178b7620a8SAndreas Gohr
1188b7620a8SAndreas Gohr    /**
119a386a536SAndreas Gohr     * Get all providers that have been already set up by the user
120a386a536SAndreas Gohr     *
121a386a536SAndreas Gohr     * @return Provider[]
122a386a536SAndreas Gohr     */
123a386a536SAndreas Gohr    public function getUserProviders()
124a386a536SAndreas Gohr    {
125a386a536SAndreas Gohr        $list = $this->getAllProviders();
126a386a536SAndreas Gohr        $list = array_filter($list, function ($provider) {
127a386a536SAndreas Gohr            return $provider->isConfigured();
128a386a536SAndreas Gohr        });
129a386a536SAndreas Gohr
130a386a536SAndreas Gohr        return $list;
131a386a536SAndreas Gohr    }
132a386a536SAndreas Gohr
133a386a536SAndreas Gohr    /**
134a386a536SAndreas Gohr     * Get the instance of the given provider
135a386a536SAndreas Gohr     *
136a386a536SAndreas Gohr     * @param string $providerID
137a386a536SAndreas Gohr     * @return Provider
138*6c996db8SAndreas Gohr     * @throws \Exception
139a386a536SAndreas Gohr     */
140a386a536SAndreas Gohr    public function getUserProvider($providerID)
141a386a536SAndreas Gohr    {
142a386a536SAndreas Gohr        $providers = $this->getUserProviders();
143a386a536SAndreas Gohr        if (isset($providers[$providerID])) return $providers[$providerID];
144*6c996db8SAndreas Gohr        throw new \Exception('Uncofigured provider requested');
145a386a536SAndreas Gohr    }
146a386a536SAndreas Gohr
147a386a536SAndreas Gohr    /**
148b6119621SAndreas Gohr     * Get the user's default provider if any
149b6119621SAndreas Gohr     *
150b6119621SAndreas Gohr     * Autoupdates the apropriate setting
151b6119621SAndreas Gohr     *
152b6119621SAndreas Gohr     * @return Provider|null
153b6119621SAndreas Gohr     */
154b6119621SAndreas Gohr    public function getUserDefaultProvider()
155b6119621SAndreas Gohr    {
156b6119621SAndreas Gohr        $setting = new Settings('twofactor', $this->getUser());
157b6119621SAndreas Gohr        $default = $setting->get('defaultmod');
158b6119621SAndreas Gohr        $providers = $this->getUserProviders();
159b6119621SAndreas Gohr
160b6119621SAndreas Gohr        if (isset($providers[$default])) return $providers[$default];
161b6119621SAndreas Gohr        // still here? no valid setting. Use first available one
162b6119621SAndreas Gohr        $first = array_shift($providers);
163b6119621SAndreas Gohr        if ($first !== null) {
164b6119621SAndreas Gohr            $this->setUserDefaultProvider($first);
165b6119621SAndreas Gohr        }
166b6119621SAndreas Gohr        return $first;
167b6119621SAndreas Gohr    }
168b6119621SAndreas Gohr
169b6119621SAndreas Gohr    /**
170b6119621SAndreas Gohr     * Set the default provider for the user
171b6119621SAndreas Gohr     *
172b6119621SAndreas Gohr     * @param Provider $provider
173b6119621SAndreas Gohr     * @return void
174b6119621SAndreas Gohr     */
175b6119621SAndreas Gohr    public function setUserDefaultProvider($provider)
176b6119621SAndreas Gohr    {
177b6119621SAndreas Gohr        $setting = new Settings('twofactor', $this->getUser());
178b6119621SAndreas Gohr        $setting->set('defaultmod', $provider->getProviderID());
179b6119621SAndreas Gohr    }
180b6119621SAndreas Gohr
181b6119621SAndreas Gohr    /**
1828b7620a8SAndreas Gohr     * Find all available provider classes
1838b7620a8SAndreas Gohr     *
1848b7620a8SAndreas Gohr     * @return string[];
1858b7620a8SAndreas Gohr     */
1868b7620a8SAndreas Gohr    protected function getProviderClasses()
1878b7620a8SAndreas Gohr    {
1888b7620a8SAndreas Gohr        // FIXME this relies on naming alone, we might want to use an action for registering
1898b7620a8SAndreas Gohr        $plugins = plugin_list('helper');
1908b7620a8SAndreas Gohr        $plugins = array_filter($plugins, function ($plugin) {
1918b7620a8SAndreas Gohr            return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor';
1928b7620a8SAndreas Gohr        });
1938b7620a8SAndreas Gohr
1948b7620a8SAndreas Gohr        $classes = [];
1958b7620a8SAndreas Gohr        foreach ($plugins as $plugin) {
1968b7620a8SAndreas Gohr            $class = 'helper_plugin_' . $plugin;
1978b7620a8SAndreas Gohr            if (is_a($class, Provider::class, true)) {
198a386a536SAndreas Gohr                $classes[$plugin] = $class;
1998b7620a8SAndreas Gohr            }
2008b7620a8SAndreas Gohr        }
2018b7620a8SAndreas Gohr
2028b7620a8SAndreas Gohr        return $classes;
203fca58076SAndreas Gohr    }
204fca58076SAndreas Gohr
205fca58076SAndreas Gohr}
206