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 * Get the path to the plugin's installation directory. Used
53 * to link in js files and whatnot.
55 * @return String the absolute path
57 protected function getPath() {
58 return preg_replace('/^' . preg_quote(INSTALLDIR, '/') . '\//', '', dirname(__FILE__));
62 * Link in a JavaScript script for the whitelist invite form
64 * @param Action $action Action being shown
66 * @return boolean hook flag
68 public function onEndShowStatusNetScripts(Action $action) {
69 $name = $action->arg('action');
70 if ($name == 'invite') {
71 $action->script($this->getPath() . '/js/whitelistinvite.js');
76 public function onRequireValidatedEmailPlugin_Override(User $user, &$knownGood)
78 $knownGood = (!empty($user->email) && $this->matchesWhitelist($user->email));
82 // @TODO Most callers are given NULL as first argument
83 public function onEndValidateUserEmail($user, $email, &$valid)
85 if ($valid) { // it's otherwise valid
86 if (!$this->matchesWhitelist($email)) {
87 $whitelist = $this->getWhitelist();
88 if (count($whitelist) == 1) {
89 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
90 // TRANS: %s is a whitelisted e-mail domain.
91 $message = sprintf(_m('Email address must be in this domain: %s.'),
94 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
95 // TRANS: %s are whitelisted e-mail domains separated by comma's (localisable).
96 $message = sprintf(_m('Email address must be in one of these domains: %s.'),
97 // TRANS: Separator for whitelisted domains.
98 implode(_m('SEPARATOR',', '), $whitelist));
100 throw new ClientException($message);
106 public function onStartAddEmailAddress(User $user, $email)
108 if (!$this->matchesWhitelist($email)) {
109 // TRANS: Exception thrown when an e-mail address does not match the site's domain whitelist.
110 throw new Exception(_m('That email address is not allowed on this site.'));
116 function onEndValidateEmailInvite($user, $email, &$valid)
119 $valid = $this->matchesWhitelist($email);
125 function matchesWhitelist($email)
127 $whitelist = $this->getWhitelist();
129 if (empty($whitelist) || empty($whitelist[0])) {
133 $userDomain = $this->domainFromEmail($email);
135 return in_array($userDomain, $whitelist);
139 * Helper function to pull out a domain from
142 * @param string $email and email address
143 * @return string the domain
145 function domainFromEmail($email)
147 $parts = explode('@', $email);
148 return strtolower(trim($parts[1]));
151 function getWhitelist()
153 $whitelist = common_config('email', 'whitelist');
155 if (is_array($whitelist)) {
156 return $this->sortWhiteList($whitelist);
158 return explode('|', $whitelist);
163 * This is a filter function passed in to array_filter()
164 * in order to strip out the user's domain, which will
165 * be re-inserted as the first element (see sortWhitelist()
168 * @param string $domain domain to check
169 * @return boolean whether to include the domain
171 function userDomainFilter($domain)
173 $user = common_current_user();
174 $userDomain = $this->domainFromEmail($user->email);
175 if ($userDomain == $domain) {
182 * This function sorts the whitelist alphabetically, and sets the
183 * current user's domain as the first element in the array of
184 * allowed domains. Mostly, this is for the JavaScript on the invite
185 * page--in the case of multiple allowed domains, it's nicer if the
186 * user's own domain is the first option, and this seemed like a good
189 * @param array $whitelist whitelist of allowed email domains
190 * @return array an ordered or sorted version of the whitelist
192 function sortWhitelist($whitelist)
194 $whitelist = array_unique($whitelist);
195 natcasesort($whitelist);
197 $user = common_current_user();
199 if (!empty($user) && !empty($user->email)) {
200 $userDomain = $this->domainFromEmail($user->email);
202 $orderedWhitelist = array_values(
205 array($this, "userDomainFilter")
209 if (in_array($userDomain, $whitelist)) {
210 array_unshift($orderedWhitelist, $userDomain);
212 return $orderedWhitelist;
219 * Show a fancier invite form when domains are restricted to the
222 * @param action $action the invite action
223 * @return boolean hook value
225 function onStartShowInviteForm($action)
227 $this->showConfirmDialog($action);
228 $form = new WhitelistInviteForm($action, $this->getWhitelist());
233 function showConfirmDialog($action)
235 // For JQuery UI modal dialog
236 $action->elementStart(
238 // TRANS: Title for invitiation deletion dialog.
239 array('id' => 'confirm-dialog', 'title' => _m('Confirmation Required'))
241 // TRANS: Confirmation text for invitation deletion dialog.
242 $action->text(_m('Really delete this invitation?'));
243 $action->elementEnd('div');
247 * This is a bit of a hack. We take the values from the custom
248 * whitelist invite form and reformat them so they look like
249 * their coming from the the normal invite form.
251 * @param action &$action the invite action
252 * @return boolean hook value
254 function onStartSendInvitations(&$action)
257 $usernames = $action->arg('username');
258 $domains = $action->arg('domain');
260 for($i = 0; $i < count($usernames); $i++) {
261 if (!empty($usernames[$i])) {
262 $emails[] = $usernames[$i] . '@' . $domains[$i] . "\n";
266 $action->args['addresses'] = implode($emails);
271 function onPluginVersion(array &$versions)
273 $versions[] = array('name' => 'DomainWhitelist',
274 'version' => GNUSOCIAL_VERSION,
275 'author' => 'Evan Prodromou, Zach Copley',
276 'homepage' => 'http://status.net/wiki/Plugin:DomainWhitelist',
278 // TRANS: Plugin description.
279 _m('Restrict domains for email users.'));