Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
system_apply_permissions.php
1 <?php
27 error_reporting(E_ALL);
28 if (ini_get('memory_limit') != '-1') ini_set('memory_limit', '-1');
29 if ((php_sapi_name() != 'cli')) {
30  trigger_error("You can only run this script from the command line\n", E_USER_ERROR);
31 }//end if
32 
33 $config = array();
34 
35 define('LOG_FILE_NAME', 'set_permissions.log');
36 
37 if (count($argv) < 4) {
38  usage();
39  exit();
40 }//end if
41 
42 // Get config from command line args.
43 process_args($config);
44 
45 require_once $config['system_root'].'/core/include/init.inc';
46 require_once SQ_INCLUDE_PATH.'/assertions.inc';
47 
48 define('LOG_FILE', SQ_SYSTEM_ROOT.'/data/private/logs/'.LOG_FILE_NAME); // This is the log file
49 define('SYNCH_FILE', SQ_TEMP_PATH.'/set_permissions.assetid'); // We need this file to store the assetids
50 
51 // Check perms.
52 check_permissions_args($config['permissions']);
53 
54 // Replace space with empty string
55 $assetids = preg_replace('/[\s]*/', '', $config['assetids']);
56 
57 // Explode them so we have the list in array
58 $rootnodes = getRootNodes($assetids);
59 
60 _disconnectFromMatrixDatabase();
61 $pid_prepare = pcntl_fork();
62  switch ($pid_prepare) {
63  case -1:
64  break;
65  case 0:
66  // Connect to DB within the child process
67  _connectToMatrixDatabase();
68 
69  $root_user = $GLOBALS['SQ_SYSTEM']->am->getSystemAsset('root_user');
70 
71  // This ridiculousness allows us to workaround Oracle, forking and CLOBs
72  // if a query is executed that returns more than 1 LOB before a fork occurs,
73  // the Oracle DB connection will be lost inside the fork.
74  // In this case, because a user asset has more than 1 attribute and custom_val in sq_ast_attr_val
75  // is of type CLOB, we attempt to check the root password inside our forked process.
76  // log in as root
77  if (!$GLOBALS['SQ_SYSTEM']->setCurrentUser($root_user)) {
78  echo "Failed logging in as root user\n";
79  exit(1);
80  }//end if
81 
82  $children = $rootnodes; // Start off with the root nodes.
83  foreach ($rootnodes as $rootnode_id) {
84  $children += array_merge($children, array_keys(($GLOBALS['SQ_SYSTEM']->am->getChildren($rootnode_id))));
85 
86  }//end foreach
87 
88  // Save the list into a file so we can access the list from the parent process
89  file_put_contents(SYNCH_FILE, implode(',', $children));
90 
91  // Disconnect from DB
92  _disconnectFromMatrixDatabase();
93 
94  exit(0);
95  // waiting for child exit signal
96  $status = null;
97  pcntl_waitpid(-1, $status);
98 
99  break;
100  default:
101  $status = null;
102  pcntl_waitpid(-1, $status);
103  break;
104  }//end switch
105 
106 $children = Array();
107 if (file_exists(SYNCH_FILE)) {
108  $children_str = file_get_contents(SYNCH_FILE);
109 } else {
110  echo "Unable to find Synch File, probably because the root user was not able to log in, or the user executing this script does not have permission to write to this folder.\n";
111  exit(0);
112 }//end else
113 
114 $children = explode(',', $children_str);
115 $children = array_unique($children); // We are only generate metadata for each asset once, despite they might be linked in different plaecs
116 
117 // Chunk them up so we can process each batch when forking
118 $chunk_children = array_chunk($children, $config['batch_size']);
119 $current_child_list = Array();
120 
121 log_to_file('======================= Start applying permissions '.date('d-m-Y h:i:s').' =======================', LOG_FILE);
122 log_to_file("Applying permissions for: " . var_export(count($children),TRUE) . " assets \n", LOG_FILE);
123 
124  $fork_num = 0; // Determine how many child process we have forked
125  while (!empty($chunk_children)) {
126  $current_child_list = array_pop($chunk_children);
127  $current_remaining = count($current_child_list);
128  $pid = pcntl_fork();
129  $fork_num++;
130  switch ($pid) {
131  case -1:
132  trigger_error('Process failed to fork', E_USER_ERROR);
133  exit(1);
134  break;
135  case 0:
136  // Connect to DB within the child process
137  _connectToMatrixDatabase();
138  $GLOBALS['SQ_SYSTEM']->setRunLevel(SQ_RUN_LEVEL_FORCED);
139 
140  $conn_id = MatrixDAL::getCurrentDbId();
141 
142  foreach ($current_child_list as $child_assetid) {
143  $child_asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($child_assetid);
144  if (!$GLOBALS['SQ_SYSTEM']->am->acquireLock($child_assetid, 'permissions')) {
145  log_to_file('Unable to acquire permissions lock for assetid ' .$child_assetid.'. Skipping this asset.', LOG_FILE);
146  continue;
147  }//end if
148 
149  if (!$child_asset->writeAccess('permissions')) {
150  log_to_file('Do not have write access for assetid ' .$child_assetid .'. Skipping this asset.', LOG_FILE);
151  $GLOBALS['SQ_SYSTEM']->am->releaseLock($child_assetid, 'permissions');
152  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
153  continue;
154  }//end if
155 
156  // For each permission setting in the permissions string
157  foreach ($config['permissions'] as $p) {
158  if ($p['granted'] == '/') {
159  if (!$GLOBALS['SQ_SYSTEM']->am->deletePermission($child_assetid, $p['userid'], $p['type'])) {
160  log_to_file('Failed deleting permission for assetid ' . $child_assetid . '.', LOG_FILE);
161  continue;
162  }
163  } else {
164  $permission_granted = (($p['granted'] == '+') ? TRUE : FALSE);
165  if (!$GLOBALS['SQ_SYSTEM']->am->setPermission($child_assetid, $p['userid'], $p['type'], $permission_granted, $config['cascades'], $config['force'])) {
166  log_to_file('Failed setting permission for assetid ' . $child_assetid . '.', LOG_FILE);
167  continue;
168  }
169  }
170  }//end foreach
171 
172  log_to_file('Applied permissions for child assetid '.$child_assetid, LOG_FILE);
173 
174  $current_remaining--;
175  echo '-- Remaining: ' . (string) ($current_remaining + (count($chunk_children) * $config['batch_size'])) . "\n";
176 
177  $GLOBALS['SQ_SYSTEM']->am->releaseLock($child_assetid, 'permissions');
178  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($child_asset);
179  $child_asset = NULL;
180  unset($child_asset);
181 
182  }//end foreach
183 
184  $GLOBALS['SQ_SYSTEM']->restoreRunLevel();
185  // Disconnect from DB
186  _disconnectFromMatrixDatabase();
187 
188  exit(0);
189  // waiting for child exit signal
190  $status = null;
191  pcntl_waitpid(-1, $status);
192 
193  break;
194  default:
195  // We only want to fork a maximum number of child process, so if we've already reached the max num, sit and wait
196  if ($fork_num >= $config['max_threads']) {
197  $status = null;
198  pcntl_waitpid(-1, $status);
199  $fork_num--;
200  }//end if
201 
202  if (empty($chunk_children)) {
203  // We wait for all the fork child to finish
204  while ($fork_num > 0) {
205  $status = null;
206  pcntl_waitpid(-1, $status);
207  $fork_num--;
208  }//end
209  }//end if
210 
211  break;
212 
213  }//end switch & thread
214  //}//end foreach
215  }//end while
216 
217 
218  log_to_file('======================= Finished Applying Permissions '.date('d-m-Y h:i:s').' =======================', LOG_FILE);
219  if (file_exists(SYNCH_FILE)) {
220  unlink(SYNCH_FILE);
221  }//end if
222  exit(0);
223 
224 
225 
226 
232 function usage() {
233  echo "\n";
234  echo "Usage: php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> \"<permission_strings>\"\n";
235  echo " php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> \"<permission_strings>\" [-nc] [-f]\n";
236  echo " php {$_SERVER['argv'][0]} <system_root> <assetid[,assetid]> \"<permission_strings>\" [--max-threads <max_threads>] [--batch-size <batch_size>]\n";
237  echo "\n";
238  echo " <permission_strings> A string representing the permissions to change.\n";
239  echo " example: 'r+101;w-102;a/103' => add read permission to User #101,\n";
240  echo " deny write permission to user #102 and remove admin permission for user #103.\n";
241  echo " -nc No cascade. Don't set the cascade flag, which allows new child assets to inherit the permissions.\n";
242  echo " -f Force permissions, even if the permissions are already applied or granted/denied are in conflict.\n";
243  echo " --max-threads Maximum concurrency. Default is 3, max allowed is 5.\n";
244  echo " --batch-size Default is 50.\n";
245  echo "\n";
246  echo "Results are logged to data/private/logs/".LOG_FILE_NAME."\n";
247 
248 }//end usage()
249 
250 
257 function process_args(&$config) {
258 
259  $config['system_root'] = (isset($_SERVER['argv'][1])) ? $_SERVER['argv'][1] : '';
260  if (empty($config['system_root'])) {
261  echo "ERROR: You need to supply the path to the System Root as the first argument\n";
262  exit();
263  }
264 
265  if (!is_dir($config['system_root']) || !is_readable($config['system_root'].'/core/include/init.inc')) {
266  echo "ERROR: Path provided doesn't point to a Matrix installation's System Root. Please provide correct path and try again.\n";
267  exit();
268  }
269 
270  $config['assetids'] = (isset($_SERVER['argv'][2])) ? $_SERVER['argv'][2] : '';
271  if (empty($config['assetids'])) {
272  echo "ERROR: You need to specify the root nodes to apply the schema from as the second argument\n";
273  exit();
274  }//end if
275 
276  $config['permissions_string'] = (isset($_SERVER['argv'][3])) ? $_SERVER['argv'][3] : '';
277  if (empty($config['permissions_string'])) {
278  echo "ERROR: You need to specify a permissions string as the third argument\n";
279  exit();
280  }//end if
281 
282  // Permissions string
283  $config['permissions'] = array();
284  $config['permission_types'] = Array('r' => 1, 'w' => 2, 'a' => 3);
285 
286  $perms = split(';', $config['permissions_string'].';');
287  foreach ($perms as $p) {
288  if (!empty($p)) {
289  array_push($config['permissions'], Array(
290  'type' => (int) $config['permission_types'][$p{0}],
291  'granted' => $p{1},
292  'userid' => substr($p, 2),
293  ));
294  }
295  }
296 
297  $config['cascades'] = !get_boolean_arg('-nc'); // Invert.. -nc means no cascade.
298  $config['force'] = get_boolean_arg('-f');
299 
300  $config['max_threads'] = (int) get_parameterised_arg('--max-threads', 3);
301  if ($config['max_threads'] > 5) {
302  $config['max_threads'] = 5;
303  } elseif ($config['max_threads'] < 1) {
304  $config['max_threads'] = 1;
305  }//end if
306 
307  $config['batch_size'] = (int) get_parameterised_arg('--batch-size', 50);
308  if ($config['batch_size'] < 5) {
309  $config['batch_size'] = 50;
310  }
311 
312  echo "\n";
313  echo "Will attempt to set permissions to asset(s) {$config['assetids']}.\n\n";
314  echo " Max threads: {$config['max_threads']}\n";
315  echo " Batch size: {$config['batch_size']}\n";
316  echo "\n";
317 
318 }//end function processArgs()
319 
320 
321 function check_permissions_args($permissions) {
322  foreach ($permissions as $p) {
323  if (($p['type'] > 3) || ($p['type'] < 1)) {
324  trigger_error("Invalid permission type in permissions string.\n", E_USER_ERROR);
325  }
326 
327  if (($p['granted'] != '+') && ($p['granted'] != '-') && ($p['granted'] != '/')) {
328  trigger_error("Invalid granted flag in permissions string.\n", E_USER_ERROR);
329  }
330 
331  if (!assert_valid_assetid($p['userid'])) {
332  trigger_error("Invalid userid in permissions string.\n", E_USER_ERROR);
333  }
334  }
335 }
336 
343 function get_boolean_arg($arg) {
344  $key = array_search($arg, $_SERVER['argv']);
345  if ($key) {
346  return TRUE;
347  } else {
348  return FALSE;
349  }//end if
350 
351 }//end get_boolean_arg()
352 
353 
361 function get_parameterised_arg($arg, $default) {
362  $key = array_search($arg, $_SERVER['argv']);
363  // Look for the parameter to this arg
364  if (($key) && (isset($_SERVER['argv'][$key + 1]))) {
365  return $_SERVER['argv'][$key + 1];
366  } else {
367  return $default;
368  }
369 
370 }//end get_parameterised_arg()
371 
372 
381 function get_line($prompt='')
382 {
383  echo $prompt;
384  // now get their entry and remove the trailing new line
385  return rtrim(fgets(STDIN, 4096));
386 
387 }//end get_line()
388 
389 
396 function _disconnectFromMatrixDatabase()
397 {
398  $conn_id = MatrixDAL::getCurrentDbId();
399  if (isset($conn_id) && !empty($conn_id)) {
401  MatrixDAL::dbClose($conn_id);
402  }//end if
403 
404 }//end _disconnectFromMatrixDatabase()
405 
406 
413 function _connectToMatrixDatabase()
414 {
415  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
416 
417 }//end _connectToMatrixDatabase()
418 
419 
424 function log_to_file($content, $file_name="set_metadata_schema.log")
425 {
426  file_put_contents($file_name, '['.date('d-m-Y h:i:s').'] '.$content."\n", FILE_APPEND);
427 
428 }//end log_to_file();
429 
430 
431 function getRootNodes($action)
432 {
433  $rootnodes = explode(',', $action);
434 
435  // Check if each of these rootnodes exists in the system
436  $rootnodes_exists = $GLOBALS['SQ_SYSTEM']->am->assetExists($rootnodes);
437 
438  $not_exists = array_diff($rootnodes, $rootnodes_exists);
439  if (!empty($not_exists)) {
440  $list_not_exists = implode(', ', $not_exists);
441  echo "These rootnode ids do not exists in the system: $list_not_exists \n";
442  exit();
443  }//end if
444 
445  return $rootnodes;
446 }//end getRootNodes()
447 
448 ?>