xref: /plugin/twofactor/Provider.php (revision 8b7620a8e0c445f8fa437fe73abebe02c1bc0940)
1<?php
2
3namespace dokuwiki\plugin\twofactor;
4
5use dokuwiki\Extension\Plugin;
6use dokuwiki\Form\Form;
7use dokuwiki\Utf8\PhpString;
8
9/**
10 * Baseclass for all second factor providers
11 */
12abstract class Provider extends Plugin
13{
14    /** @var Settings */
15    protected $settings;
16
17    /** @var string */
18    protected $providerID;
19
20    /**
21     * Initializes the provider for the given user
22     * @param string $user Current user
23     * @throws \Exception
24     */
25    public function __construct($user)
26    {
27        $this->providerID = substr(get_called_class(), strlen('helper_plugin_'));
28        $this->settings = new Settings($this->providerID, $user);
29    }
30
31    /**
32     * The ID of this provider
33     *
34     * @return string
35     */
36    public function getProviderID()
37    {
38        return $this->providerID;
39    }
40
41    /**
42     * Pretty Label for this provider
43     *
44     * @return string
45     */
46    public function getLabel()
47    {
48        return PhpString::ucfirst($this->providerID);
49    }
50
51    /**
52     * Clear all settings
53     */
54    public function reset()
55    {
56        $this->settings->purge();
57    }
58
59    /**
60     * Has this provider been fully configured by the user and thus can be used
61     * for authentication?
62     *
63     * @return bool
64     */
65    abstract public function isConfigured();
66
67    /**
68     * Render the configuration form
69     *
70     * This method should add the needed form elements to (re)configure the provider.
71     * The contents of the form may change depending on the current settings.
72     *
73     * No submit button should be added - this is handled by the main plugin.
74     *
75     * @param Form $form The initial form to add elements to
76     * @return Form
77     */
78    abstract public function renderProfileForm(Form $form);
79
80    /**
81     * Handle any input data
82     *
83     * @return void
84     */
85    abstract public function handleProfileForm();
86
87    /**
88     * Transmits the code to the user
89     *
90     * @param string $code The code to transmit
91     * @return string Informational message for the user
92     * @throw \Exception when the message can't be sent
93     */
94    abstract public function transmitMessage($code);
95
96    // region OTP methods
97
98    /**
99     * Create and store a new secret for this provider
100     *
101     * @return string the new secret
102     * @throws \Exception when no suitable random source is available
103     */
104    public function initSecret()
105    {
106        $ga = new GoogleAuthenticator();
107        $secret = $ga->createSecret();
108
109        $this->settings->set('secret', $secret);
110        return $secret;
111    }
112
113    /**
114     * Generate an auth code
115     *
116     * @return string
117     * @throws \Exception when no code can be created
118     */
119    public function generateCode()
120    {
121        $secret = $this->settings->get('secret');
122        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
123
124        $ga = new GoogleAuthenticator();
125        return $ga->getCode($secret);
126    }
127
128    /**
129     * Check the given code
130     *
131     * @param string $code
132     * @param int $tolerance
133     * @return string
134     * @throws \Exception when no code can be created
135     */
136    public function checkCode($code, $tolerance = 2)
137    {
138        $secret = $this->settings->get('secret');
139        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
140
141        $ga = new GoogleAuthenticator();
142        return $ga->verifyCode($secret, $code, $tolerance);
143    }
144
145    // endregion
146
147    // region old shit
148
149    /**
150     * This is called to see if the user can use it to login.
151     * @return bool - True if this module has access to all needed information
152     * to perform a login.
153     */
154    abstract public function canUse($user = null);
155
156    /**
157     * This is called to see if the module provides login functionality on the
158     * main login page.
159     * @return bool - True if this module provides main login functionality.
160     */
161    abstract public function canAuthLogin();
162
163    /**
164     * This is called to process the user configurable portion of the module
165     * inside the user's profile.
166     * @return mixed - True if the user's settings were changed, false if
167     *     settings could not be changed, null if no settings were changed,
168     *     the string 'verified' if the module was successfully verified,
169     *     the string 'failed' if the module failed verification,
170     *       the string 'otp' if the module is requesting a one-time password
171     *     for verification,
172     *     the string 'deleted' if the module was unenrolled.
173     */
174    public function processProfileForm()
175    {
176        return null;
177    }
178
179    /**
180     * This is called to see if the module can send a message to the user.
181     * @return bool - True if a message can be sent to the user.
182     */
183    abstract public function canTransmitMessage();
184
185    /**
186     * This is called to validate the code provided.  The default is to see if
187     * the code matches the one-time password.
188     * @return bool - True if the user has successfully authenticated using
189     * this mechanism.
190     */
191    public function processLogin($code, $user = null)
192    {
193        $twofactor = plugin_load('action', 'twofactor');
194        $otpQuery = $twofactor->get_otp_code();
195        if (!$otpQuery) {
196            return false;
197        }
198        list($otp, $modname) = $otpQuery;
199        return ($code == $otp && $code != '' && (count($modname) == 0 || in_array(get_called_class(), $modname)));
200    }
201
202    /**
203     * This is a helper function to get text strings from the twofactor class
204     * calling this module.
205     * @return string - Language string from the calling class.
206     */
207    protected function _getSharedLang($key)
208    {
209        $twofactor = plugin_load('action', 'twofactor');
210        return $twofactor->getLang($key);
211    }
212
213    /**
214     * This is a helper function to get shared configuration options from the
215     * twofactor class.
216     * @return string - Language string from the calling class.
217     */
218    protected function _getSharedConfig($key)
219    {
220        $twofactor = plugin_load('action', 'twofactor');
221        return $twofactor->getConf($key);
222    }
223
224    /**
225     * This is a helper function to check for the existence of shared
226     * twofactor settings.
227     * @return string - Language string from the calling class.
228     */
229    protected function _sharedSettingExists($key)
230    {
231        return $this->attribute->exists("twofactor", $key);
232    }
233
234    /**
235     * This is a helper function to get shared twofactor settings.
236     * @return string - Language string from the calling class.
237     */
238    protected function _sharedSettingGet($key, $default = null, $user = null)
239    {
240        return $this->_sharedSettingExists($key) ? $this->attribute->get("twofactor", $key, $success, $user) : $default;
241    }
242
243    /**
244     * This is a helper function to set shared twofactor settings.
245     * @return string - Language string from the calling class.
246     */
247    protected function _sharedSettingSet($key, $value)
248    {
249        return $this->attribute->set("twofactor", $key, $value);
250    }
251
252
253
254    /**
255     * This is a helper function that attempts to load the named modules.
256     * @return array - An array of instanced objects from the loaded modules.
257     */
258    static public function _loadModules($mods)
259    {
260        $objects = array();
261        foreach ($mods as $mod) {
262            $obj = plugin_load('helper', $mod);
263            if ($obj && is_a($obj, 'Twofactor_Auth_Module')) {
264                $objects[$mod] = $obj;
265            }
266        }
267        return $objects;
268    }
269
270    // endregion
271}
272