xref: /plugin/twofactor/Provider.php (revision 30625b49d43f35ae8dc732acccd71d5a391980ea)
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    // region Introspection methods
32
33    /**
34     * The ID of this provider
35     *
36     * @return string
37     */
38    public function getProviderID()
39    {
40        return $this->providerID;
41    }
42
43    /**
44     * Pretty Label for this provider
45     *
46     * @return string
47     */
48    public function getLabel()
49    {
50        return PhpString::ucfirst($this->providerID);
51    }
52
53    // endregion
54    // region Configuration methods
55
56    /**
57     * Clear all settings
58     */
59    public function reset()
60    {
61        $this->settings->purge();
62    }
63
64    /**
65     * Has this provider been fully configured and verified by the user and thus can be used
66     * for authentication?
67     *
68     * @return bool
69     */
70    abstract public function isConfigured();
71
72    /**
73     * Render the configuration form
74     *
75     * This method should add the needed form elements to (re)configure the provider.
76     * The contents of the form may change depending on the current settings.
77     *
78     * No submit button should be added - this is handled by the main plugin.
79     *
80     * @param Form $form The initial form to add elements to
81     * @return Form
82     */
83    abstract public function renderProfileForm(Form $form);
84
85    /**
86     * Handle any input data
87     *
88     * @return void
89     */
90    abstract public function handleProfileForm();
91
92    // endregion
93    // region OTP methods
94
95    /**
96     * Create and store a new secret for this provider
97     *
98     * @return string the new secret
99     * @throws \Exception when no suitable random source is available
100     */
101    public function initSecret()
102    {
103        $ga = new GoogleAuthenticator();
104        $secret = $ga->createSecret();
105
106        $this->settings->set('secret', $secret);
107        return $secret;
108    }
109
110    /**
111     * Generate an auth code
112     *
113     * @return string
114     * @throws \Exception when no code can be created
115     */
116    public function generateCode()
117    {
118        $secret = $this->settings->get('secret');
119        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
120
121        $ga = new GoogleAuthenticator();
122        return $ga->getCode($secret);
123    }
124
125    /**
126     * Check the given code
127     *
128     * @param string $code
129     * @param int $tolerance
130     * @return bool
131     * @throws \Exception when no code can be created
132     */
133    public function checkCode($code, $tolerance = 2)
134    {
135        $secret = $this->settings->get('secret');
136        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
137
138        $ga = new GoogleAuthenticator();
139        return $ga->verifyCode($secret, $code, $tolerance);
140    }
141
142    /**
143     * Transmits the code to the user
144     *
145     * If a provider does not transmit anything (eg. TOTP) simply
146     * return the message.
147     *
148     * @param string $code The code to transmit
149     * @return string Informational message for the user
150     * @throw \Exception when the message can't be sent
151     */
152    abstract public function transmitMessage($code);
153
154    // endregion
155}
156