-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathprivate-feed-keys.php
More file actions
executable file
·333 lines (291 loc) · 10.7 KB
/
private-feed-keys.php
File metadata and controls
executable file
·333 lines (291 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
<?php
/*
Plugin Name: Private Feed Keys
Plugin URI: https://github.com/adamfranco/private-feed-keys
Description: Allows subscription to RSS feeds on private blogs that require authentication. Works with "More Privacy Options" on multi-site installs.
Version: 1.1
Author: Adam Franco
Author URI: http://www.adamfranco.com/
This plugin allows users to subscribe to feeds requiring authentication. When using [More Privacy Options](http://wordpress.org/extend/plugins/more-privacy-options/) in a multi-site installation RSS feeds for blogs marked as private require authentication. This plugin adds a user and site specific 40-character key on private blogs, creating a unique feed URL for each registered on user the blog. This allows feeds on private blogs to be subscribed to using feed readers that do not support authentication. As or on sites that do not use local authentication.
This plugin is similar in concept to the [Feed Key](http://code.andrewhamilton.net/wordpress/plugins/feed-key/) plugin, but designed from the ground up to operate in a multi-site context where access is controlled by the [More Privacy Options](http://wordpress.org/extend/plugins/more-privacy-options/) plugin.
Primary differences from Feed Key:
* Only adds keys to the feed URLs on private sites, not all sites in the network.
* Keys are per site and per user, preventing exposure of the key for a single site from giving access to other sites the user can see.
* Presence of a feed key authenticates feed requests as the user that matches the key rather than blocking requests that don't include a feed key.
* If no key is present the RSS feed request continues without interference for handling by other authentication hooks.
* Access control is still determined by other authentication plugins, ensuring that if a user is removed as a subscriber of a private blog, access to the feed will be denied.
* Users can revoke their own keys on a per-blog basis.
Licensed under the [GNU General Public License 2.0 (GPL)](http://www.gnu.org/licenses/gpl.html)
*/
// Install actions
register_activation_hook(__FILE__, 'private_feed_keys_install');
// Authentication actions
add_action('wp_authenticate', 'private_feed_keys_authenticate', 9, 2);
// The More Privacy Options plugin checks authentication in send_headers, so be sure to
// authenticate before it.
add_action('send_headers', 'private_feed_keys_authenticate', 9, 2);
// Add filters to include our parameters on feed URLs for authenticated users.
add_filter('feed_link', 'private_feed_keys_filter_link');
add_filter('author_feed_link', 'private_feed_keys_filter_link');
add_filter('category_feed_link', 'private_feed_keys_filter_link');
add_filter('taxonomy_feed_link', 'private_feed_keys_filter_link');
add_filter('post_comments_feed_link', 'private_feed_keys_filter_link');
// User key listing and revocation
add_action('show_user_profile', 'private_feed_keys_edit_user_profile');
add_action('edit_user_profile', 'private_feed_keys_edit_user_profile');
add_action('personal_options_update', 'private_feed_keys_update_user_profile');
add_action('edit_user_profile_update', 'private_feed_keys_update_user_profile');
/**
* Install hook.
*/
function private_feed_keys_install () {
global $wpdb;
$pfk_db_version = '0.1';
$table_name = $wpdb->base_prefix . "private_feed_keys";
if($wpdb->get_var("SHOW TABLES LIKE '$table_name'") != $table_name) {
$sql = "CREATE TABLE " . $table_name . " (
blog_id int(11) NOT NULL,
user_id int(11) NOT NULL,
feed_key varchar(40) NOT NULL,
created timestamp NOT NULL default CURRENT_TIMESTAMP,
last_access timestamp NULL default NULL,
num_access int(11) NOT NULL default '0',
PRIMARY KEY (blog_id,user_id),
KEY feed_key (feed_key),
KEY blog_id (blog_id,feed_key)
);";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
add_option("private_feed_keys_db_version", $pfk_db_version);
}
}
/**
* Bypass other authentication if requesting a feed and a valid key is included.
*
* @return void
*/
function private_feed_keys_authenticate () {
if (!empty($_GET['FEED_KEY']))
$feed_key = $_GET['FEED_KEY'];
else if (!empty($_GET['feed_key']))
$feed_key = $_GET['feed_key'];
else
$feed_key = null;
if (empty($feed_key) || $current_user->ID)
return;
global $wpdb, $blog_id, $current_user, $wp;
// The global $wp_query object isn't ready yet, so we'll just use our own for the is_feed test.
$query = new WP_Query();
$query->query($wp->query_vars);
if ($query->is_feed()) {
$table_name = $wpdb->base_prefix . "private_feed_keys";
$row = $wpdb->get_row($wpdb->prepare(
"SELECT
user_id, num_access
FROM
$table_name
WHERE
blog_id = %d
AND feed_key = %s",
$blog_id, $feed_key
));
if ($row) {
$user_id = $row->user_id;
$num_access = intval($row->num_access);
} else {
return;
}
// If we have a valid key, authenticate their user and skip later
// authentication hooks. If not valid, continue on to other authentication hooks.
if ($user_id) {
remove_all_actions('wp_authenticate');
// Authenticate the user.
wp_set_current_user($user_id );
// Update our counter.
$wpdb->query($wpdb->prepare(
"UPDATE
$table_name
SET
last_access = NOW(),
num_access = %d
WHERE
blog_id = %d
AND feed_key = %s",
$num_access + 1, $blog_id, $feed_key
));
}
}
}
/**
* Add our parameters to the feed link.
*
* @param string $url The feed URL.
* @param string $type The type of feed (e.g. "rss2", "atom", etc.)
* @return string
*/
function private_feed_keys_filter_link ($url) {
global $current_user;
// Don't add feed keys for anonymous users.
if (empty($current_user->ID))
return $url;
// Don't add feed keys for publicly visible blogs
//
// @todo Make this call pluggable to work with privacy modules other than
// "More Privacy Options".
if (intval(get_option('blog_public')) >= 0)
return $url;
if (strpos($url, '?'))
$url .= '&';
else
$url .= '?';
return $url.'FEED_KEY='.private_feed_keys_get_key();
}
/**
* Answer a feed-key for the current user and current blog.
*
* @return string
*/
function private_feed_keys_get_key () {
global $current_user, $blog_id, $wpdb;
if (!$current_user->ID)
throw new Exception(__FUNCTION__.' should only be called when a user is authenticated.');
if (!$blog_id)
throw new Exception(__FUNCTION__.' should only be called when on a blog.');
// Return the existing key if one exists
$table_name = $wpdb->base_prefix . "private_feed_keys";
$feed_key = $wpdb->get_var($wpdb->prepare(
"SELECT
feed_key
FROM
$table_name
WHERE
blog_id = %d
AND user_id = %d",
$blog_id, $current_user->ID
));
if ($feed_key)
return $feed_key;
// Generate a new key.
$feed_key = private_feed_keys_generate();
$result = $wpdb->insert($table_name, array('blog_id' => $blog_id, 'user_id' => $current_user->ID, 'feed_key' => $feed_key), array('%d', '%d', '%s'));
if (!$result)
throw new Exception("Could not insert feed key for ".$blog_id.", ".$current_user->ID);
return $feed_key;
}
/**
* Generate a new feed key.
*
* @return string
*/
function private_feed_keys_generate () {
global $current_user, $blog_id;
return sha1(mt_rand().$blog_id.$current_user->user_login);
}
/**
* Add our keys to the user profile screen.
*
* @return void
*/
function private_feed_keys_edit_user_profile ($user) {
global $wpdb;
print "\n<h3>"._("Private Feed Keys")."</h3>";
if (isset($_SESSION['private_feed_keys_revoked'])) {
if (is_wp_error($_SESSION['private_feed_keys_revoked'])) {
print "\n<div class='error'><p>";
print $_SESSION['private_feed_keys_revoked']->get_error_message();
print "\n</p></div>";
} else {
print "\n<div class='updated'>\n\t<p><strong>";
print $_SESSION['private_feed_keys_revoked'];
if ($_SESSION['private_feed_keys_revoked'] > 1)
print " Feed Keys revoked.";
else
print " Feed Key revoked.";
print "</strong></p>\n</div>";
}
unset($_SESSION['private_feed_keys_revoked']);
}
print "\n<p class='description'>"._("Below are feed keys that allow you to subscribe to private blogs. A separate key has been generated for each private blog you have visited.")."</p>";
print "\n<p class='description'>"._("If you have accidentally shared a feed URL for one of these blogs with someone else, you can revoke its key to prevent unauthorized access. Please note that if you revoke a key you will have to resubscribe to any feeds from that blog.")."</p>";
print "\n<table class='form-table'>";
print "\n<thead>";
print "\n\t<tr>";
print "\n\t\t<th>Blog</th>";
print "\n\t\t<th>Feed Key</th>";
print "\n\t\t<th>Date Created</th>";
print "\n\t\t<th>Last Access</th>";
print "\n\t\t<th># Accessed</th>";
print "\n\t\t<th>Actions</th>";
print "\n\t</tr>";
print "\n</thead>";
print "\n<tbody>";
$table_name = $wpdb->base_prefix . "private_feed_keys";
$results = $wpdb->get_results($wpdb->prepare(
"SELECT
*
FROM
$table_name
WHERE
user_id = %d
AND num_access > 0
ORDER BY
last_access
",
$user->ID
));
if (empty($results)) {
print "\n\t<tr><td colspan='6' style='text-align: center; font-weight: bold;'>"._("You have no Feed Keys in use.")."</td></tr>";
} else {
foreach ($results as $row) {
$details = get_blog_details($row->blog_id, true);
print "\n\t<tr>";
print "\n\t\t<td><a href='".$details->siteurl."' target='_blank'>".$details->blogname."</a></td>";
print "\n\t\t<td>".$row->feed_key."</td>";
print "\n\t\t<td>".$row->created."</td>";
print "\n\t\t<td>".$row->last_access."</td>";
print "\n\t\t<td>".$row->num_access."</td>";
print "\n\t\t<td><input type='checkbox' name='private_feed_keys_revoke' value='".$row->blog_id."'/> revoke key</td>";
print "\n\t</tr>";
}
}
print "\n</tbody>";
print "\n</table>";
}
/**
* Revoke any keys chosen in user-profile saving.
*
* @return boolean
*/
function private_feed_keys_update_user_profile ($user_id) {
global $wpdb;
if (!current_user_can('edit_user', $user_id))
return false;
if (isset($_POST['private_feed_keys_revoke'])) {
if (is_array($_POST['private_feed_keys_revoke'])) {
$blog_ids = $_POST['private_feed_keys_revoke'];
foreach ($blog_ids as $key => $val) {
$blog_ids[$key] = intval($val);
}
} else {
$blog_ids = array(intval($_POST['private_feed_keys_revoke']));
}
if (!count($blog_ids))
return false;
// Delete the keys
$table_name = $wpdb->base_prefix . "private_feed_keys";
$numRevoked = $wpdb->query($wpdb->prepare(
"DELETE FROM $table_name
WHERE
user_id = %d
AND blog_id IN (".implode(',', $blog_ids).")",
$user_id
));
if ($numRevoked) {
$_SESSION['private_feed_keys_revoked'] = $numRevoked;
return true;
} else {
$_SESSION['private_feed_keys_revoked'] = new WP_Error('private_feed_keys_revoke_failed', __("Failed to revoke keys."));
return false;
}
}
}