1<?php
2/**
3 * @package Habari
4 *
5 */
6
7/**
8 * Access Control List class
9 *
10 * The default Habari ACL class implements groups, and group permissions
11 * Users are assigned to one or more groups.
12 * Groups are assigned one or more permissions.
13 * Membership in any group that grants a permission
14 * means you have that permission.  Membership in any group that denies
15 * that permission denies the user that permission, even if another group
16 * grants that permission.
17 *
18 */
19
20class ACL
21{
22	/**
23	 * How to handle a permission request for a permission that is not in the permission list.
24	 * For example, if you request $user->can('some non-existent permission') then this value is returned.
25	 */
26	const ACCESS_NONEXISTENT_PERMISSION = 0;
27	const CACHE_NULL = -1;
28
29	public static $access_names = array( 'read', 'edit', 'delete', 'create' );
30	private static $token_cache = null;
31
32	/**
33	 * Check a permission bitmask for a particular access type.
34	 * @param Bitmask $bitmask The permission bitmask
35	 * @param mixed $access The name of the access to check against (read, write, full)
36	 * @return bool Returns true if the given access meets exceeds the access to check against
37	 */
38	public static function access_check( $bitmask, $access )
39	{
40		if ( $access instanceof Bitmask ) {
41			return ( $bitmask->value & $access->value ) == $access->value;
42		}
43
44		switch ( $access ) {
45			case 'full':
46				return $bitmask->value == $bitmask->full;
47			case 'any':
48				return $bitmask->value != 0;
49			case 'deny':
50				return $bitmask->value == 0;
51			default:
52				return $bitmask->$access;
53		}
54	}
55
56	/**
57	 * Get a Bitmask object representing the supplied access integer
58	 *
59	 * @param integer $mask The access mask, usually stored in the database
60	 * @return Bitmask An object representing the access value
61	 */
62	public static function get_bitmask( $mask )
63	{
64		$bitmask = new Bitmask( self::$access_names, $mask );
65		return $bitmask;
66	}
67
68	/**
69	 * Create a new permission token, and save it to the permission tokens table
70	 * @param string $name The name of the permission
71	 * @param string $description The description of the permission
72	 * @param string $group The token group for organizational purposes
73	 * @param bool $crud Indicates if the token is a CRUD or boolean type token (default is boolean)
74	 * @return mixed the ID of the newly created permission, or boolean false
75	 */
76	public static function create_token( $name, $description, $group, $crud = false )
77	{
78		$name = self::normalize_token( $name );
79		$crud = ( $crud ) ? 1 : 0;
80		// first, make sure this isn't a duplicate
81		if ( ACL::token_exists( $name ) ) {
82			return false;
83		}
84		$allow = true;
85		// Plugins have the opportunity to prevent adding this token
86		$allow = Plugins::filter( 'token_create_allow', $allow, $name, $description, $group, $crud );
87		if ( ! $allow ) {
88			return false;
89		}
90		Plugins::act( 'token_create_before', $name, $description, $group, $crud );
91
92		$result = DB::query( 'INSERT INTO {tokens} (name, description, token_group, token_type) VALUES (?, ?, ?, ?)', array( $name, $description, $group, $crud ) );
93
94		if ( ! $result ) {
95			// if it didn't work, don't bother trying to log it
96			return false;
97		}
98
99		self::clear_caches();
100
101		// Add the token to the admin group
102		$token = ACL::token_id( $name );
103		$admin = UserGroup::get( 'admin' );
104		if ( $admin ) {
105			ACL::grant_group( $admin->id, $token, 'full' );
106		}
107
108		EventLog::log( 'New permission token created: ' . $name, 'info', 'default', 'habari' );
109		Plugins::act( 'permission_create_after', $name, $description, $group, $crud );
110		return $result;
111	}
112
113	/**
114	 * Remove a permission token, and any assignments of it
115	 * @param mixed $permission a permission ID or name
116	 * @return bool whether the permission was deleted or not
117	 */
118	public static function destroy_token( $token )
119	{
120		// make sure the permission exists, first
121		if ( ! self::token_exists( $token ) ) {
122			return false;
123		}
124
125		// grab token ID
126		$token_id = self::token_id( $token );
127
128		$allow = true;
129		// plugins have the opportunity to prevent deletion
130		$allow = Plugins::filter( 'token_destroy_allow', $allow, $token_id );
131		if ( ! $allow ) {
132			return false;
133		}
134		Plugins::act( 'token_destroy_before', $token_id );
135		// capture the token name
136		$name = DB::get_value( 'SELECT name FROM {tokens} WHERE id=?', array( $token_id ) );
137		// remove all references to this permissions
138		$result = DB::query( 'DELETE FROM {group_token_permissions} WHERE token_id=?', array( $token_id ) );
139		$result = DB::query( 'DELETE FROM {user_token_permissions} WHERE token_id=?', array( $token_id ) );
140		$result = DB::query( 'DELETE FROM {post_tokens} WHERE token_id=?', array( $token_id ) );
141		ACL::clear_caches();
142		// remove this token
143		$result = DB::query( 'DELETE FROM {tokens} WHERE id=?', array( $token_id ) );
144		if ( ! $result ) {
145			// if it didn't work, don't bother trying to log it
146			return false;
147		}
148		EventLog::log( sprintf( _t( 'Permission token deleted: %s' ), $name ), 'info', 'default', 'habari' );
149		Plugins::act( 'token_destroy_after', $token_id );
150		return $result;
151	}
152
153	/**
154	 * Get an array of QueryRecord objects containing all permission tokens
155	 * @param string $order the order in which to sort the returning array
156	 * @return array an array of QueryRecord objects containing all tokens
157	 */
158	public static function all_tokens( $order = 'id' )
159	{
160		$order = strtolower( $order );
161		if ( ( 'id' != $order ) && ( 'name' != $order ) && ( 'description' != $order ) ) {
162			$order = 'id';
163		}
164		$tokens = DB::get_results( 'SELECT id, name, description, token_group, token_type FROM {tokens} ORDER BY ' . $order );
165		return $tokens ? $tokens : array();
166	}
167
168	/**
169	 * Get a permission token's name by its ID
170	 * @param int $id a token ID
171	 * @return string the name of the permission, or boolean false
172	 */
173	public static function token_name( $id )
174	{
175		if ( ! is_int( $id ) ) {
176			return false;
177		}
178		else {
179			$tokens = ACL::cache_tokens();
180			return isset( $tokens[ $id ] ) ? $tokens[ $id ] : false;
181		}
182	}
183
184	/**
185	 * Get an associative array of token ids and their name.
186	 *
187	 * @return array an array in the form id => name
188	 */
189	private static function cache_tokens()
190	{
191		if ( ACL::$token_cache == null ) {
192			ACL::$token_cache = DB::get_keyvalue( 'SELECT id, name FROM {tokens}' );
193		}
194		return ACL::$token_cache;
195	}
196
197	/**
198	 * Get a permission token's ID by its name
199	 * @param string $name the name of the permission
200	 * @return int the permission's ID
201	 */
202	public static function token_id( $name )
203	{
204		if ( is_numeric( $name ) ) {
205			return intval( $name );
206		}
207		$name = self::normalize_token( $name );
208		if ( $token = array_search( $name, ACL::cache_tokens() ) ) {
209			return $token;
210		}
211		return false;
212	}
213
214	/**
215	 * Fetch a permission token's description from the DB
216	 * @param mixed $permission a permission name or ID
217	 * @return string the description of the permission
218	 */
219	public static function token_description( $permission )
220	{
221		if ( is_int( $permission ) ) {
222			$query = 'id';
223		}
224		else {
225			$query = 'name';
226			$permission = self::normalize_token( $permission );
227		}
228		return DB::get_value( "SELECT description FROM {tokens} WHERE $query=?", array( $permission ) );
229	}
230
231	/**
232	 * Determine whether a permission token exists
233	 * @param mixed $permission a permission name or ID
234	 * @return bool whether the permission exists or not
235	 */
236	public static function token_exists( $permission )
237	{
238		if ( is_numeric( $permission ) ) {
239			$query = 'id';
240		}
241		else {
242			$query = 'name';
243			$permission = self::normalize_token( $permission );
244		}
245		return ( (int) DB::get_value( "SELECT COUNT(id) FROM {tokens} WHERE $query=?", array( $permission ) ) > 0 );
246	}
247
248	/**
249	 * Determine whether a group can perform a specific action
250	 * @param mixed $group A group ID or name
251	 * @param mixed $token_id A permission token ID or name
252	 * @param string $access Check for 'create', 'read', 'update', 'delete', or 'full' access
253	 * @return bool Whether the group can perform the action
254	 */
255	public static function group_can( $group, $token_id, $access = 'full' )
256	{
257		$bitmask = self::get_group_token_access( $group, $token_id );
258
259		if ( isset( $bitmask ) && self::access_check( $bitmask, $access ) ) {
260			// the permission has been granted to this group
261			return true;
262		}
263		// either the permission hasn't been granted, or it's been
264		// explicitly denied.
265		return false;
266	}
267
268	/**
269	 * Determine whether a group is explicitly denied permission to perform a specific action
270	 * This function does not return true if the group is merely not granted a permission
271	 * @param mixed $user A group ID or a group name
272	 * @param mixed $token_id A permission ID or name
273	 * @return bool True if access to the token is denied to the group
274	 */
275	public static function group_cannot( $group, $token_id )
276	{
277
278		$result = self::get_group_token_access( $group, $token_id );
279		if ( isset( $result ) && self::access_check( $result, 'deny' ) ) {
280			return true;
281		}
282
283		// The permission has been granted, or it hasn't been explicitly denied.
284		return false;
285	}
286
287	/**
288	 * Determine whether a user can perform a specific action
289	 * @param mixed $user A user object, user ID or a username
290	 * @param mixed $token_id A permission ID or name
291	 * @param string $access Check for 'create', 'read', 'update', 'delete', or 'full' access
292	 * @return bool Whether the user can perform the action
293	 */
294	public static function user_can( $user, $token_id, $access = 'full' )
295	{
296
297		$result = self::get_user_token_access( $user, $token_id );
298
299		if ( isset( $result ) && self::access_check( $result, $access ) ) {
300			return true;
301		}
302
303		$super_user_access = self::get_user_token_access( $user, 'super_user' );
304		if ( isset( $super_user_access ) && self::access_check( $super_user_access, 'any' ) ) {
305			return true;
306		}
307
308		// either the permission hasn't been granted, or it's been
309		// explicitly denied.
310		return false;
311	}
312
313	/**
314	 * Determine whether a user is explicitly denied permission to perform a specific action
315	 * This function does not return true if the user is merely not granted a permission
316	 * @param mixed $user A User object, user ID or a username
317	 * @param mixed $token_id A permission ID or name
318	 * @return bool True if access to the token is denied to the user
319	 */
320	public static function user_cannot( $user, $token_id )
321	{
322
323		$result = self::get_user_token_access( $user, $token_id );
324		if ( isset( $result ) && self::access_check( $result, 'deny' ) ) {
325			return true;
326		}
327
328		// The permission has been granted, or it hasn't been explicitly denied.
329		return false;
330	}
331
332	/**
333	 * Return the access bitmask to a specific token for a specific user
334	 *
335	 * @param mixed $user A User object instance or user id
336	 * @param mixed $token_id A permission token name or token ID
337	 * @return integer An access bitmask
338	 */
339	public static function get_user_token_access( $user, $token )
340	{
341		// Use only numeric ids internally
342		$token_id = self::token_id( $token );
343
344		/**
345		 * Do we allow perms that don't exist?
346		 * When ACL is functional ACCESS_NONEXISTENT_PERMISSION should be false by default.
347		 */
348		if ( is_null( $token_id ) ) {
349			return self::get_bitmask( self::ACCESS_NONEXISTENT_PERMISSION );
350		}
351
352		// if we were given a user ID, use that to fetch the group membership from the DB
353		if ( is_numeric( $user ) ) {
354			$user_id = $user;
355		}
356		else {
357			// otherwise, make sure we have a User object, and get
358			// the groups from that
359			if ( ! $user instanceof User ) {
360				$user = User::get( $user );
361			}
362			$user_id = $user->id;
363		}
364
365		if ( defined( 'LOCKED_OUT_SUPER_USER' ) && $token == 'super_user' ) {
366			$su = User::get( LOCKED_OUT_SUPER_USER );
367			if ( $su->id == $user_id ) {
368				return new Bitmask( self::$access_names, 'read');
369			}
370		}
371
372		// check the cache first for the user's access_mask on the token
373		if ( isset( $_SESSION[ 'user_token_access' ][ $user_id ][ $token_id ] ) ) {
374//			Utils::debug($token, $_SESSION['user_token_access'][$token_id]);
375			if ( $_SESSION[ 'user_token_access' ][ $user_id ][ $token_id ] == ACL::CACHE_NULL ) {
376				return null;
377			}
378			else {
379				return self::get_bitmask( $_SESSION[ 'user_token_access' ][ $user_id ][ $token_id ] );
380			}
381		}
382
383		/**
384		 * Jay Pipe's explanation of the following SQL
385		 * 1) Look into user_permissions for the user and the token.
386		 * If exists, use that permission flag for the check. If not,
387		 * go to 2)
388		 *
389		 * 2) Look into the group_permissions joined to
390		 * users_groups for the user and the token.  Order the results
391		 * by the access bitmask. The lower the mask value, the
392		 * fewest permissions that group has. Use the first record's
393		 * access mask to check the ACL.
394		 *
395		 * This gives the system very fine grained control and grabbing
396		 * the permission flag and can be accomplished in a single SQL
397		 * call.
398		 */
399
400		$exceptions = '';
401		$default_groups = array();
402		$default_groups = Plugins::filter( 'user_default_groups', $default_groups, $user_id );
403		$default_groups = array_filter( array_map( 'intval', $default_groups ) );
404		switch ( count( $default_groups ) ) {
405			case 0: // do nothing
406				break;
407			case 1: // single argument
408				$exceptions = 'OR ug.group_id = ' . reset( $default_groups );
409				break;
410			default: // multiple arguments
411				$exceptions = 'OR ug.group_id IN (' . implode( ',', $default_groups ) . ')';
412				break;
413		}
414
415		$sql = <<<SQL
416SELECT access_mask
417	FROM {user_token_permissions}
418	WHERE user_id = ?
419	AND token_id = ?
420UNION ALL
421SELECT gp.access_mask
422	FROM {users_groups} ug
423	INNER JOIN {group_token_permissions} gp
424	ON ((ug.group_id = gp.group_id
425	AND ug.user_id = ?)
426	{$exceptions})
427	AND gp.token_id = ?
428	ORDER BY access_mask ASC
429SQL;
430
431		if ( $token_id == '' ) { $token_id = '0'; }
432
433		$accesses = DB::get_column( $sql, array( $user_id, $token_id, $user_id, $token_id ) );
434
435		$accesses = Plugins::filter( 'user_token_access', $accesses, $user_id, $token_id );
436
437		if ( count( $accesses ) == 0 ) {
438			$_SESSION[ 'user_token_access' ][ $user_id ][ $token_id ] = ACL::CACHE_NULL;
439			return null;
440		}
441		else {
442			$result = 0;
443			foreach ( (array) $accesses as $access ) {
444				if ( $access == 0 ) {
445					$result = 0;
446					break;
447				}
448				else {
449					$result |= $access;
450				}
451			}
452
453			$_SESSION[ 'user_token_access' ][ $user_id ][ $token_id ] = $result;
454			return self::get_bitmask( $result );
455		}
456	}
457
458	/**
459	 * Get all the tokens for a given user with a particular kind of access
460	 * @param mixed $user A user object, user ID or a username
461	 * @param string $access Check for 'create' or 'read', 'update', or 'delete' access
462	 * @return array of token IDs
463	 */
464	public static function user_tokens( $user, $access = 'full', $posts_only = false )
465	{
466		static $post_tokens = null;
467
468		$bitmask = new Bitmask ( self::$access_names );
469		$tokens = array();
470
471		// convert $user to an ID
472		if ( is_numeric( $user ) ) {
473			$user_id = $user;
474		}
475		else {
476			if ( ! $user instanceof User ) {
477				$user = User::get( $user );
478			}
479			$user_id = $user->id;
480		}
481
482		// Implement cache RIGHT HERE
483		if ( isset( $_SESSION[ 'user_tokens' ][ $user_id ][ $access ] ) ) {
484			return $_SESSION[ 'user_tokens' ][ $user_id ][ $access ];
485		}
486
487		$super_user_access = self::get_user_token_access( $user, 'super_user' );
488		if ( isset( $super_user_access ) && self::access_check( $super_user_access, 'any' ) ) {
489			$token_ids = DB::get_column( 'SELECT id as token_id FROM {tokens}' );
490			$result = array();
491			foreach ( $token_ids as $id ) {
492				$result_row = new StdClass();
493				$result_row->token_id = $id;
494				$result_row->access_mask = $bitmask->full;
495				$result[] = $result_row;
496			}
497		}
498		else {
499
500			$sql = <<<SQL
501SELECT token_id, access_mask
502	FROM {user_token_permissions}
503	WHERE user_id = :user_id
504UNION ALL
505SELECT gp.token_id, gp.access_mask
506	FROM {users_groups} ug
507	INNER JOIN {group_token_permissions} gp
508	ON ug.group_id = gp.group_id
509	AND ug.user_id = :user_id
510	ORDER BY token_id ASC
511SQL;
512			$result = DB::get_results( $sql, array( ':user_id' => $user_id ) );
513		}
514
515		if ( $posts_only && !isset( $post_tokens ) ) {
516			$post_tokens = DB::get_column( 'SELECT token_id FROM {post_tokens} GROUP BY token_id' );
517		}
518
519		foreach ( (array) $result as $token ) {
520			$bitmask->value = $token->access_mask;
521			if ( $access === 'deny' ) {
522				if ( $bitmask->value === 0 ) {
523					$tokens[] = $token->token_id;
524				}
525			}
526			elseif ( $bitmask->$access ) {
527				$tokens[] = $token->token_id;
528			}
529		}
530
531		if ( $posts_only ) {
532			$tokens = array_intersect( $tokens, $post_tokens );
533		}
534
535		$_SESSION[ 'user_tokens' ][ $user_id ][ $access ] = $tokens;
536		return $tokens;
537	}
538
539	/**
540	 * Get the access bitmask of a group for a specific permission token
541	 * @param integer $group The group ID
542	 * @param mixed $token_id A permission name or ID
543	 * @return an access bitmask
544	 */
545	public static function get_group_token_access( $group, $token_id )
546	{
547		// Use only numeric ids internally
548		$group = UserGroup::id( $group );
549		$token_id = self::token_id( $token_id );
550		$sql = 'SELECT access_mask FROM {group_token_permissions} WHERE
551			group_id=? AND token_id=?;';
552
553		$result = DB::get_value( $sql, array( $group, $token_id ) );
554
555		if ( isset( $result ) ) {
556			return self::get_bitmask( $result );
557		}
558		return null;
559	}
560
561	/**
562	 * Grant a permission to a group
563	 * @param integer $group_id The group ID
564	 * @param mixed $token_id The name or ID of the permission token to grant
565	 * @param string $access The kind of access to assign the group
566	 * @return Result of the DB query
567	 */
568	public static function grant_group( $group_id, $token_id, $access = 'full' )
569	{
570		$token_id = self::token_id( $token_id );
571		$results = DB::get_column( 'SELECT access_mask FROM {group_token_permissions} WHERE group_id=? AND token_id=?', array( $group_id, $token_id ) );
572		$access_mask = 0;
573		$row_exists = false;
574		if ( $results ) {
575			$row_exists = true;
576			if ( in_array( 0, $results ) ) {
577					$access_mask = 0;
578				}
579			else {
580				$access_mask = Utils::array_or( $results );
581			}
582		}
583
584		$bitmask = self::get_bitmask( $access_mask );
585		$orig_value = $bitmask->value;
586
587		if ( $access instanceof Bitmask ) {
588			$bitmask->value = $access->value;
589		}
590		elseif ( $access == 'full' ) {
591			$bitmask->value = $bitmask->full;
592		}
593		elseif ( $access == 'deny' ) {
594			$bitmask->value = 0;
595		}
596		else {
597			$bitmask->$access = true;
598		}
599
600		// Only update if the value is changed
601		if ( $orig_value != $bitmask->value || ( $orig_value == 0 && !$row_exists && $bitmask->value == 0 ) ) {
602			// DB::update will insert if the token is not already in the group tokens table
603			$result = DB::update(
604				'{group_token_permissions}',
605				array( 'access_mask' => $bitmask->value ),
606				array( 'group_id' => $group_id, 'token_id' => $token_id )
607			);
608			ACL::clear_caches();
609
610			$ug = UserGroup::get_by_id( $group_id );
611			$ug->clear_permissions_cache();
612			$msg = _t( 'Group %1$s: Access to %2$s changed to %3$s', array( $ug->name, ACL::token_name( $token_id ), $bitmask ) );
613			EventLog::log( $msg, 'notice', 'user', 'habari' );
614		}
615		else {
616			$result = true;
617		}
618
619		return $result;
620	}
621
622	/**
623	 * Grant a permission to a user
624	 * @param integer $user_id The user ID
625	 * @param integer $token_id The name or ID of the permission token to grant
626	 * @param string $access The kind of access to assign the group
627	 * @return Result of the DB query
628	 */
629	public static function grant_user( $user_id, $token_id, $access = 'full' )
630	{
631		$token_id = self::token_id( $token_id );
632		$access_mask = DB::get_value( 'SELECT access_mask FROM {user_token_permissions} WHERE user_id=? AND token_id=?',
633			array( $user_id, $token_id ) );
634		if ( $access_mask ===  false ) {
635			$permission_bit = 0; // default is 'deny' (bitmask 0)
636		}
637
638		$bitmask = self::get_bitmask( $access_mask );
639
640		if ( $access == 'full' ) {
641			$bitmask->value= $bitmask->full;
642		}
643		elseif ( $access == 'deny' ) {
644			$bitmask->value = 0;
645		}
646		else {
647			$bitmask->$access = true;
648		}
649
650		$result = DB::update(
651			'{user_token_permissions}',
652			array( 'access_mask' => $bitmask->value ),
653			array( 'user_id' => $user_id, 'token_id' => $token_id )
654		);
655
656		ACL::clear_caches();
657
658		return $result;
659	}
660
661	/**
662	 * Deny permission to a group
663	 * @param integer $group_id The group ID
664	 * @param mixed $token_id The name or ID of the permission token
665	 * @return Result of the DB query
666	 */
667	public static function deny_group( $group_id, $token_id )
668	{
669		self::grant_group( $group_id, $token_id, 'deny' );
670	}
671
672	/**
673	 * Deny permission to a user
674	 * @param integer $user_id The user ID
675	 * @param mixed $token_id The name or ID of the permission token
676	 * @return Result of the DB query
677	 */
678	public static function deny_user( $user_id, $token_id )
679	{
680		self::grant_user( $group_id, $token_id, 'deny' );
681	}
682
683	/**
684	 * Remove a permission token from the group permissions table
685	 * @param integer $group_id The group ID
686	 * @param mixed $token_id The name or ID of the permission token
687	 * @return the result of the DB query
688	 */
689	public static function revoke_group_token( $group_id, $token_id )
690	{
691		$token_id = self::token_id( $token_id );
692		$ug = UserGroup::get_by_id( $group_id );
693
694		$access = self::get_group_token_access( $group_id, $token_id );
695
696		if ( empty( $access ) ) {
697			$result = true;
698		}
699		else {
700			$result = DB::delete( '{group_token_permissions}',
701				array( 'group_id' => $group_id, 'token_id' => $token_id ) );
702			EventLog::log( _t( 'Group %1$s: Permission to %2$s revoked.', array( $ug->name, ACL::token_name( $token_id ) ) ), 'notice', 'user', 'habari' );
703		}
704
705		$ug->clear_permissions_cache();
706		ACL::clear_caches();
707
708		return $result;
709	}
710
711	/**
712	 * Remove a permission token from the user permissions table
713	 * @param integer $user_id The user ID
714	 * @param mixed $token_id The name or ID of the permission token
715	 * @return the result of the DB query
716	 */
717	public static function revoke_user_token( $user_id, $token_id )
718	{
719		$token_id = self::token_id( $token_id );
720		$result = DB::delete( '{user_token_permissions}',
721			array( 'user_id' => $user_id, 'token_id' => $token_id ) );
722
723		ACL::clear_caches();
724
725		return $result;
726	}
727
728	/**
729	 * Convert a token name into a valid format
730	 *
731	 * @param string $name The name of a permission
732	 * @return string The permission with spaces converted to underscores and all lowercase
733	 */
734	public static function normalize_token( $name )
735	{
736		return strtolower( preg_replace( '/\s+/u', '_', trim( $name ) ) );
737	}
738
739	/**
740	 * Clears all caches used to hold permissions
741	 *
742	 */
743	public static function clear_caches()
744	{
745		if ( isset( $_SESSION[ 'user_token_access' ] ) ) {
746			unset( $_SESSION[ 'user_token_access' ] );
747
748		}
749		if ( isset( $_SESSION[ 'user_tokens' ] ) ) {
750			unset( $_SESSION[ 'user_tokens' ] );
751		}
752		self::$token_cache = null;
753	}
754
755	/**
756	 * Creates the default set of permissions.
757	 */
758	public static function create_default_tokens()
759	{
760		// super user token
761		self::create_token( 'super_user', 'Permissions for super users', 'Super User' );
762
763		// admin tokens
764		self::create_token( 'manage_all_comments', _t( 'Manage comments on all posts' ), 'Administration' );
765		self::create_token( 'manage_own_post_comments', _t( 'Manage comments on one\'s own posts' ), 'Administration' );
766		self::create_token( 'manage_tags', _t( 'Manage tags' ), 'Administration' );
767		self::create_token( 'manage_options', _t( 'Manage options' ), 'Administration' );
768		self::create_token( 'manage_theme', _t( 'Change theme' ), 'Administration' );
769		self::create_token( 'manage_theme_config', _t( 'Configure the active theme' ), 'Administration' );
770		self::create_token( 'manage_plugins', _t( 'Activate/deactivate plugins' ), 'Administration' );
771		self::create_token( 'manage_plugins_config', _t( 'Configure active plugins' ), 'Administration' );
772		self::create_token( 'manage_import', _t( 'Use the importer' ), 'Administration' );
773		self::create_token( 'manage_users', _t( 'Add, remove, and edit users' ), 'Administration' );
774		self::create_token( 'manage_self', _t( 'Edit own profile' ), 'Administration' );
775		self::create_token( 'manage_groups', _t( 'Manage groups and permissions' ), 'Administration' );
776		self::create_token( 'manage_logs', _t( 'Manage logs' ), 'Administration' );
777		self::create_token( 'manage_dash_modules', _t( 'Manage dashboard modules' ), 'Administration' );
778
779		// content tokens
780		self::create_token( 'own_posts', _t( 'Permissions on one\'s own posts' ), _t( 'Content' ), true );
781		self::create_token( 'post_any', _t( 'Permissions to all posts' ), _t( 'Content' ), true );
782		self::create_token( 'post_unpublished', _t( "Permissions to other users' unpublished posts" ), _t( 'Content' ), true );
783		foreach ( Post::list_active_post_types() as $name => $posttype ) {
784			self::create_token( 'post_' . Utils::slugify( $name ), _t( 'Permissions to posts of type "%s"', array( $name ) ), _t( 'Content' ), true );
785		}
786
787		// comments tokens
788		self::create_token( 'comment', 'Make comments on any post', _t( 'Comments' ) );
789	}
790
791	/**
792	 * Reset premissions to their default state
793	 */
794	public static function rebuild_permissions( $user = null )
795	{
796		// Clear out all permission-related values
797		DB::query( 'DELETE FROM {tokens}' );
798		DB::query( 'DELETE FROM {group_token_permissions}' );
799		//DB::query( 'DELETE FROM {groups}' );
800		DB::query( 'DELETE FROM {post_tokens}' );
801		DB::query( 'DELETE FROM {user_token_permissions}' );
802		//DB::query('DELETE FROM {users_groups}');
803
804		// Create initial groups if they don't already exist
805		$admin_group = UserGroup::get_by_name( _t( 'admin' ) );
806		if ( ! $admin_group instanceof UserGroup ) {
807			$admin_group = UserGroup::create( array( 'name' => _t( 'admin' ) ) );
808		}
809
810		$anonymous_group = UserGroup::get_by_name( _t( 'anonymous' ) );
811		if ( ! $anonymous_group instanceof UserGroup ) {
812			$anonymous_group = UserGroup::create( array( 'name' => _t( 'anonymous' ) ) );
813		}
814
815		// Add all users or the passed user to the admin group
816		if ( empty($user) ) {
817			$users = Users::get_all();
818			$ids = array();
819			foreach ( $users as $user ) {
820				$ids[] = $user->id;
821			}
822			$admin_group->add( $ids );
823		}
824		else {
825			$admin_group->add( $user );
826		}
827
828		// create default permissions
829		self::create_default_tokens();
830		// Make the admin group all superusers
831		$admin_group->grant( 'super_user' );
832		// Add entry and page read access to the anonymous group
833		$anonymous_group->grant( 'post_entry', 'read' );
834		$anonymous_group->grant( 'post_page', 'read' );
835		$anonymous_group->grant( 'comment' );
836
837		// Add the anonymous user to the anonymous group
838		$anonymous_group->add( 0 );
839
840		// Create the default authenticated group
841		$authenticated_group = UserGroup::get_by_name( _t( 'authenticated' ) );
842		if ( ! $authenticated_group instanceof UserGroup ) {
843			$authenticated_group = UserGroup::create( array( 'name' => _t( 'authenticated' ) ) );
844		}
845		$authenticated_group->grant( 'post_entry', 'read' );
846		$authenticated_group->grant( 'post_page', 'read' );
847		$authenticated_group->grant( 'comment' );
848
849	}
850
851	/**
852	 * Dummy function to inject strings into the .pot
853	 */
854	private static function translations() {
855		// @locale The names of the CRUD group token permissions
856		_t( 'read' ); _t( 'edit' ); _t( 'delete' ); _t( 'create' );
857	}
858
859}
860?>
861