1<?php
2abstract class Twofactor_Auth_Module extends DokuWiki_Plugin {
3	protected $twofactor = null;
4
5	/**
6	 * As a requirement, this class and its subclasses require the attribute
7	 * plugin for access to user data. An array will be passed in that the
8	 * calling class will handle saving data changes.  As such, the calling
9	 * class will ensure that the correct user's settings are presented to
10	 * this module.
11	 */
12	public function __construct(){
13        $this->loadConfig();
14		$this->attribute = plugin_load('helper', 'attribute');
15        $this->success = $this->attribute != null && strstr(get_called_class(), 'helper_plugin_');
16		$this->moduleName = substr(get_called_class(), strlen('helper_plugin_'));
17	}
18
19    /**
20     * Return info about supported methods in this Helper Plugin
21     *
22     * @return array of public methods
23     */
24    public function getMethods() {
25        $result   = array();
26        $result[] = array(
27            'name'       => 'canUse',
28            'desc'       => "This is called to see if the user can use it to login.",
29            'parameters' => array(
30                'user' => 'string',
31            ),
32            'return'     => array('useable' => 'boolean'),
33        );
34        $result[] = array(
35            'name'       => 'canAuthLogin',
36            'desc'       => "This is called to see if the module provides login functionality on the main login page.",
37            'parameters' => array(
38            ),
39            'return'     => array('OTP_at_login' => 'boolean'),
40        );
41        $result[] = array(
42            'name'       => 'renderProfileForm',
43            'desc'       => "This is called to render the user configurable portion of the module inside the user's profile.  Default is to render nothing.",
44            'parameters' => array(
45            ),
46            'return'     => array('html_elements' => 'array'),
47        );
48        $result[] = array(
49            'name'       => 'processProfileForm',
50            'desc'       => "This is called to process the user configurable portion of the module inside the user's profile.",
51            'parameters' => array(
52            ),
53            'return'     => array('results' => 'mixed'),
54        );
55        $result[] = array(
56            'name'       => 'canTransmitMessage',
57            'desc'       => "This is called to see if the module can send a message to the user.",
58            'parameters' => array(
59            ),
60            'return'     => array('useable' => 'boolean'),
61        );
62        $result[] = array(
63            'name'       => 'transmitMessage',
64            'desc'       => "This is called to relay a message to the user.  The message should usually have a code for the user, but might be used to send a notice that someone has logged in using their account.",
65            'parameters' => array(
66                'message' => 'string',
67            ),
68            'return'     => array('success' => 'boolean'), // returns false on error.
69        );
70        $result[] = array(
71            'name'       => 'processLogin',
72            'desc'       => "This is called to validate the code provided.  The default is to see if the code matches the one-time password.",
73            'parameters' => array(
74                'code' => 'string',
75                'user' => 'string',
76            ),
77            'return'     => array('success' => 'boolean'),
78        );
79        return $result;
80    }
81
82	/**
83	 * This is called to see if the user can use it to login.
84	 * @return bool - True if this module has access to all needed information
85	 * to perform a login.
86	 */
87    abstract public function canUse($user = null);
88
89	/**
90	 * This is called to see if the module provides login functionality on the
91	 * main login page.
92	 * @return bool - True if this module provides main login functionality.
93	 */
94    abstract public function canAuthLogin();
95
96	/**
97	 * This is called to render the user configurable portion of the module
98	 * inside the user's profile.  Default is to render nothing.
99	 * NOTE: Use string indexes for fields that can be consolidated, eg phone
100	 *       numbers.
101	 * @return array - Array of HTML form elements to insert into the profile
102	 *     page.
103	 */
104    public function renderProfileForm() { return array(); }
105
106	/**
107	 * This is called to process the user configurable portion of the module
108	 * inside the user's profile.
109	 * @return mixed - True if the user's settings were changed, false if
110	 *     settings could not be changed, null if no settings were changed,
111	 *     the string 'verified' if the module was successfully verified,
112	 *     the string 'failed' if the module failed verification,
113	 *	   the string 'otp' if the module is requesting a one-time password
114	 *     for verification,
115     *     the string 'deleted' if the module was unenrolled.
116	 */
117    public function processProfileForm() { return null; }
118
119	/**
120	 * This is called to see if the module can send a message to the user.
121	 * @return bool - True if a message can be sent to the user.
122	 */
123	abstract public function canTransmitMessage();
124
125	/**
126	 * This is called to relay a message to the user.  The message should
127	 * usually have a code for the user, but might be used to send a notice
128	 * that someone has logged in using their account.
129	 * @return bool - True if the message was sucecssfully transmitted.
130	 */
131	public function transmitMessage($subject, $message, $force = false) { return false; }
132
133	/**
134	 * This is called to validate the code provided.  The default is to see if
135	 * the code matches the one-time password.
136	 * @return bool - True if the user has successfully authenticated using
137	 * this mechanism.
138	 */
139	public function processLogin($code, $user = null) {
140		$twofactor = plugin_load('action', 'twofactor');
141		$otpQuery = $twofactor->get_otp_code();
142		if (!$otpQuery) { return false; }
143		list($otp, $modname) = $otpQuery;
144		return ($code == $otp && $code != '' && (count($modname) == 0 || in_array(get_called_class(), $modname)));
145	}
146
147	/**
148	 * This is a helper function to get text strings from the twofactor class
149	 * calling this module.
150	 * @return string - Language string from the calling class.
151	 */
152	protected function _getSharedLang($key) {
153		$twofactor = plugin_load('action', 'twofactor');
154		return $twofactor->getLang($key);
155	}
156
157	/**
158	 * This is a helper function to get shared configuration options from the
159	 * twofactor class.
160	 * @return string - Language string from the calling class.
161	 */
162	protected function _getSharedConfig($key) {
163		$twofactor = plugin_load('action', 'twofactor');
164		return $twofactor->getConf($key);
165	}
166
167	/**
168	 * This is a helper function to check for the existence of shared
169	 * twofactor settings.
170	 * @return string - Language string from the calling class.
171	 */
172	protected function _sharedSettingExists($key) {
173		return $this->attribute->exists("twofactor", $key);
174	}
175
176	/**
177	 * This is a helper function to get shared twofactor settings.
178	 * @return string - Language string from the calling class.
179	 */
180	protected function _sharedSettingGet($key, $default = null, $user = null) {
181		return $this->_sharedSettingExists($key) ? $this->attribute->get("twofactor", $key, $success, $user) : $default;
182	}
183
184	/**
185	 * This is a helper function to set shared twofactor settings.
186	 * @return string - Language string from the calling class.
187	 */
188	protected function _sharedSettingSet($key, $value) {
189		return $this->attribute->set("twofactor", $key, $value);
190	}
191
192	/**
193	 * This is a helper function to check for the existence of module
194	 * specific settings.
195	 * @return string - Language string from the calling class.
196	 */
197	protected function _settingExists($key, $user = null) {
198		return $this->attribute->exists($this->moduleName, $key, $user);
199	}
200
201	/**
202	 * This is a helper function to get module specific settings.
203	 * @return string - Language string from the calling class.
204	 */
205	protected function _settingGet($key, $default = null, $user = null) {
206		return $this->_settingExists($key, $user) ? $this->attribute->get($this->moduleName, $key, $success, $user) : $default;
207	}
208
209	/**
210	 * This is a helper function to set module specific settings.
211	 * @return string - Language string from the calling class.
212	 */
213	protected function _settingSet($key, $value) {
214		return $this->attribute->set($this->moduleName, $key, $value);
215	}
216
217	/**
218	 * This is a helper function to delete module specific settings.
219	 * @return string - Language string from the calling class.
220	 */
221	protected function _settingDelete($key) {
222		return $this->attribute->del($this->moduleName, $key);
223	}
224
225	/**
226	 * This is a helper function that lists the names of all available
227	 * modules.
228	 * @return array - Names of availble modules.
229	 */
230	static public function _listModules(){
231		$modules = plugin_list();
232		return array_filter($modules, function($x){ return substr($x, 0, 9)==='twofactor' && $x !== 'twofactor';});
233	}
234
235	/**
236	 * This is a helper function that attempts to load the named modules.
237	 * @return array - An array of instanced objects from the loaded modules.
238	 */
239	static public function _loadModules($mods){
240		$objects = array();
241		foreach ($mods as $mod) {
242			$obj = plugin_load('helper', $mod);
243			if ($obj && is_a($obj, 'Twofactor_Auth_Module')) {
244				$objects[$mod] = $obj;
245			}
246		}
247		return $objects;
248	}
249}