xref: /plugin/twofactor/Manager.php (revision 5f8f561a23f3113afc6c34a012fc6a75c51435c8)
1<?php
2
3namespace dokuwiki\plugin\twofactor;
4
5use dokuwiki\Extension\Event;
6use dokuwiki\Extension\Plugin;
7
8/**
9 * Manages the child plugins etc.
10 */
11class Manager extends Plugin
12{
13    /**
14     * Generally all our actions should run before all other plugins
15     */
16    const EVENT_PRIORITY = -5000;
17
18    /** @var Manager */
19    protected static $instance;
20
21    /** @var bool */
22    protected $ready = false;
23
24    /** @var Provider[] */
25    protected $providers;
26
27    /** @var bool */
28    protected $providersInitialized;
29
30    /**
31     * Constructor
32     */
33    protected function __construct()
34    {
35        $attribute = plugin_load('helper', 'attribute');
36        if ($attribute === null) {
37            msg('The attribute plugin is not available, 2fa disabled', -1);
38            return;
39        }
40
41        $this->loadProviders();
42        if (!count($this->providers)) {
43            msg('No suitable 2fa providers found, 2fa disabled', -1);
44            return;
45        }
46
47        $this->ready = true;
48    }
49
50    /**
51     * This is not a conventional class, plugin name can't be determined automatically
52     * @inheritdoc
53     */
54    public function getPluginName()
55    {
56        return 'twofactor';
57    }
58
59    /**
60     * Get the instance of this singleton
61     *
62     * @return Manager
63     */
64    public static function getInstance()
65    {
66        if (self::$instance === null) {
67            self::$instance = new Manager();
68        }
69        return self::$instance;
70    }
71
72    /**
73     * Is the plugin ready to be used?
74     *
75     * @return bool
76     */
77    public function isReady()
78    {
79        if (!$this->ready) return false;
80        try {
81            $this->getUser();
82        } catch (\Exception $ignored) {
83            return false;
84        }
85
86        return true;
87    }
88
89    /**
90     * Is a 2fa login required?
91     *
92     * @return bool
93     */
94    public function isRequired()
95    {
96        $set = $this->getConf('optinout');
97        if ($set === 'mandatory') {
98            return true;
99        }
100        if ($set === 'optout') {
101            $setting = new Settings('twofactor', $this->getUser());
102            if ($setting->get('state') !== 'optout') {
103                return true;
104            }
105        }
106
107        return false;
108    }
109
110    /**
111     * Convenience method to get current user
112     *
113     * @return string
114     */
115    public function getUser()
116    {
117        global $INPUT;
118        $user = $INPUT->server->str('REMOTE_USER');
119        if (!$user) {
120            throw new \RuntimeException('2fa user specifics used before user available');
121        }
122        return $user;
123    }
124
125    /**
126     * Get or set the user opt-out state
127     *
128     * true: user opted out
129     * false: user did not opt out
130     *
131     * @param bool|null $set
132     * @return bool
133     */
134    public function userOptOutState($set = null)
135    {
136        // is optout allowed?
137        if ($this->getConf('optinout') !== 'optout') return false;
138
139        $settings = new Settings('twofactor', $this->getUser());
140
141        if ($set === null) {
142            $current = $settings->get('state');
143            return $current === 'optout';
144        }
145
146        if ($set) {
147            $settings->set('state', 'optout');
148        } else {
149            $settings->delete('state');
150        }
151        return $set;
152    }
153
154    /**
155     * Get all available providers
156     *
157     * @return Provider[]
158     */
159    public function getAllProviders()
160    {
161        $user = $this->getUser();
162
163        if (!$this->providersInitialized) {
164            // initialize providers with user and ensure the ID is correct
165            foreach ($this->providers as $providerID => $provider) {
166                if ($providerID !== $provider->getProviderID()) {
167                    $this->providers[$provider->getProviderID()] = $provider;
168                    unset($this->providers[$providerID]);
169                }
170                $provider->init($user);
171            }
172            $this->providersInitialized = true;
173        }
174
175        return $this->providers;
176    }
177
178    /**
179     * Get all providers that have been already set up by the user
180     *
181     * @return Provider[]
182     */
183    public function getUserProviders()
184    {
185        $list = $this->getAllProviders();
186        $list = array_filter($list, function ($provider) {
187            return $provider->isConfigured();
188        });
189
190        return $list;
191    }
192
193    /**
194     * Get the instance of the given provider
195     *
196     * @param string $providerID
197     * @return Provider
198     * @throws \Exception
199     */
200    public function getUserProvider($providerID)
201    {
202        $providers = $this->getUserProviders();
203        if (isset($providers[$providerID])) return $providers[$providerID];
204        throw new \Exception('Uncofigured provider requested');
205    }
206
207    /**
208     * Get the user's default provider if any
209     *
210     * Autoupdates the apropriate setting
211     *
212     * @return Provider|null
213     */
214    public function getUserDefaultProvider()
215    {
216        $setting = new Settings('twofactor', $this->getUser());
217        $default = $setting->get('defaultmod');
218        $providers = $this->getUserProviders();
219
220        if (isset($providers[$default])) return $providers[$default];
221        // still here? no valid setting. Use first available one
222        $first = array_shift($providers);
223        if ($first !== null) {
224            $this->setUserDefaultProvider($first);
225        }
226        return $first;
227    }
228
229    /**
230     * Set the default provider for the user
231     *
232     * @param Provider $provider
233     * @return void
234     */
235    public function setUserDefaultProvider($provider)
236    {
237        $setting = new Settings('twofactor', $this->getUser());
238        $setting->set('defaultmod', $provider->getProviderID());
239    }
240
241    /**
242     * Load all available provider classes
243     *
244     * @return Provider[];
245     */
246    protected function loadProviders()
247    {
248        /** @var Provider[] providers */
249        $this->providers = [];
250        $event = new Event('PLUGIN_TWOFACTOR_PROVIDER_REGISTER', $this->providers);
251        $event->advise_before(false);
252        $event->advise_after();
253        return $this->providers;
254    }
255
256}
257