xref: /plugin/twofactor/Provider.php (revision 6c996db8dff158c7317a04e1185060c59b19ad7d)
1fca58076SAndreas Gohr<?php
2fca58076SAndreas Gohr
3fca58076SAndreas Gohrnamespace dokuwiki\plugin\twofactor;
4fca58076SAndreas Gohr
5fca58076SAndreas Gohruse dokuwiki\Extension\Plugin;
6a635cb20SAndreas Gohruse dokuwiki\Form\Form;
7a635cb20SAndreas Gohruse dokuwiki\Utf8\PhpString;
8fca58076SAndreas Gohr
9fca58076SAndreas Gohr/**
10fca58076SAndreas Gohr * Baseclass for all second factor providers
11fca58076SAndreas Gohr */
12fca58076SAndreas Gohrabstract class Provider extends Plugin
13fca58076SAndreas Gohr{
14a635cb20SAndreas Gohr    /** @var Settings */
15a635cb20SAndreas Gohr    protected $settings;
16fca58076SAndreas Gohr
17a635cb20SAndreas Gohr    /** @var string */
18a635cb20SAndreas Gohr    protected $providerID;
19a635cb20SAndreas Gohr
20a635cb20SAndreas Gohr    /**
21a635cb20SAndreas Gohr     * Initializes the provider for the given user
22a635cb20SAndreas Gohr     * @param string $user Current user
23a635cb20SAndreas Gohr     * @throws \Exception
24a635cb20SAndreas Gohr     */
25a635cb20SAndreas Gohr    public function __construct($user)
26a635cb20SAndreas Gohr    {
27a635cb20SAndreas Gohr        $this->providerID = substr(get_called_class(), strlen('helper_plugin_'));
28a635cb20SAndreas Gohr        $this->settings = new Settings($this->providerID, $user);
29a635cb20SAndreas Gohr    }
30a635cb20SAndreas Gohr
3130625b49SAndreas Gohr    // region Introspection methods
3230625b49SAndreas Gohr
33a635cb20SAndreas Gohr    /**
34a635cb20SAndreas Gohr     * The ID of this provider
35a635cb20SAndreas Gohr     *
36a635cb20SAndreas Gohr     * @return string
37a635cb20SAndreas Gohr     */
38a635cb20SAndreas Gohr    public function getProviderID()
39a635cb20SAndreas Gohr    {
40a635cb20SAndreas Gohr        return $this->providerID;
41a635cb20SAndreas Gohr    }
42a635cb20SAndreas Gohr
43a635cb20SAndreas Gohr    /**
44a635cb20SAndreas Gohr     * Pretty Label for this provider
45a635cb20SAndreas Gohr     *
46a635cb20SAndreas Gohr     * @return string
47a635cb20SAndreas Gohr     */
48a635cb20SAndreas Gohr    public function getLabel()
49a635cb20SAndreas Gohr    {
50a635cb20SAndreas Gohr        return PhpString::ucfirst($this->providerID);
51a635cb20SAndreas Gohr    }
52a635cb20SAndreas Gohr
5330625b49SAndreas Gohr    // endregion
5430625b49SAndreas Gohr    // region Configuration methods
5530625b49SAndreas Gohr
56a635cb20SAndreas Gohr    /**
57a635cb20SAndreas Gohr     * Clear all settings
58a635cb20SAndreas Gohr     */
59a635cb20SAndreas Gohr    public function reset()
60a635cb20SAndreas Gohr    {
61a635cb20SAndreas Gohr        $this->settings->purge();
62a635cb20SAndreas Gohr    }
63a635cb20SAndreas Gohr
64a635cb20SAndreas Gohr    /**
65a386a536SAndreas Gohr     * Has this provider been fully configured and verified by the user and thus can be used
66a635cb20SAndreas Gohr     * for authentication?
67a635cb20SAndreas Gohr     *
68a635cb20SAndreas Gohr     * @return bool
69a635cb20SAndreas Gohr     */
70a635cb20SAndreas Gohr    abstract public function isConfigured();
71a635cb20SAndreas Gohr
72a635cb20SAndreas Gohr    /**
73a635cb20SAndreas Gohr     * Render the configuration form
74a635cb20SAndreas Gohr     *
75a635cb20SAndreas Gohr     * This method should add the needed form elements to (re)configure the provider.
76a635cb20SAndreas Gohr     * The contents of the form may change depending on the current settings.
77a635cb20SAndreas Gohr     *
78a635cb20SAndreas Gohr     * No submit button should be added - this is handled by the main plugin.
79a635cb20SAndreas Gohr     *
80a635cb20SAndreas Gohr     * @param Form $form The initial form to add elements to
81a635cb20SAndreas Gohr     * @return Form
82a635cb20SAndreas Gohr     */
83a635cb20SAndreas Gohr    abstract public function renderProfileForm(Form $form);
84a635cb20SAndreas Gohr
85a635cb20SAndreas Gohr    /**
86a635cb20SAndreas Gohr     * Handle any input data
87a635cb20SAndreas Gohr     *
88a635cb20SAndreas Gohr     * @return void
89a635cb20SAndreas Gohr     */
90a635cb20SAndreas Gohr    abstract public function handleProfileForm();
91a635cb20SAndreas Gohr
9230625b49SAndreas Gohr    // endregion
93a635cb20SAndreas Gohr    // region OTP methods
94a635cb20SAndreas Gohr
95a635cb20SAndreas Gohr    /**
96a635cb20SAndreas Gohr     * Create and store a new secret for this provider
97a635cb20SAndreas Gohr     *
98a635cb20SAndreas Gohr     * @return string the new secret
99a635cb20SAndreas Gohr     * @throws \Exception when no suitable random source is available
100a635cb20SAndreas Gohr     */
101a635cb20SAndreas Gohr    public function initSecret()
102a635cb20SAndreas Gohr    {
103a635cb20SAndreas Gohr        $ga = new GoogleAuthenticator();
104a635cb20SAndreas Gohr        $secret = $ga->createSecret();
105a635cb20SAndreas Gohr
106a635cb20SAndreas Gohr        $this->settings->set('secret', $secret);
107a635cb20SAndreas Gohr        return $secret;
108a635cb20SAndreas Gohr    }
109a635cb20SAndreas Gohr
110a635cb20SAndreas Gohr    /**
111*6c996db8SAndreas Gohr     * Get the secret for this provider
112*6c996db8SAndreas Gohr     *
113*6c996db8SAndreas Gohr     * @return string
114*6c996db8SAndreas Gohr     */
115*6c996db8SAndreas Gohr    public function getSecret()
116*6c996db8SAndreas Gohr    {
117*6c996db8SAndreas Gohr        return $this->settings->get('secret');
118*6c996db8SAndreas Gohr    }
119*6c996db8SAndreas Gohr
120*6c996db8SAndreas Gohr    /**
121a635cb20SAndreas Gohr     * Generate an auth code
122a635cb20SAndreas Gohr     *
123a635cb20SAndreas Gohr     * @return string
124a635cb20SAndreas Gohr     * @throws \Exception when no code can be created
125a635cb20SAndreas Gohr     */
126a635cb20SAndreas Gohr    public function generateCode()
127a635cb20SAndreas Gohr    {
128a635cb20SAndreas Gohr        $secret = $this->settings->get('secret');
129a635cb20SAndreas Gohr        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
130a635cb20SAndreas Gohr
131a635cb20SAndreas Gohr        $ga = new GoogleAuthenticator();
132a635cb20SAndreas Gohr        return $ga->getCode($secret);
133a635cb20SAndreas Gohr    }
134a635cb20SAndreas Gohr
135a635cb20SAndreas Gohr    /**
136a635cb20SAndreas Gohr     * Check the given code
137a635cb20SAndreas Gohr     *
138a635cb20SAndreas Gohr     * @param string $code
139a635cb20SAndreas Gohr     * @param int $tolerance
140a386a536SAndreas Gohr     * @return bool
141a635cb20SAndreas Gohr     * @throws \Exception when no code can be created
142a635cb20SAndreas Gohr     */
143a635cb20SAndreas Gohr    public function checkCode($code, $tolerance = 2)
144a635cb20SAndreas Gohr    {
145a635cb20SAndreas Gohr        $secret = $this->settings->get('secret');
146a635cb20SAndreas Gohr        if (!$secret) throw new \Exception('No secret for provider ' . $this->getProviderID());
147a635cb20SAndreas Gohr
148a635cb20SAndreas Gohr        $ga = new GoogleAuthenticator();
149a635cb20SAndreas Gohr        return $ga->verifyCode($secret, $code, $tolerance);
150a635cb20SAndreas Gohr    }
151a635cb20SAndreas Gohr
152a635cb20SAndreas Gohr    /**
15330625b49SAndreas Gohr     * Transmits the code to the user
15430625b49SAndreas Gohr     *
15530625b49SAndreas Gohr     * If a provider does not transmit anything (eg. TOTP) simply
15630625b49SAndreas Gohr     * return the message.
15730625b49SAndreas Gohr     *
15830625b49SAndreas Gohr     * @param string $code The code to transmit
15930625b49SAndreas Gohr     * @return string Informational message for the user
16030625b49SAndreas Gohr     * @throw \Exception when the message can't be sent
161a635cb20SAndreas Gohr     */
16230625b49SAndreas Gohr    abstract public function transmitMessage($code);
163a635cb20SAndreas Gohr
164a635cb20SAndreas Gohr    // endregion
165fca58076SAndreas Gohr}
166