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 function onEndShowStatusNetScripts($action) {
69 $name = $action->arg('action');
70 if ($name == 'invite') {
71 $action->script($this->getPath() . '/js/whitelistinvite.js');
76 function onRequireValidatedEmailPlugin_Override($user, &$knownGood)
78 $knownGood = (!empty($user->email) && $this->matchesWhitelist($user->email));
82 function onEndValidateUserEmail($user, $email, &$valid)
84 if ($valid) { // it's otherwise valid
85 if (!$this->matchesWhitelist($email)) {
86 $whitelist = $this->getWhitelist();
87 if (count($whitelist) == 1) {
88 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
89 // TRANS: %s is a whitelisted e-mail domain.
90 $message = sprintf(_m('Email address must be in this domain: %s.'),
93 // TRANS: Client exception thrown when a given e-mailaddress is not in the domain whitelist.
94 // TRANS: %s are whitelisted e-mail domains separated by comma's (localisable).
95 $message = sprintf(_m('Email address must be in one of these domains: %s.'),
96 // TRANS: Separator for whitelisted domains.
97 implode(_m('SEPARATOR',', '), $whitelist));
99 throw new ClientException($message);
105 function onStartAddEmailAddress($user, $email)
107 if (!$this->matchesWhitelist($email)) {
108 // TRANS: Exception thrown when an e-mail address does not match the site's domain whitelist.
109 throw new Exception(_m('That email address is not allowed on this site.'));
115 function onEndValidateEmailInvite($user, $email, &$valid)
118 $valid = $this->matchesWhitelist($email);
124 function matchesWhitelist($email)
126 $whitelist = $this->getWhitelist();
128 if (empty($whitelist) || empty($whitelist[0])) {
132 $userDomain = $this->domainFromEmail($email);
134 return in_array($userDomain, $whitelist);
138 * Helper function to pull out a domain from
141 * @param string $email and email address
142 * @return string the domain
144 function domainFromEmail($email)
146 $parts = explode('@', $email);
147 return strtolower(trim($parts[1]));
150 function getWhitelist()
152 $whitelist = common_config('email', 'whitelist');
154 if (is_array($whitelist)) {
155 return $this->sortWhiteList($whitelist);
157 return explode('|', $whitelist);
162 * This is a filter function passed in to array_filter()
163 * in order to strip out the user's domain, which will
164 * be re-inserted as the first element (see sortWhitelist()
167 * @param string $domain domain to check
168 * @return boolean whether to include the domain
170 function userDomainFilter($domain)
172 $user = common_current_user();
173 $userDomain = $this->domainFromEmail($user->email);
174 if ($userDomain == $domain) {
181 * This function sorts the whitelist alphabetically, and sets the
182 * current user's domain as the first element in the array of
183 * allowed domains. Mostly, this is for the JavaScript on the invite
184 * page--in the case of multiple allowed domains, it's nicer if the
185 * user's own domain is the first option, and this seemed like a good
188 * @param array $whitelist whitelist of allowed email domains
189 * @return array an ordered or sorted version of the whitelist
191 function sortWhitelist($whitelist)
193 $whitelist = array_unique($whitelist);
194 natcasesort($whitelist);
196 $user = common_current_user();
198 if (!empty($user) && !empty($user->email)) {
199 $userDomain = $this->domainFromEmail($user->email);
201 $orderedWhitelist = array_values(
204 array($this, "userDomainFilter")
208 if (in_array($userDomain, $whitelist)) {
209 array_unshift($orderedWhitelist, $userDomain);
211 return $orderedWhitelist;
218 * Show a fancier invite form when domains are restricted to the
221 * @param action $action the invite action
222 * @return boolean hook value
224 function onStartShowInviteForm($action)
226 $this->showConfirmDialog($action);
227 $form = new WhitelistInviteForm($action, $this->getWhitelist());
232 function showConfirmDialog($action)
234 // For JQuery UI modal dialog
235 $action->elementStart(
237 // TRANS: Title for invitiation deletion dialog.
238 array('id' => 'confirm-dialog', 'title' => _m('Confirmation Required'))
240 // TRANS: Confirmation text for invitation deletion dialog.
241 $action->text(_m('Really delete this invitation?'));
242 $action->elementEnd('div');
246 * This is a bit of a hack. We take the values from the custom
247 * whitelist invite form and reformat them so they look like
248 * their coming from the the normal invite form.
250 * @param action &$action the invite action
251 * @return boolean hook value
253 function onStartSendInvitations(&$action)
256 $usernames = $action->arg('username');
257 $domains = $action->arg('domain');
259 for($i = 0; $i < count($usernames); $i++) {
260 if (!empty($usernames[$i])) {
261 $emails[] = $usernames[$i] . '@' . $domains[$i] . "\n";
265 $action->args['addresses'] = implode($emails);
270 function onPluginVersion(array &$versions)
272 $versions[] = array('name' => 'DomainWhitelist',
273 'version' => GNUSOCIAL_VERSION,
274 'author' => 'Evan Prodromou, Zach Copley',
275 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/DomainWhitelist',
277 // TRANS: Plugin description.
278 _m('Restrict domains for email users.'));