PluginProbe
Friends / 2.9.1
Friends v2.9.1
4.3.2 4.3.1 4.3.0 4.2.2 4.2.1 4.2.0 4.1.0 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.1 All 88 releases
friends / includes / class-access-control.php

class-access-control.php in Friends 2.9.1, at includes/class-access-control.php

341 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Friends Access Control
4 *
5 * This contains the functions for access control.
6 *
7 * @package Friends
8 */
9
10 namespace Friends;
11
12 /**
13 * This is the class for the Friends Plugin Access Control.
14 *
15 * @since 0.6
16 *
17 * @package Friends
18 * @author Alex Kirk
19 */
20 class Access_Control {
21 /**
22 * States whether this is an authenticated feed call.
23 *
24 * @var boolean
25 */
26 private $feed_authenticated = null;
27
28 /**
29 * Contains a reference to the Friends class.
30 *
31 * @var Friends
32 */
33 private $friends = null;
34
35 /**
36 * Constructor
37 *
38 * @param Friends $friends A reference to the Friends object.
39 */
40 public function __construct( Friends $friends ) {
41 $this->friends = $friends;
42 $this->register_hooks();
43 }
44
45 /**
46 * Register the WordPress hooks
47 */
48 private function register_hooks() {
49 add_filter( 'determine_current_user', array( $this, 'authenticate' ), 1 );
50 add_filter( 'option_comment_whitelist', array( $this, 'option_comment_whitelist' ) );
51 add_action( 'set_user_role', array( $this, 'notify_new_friend_request' ), 10, 3 );
52 add_action( 'map_meta_cap', array( $this, 'strict_friend_checking_for_super_admin' ), 10, 4 );
53 add_action( 'delete_user', array( $this, 'delete_friend_token' ) );
54 add_action( 'init', array( $this, 'remote_login' ) );
55 }
56
57 /**
58 * Whether the feed is authenticated
59 *
60 * @return bool The authentication status of the feed.
61 */
62 public function feed_is_authenticated() {
63 return (bool) $this->get_authenticated_feed_user();
64 }
65
66 /**
67 * Get authenticated feed user
68 *
69 * @return User|null The authentication status of the feed.
70 */
71 public function get_authenticated_feed_user() {
72 if ( is_null( $this->feed_authenticated ) ) {
73 $this->authenticate( 0 );
74 }
75
76 if ( is_null( $this->feed_authenticated ) ) {
77 return null;
78 }
79
80 return new User( $this->feed_authenticated );
81 }
82
83 /**
84 * Whether the private RSS feed is authenticated. This is the feed for the admins of the site that will contain the friends posts.
85 *
86 * @return bool The authentication status of the feed.
87 */
88 public static function private_rss_is_authenticated() {
89 if ( isset( $_GET['auth'] ) && get_option( 'friends_private_rss_key' ) === $_GET['auth'] ) {
90 return true;
91 }
92
93 return false;
94 }
95
96 /**
97 * Verify a friend token
98 *
99 * @param string $token The token to verify.
100 * @param integer $until Valid until timestamp.
101 * @param string $auth The auth code.
102 *
103 * @return int|bool The user id or false.
104 */
105 public function verify_token( $token, $until, $auth ) {
106 if ( ! get_option( 'friends_enable_wp_friendships' ) ) {
107 return false;
108 }
109 $user_id = get_option( 'friends_in_token_' . $token );
110 if ( ! $user_id ) {
111 $me = User::get_user( User::get_user_login_for_url( $token ) );
112 if ( ! $me || is_wp_error( $me ) ) {
113 return false;
114 }
115 $user_id = $me->ID;
116 } else {
117 settype( $user_id, 'int' );
118
119 if ( get_user_option( 'friends_in_token', $user_id ) !== $token ) {
120 return false;
121 }
122 }
123
124 // Allow for some grace period by skipping the auth check for older versions.
125 if ( ! is_null( $until ) && ! is_null( $auth ) ) {
126 if ( ! password_verify( $until . get_user_option( 'friends_out_token', $user_id ), $auth ) ) {
127 return false;
128 }
129
130 if ( time() > $until ) {
131 return false;
132 }
133 }
134
135 return $user_id;
136 }
137
138 /**
139 * Log in a friend via URL parameter
140 */
141 public function remote_login() {
142 if ( ! get_option( 'friends_enable_wp_friendships' ) ) {
143 return false;
144 }
145 if ( ! isset( $_GET['friend_auth'] ) ) {
146 return;
147 }
148 $tokens = explode( '-', $_GET['friend_auth'] );
149 if ( 3 === count( $tokens ) ) {
150 $user_id = $this->verify_token( $tokens[0], $tokens[1], $tokens[2] );
151 } elseif ( 2 === count( $tokens ) && isset( $_GET['me'] ) ) {
152 $user_id = $this->verify_token( $_GET['me'], $tokens[0], $tokens[1] );
153 } else {
154 return;
155 }
156
157 if ( ! $user_id ) {
158 return;
159 }
160 $user = new User( $user_id );
161 if ( ! $user->has_cap( 'friend' ) ) {
162 return;
163 }
164
165 wp_set_auth_cookie( $user_id );
166 wp_safe_redirect( str_replace( array( '?friend_auth=' . $_GET['friend_auth'], '&friend_auth=' . $_GET['friend_auth'], '?me=' . $_GET['me'], '&me=' . $_GET['me'] ), '', $_SERVER['REQUEST_URI'] ) );
167 exit;
168 }
169
170 /**
171 * Authenticate a user for a feed.
172 *
173 * @param int $incoming_user_id An already authenticated user.
174 * @return int The new authenticated user.
175 */
176 public function authenticate( $incoming_user_id ) {
177 if ( false === $incoming_user_id ) {
178 return false;
179 }
180
181 $user_id = false;
182 if ( isset( $_GET['friend'] ) && isset( $_GET['until'] ) && isset( $_GET['auth'] ) ) {
183 $user_id = $this->verify_token( $_GET['friend'], $_GET['until'], $_GET['auth'] );
184 } elseif ( isset( $_GET['me'] ) && isset( $_GET['until'] ) && isset( $_GET['auth'] ) ) {
185 $user_id = $this->verify_token( $_GET['me'], $_GET['until'], $_GET['auth'] );
186 }
187
188 if ( $user_id ) {
189 $user = new User( $user_id );
190 if ( $user->has_cap( 'friend' ) ) {
191 $this->feed_authenticated = $user_id;
192 return $this->feed_authenticated;
193 }
194 }
195
196 return $incoming_user_id;
197 }
198
199 /**
200 * Gets the friend auth.
201 *
202 * @param User $friend_user The friend user.
203 * @param integer $validity The validity.
204 *
205 * @return string The friend auth.
206 */
207 public function get_friend_auth( User $friend_user, $validity = 3600 ) {
208 static $tokens = array();
209
210 if ( ! isset( $tokens[ $friend_user->ID ] ) ) {
211 $tokens[ $friend_user->ID ] = array();
212 $out_token = $friend_user->get_user_option( 'friends_out_token' );
213 $in_token = $friend_user->get_user_option( 'friends_in_token' );
214
215 if ( $in_token && $out_token ) {
216 $until = time() + $validity;
217 $auth = password_hash( $until . $in_token, PASSWORD_DEFAULT );
218
219 $tokens[ $friend_user->ID ] = array(
220 'me' => User::get_user_login_for_url( home_url(), false ),
221 'until' => $until,
222 'auth' => $auth,
223 );
224 }
225 }
226
227 return $tokens[ $friend_user->ID ];
228 }
229
230 /**
231 * Appends an auth to an URL.
232 *
233 * @param string $url The url.
234 * @param User $friend_user The friend user.
235 * @param integer $validity The validity in seconds.
236 *
237 * @return string The url with an appended auth.
238 */
239 public function append_auth( $url, User $friend_user, $validity = 3600 ) {
240 if ( $validity < 0 ) {
241 return $url;
242 }
243 $friend_auth = $this->get_friend_auth( $friend_user, $validity );
244 if ( ! empty( $friend_auth ) ) {
245 $sep = false === strpos( $url, '?' ) ? '?' : '&';
246
247 $url .= $sep . 'me=' . urlencode( $friend_auth['me'] );
248 $url .= '&until=' . urlencode( $friend_auth['until'] );
249 $url .= '&auth=' . urlencode( $friend_auth['auth'] );
250 }
251
252 return $url;
253 }
254
255 /**
256 * Delete options associated with a user
257 *
258 * @param int $user_id The user id.
259 * @return The old token.
260 */
261 public function delete_friend_token( $user_id ) {
262 $current_secret = get_user_option( 'friends_in_token', $user_id );
263
264 if ( $current_secret ) {
265 delete_option( 'friends_in_token_' . $current_secret );
266 }
267
268 $user = new User( $user_id );
269
270 // No need to delete user options as the user will be deleted.
271 return $current_secret;
272 }
273
274 /**
275 * Update a friend request token
276 *
277 * @param int $user_id The user id.
278 * @param string $new_role The new role.
279 * @param array $old_roles The old roles.
280 *
281 * @return string The new token.
282 */
283 public function notify_new_friend_request( $user_id, $new_role, $old_roles ) {
284 if ( 'friend_request' !== $new_role || in_array( $new_role, $old_roles, true ) ) {
285 return;
286 }
287
288 do_action( 'notify_new_friend_request', new User( $user_id ) );
289 }
290
291 /**
292 * Demotes the super admin for the friend roles so that they can interact in the Friends system like a normal user.
293 *
294 * @param string[] $caps Primitive capabilities required of the user.
295 * @param string $cap Capability being checked.
296 * @param int $user_id The user ID.
297 * @param array $args Adds context to the capability check, typically
298 * starting with an object ID.
299 *
300 * @return array
301 */
302 public function strict_friend_checking_for_super_admin( $caps, $cap, $user_id, $args ) {
303 if ( ! in_array( $cap, array( 'friend', 'acquaintance', 'pending_friend_request', 'friend_request', 'subscription' ) ) ) {
304 return $caps;
305 }
306 if ( ! is_multisite() || ! is_super_admin( $user_id ) ) {
307 return $caps;
308 }
309
310 $user = get_user_by( 'id', $user_id );
311 if ( ! $user ) {
312 return $caps;
313 }
314
315 foreach ( $user->roles as $role ) {
316 // If they have the role we are checking for, we'll respond with unmapped caps.
317 if ( $cap === $role ) {
318 return $caps;
319 }
320 }
321
322 // If the super admin doesn't have the role, respond with do_not_allow so that they don't qualify
323 // for the capapbility, despite being a super admin (which automatically has any capability).
324 return array( 'do_not_allow' );
325 }
326
327 /**
328 * Prevent a friend's first comment ending up in moderation.
329 *
330 * @param string $value The value retrieved from the Database.
331 * @return string The filtered value.
332 */
333 public function option_comment_whitelist( $value ) {
334 // Don't moderate the first comment by a friend.
335 if ( current_user_can( 'friend' ) || current_user_can( 'acquaintance' ) ) {
336 return '0';
337 }
338 return $value;
339 }
340 }
341