xref: /plugin/twofactor/Manager.php (revision b6119621988e00fd0cbd3059406f4ed18460274b)
1<?php
2
3namespace dokuwiki\plugin\twofactor;
4
5use dokuwiki\Extension\Plugin;
6
7/**
8 * Manages the child plugins etc.
9 */
10class Manager extends Plugin
11{
12    /** @var Manager */
13    protected static $instance;
14
15    /** @var bool */
16    protected $ready = false;
17
18    /** @var string[] */
19    protected $classes = [];
20
21    /** @var Provider[] */
22    protected $providers;
23
24    /**
25     * Constructor
26     */
27    protected function __construct()
28    {
29        $this->classes = $this->getProviderClasses();
30
31        $attribute = plugin_load('helper', 'attribute');
32        if ($attribute === null) {
33            msg('The attribute plugin is not available, 2fa disabled', -1);
34        }
35
36        if (!count($this->classes)) {
37            msg('No suitable 2fa providers found, 2fa disabled', -1);
38            return;
39        }
40
41        $this->ready = true;
42    }
43
44    /**
45     * Get the instance of this singleton
46     *
47     * @return Manager
48     */
49    public static function getInstance()
50    {
51        if (self::$instance === null) {
52            self::$instance = new Manager();
53        }
54        return self::$instance;
55    }
56
57    /**
58     * Is the plugin ready to be used?
59     *
60     * @return bool
61     */
62    public function isReady()
63    {
64        return $this->ready;
65    }
66
67    /**
68     * Is a 2fa login required?
69     *
70     * @return bool
71     */
72    public function isRequired()
73    {
74        $set = $this->getConf('optinout');
75        if ($set === 'mandatory') {
76            return true;
77        }
78        // FIXME handle other options:
79        // when optout, return true unless user opted out
80
81        return false;
82    }
83
84    /**
85     * Convenience method to get current user
86     *
87     * @return string
88     */
89    public function getUser()
90    {
91        global $INPUT;
92        $user = $INPUT->server->str('REMOTE_USER');
93        if (!$user) {
94            throw new \RuntimeException('2fa user specifics used before user available');
95        }
96        return $user;
97    }
98
99    /**
100     * Get all available providers
101     *
102     * @return Provider[]
103     */
104    public function getAllProviders()
105    {
106        $user = $this->getUser();
107
108        if ($this->providers === null) {
109            $this->providers = [];
110            foreach ($this->classes as $plugin => $class) {
111                $this->providers[$plugin] = new $class($user);
112            }
113        }
114
115        return $this->providers;
116    }
117
118    /**
119     * Get all providers that have been already set up by the user
120     *
121     * @return Provider[]
122     */
123    public function getUserProviders()
124    {
125        $list = $this->getAllProviders();
126        $list = array_filter($list, function ($provider) {
127            return $provider->isConfigured();
128        });
129
130        return $list;
131    }
132
133    /**
134     * Get the instance of the given provider
135     *
136     * @param string $providerID
137     * @return Provider
138     */
139    public function getUserProvider($providerID)
140    {
141        $providers = $this->getUserProviders();
142        if (isset($providers[$providerID])) return $providers[$providerID];
143        throw new \RuntimeException('Uncofigured provider requested');
144    }
145
146    /**
147     * Get the user's default provider if any
148     *
149     * Autoupdates the apropriate setting
150     *
151     * @return Provider|null
152     */
153    public function getUserDefaultProvider()
154    {
155        $setting = new Settings('twofactor', $this->getUser());
156        $default = $setting->get('defaultmod');
157        $providers = $this->getUserProviders();
158
159        if (isset($providers[$default])) return $providers[$default];
160        // still here? no valid setting. Use first available one
161        $first = array_shift($providers);
162        if ($first !== null) {
163            $this->setUserDefaultProvider($first);
164        }
165        return $first;
166    }
167
168    /**
169     * Set the default provider for the user
170     *
171     * @param Provider $provider
172     * @return void
173     */
174    public function setUserDefaultProvider($provider)
175    {
176        $setting = new Settings('twofactor', $this->getUser());
177        $setting->set('defaultmod', $provider->getProviderID());
178    }
179
180    /**
181     * Find all available provider classes
182     *
183     * @return string[];
184     */
185    protected function getProviderClasses()
186    {
187        // FIXME this relies on naming alone, we might want to use an action for registering
188        $plugins = plugin_list('helper');
189        $plugins = array_filter($plugins, function ($plugin) {
190            return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor';
191        });
192
193        $classes = [];
194        foreach ($plugins as $plugin) {
195            $class = 'helper_plugin_' . $plugin;
196            if (is_a($class, Provider::class, true)) {
197                $classes[$plugin] = $class;
198            }
199        }
200
201        return $classes;
202    }
203
204}
205