PageRenderTime 66ms CodeModel.GetById 7ms RepoModel.GetById 10ms app.codeStats 0ms

/system/classes/acl.php

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