xref: /plugin/twofactor/Manager.php (revision 6c996db8dff158c7317a04e1185060c59b19ad7d)
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     * @throws \Exception
139     */
140    public function getUserProvider($providerID)
141    {
142        $providers = $this->getUserProviders();
143        if (isset($providers[$providerID])) return $providers[$providerID];
144        throw new \Exception('Uncofigured provider requested');
145    }
146
147    /**
148     * Get the user's default provider if any
149     *
150     * Autoupdates the apropriate setting
151     *
152     * @return Provider|null
153     */
154    public function getUserDefaultProvider()
155    {
156        $setting = new Settings('twofactor', $this->getUser());
157        $default = $setting->get('defaultmod');
158        $providers = $this->getUserProviders();
159
160        if (isset($providers[$default])) return $providers[$default];
161        // still here? no valid setting. Use first available one
162        $first = array_shift($providers);
163        if ($first !== null) {
164            $this->setUserDefaultProvider($first);
165        }
166        return $first;
167    }
168
169    /**
170     * Set the default provider for the user
171     *
172     * @param Provider $provider
173     * @return void
174     */
175    public function setUserDefaultProvider($provider)
176    {
177        $setting = new Settings('twofactor', $this->getUser());
178        $setting->set('defaultmod', $provider->getProviderID());
179    }
180
181    /**
182     * Find all available provider classes
183     *
184     * @return string[];
185     */
186    protected function getProviderClasses()
187    {
188        // FIXME this relies on naming alone, we might want to use an action for registering
189        $plugins = plugin_list('helper');
190        $plugins = array_filter($plugins, function ($plugin) {
191            return $plugin !== 'twofactor' && substr($plugin, 0, 9) === 'twofactor';
192        });
193
194        $classes = [];
195        foreach ($plugins as $plugin) {
196            $class = 'helper_plugin_' . $plugin;
197            if (is_a($class, Provider::class, true)) {
198                $classes[$plugin] = $class;
199            }
200        }
201
202        return $classes;
203    }
204
205}
206