xref: /plugin/twofactor/Manager.php (revision b6119621988e00fd0cbd3059406f4ed18460274b)
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    /**
68*b6119621SAndreas Gohr     * Is a 2fa login required?
69*b6119621SAndreas Gohr     *
70*b6119621SAndreas 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;
92*b6119621SAndreas Gohr        $user = $INPUT->server->str('REMOTE_USER');
93*b6119621SAndreas Gohr        if (!$user) {
94*b6119621SAndreas Gohr            throw new \RuntimeException('2fa user specifics used before user available');
95*b6119621SAndreas Gohr        }
96*b6119621SAndreas 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
138a386a536SAndreas Gohr     */
139a386a536SAndreas Gohr    public function getUserProvider($providerID)
140a386a536SAndreas Gohr    {
141a386a536SAndreas Gohr        $providers = $this->getUserProviders();
142a386a536SAndreas Gohr        if (isset($providers[$providerID])) return $providers[$providerID];
143a386a536SAndreas Gohr        throw new \RuntimeException('Uncofigured provider requested');
144a386a536SAndreas Gohr    }
145a386a536SAndreas Gohr
146a386a536SAndreas Gohr    /**
147*b6119621SAndreas Gohr     * Get the user's default provider if any
148*b6119621SAndreas Gohr     *
149*b6119621SAndreas Gohr     * Autoupdates the apropriate setting
150*b6119621SAndreas Gohr     *
151*b6119621SAndreas Gohr     * @return Provider|null
152*b6119621SAndreas Gohr     */
153*b6119621SAndreas Gohr    public function getUserDefaultProvider()
154*b6119621SAndreas Gohr    {
155*b6119621SAndreas Gohr        $setting = new Settings('twofactor', $this->getUser());
156*b6119621SAndreas Gohr        $default = $setting->get('defaultmod');
157*b6119621SAndreas Gohr        $providers = $this->getUserProviders();
158*b6119621SAndreas Gohr
159*b6119621SAndreas Gohr        if (isset($providers[$default])) return $providers[$default];
160*b6119621SAndreas Gohr        // still here? no valid setting. Use first available one
161*b6119621SAndreas Gohr        $first = array_shift($providers);
162*b6119621SAndreas Gohr        if ($first !== null) {
163*b6119621SAndreas Gohr            $this->setUserDefaultProvider($first);
164*b6119621SAndreas Gohr        }
165*b6119621SAndreas Gohr        return $first;
166*b6119621SAndreas Gohr    }
167*b6119621SAndreas Gohr
168*b6119621SAndreas Gohr    /**
169*b6119621SAndreas Gohr     * Set the default provider for the user
170*b6119621SAndreas Gohr     *
171*b6119621SAndreas Gohr     * @param Provider $provider
172*b6119621SAndreas Gohr     * @return void
173*b6119621SAndreas Gohr     */
174*b6119621SAndreas Gohr    public function setUserDefaultProvider($provider)
175*b6119621SAndreas Gohr    {
176*b6119621SAndreas Gohr        $setting = new Settings('twofactor', $this->getUser());
177*b6119621SAndreas Gohr        $setting->set('defaultmod', $provider->getProviderID());
178*b6119621SAndreas Gohr    }
179*b6119621SAndreas Gohr
180*b6119621SAndreas Gohr    /**
1818b7620a8SAndreas Gohr     * Find all available provider classes
1828b7620a8SAndreas Gohr     *
1838b7620a8SAndreas Gohr     * @return string[];
1848b7620a8SAndreas Gohr     */
1858b7620a8SAndreas Gohr    protected function getProviderClasses()
1868b7620a8SAndreas Gohr    {
1878b7620a8SAndreas Gohr        // FIXME this relies on naming alone, we might want to use an action for registering
1888b7620a8SAndreas Gohr        $plugins = plugin_list('helper');
1898b7620a8SAndreas Gohr        $plugins = array_filter($plugins, function ($plugin) {
1908b7620a8SAndreas Gohr            return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor';
1918b7620a8SAndreas Gohr        });
1928b7620a8SAndreas Gohr
1938b7620a8SAndreas Gohr        $classes = [];
1948b7620a8SAndreas Gohr        foreach ($plugins as $plugin) {
1958b7620a8SAndreas Gohr            $class = 'helper_plugin_' . $plugin;
1968b7620a8SAndreas Gohr            if (is_a($class, Provider::class, true)) {
197a386a536SAndreas Gohr                $classes[$plugin] = $class;
1988b7620a8SAndreas Gohr            }
1998b7620a8SAndreas Gohr        }
2008b7620a8SAndreas Gohr
2018b7620a8SAndreas Gohr        return $classes;
202fca58076SAndreas Gohr    }
203fca58076SAndreas Gohr
204fca58076SAndreas Gohr}
205