3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2011, StatusNet, Inc.
6 * Restrict the email addresses in a domain to a select whitelist
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * @author Evan Prodromou <evan@status.net>
26 * @author Zach Copley <zach@status.net>
27 * @copyright 2011 StatusNet, Inc.
28 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
29 * @link http://status.net/
32 if (!defined('STATUSNET')) {
33 // This check helps protect against security problems;
34 // your code file can't be executed directly from the web.
39 * Restrict the email addresses to a domain whitelist
43 * @author Evan Prodromou <evan@status.net>
44 * @author Zach Copley <zach@status.net>
45 * @copyright 2011 StatusNet, Inc.
46 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
47 * @link http://status.net/
49 class DomainWhitelistPlugin extends Plugin
52 * Load related modules when needed
54 * @param string $cls Name of the class to be loaded
56 * @return boolean hook value; true means continue processing, false
59 function onAutoload($cls) {
60 $base = dirname(__FILE__);
61 $lower = strtolower($cls);
63 $files = array("$base/classes/$cls.php",
64 "$base/lib/$lower.php");
65 if (substr($lower, -6) == 'action') {
66 $files[] = "$base/actions/" . substr($lower, 0, -6) . ".php";
68 foreach ($files as $file) {
69 if (file_exists($file)) {
78 * Get the path to the plugin's installation directory. Used
79 * to link in js files and whatnot.
81 * @return String the absolute path
83 protected function getPath() {
84 return preg_replace('/^' . preg_quote(INSTALLDIR, '/') . '\//', '', dirname(__FILE__));
88 * Link in a JavaScript script for the whitelist invite form
90 * @param Action $action Action being shown
92 * @return boolean hook flag
94 function onEndShowStatusNetScripts($action) {
95 $name = $action->arg('action');
96 if ($name == 'invite') {
97 $action->script($this->getPath() . '/js/whitelistinvite.js');
102 function onRequireValidatedEmailPlugin_Override($user, &$knownGood)
104 $knownGood = (!empty($user->email) && $this->matchesWhitelist($user->email));
108 function onEndValidateUserEmail($user, $email, &$valid)
110 if ($valid) { // it's otherwise valid
111 if (!$this->matchesWhitelist($email)) {
112 $whitelist = $this->getWhitelist();
113 if (count($whitelist) == 1) {
114 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
115 // TRANS: %s is a whitelisted e-mail domain.
116 $message = sprintf(_m('Email address must be in this domain: %s.'),
119 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
120 // TRANS: %s are whitelisted e-mail domains separated by comma's (localisable).
121 $message = sprintf(_m('Email address must be in one of these domains: %s.'),
122 // TRANS: Separator for whitelisted domains.
123 implode(_m('SEPARATOR',', '), $whitelist));
125 throw new ClientException($message);
131 function onStartAddEmailAddress($user, $email)
133 if (!$this->matchesWhitelist($email)) {
134 // TRANS: Exception thrown when an e-mail address does not match the site's domain whitelist.
135 throw new Exception(_m('That email address is not allowed on this site.'));
141 function onEndValidateEmailInvite($user, $email, &$valid)
144 $valid = $this->matchesWhitelist($email);
150 function matchesWhitelist($email)
152 $whitelist = $this->getWhitelist();
154 if (empty($whitelist) || empty($whitelist[0])) {
158 $userDomain = $this->domainFromEmail($email);
160 return in_array($userDomain, $whitelist);
164 * Helper function to pull out a domain from
167 * @param string $email and email address
168 * @return string the domain
170 function domainFromEmail($email)
172 $parts = explode('@', $email);
173 return strtolower(trim($parts[1]));
176 function getWhitelist()
178 $whitelist = common_config('email', 'whitelist');
180 if (is_array($whitelist)) {
181 return $this->sortWhiteList($whitelist);
183 return explode('|', $whitelist);
188 * This is a filter function passed in to array_filter()
189 * in order to strip out the user's domain, which will
190 * be re-inserted as the first element (see sortWhitelist()
193 * @param string $domain domain to check
194 * @return boolean whether to include the domain
196 function userDomainFilter($domain)
198 $user = common_current_user();
199 $userDomain = $this->domainFromEmail($user->email);
200 if ($userDomain == $domain) {
207 * This function sorts the whitelist alphabetically, and sets the
208 * current user's domain as the first element in the array of
209 * allowed domains. Mostly, this is for the JavaScript on the invite
210 * page--in the case of multiple allowed domains, it's nicer if the
211 * user's own domain is the first option, and this seemed like a good
214 * @param array $whitelist whitelist of allowed email domains
215 * @return array an ordered or sorted version of the whitelist
217 function sortWhitelist($whitelist)
219 $whitelist = array_unique($whitelist);
220 natcasesort($whitelist);
222 $user = common_current_user();
224 if (!empty($user) && !empty($user->email)) {
225 $userDomain = $this->domainFromEmail($user->email);
227 $orderedWhitelist = array_values(
230 array($this, "userDomainFilter")
234 if (in_array($userDomain, $whitelist)) {
235 array_unshift($orderedWhitelist, $userDomain);
237 return $orderedWhitelist;
244 * Show a fancier invite form when domains are restricted to the
247 * @param action $action the invite action
248 * @return boolean hook value
250 function onStartShowInviteForm($action)
252 $this->showConfirmDialog($action);
253 $form = new WhitelistInviteForm($action, $this->getWhitelist());
258 function showConfirmDialog($action)
260 // For JQuery UI modal dialog
261 $action->elementStart(
263 // TRANS: Title for invitiation deletion dialog.
264 array('id' => 'confirm-dialog', 'title' => _m('Confirmation Required'))
266 // TRANS: Confirmation text for invitation deletion dialog.
267 $action->text(_m('Really delete this invitation?'));
268 $action->elementEnd('div');
272 * This is a bit of a hack. We take the values from the custom
273 * whitelist invite form and reformat them so they look like
274 * their coming from the the normal invite form.
276 * @param action &$action the invite action
277 * @return boolean hook value
279 function onStartSendInvitations(&$action)
282 $usernames = $action->arg('username');
283 $domains = $action->arg('domain');
285 for($i = 0; $i < count($usernames); $i++) {
286 if (!empty($usernames[$i])) {
287 $emails[] = $usernames[$i] . '@' . $domains[$i] . "\n";
291 $action->args['addresses'] = implode($emails);
296 function onPluginVersion(&$versions)
298 $versions[] = array('name' => 'DomainWhitelist',
299 'version' => STATUSNET_VERSION,
300 'author' => 'Evan Prodromou, Zach Copley',
301 'homepage' => 'http://status.net/wiki/Plugin:DomainWhitelist',
303 // TRANS: Plugin description.
304 _m('Restrict domains for email users.'));