1 | <?php |
---|
2 | require_once(GALAXIA_LIBRARY.SEP.'src'.SEP.'common'.SEP.'Base.php'); |
---|
3 | |
---|
4 | /** |
---|
5 | * Handles most security issues in the engine |
---|
6 | * |
---|
7 | * @package Galaxia |
---|
8 | * @license http://www.gnu.org/copyleft/gpl.html GPL |
---|
9 | */ |
---|
10 | class WfSecurity extends Base { |
---|
11 | |
---|
12 | /** |
---|
13 | * @var array processes config values cached for this object life duration, init is done at first use for each process |
---|
14 | * @access public |
---|
15 | */ |
---|
16 | var $processesConfig= Array(); |
---|
17 | |
---|
18 | /** |
---|
19 | * Constructor |
---|
20 | * |
---|
21 | * @param object &$db ADOdb |
---|
22 | * @return object WfSecurity |
---|
23 | * @access public |
---|
24 | */ |
---|
25 | function WfSecurity() |
---|
26 | { |
---|
27 | $this->child_name = 'WfSecurity'; |
---|
28 | parent::Base(); |
---|
29 | } |
---|
30 | |
---|
31 | /** |
---|
32 | * Loads config values for a given process. |
---|
33 | * Config values for a given process are cached while this WfSecurity object stay alive |
---|
34 | * |
---|
35 | * @param int $pId Process id |
---|
36 | * @access private |
---|
37 | * @return void |
---|
38 | */ |
---|
39 | function loadConfigValues($pId) |
---|
40 | { |
---|
41 | //check if we already have the config values for this processId |
---|
42 | if (!(isset($this->processesConfig[$pId]))) |
---|
43 | { |
---|
44 | //define conf values we need |
---|
45 | $arrayConf=array( |
---|
46 | 'ownership_give_abort_right' =>1, |
---|
47 | 'ownership_give_exception_right' =>1, |
---|
48 | 'ownership_give_release_right' =>1, |
---|
49 | 'role_give_abort_right' =>0, |
---|
50 | 'role_give_release_right' =>0, |
---|
51 | 'role_give_exception_right' =>0, |
---|
52 | 'disable_advanced_actions' =>0, |
---|
53 | 'iframe_view_height' =>-1, |
---|
54 | 'execute_activities_using_secure_connection' =>0 |
---|
55 | ); |
---|
56 | //check theses values for this process and store the result for this object life duration |
---|
57 | $myProcess = &Factory::newInstance('Process'); |
---|
58 | $myProcess->getProcess($pId); |
---|
59 | $this->processesConfig[$pId] = $myProcess->getConfigValues($arrayConf); |
---|
60 | unset($myProcess); |
---|
61 | } |
---|
62 | } |
---|
63 | |
---|
64 | /** |
---|
65 | * Checks if a user has a access to an activity, use it at runtime. |
---|
66 | * To do so it checks if the user is in the users having the roles associated with the activity |
---|
67 | * or if he is in the groups having roles associated with the activity |
---|
68 | * |
---|
69 | * @access public |
---|
70 | * @param int $user User id |
---|
71 | * @param int $activityId Activity id |
---|
72 | * @param bool $readonly False by default. If true we only check read-only access level for the user on this activity |
---|
73 | * @return bool True if access is granted false in other case. Errors are stored in the object |
---|
74 | */ |
---|
75 | function checkUserAccess($user, $activity_id, $readonly=false) |
---|
76 | { |
---|
77 | /* if activity is non-interactive is not necessary checking user access */ |
---|
78 | $activity = &Factory::getInstance('BaseActivity')->getActivity($activity_id); |
---|
79 | if (!$activity->isInteractive()) |
---|
80 | return true; |
---|
81 | |
---|
82 | //group mapping, warning groups and user can have the same id |
---|
83 | if ($user[0] != 'p') |
---|
84 | $groups = galaxia_retrieve_user_groups($user); |
---|
85 | else |
---|
86 | $groups = false; |
---|
87 | |
---|
88 | $query = 'select count(*) from '.GALAXIA_TABLE_PREFIX.'activity_roles gar |
---|
89 | INNER JOIN '.GALAXIA_TABLE_PREFIX.'roles gr ON gar.wf_role_id=gr.wf_role_id |
---|
90 | INNER JOIN '.GALAXIA_TABLE_PREFIX.'user_roles gur ON gur.wf_role_id=gr.wf_role_id |
---|
91 | where gar.wf_activity_id=? |
---|
92 | and ( (gur.wf_user=? and gur.wf_account_type=?)'; |
---|
93 | if (is_array($groups)) |
---|
94 | { |
---|
95 | foreach ($groups as &$group) |
---|
96 | $group = "'{$group}'"; |
---|
97 | $query .= ' or (gur.wf_user in ('.implode(',',$groups).") and gur.wf_account_type='g')"; |
---|
98 | } |
---|
99 | |
---|
100 | $query .= " or ('p'||gur.wf_role_id = '$user')"; |
---|
101 | |
---|
102 | $query .= ')'; |
---|
103 | |
---|
104 | if (!($readonly)) |
---|
105 | { |
---|
106 | $query.= 'and NOT(gar.wf_readonly=1)'; |
---|
107 | } |
---|
108 | $result= $this->getOne($query ,array($activity_id, $user, 'u')); |
---|
109 | if ($result) |
---|
110 | { |
---|
111 | //echo "<br>Access granted for ".$user; |
---|
112 | return true; |
---|
113 | } |
---|
114 | else |
---|
115 | { |
---|
116 | $this->error[]= tra('Access denied for user %1 on activity %2, no role', $user, $activity_id); |
---|
117 | return false; |
---|
118 | } |
---|
119 | } |
---|
120 | |
---|
121 | /** |
---|
122 | * Checks at RUNTIME whether running user is authorized for a given action on some activity/instance. |
---|
123 | * This function will check the given action for the current running user, lock the table rows if necessary to ensure |
---|
124 | * nothing will move from another process between the check and the later action. |
---|
125 | * loacked tables can be instances and instance-activities. |
---|
126 | * NOTA BENE: there is no lock on activity/processes table, we assume the admin is not changing the activity data |
---|
127 | * on a running/production process, this is why there is versioning and activation on processes |
---|
128 | * Encapsulate this function call in a transaction, locks will be removed at the end of the transaction, whent COMMIT |
---|
129 | * or ROLLBACK will be launched |
---|
130 | * |
---|
131 | * @param int $activityId Activity id, it may be 0 |
---|
132 | * @param int $instanceId InstanceId, it may be 0 |
---|
133 | * @param string $action ONE action asked, it must be one of: 'grab', 'release', 'exception', 'resume', 'abort', 'run', 'send', 'view', 'viewrun', 'complete' (internal action before completing), 'restart' admin function, restarting a failed automatic activity |
---|
134 | * be carefull, View can be done in 2 ways: viewrun : by the view activity if the process has a view activity, and only by this way in such case, view: by a general view form with access to everybody if the process has no view activity |
---|
135 | * @return bool True if action access is granted false in other case. Errors are stored in the object |
---|
136 | * @access public |
---|
137 | */ |
---|
138 | function checkUserAction($activityId, $instanceId,$action) |
---|
139 | { |
---|
140 | //Warning: |
---|
141 | //start and standalone activities have no instances associated |
---|
142 | //aborted and completed instances have no activities associated |
---|
143 | |
---|
144 | //$this->error[] = 'DEBUG: action:'.$action; |
---|
145 | if ($action!='run' && $action!='send' && $action!='view' && $action!='viewrun' && $action!='complete' && $action!='grab' && $action!='release' && $action!='exception' && $action!='resume' && $action!='abort' && $action!='restart') |
---|
146 | { |
---|
147 | $this->error[] = tra('Security check: Cannot understand asked action'); |
---|
148 | return false; |
---|
149 | } |
---|
150 | |
---|
151 | $user = galaxia_retrieve_running_user(); |
---|
152 | //$this->error[] = 'DEBUG: running user:'.$user; |
---|
153 | if ( (!(isset($user))) || (empty($user)) ) |
---|
154 | { |
---|
155 | $this->error[] = tra('Cannot retrieve the user running the security check'); |
---|
156 | return false; |
---|
157 | } |
---|
158 | |
---|
159 | //0 - prepare RowLocks ---------------------------------------------------------- |
---|
160 | $lock_instance_activities = false; |
---|
161 | $lock_instances = false; |
---|
162 | switch($action) |
---|
163 | { |
---|
164 | case 'view': |
---|
165 | case 'viewrun': |
---|
166 | //no impact on write mode, no lock |
---|
167 | break; |
---|
168 | case 'grab': |
---|
169 | //impacted tables is instance_activities |
---|
170 | $lock_instance_activities = true; |
---|
171 | break; |
---|
172 | case 'release' : |
---|
173 | //impacted tables is instance_activities |
---|
174 | $lock_instance_activities = true; |
---|
175 | break; |
---|
176 | case 'exception': |
---|
177 | //impacted tables is instances |
---|
178 | $lock_instances = true; |
---|
179 | break; |
---|
180 | case 'resume': |
---|
181 | //impacted tables is instances |
---|
182 | $lock_instances = true; |
---|
183 | break; |
---|
184 | case 'abort': |
---|
185 | //impacted tables are instances and instance_activities (deleting rows) |
---|
186 | $lock_instance_activities = true; |
---|
187 | $lock_instances = true; |
---|
188 | break; |
---|
189 | case 'run': |
---|
190 | //impacted tables is instance_activities (new running user) |
---|
191 | $lock_instance_activities = true; |
---|
192 | break; |
---|
193 | case 'send': |
---|
194 | //impacted tables is instance_activities (deleting/adding row) |
---|
195 | $lock_instance_activities = true; |
---|
196 | break; |
---|
197 | case 'complete': |
---|
198 | //impacted tables are instances and instance_activities |
---|
199 | $lock_instance_activities = true; |
---|
200 | $lock_instances = true; |
---|
201 | break; |
---|
202 | case 'restart': |
---|
203 | //nothing to do, it will be done by the run part. |
---|
204 | break; |
---|
205 | } |
---|
206 | // no lock on instance_activities without a lock on instances |
---|
207 | // to avoid changing status of an instance or deletion of an instance while impacting instance_activities |
---|
208 | if ($lock_instance_activities) $lock_instances = true; |
---|
209 | |
---|
210 | //1 - load data ----------------------------------------------------------------- |
---|
211 | $_no_activity=false; |
---|
212 | $_no_instance=false; |
---|
213 | |
---|
214 | //retrieve some activity datas and process data |
---|
215 | if ($activityId==0) |
---|
216 | { |
---|
217 | $_no_activity = true; |
---|
218 | } |
---|
219 | else |
---|
220 | { |
---|
221 | $query = 'select ga.wf_activity_id, ga.wf_type, ga.wf_is_interactive, ga.wf_is_autorouted, |
---|
222 | gp.wf_name as wf_procname, gp.wf_is_active, gp.wf_version, gp.wf_p_id |
---|
223 | from '.GALAXIA_TABLE_PREFIX.'activities ga |
---|
224 | INNER JOIN '.GALAXIA_TABLE_PREFIX.'processes gp ON gp.wf_p_id=ga.wf_p_id |
---|
225 | where ga.wf_activity_id = ?'; |
---|
226 | $result = $this->query($query, array($activityId)); |
---|
227 | $resactivity = Array(); |
---|
228 | if (!!$result) |
---|
229 | { |
---|
230 | $resactivity = $result->fetchRow(); |
---|
231 | $pId = $resactivity['wf_p_id']; |
---|
232 | //DEBUG |
---|
233 | //$debugactivity = implode(",",$resactivity); |
---|
234 | //$this->error[] = 'DEBUG: '. date("[d/m/Y h:i:s]").'activity:'.$debugactivity; |
---|
235 | } |
---|
236 | if (count($resactivity)==0) |
---|
237 | { |
---|
238 | $_no_activity = true; |
---|
239 | } |
---|
240 | } |
---|
241 | |
---|
242 | //retrieve some instance and process data (need process data here as well if there is no activity) |
---|
243 | if ($instanceId==0) |
---|
244 | { |
---|
245 | $_no_instance = true; |
---|
246 | } |
---|
247 | else |
---|
248 | { |
---|
249 | if ($lock_instances) |
---|
250 | { |
---|
251 | //we need to make a row lock now, before any read action |
---|
252 | $where = 'wf_instance_id='.(int)$instanceId; |
---|
253 | //$this->error[]= '<br> Debug:locking instances '.$where; |
---|
254 | if (!($this->db->RowLock(GALAXIA_TABLE_PREFIX.'instances', $where))) |
---|
255 | { |
---|
256 | $this->error[] = tra('failed to obtain lock on %1 table', 'instances'); |
---|
257 | return false; |
---|
258 | } |
---|
259 | } |
---|
260 | $query = 'select gi.wf_instance_id, gi.wf_owner, gi.wf_status, |
---|
261 | gp.wf_name as wf_procname, gp.wf_is_active, gp.wf_version, gp.wf_p_id |
---|
262 | from '.GALAXIA_TABLE_PREFIX.'instances gi |
---|
263 | INNER JOIN '.GALAXIA_TABLE_PREFIX.'processes gp ON gp.wf_p_id=gi.wf_p_id |
---|
264 | where gi.wf_instance_id=?'; |
---|
265 | $result = $this->query($query,array($instanceId)); |
---|
266 | if (!!$result) |
---|
267 | { |
---|
268 | |
---|
269 | $resinstance = $result->fetchRow(); |
---|
270 | $pId = $resinstance['wf_p_id']; |
---|
271 | //DEBUG |
---|
272 | //$debuginstance = implode(",",$resinstance); |
---|
273 | //$this->error[] = 'DEBUG: '. date("[d/m/Y h:i:s]").'instance:'.$debuginstance; |
---|
274 | } |
---|
275 | if (count($resinstance)==0) |
---|
276 | { |
---|
277 | $_no_instance = true; |
---|
278 | } |
---|
279 | } |
---|
280 | |
---|
281 | if ($_no_activity && $_no_instance) |
---|
282 | { |
---|
283 | $this->error[] = tra('Action %1 is impossible if we have no activity and no instance designated for it!',$action); |
---|
284 | return false; |
---|
285 | } |
---|
286 | |
---|
287 | //retrieve some instance/activity data |
---|
288 | //if no_activity or no_instance we are with out-flow/without instances activities or with instances terminated |
---|
289 | //we would not obtain anything there |
---|
290 | if (!($_no_activity || $_no_instance)) |
---|
291 | { |
---|
292 | if ($lock_instance_activities) |
---|
293 | { |
---|
294 | //we need to lock this row now, before any read action |
---|
295 | $where = 'wf_instance_id='.(int)$instanceId.' and wf_activity_id='.(int)$activityId; |
---|
296 | //$this->error[] = '<br> Debug:locking instance_activities '.$where; |
---|
297 | if (!($this->db->RowLock(GALAXIA_TABLE_PREFIX.'instance_activities', $where))) |
---|
298 | { |
---|
299 | if ($this->db->getOne('SELECT 1 FROM ' . GALAXIA_TABLE_PREFIX . 'instance_activities WHERE ' . $where)) |
---|
300 | { |
---|
301 | $this->error[] = tra('failed to obtain lock on %1 table','instances_activities'); |
---|
302 | return false; |
---|
303 | } |
---|
304 | else |
---|
305 | { |
---|
306 | $this->error[] = tra("This instance doesn't exist in this activity. Probably, this instance has already been executed"); |
---|
307 | return false; |
---|
308 | } |
---|
309 | } |
---|
310 | } |
---|
311 | $query = 'select gia.wf_instance_id, gia.wf_user, gia.wf_status |
---|
312 | from '.GALAXIA_TABLE_PREFIX.'instance_activities gia |
---|
313 | where gia.wf_activity_id = ? and gia.wf_instance_id = ?'; |
---|
314 | $result = $this->query($query, array($activityId, $instanceId)); |
---|
315 | $res_inst_act = Array(); |
---|
316 | if (!!$result) |
---|
317 | { |
---|
318 | $res_inst_act = $result->fetchRow(); |
---|
319 | //DEBUG |
---|
320 | //$debuginstact = implode(",",$res_inst_act); |
---|
321 | //$this->error[] = 'DEBUG: '. date("[d/m/Y h:i:s]").'instance/activity:'.$debuginstact; |
---|
322 | |
---|
323 | } |
---|
324 | } |
---|
325 | |
---|
326 | //Now that we have the process we can load config values |
---|
327 | //$this->error[] = 'DEBUG: load config values for process:'.$pId; |
---|
328 | $this->loadConfigValues($pId); |
---|
329 | //$debuconfig = '';foreach ($this->processesConfig[$pId] as $label => $value){$debugconfig .= ':'.$label.'=>'.$value;} $this->error[] = 'DEBUG: config:'.$debugconfig; |
---|
330 | |
---|
331 | |
---|
332 | |
---|
333 | //2 - decide which tests must be done ------------------------------------------------ |
---|
334 | //init tests |
---|
335 | $_check_active_process = false; // is the process is valid? |
---|
336 | $_check_instance = false; //have we got an instance? |
---|
337 | $_check_instance_status = array(); //use to test some status between 'active','exception','aborted','completed' |
---|
338 | $_fail_on_exception = false; //no comment |
---|
339 | $_check_activity = false; //have we got an activity? |
---|
340 | //is there a relationship between instance and activity? this one can be decided already |
---|
341 | $_check_instance_activity = !(($_no_instance) || ($_no_activity)); |
---|
342 | $_bypass_user_role_if_owner = false; //if our user is the owner we ignore user tests |
---|
343 | $_bypass_user_on_non_interactive = false; //if activty is not interactive we do not perform user tests |
---|
344 | $_bypass_user_if_admin = false; //is our user a special rights user? |
---|
345 | $_bypass_instance_on_pseudo = false; //should we jump the instance check when in 'start' activity? |
---|
346 | $_check_is_user = false; //is the actual_user our user? |
---|
347 | $_check_is_not_star = false; //is the actual <>*? |
---|
348 | $_check_is_star = false; // is the actual user *? |
---|
349 | $_check_is_in_role = false; //is our user in associated roles with readonly=false? |
---|
350 | $_check_is_in_role_in_readonly = false; //is our user in associated roles? |
---|
351 | $_check_no_view_activity = false; //is the process having no view activities? |
---|
352 | $_check_is_admin_only = false; //is the action vaible only for admins? |
---|
353 | |
---|
354 | //first have a look at the action asked |
---|
355 | switch($action) |
---|
356 | { |
---|
357 | case 'restart': |
---|
358 | // we need an activity 'in_flow' ie: not start or standalone that means we need an instance |
---|
359 | // we need an instance not completed or aborted that means we need an activity |
---|
360 | // but if we have an instance it musn't be in 'exception' as well |
---|
361 | // authorization is given to admin only |
---|
362 | $_check_active_process = true; |
---|
363 | $_check_activity = true; |
---|
364 | $_check_instance = true; |
---|
365 | $_fail_on_exception = true; |
---|
366 | $_check_is_admin_only = true; |
---|
367 | break; |
---|
368 | case 'view': |
---|
369 | //process can be inactive |
---|
370 | //we need an existing instance |
---|
371 | //no activity needed |
---|
372 | $_check_instance = true; |
---|
373 | $_bypass_user_if_admin = true; |
---|
374 | //but be carefull the view function is forbidden on process having the viewrun action with activities |
---|
375 | $_check_no_view_activity = true; |
---|
376 | break; |
---|
377 | case 'viewrun': |
---|
378 | //process can be inactive |
---|
379 | //we need an existing instance |
---|
380 | //we need an activity |
---|
381 | //need a read-only role at least on this activity |
---|
382 | $_check_instance = true; |
---|
383 | $_bypass_user_if_admin = true; |
---|
384 | $_check_activity = true; |
---|
385 | $_check_is_in_role_in_readonly = true; |
---|
386 | //The view type is a special activity related to all instances |
---|
387 | $_check_instance_activity = false; |
---|
388 | break; |
---|
389 | case 'complete': |
---|
390 | // we need an activity 'in_flow' ie: not start or standalone that means we need an instance |
---|
391 | // (the 'view' activity is not 'in_flow' and has instance, but no relashionship, no need to |
---|
392 | // test it here or later for grab or others actions). |
---|
393 | // warning we can complete a start activity, in this case it is the contrary, we musn't have an instance |
---|
394 | // we need an instance not completed or aborted that means we need an activity |
---|
395 | // but if we have an instance it musn't be in 'exception' as well |
---|
396 | // authorization is given to currentuser only, |
---|
397 | // for interactive activities (except start), instance user need to be the actual user |
---|
398 | // 'view' cannot be completed |
---|
399 | $_check_active_process = true; |
---|
400 | $_check_instance = true; |
---|
401 | $_bypass_instance_on_pseudo = true; |
---|
402 | $_fail_on_exception = true; |
---|
403 | $_check_activity = true; |
---|
404 | $_bypass_user_on_non_interactive = true; |
---|
405 | $_check_is_user = true; |
---|
406 | $_check_is_not_star = true; |
---|
407 | break; |
---|
408 | case 'grab': |
---|
409 | // we need an activity 'in_flow' ie: not start or standalone that means we need an instance |
---|
410 | // we need an instance not completed or aborted that means we need an activity |
---|
411 | // authorization are given to currentuser, role, never owner actually |
---|
412 | // TODO: add conf setting to give grab access to owner (that mean run access as well maybe) |
---|
413 | // current user MUST be '*' or user (no matter to grab something we already have) |
---|
414 | // check is star is done after check_is_user which can be false |
---|
415 | $_check_active_process = true; |
---|
416 | $_check_activity = true; |
---|
417 | $_check_instance = true; |
---|
418 | $_check_is_user = true; |
---|
419 | $_check_is_star = true; |
---|
420 | $_bypass_user_if_admin = true; |
---|
421 | $_check_is_in_role = true; |
---|
422 | break; |
---|
423 | case 'release' : |
---|
424 | // we need an activity 'in_flow' ie: not start or standalone that means we need an instance |
---|
425 | // we need an instance not completed or aborted that means we need an activity |
---|
426 | // authorization are given to currentuser, maybe role, maybe owner, |
---|
427 | // current must not be '*' |
---|
428 | $_check_active_process = true; |
---|
429 | $_check_activity = true; |
---|
430 | $_check_instance = true; |
---|
431 | $_check_is_user = true; |
---|
432 | $_check_is_not_star = true; |
---|
433 | $_bypass_user_if_admin = true; |
---|
434 | if ($this->processesConfig[$pId]['role_give_release_right']) $_check_is_in_role = true; |
---|
435 | if ($this->processesConfig[$pId]['ownership_give_release_right']) $_bypass_user_role_if_owner = true; |
---|
436 | break; |
---|
437 | case 'exception': |
---|
438 | // we need an activity 'in_flow' ie: not start or standalone that means we need an instance |
---|
439 | // we need an instance not completed or aborted that means we need an activity |
---|
440 | // authorization are given to currentuser, maybe role, maybe owner, |
---|
441 | $_check_active_process = true; |
---|
442 | $_check_activity = true; |
---|
443 | $_check_instance = true; |
---|
444 | $_check_instance_status = array('active'); |
---|
445 | $_bypass_user_if_admin = true; |
---|
446 | $_check_is_user = true; |
---|
447 | if ($this->processesConfig[$pId]['role_give_exception_right']) $_check_is_in_role = true; |
---|
448 | if ($this->processesConfig[$pId]['ownership_give_exception_right']) $_bypass_user_role_if_owner = true; |
---|
449 | break; |
---|
450 | case 'resume': |
---|
451 | // like exception but inversed activity status |
---|
452 | $_check_active_process = true; |
---|
453 | $_check_activity = true; |
---|
454 | $_check_instance = true; |
---|
455 | $_check_instance_status = array('exception'); |
---|
456 | $_bypass_user_if_admin = true; |
---|
457 | $_check_is_user = true; |
---|
458 | if ($this->processesConfig[$pId]['role_give_exception_right']) $_check_is_in_role = true; |
---|
459 | if ($this->processesConfig[$pId]['ownership_give_exception_right']) $_bypass_user_role_if_owner = true; |
---|
460 | break; |
---|
461 | case 'abort': |
---|
462 | // process can be inactive |
---|
463 | // we do not need an activity |
---|
464 | // we need an instance |
---|
465 | // authorization are given to currentuser, maybe role, maybe owner, |
---|
466 | // TODO: add conf setting to refuse abort by user |
---|
467 | $_check_instance = true; |
---|
468 | $_check_instance_status = array('active','exception','completed'); |
---|
469 | $_bypass_user_if_admin = true; |
---|
470 | $_check_is_user = true; |
---|
471 | if ($this->processesConfig[$pId]['role_give_abort_right']) $_check_is_in_role = true; |
---|
472 | if ($this->processesConfig[$pId]['ownership_give_abort_right']) $_bypass_user_role_if_owner = true; |
---|
473 | break; |
---|
474 | case 'run': |
---|
475 | // the hell door: |
---|
476 | // all activities can be runned, even without instance, even if non interactive |
---|
477 | // if we have one we need an instance not completed or aborted that means we need an activity |
---|
478 | // but if we have an instance it musn't be in 'exception' as well |
---|
479 | // for interactive activities (except start and standalone), instance user need to be the actual user |
---|
480 | // run is ok if user is in role and actual user is '*', no rights for owner actually |
---|
481 | // no user bypassing on admin user, admin must grab (release if needed) the instance before |
---|
482 | $_check_active_process = true; |
---|
483 | $_check_activity = true; |
---|
484 | $_fail_on_exception = true; |
---|
485 | $_bypass_user_on_non_interactive = true; |
---|
486 | $_check_is_user = true; |
---|
487 | $_check_is_star = true; |
---|
488 | $_check_is_in_role = true; |
---|
489 | break; |
---|
490 | case 'send': |
---|
491 | // we need an instance not completed or aborted that means we need an activity |
---|
492 | // but if we have an instance it musn't be in 'exception' as well |
---|
493 | // authorization are given to currentuser, maybe role, no rights for owner actually |
---|
494 | // run is ok if user is in role and actual user is '*' |
---|
495 | // no user bypassing on admin user, admin must grab (release if needed) the instance before |
---|
496 | $_check_active_process = true; |
---|
497 | $_check_activity = true; |
---|
498 | $_fail_on_exception = true; |
---|
499 | $_bypass_user_if_admin = true; |
---|
500 | $_check_is_user = true; |
---|
501 | $_check_is_star = true; |
---|
502 | $_check_is_in_role = true; |
---|
503 | break; |
---|
504 | } |
---|
505 | |
---|
506 | //3- now perform asked tests --------------------------------------------------------------------- |
---|
507 | if ($_check_active_process) // require an active process? |
---|
508 | { |
---|
509 | //$this->error[] = 'DEBUG: check active process'; |
---|
510 | if ($_no_instance) //we need an instance or an activity to perfom the check |
---|
511 | { |
---|
512 | //we cannot be there without instance and without activity, we now we have one activity at least |
---|
513 | if (!($resactivity['wf_is_active']=='y')) |
---|
514 | { |
---|
515 | $this->error[] = tra('Process %1 %2 is not active, action %3 is impossible', $resactivity['wf_procname'], $resactivity['wf_version'], $action); |
---|
516 | return false; |
---|
517 | } |
---|
518 | } |
---|
519 | else |
---|
520 | { |
---|
521 | if (!($resinstance['wf_is_active']=='y')) |
---|
522 | { |
---|
523 | $this->error[] = tra('Process %1 %2 is not active, action %3 is impossible', $resinstance['wf_procname'], $resactivity['wf_version'], $action); |
---|
524 | return false; |
---|
525 | } |
---|
526 | } |
---|
527 | } |
---|
528 | |
---|
529 | if ($_check_instance) |
---|
530 | { |
---|
531 | //$this->error[] = 'DEBUG: check instance'; |
---|
532 | if ( (!($_bypass_instance_on_pseudo)) && ($_no_instance)) |
---|
533 | { |
---|
534 | $this->error[] = tra('Action %1 needs an instance and instance %2 does not exists', $action, $instanceId); |
---|
535 | return false; |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | if ($_check_activity) |
---|
540 | { |
---|
541 | //$this->error[] = 'DEBUG: check activity'; |
---|
542 | if ($_no_activity) |
---|
543 | { |
---|
544 | $this->error[] = tra('Action %1 needs an activity and activity %2 does not exists', $action, $activityId); |
---|
545 | return false; |
---|
546 | } |
---|
547 | } |
---|
548 | |
---|
549 | if ($_check_instance_activity) //is there a realtionship between instance and activity |
---|
550 | { |
---|
551 | //$this->error[] = 'DEBUG: check activity-instance relationship'.count($res_inst_act); |
---|
552 | if ( (!isset($res_inst_act)) || empty($res_inst_act) || (count($res_inst_act)==0) ) |
---|
553 | { |
---|
554 | $this->error[] = tra('Instance %1 is not associated with activity %2, action %3 is impossible.', $instanceId, $activityId, $action); |
---|
555 | return false; |
---|
556 | } |
---|
557 | } |
---|
558 | |
---|
559 | if (!(count($_check_instance_status) == 0)) //use to test some status between 'active','exception','aborted','completed' |
---|
560 | { |
---|
561 | //DEBUG |
---|
562 | //$debug_status = implode(",",$_check_instance_status); |
---|
563 | //$this->error[] = 'DEBUG: check instance status, actually :'.$resinstance['wf_status'].' need:'.$debug_status; |
---|
564 | if (!(in_array($resinstance['wf_status'],$_check_instance_status))) |
---|
565 | { |
---|
566 | $this->error[] = tra('Instance %1 is in %2 state, action %3 is impossible.', $instanceId, $resinstance['wf_status'], $action); |
---|
567 | return false; |
---|
568 | } |
---|
569 | } |
---|
570 | if (($_fail_on_exception) && ($resinstance['wf_status']=='exception')) |
---|
571 | { |
---|
572 | $this->error[] = tra('Instance %1 is in exception, action %2 is not possible.', $instanceId, $action); |
---|
573 | return false; |
---|
574 | } |
---|
575 | |
---|
576 | // Test on the process to see if he has a view activity |
---|
577 | if ($_check_no_view_activity) |
---|
578 | { |
---|
579 | if (!(isset($this->pm))) |
---|
580 | { |
---|
581 | $this->pm = &Factory::newInstance('ProcessManager'); |
---|
582 | } |
---|
583 | //$this->error[] = 'DEBUG: checking to see if there is no view activities on process :'.$pId.':'.$this->pm->get_process_view_activity($pId); |
---|
584 | /** whithout this check we can see the instance data if any view activity exists */ |
---|
585 | /*if ($this->pm->get_process_view_activity($pId)) |
---|
586 | { |
---|
587 | $this->error[] = tra('This process has a view activity. Access in view mode is granted only for this view activty.'); |
---|
588 | return false; |
---|
589 | }*/ |
---|
590 | } |
---|
591 | |
---|
592 | // user tests --------------- |
---|
593 | $checks = true; |
---|
594 | //is our actual workflow user a special rights user? |
---|
595 | // TODO test actual workflow user diff of $user |
---|
596 | //$this->error[] = 'DEBUG: user can admin instance :'.galaxia_user_can_admin_instance().' bypass?:'.$_bypass_user_if_admin; |
---|
597 | $is_admin = galaxia_user_can_admin_instance(); |
---|
598 | if (! ( (($_bypass_user_if_admin) && ($is_admin)) || (($_check_is_admin_only) && ($is_admin))) ) |
---|
599 | { |
---|
600 | //if our user is the owner we ignore user tests |
---|
601 | //$this->error[] = 'DEBUG: user is owner :'.$resinstance['wf_owner'].' bypass?:'.$_bypass_user_role_if_owner; |
---|
602 | if (!( ($_bypass_user_role_if_owner) && ((int)$resinstance['wf_owner']==(int)$user) )) |
---|
603 | { |
---|
604 | //$this->error[] = 'DEBUG: no_activity:'.$_no_activity.' interactive? :'.$resactivity['wf_is_interactive'].' bypass?:'.$_bypass_user_on_non_interactive; |
---|
605 | //if activity is not interactive we do not perform user tests |
---|
606 | if (!( (!($_no_activity)) && ($_bypass_user_on_non_interactive) && ($resactivity['wf_is_interactive']=='n') )) |
---|
607 | { |
---|
608 | //$this->error[] = 'DEBUG: no bypassing done:'; |
---|
609 | //is the actual_user our user? |
---|
610 | if ( (!($_no_instance)) && $_check_is_user) |
---|
611 | { |
---|
612 | //$this->error[] = 'DEBUG: check user is actual instance user:'.$user.':'.$res_inst_act['wf_user']; |
---|
613 | if (!((int)$res_inst_act['wf_user']==(int)$user)) |
---|
614 | { |
---|
615 | //user test was false, but maybe we'll have better chance later |
---|
616 | $checks = false; |
---|
617 | } |
---|
618 | } |
---|
619 | // special '*' user |
---|
620 | if ($res_inst_act['wf_user']=='*') |
---|
621 | { |
---|
622 | //$this->error[] = 'DEBUG: we have the special * user:'; |
---|
623 | //is the actual *? |
---|
624 | if ($_check_is_star) |
---|
625 | { |
---|
626 | // redemption here |
---|
627 | //$this->error[] = 'DEBUG Ok, we have a star'; |
---|
628 | $checks = true; |
---|
629 | } |
---|
630 | |
---|
631 | //is the actual <>*? |
---|
632 | if ($_check_is_not_star) |
---|
633 | { |
---|
634 | //no redemption here |
---|
635 | $this->error[] = tra('Action %1 is impossible, there are no user assigned to this activity for this instance', $action); |
---|
636 | return false; |
---|
637 | } |
---|
638 | //perform the role test if actual user is '*' |
---|
639 | //$this->error[] = 'DEBUG: role checking?:'.$_check_is_in_role; |
---|
640 | if ($_check_is_in_role) |
---|
641 | { |
---|
642 | //$this->error[] = 'DEBUG: we have *, checking role of user:'.$user; |
---|
643 | $checks=$this->checkUserAccess($user, $activityId); |
---|
644 | } |
---|
645 | //$this->error[] = 'DEBUG: role checking in read-only at least?:'.$_check_is_in_role_in_readonly; |
---|
646 | if ($_check_is_in_role_in_readonly) |
---|
647 | { |
---|
648 | //$this->error[] = 'DEBUG: we have *, checking readonly role of user:'.$user; |
---|
649 | $checks=$this->checkUserAccess($user, $activityId, true); |
---|
650 | } |
---|
651 | } |
---|
652 | else |
---|
653 | { |
---|
654 | if (substr($res_inst_act['wf_user'], 0, 1) == 'p') |
---|
655 | { |
---|
656 | $role_id = substr($res_inst_act['wf_user'], 1); |
---|
657 | $groups = galaxia_retrieve_user_groups($user); |
---|
658 | $sql = "SELECT 1 "; |
---|
659 | $sql .= "FROM " . GALAXIA_TABLE_PREFIX . "user_roles WHERE ("; |
---|
660 | if (is_array($groups)) |
---|
661 | { |
---|
662 | foreach ($groups as &$group) |
---|
663 | $group = "'{$group}'"; |
---|
664 | $sql .= "(wf_user IN (" . implode(',',$groups) . ") AND wf_account_type = 'g') OR "; |
---|
665 | } |
---|
666 | $sql .= "(wf_user = '$user' AND wf_account_type = 'u')) AND "; |
---|
667 | $sql .= "(wf_role_id = $role_id)"; |
---|
668 | $result = $this->getOne($sql); |
---|
669 | |
---|
670 | if (is_null($result)) |
---|
671 | $checks = false; |
---|
672 | else |
---|
673 | $checks = true; |
---|
674 | |
---|
675 | } |
---|
676 | else |
---|
677 | { |
---|
678 | //we have not *, do we need * as the actual? (done only if check_is_user is false) |
---|
679 | //notice that if check_user was false and we have not the '*' user and if you do not want |
---|
680 | //the check_is_star it means the user can bypass the actual user if you have a check_is_in_role ok! |
---|
681 | if ( (!($checks)) && ($_check_is_star)) |
---|
682 | { |
---|
683 | // that was necessary |
---|
684 | $this->error[] = tra('Action %1 is impossible, another user is already in place', $action); |
---|
685 | return false; |
---|
686 | } |
---|
687 | //is our user in associated roles (done even if check_is_user was true) |
---|
688 | //$this->error[] = 'DEBUG: role checking?:'.$_check_is_in_role; |
---|
689 | if ($_check_is_in_role) |
---|
690 | { |
---|
691 | //$this->error[] = 'DEBUG: we have not *, checking role for user:'.$user; |
---|
692 | $checks=$this->checkUserAccess($user, $activityId); |
---|
693 | } |
---|
694 | //$this->error[] = 'DEBUG: role checking in read-only at least?:'.$_check_is_in_role_in_readonly; |
---|
695 | if ($_check_is_in_role_in_readonly) |
---|
696 | { |
---|
697 | //$this->error[] = 'DEBUG: we have not *, checking role in read-only for user:'.$user; |
---|
698 | $checks=$this->checkUserAccess($user, $activityId, true); |
---|
699 | } |
---|
700 | } |
---|
701 | } |
---|
702 | } |
---|
703 | } |
---|
704 | } |
---|
705 | //$this->error[] = 'DEBUG: final check:'.$checks; |
---|
706 | return $checks; |
---|
707 | } |
---|
708 | |
---|
709 | /** |
---|
710 | * Gets avaible actions for a given user on some activity and instance assuming he already have access to it. |
---|
711 | * To be able to decide this function needs all the parameters, use the GUI object equivalent function if you want less parameters. |
---|
712 | * |
---|
713 | * @access public |
---|
714 | * @param int $user User id |
---|
715 | * @param int $instanceId Instance id |
---|
716 | * @param int $activityId Activity id |
---|
717 | * @param bool $readonly It has to be true if the user has only read-only level access with his role mappings |
---|
718 | * @param int $pId Process id |
---|
719 | * @param string $actType Activity type |
---|
720 | * @param string $actInteractive 'y' or 'n' and is the activity interactivity |
---|
721 | * @param string $actAutorouted 'y' or 'n' and is the activity routage |
---|
722 | * @param string $actStatus Activity status ('running' or 'completed') |
---|
723 | * @param int $instanceOwner Instance owner id |
---|
724 | * @param string $instanceStatus Instance status ('running', 'completed', 'aborted' or 'exception') |
---|
725 | * @param mixed $currentUser Current instance/activity user id or '*'. |
---|
726 | * @param bool $viewactivity False if the process has no view activity, else it's the id of the view activity |
---|
727 | * @return array An array of this form: |
---|
728 | * array('action name' => 'action description') |
---|
729 | * 'actions names' are: 'grab', 'release', 'run', 'send', 'view', 'viewrun', 'exception', 'resume', 'monitor' |
---|
730 | * note that for the 'viewrun' key value is an array with a 'lang' key for the translation and a 'link' key for the view activity id |
---|
731 | * Some config values can change theses rules but basically here they are: |
---|
732 | * * 'grab' : be the user of this activity. User has access to it and instance status is ok. |
---|
733 | * * 'release' : let * be the user of this activity. Must be the actual user or the owner of the instance. |
---|
734 | * * 'run' : run an associated form. This activity is interactive, user has access, instance status is ok. |
---|
735 | * * 'send' : send this instance, activity was non-autorouted and he has access and status is ok. |
---|
736 | * * 'view' : view the instance, activity ok, always avaible if no view activity on the process except for start or standalone act. |
---|
737 | * * 'viewrun' : view the instance in a view activity, need role on view activity, always avaible except for start or standalone act. |
---|
738 | * * 'abort' : abort an instance, ok when we are the user |
---|
739 | * * 'exception' : set the instance status to exception, need to be the user |
---|
740 | * * 'resume' : back to running when instance status was exception, need to be the user |
---|
741 | * * 'monitor' : admin the instance, for special rights users |
---|
742 | * 'actions description' are translated explanations like 'release access to this activity' |
---|
743 | * This function will as well load process configuration which could have some impact on the rights. |
---|
744 | * Theses config data will be cached during the existence of this WfSecurity object. |
---|
745 | * WARNING: this is a snapshot, the engine give you a snaphsot of the rights a user have on an instance-activity |
---|
746 | * at a given time, this is not meaning theses rights will still be there when the user launch the action. |
---|
747 | * You should absolutely use the GUI Object or runtime to execute theses actions (except monitor) and they could be rejected. |
---|
748 | * WARNING: we do not check the user access rights. If you launch this function for a list of instances obtained via a |
---|
749 | * GUI object theses access rights are allready checked (for example we do not check your readonly parameter is true). |
---|
750 | * In fact this function is GUI oriented, it is not granting rights |
---|
751 | */ |
---|
752 | function getUserActions($user, $instanceId, $activityId, $readonly, $pId, $actType, $actInteractive, $actAutorouted, $actStatus, $instanceOwner, $instanceStatus, $currentUser, $view_activity) |
---|
753 | { |
---|
754 | $result= array();//returned array |
---|
755 | $stopflow=false;//true when the instance is in a state where the flow musn't advance |
---|
756 | //ie: we can't send or run it |
---|
757 | $deathflow=false;//true when the instance is in a state where the flow will never advance anymore |
---|
758 | //ie: we can't send, run, grab, release, exception or resume it |
---|
759 | $associated_instance=true;//false when no instance is associated with the activity |
---|
760 | // ie: we cannot send, grab, release, exception, resume or view the instance but we can run |
---|
761 | // it covers standalone activities and start activities not completed |
---|
762 | $_run = false; |
---|
763 | $_send = false; |
---|
764 | $_grab = false; |
---|
765 | $_release = false; |
---|
766 | $_abort = false; |
---|
767 | $_view = false; |
---|
768 | $_viewrun = false; |
---|
769 | $_resume = false; |
---|
770 | $_exception = false; |
---|
771 | // this can be decided right now, it depends only on user rights |
---|
772 | $_monitor = galaxia_user_can_admin_instance($user); |
---|
773 | |
---|
774 | $this->loadConfigValues($pId); |
---|
775 | |
---|
776 | // check the instance status |
---|
777 | // 'completed' => no action except 'view'/'viewrun' or 'abort' or 'monitor' |
---|
778 | // 'aborted' => no action except 'view'/'viewrun' or 'monitor' |
---|
779 | // 'active' => ok first add 'exception' |
---|
780 | // 'exception' => first add 'resume', no 'run' or 'send' after |
---|
781 | $_view = true; |
---|
782 | if ($view_activity) |
---|
783 | { |
---|
784 | //we should have a 'viewrun' instead of a 'view' action, but maybe we do not have access on this view activity |
---|
785 | //this access right will be checked by gui_get_process_view_activity |
---|
786 | $_viewrun = true; |
---|
787 | $_iframe_height = $this->processesConfig[$pId]['iframe_view_height']; |
---|
788 | } |
---|
789 | |
---|
790 | |
---|
791 | //on readonly mode things are simplier, no more rights |
---|
792 | if (!($readonly)) |
---|
793 | { |
---|
794 | if ($instanceStatus == 'aborted') |
---|
795 | { |
---|
796 | $deathflow=true; |
---|
797 | } |
---|
798 | else |
---|
799 | { |
---|
800 | // first check ABORT |
---|
801 | if ( ($user==$currentUser) || |
---|
802 | (($user==$instanceOwner)&&($this->processesConfig[$pId]['ownership_give_abort_right'])) || |
---|
803 | ($this->processesConfig[$pId]['role_give_abort_right'])) |
---|
804 | {// we are the assigned user |
---|
805 | //OR we are the owner and it gives rights |
---|
806 | //OR we have the role and it gives rights |
---|
807 | $_abort =true; |
---|
808 | } |
---|
809 | // now handle resume and exception but before detect completed instances |
---|
810 | if ($instanceStatus == 'completed') |
---|
811 | { |
---|
812 | $deathflow=true; |
---|
813 | } |
---|
814 | else |
---|
815 | { |
---|
816 | if ($instanceStatus == 'exception') |
---|
817 | { |
---|
818 | $stopflow = true; |
---|
819 | if ( ($user==$currentUser) || |
---|
820 | (($user==$instanceOwner)&&($this->processesConfig[$pId]['ownership_give_exception_right'])) || |
---|
821 | ($this->processesConfig[$pId]['role_give_exception_right'])) |
---|
822 | {// we are the assigned user OR we are the owner and it gives rights |
---|
823 | $_resume = true; |
---|
824 | } |
---|
825 | } |
---|
826 | elseif ($instanceStatus == 'active') |
---|
827 | { |
---|
828 | //handle rules about ownership |
---|
829 | if ( ($user==$currentUser) || |
---|
830 | (($user==$instanceOwner)&&($this->processesConfig[$pId]['ownership_give_exception_right'])) || |
---|
831 | ($this->processesConfig[$pId]['role_give_exception_right'])) |
---|
832 | {// we are the assigned user OR we are the owner and it gives rights |
---|
833 | $_exception = true; |
---|
834 | } |
---|
835 | } |
---|
836 | } |
---|
837 | } |
---|
838 | |
---|
839 | //now we check the activity |
---|
840 | // start (only uncompleted) and standalone activities have no instance associated. |
---|
841 | // If we are not in a 'stop' or 'death' flow we can check interactivity |
---|
842 | // interactive -> run |
---|
843 | // not interactive -> send (except for 'standalone') |
---|
844 | // if we are not in a 'death flow' we can add grab and release actions |
---|
845 | if ( ($actType=='standalone') || (($actType=='start') && (!($actStatus=='completed'))) ) |
---|
846 | { |
---|
847 | $associated_instance=false; |
---|
848 | // there's no instance to view in fact |
---|
849 | $_view = false; |
---|
850 | $_viewrun = false; |
---|
851 | } |
---|
852 | if (($actInteractive=='y') && (!($deathflow))) |
---|
853 | { |
---|
854 | if ($associated_instance) |
---|
855 | { |
---|
856 | if ($currentUser=='*') |
---|
857 | { |
---|
858 | $_grab = true; |
---|
859 | } |
---|
860 | else |
---|
861 | { |
---|
862 | if ( ($user==$currentUser) || |
---|
863 | (($user==$instanceOwner)&&($this->processesConfig[$pId]['ownership_give_release_right'])) || |
---|
864 | ($this->processesConfig[$pId]['role_give_release_right'])) |
---|
865 | {// we are the assigned user |
---|
866 | //OR we are the owner and it gives rights |
---|
867 | //OR we have the role and it gives rights |
---|
868 | $_release = true; |
---|
869 | } |
---|
870 | } |
---|
871 | } |
---|
872 | if (($actStatus=='running') && !($stopflow) && !($deathflow)) |
---|
873 | { |
---|
874 | if (($currentUser=='*') || ($currentUser==$user)) |
---|
875 | { |
---|
876 | $_run = true; |
---|
877 | } |
---|
878 | } |
---|
879 | } |
---|
880 | //for non autorouted activities we'll have to send, useless on standalone but usefull for start |
---|
881 | //activities which can be sended if completed and of course for all other activities |
---|
882 | if ($actAutorouted=='n') |
---|
883 | { |
---|
884 | if ($associated_instance) |
---|
885 | { |
---|
886 | if (($actStatus=='completed') && !($stopflow) && !($deathflow)) |
---|
887 | { |
---|
888 | $_send = true; |
---|
889 | } |
---|
890 | } |
---|
891 | } |
---|
892 | }//end if !$readonly |
---|
893 | |
---|
894 | //build final array |
---|
895 | if ($_run) $result['run']=tra('Execute this activity'); |
---|
896 | if ($_send) $result['send']=tra('Send this instance to the next activity'); |
---|
897 | if ($_grab) $result['grab']=tra('Assign me this activity'); |
---|
898 | if ($_release) $result['release']=tra('Release access to this activity'); |
---|
899 | if ($_abort) $result['abort']=tra('Abort this instance'); |
---|
900 | if ($_view) $result['view']=tra('View this instance'); |
---|
901 | if ($_viewrun) $result['viewrun']= array('lang' => tra('View this instance'), 'link' => $view_activity); |
---|
902 | if ($_iframe_height) $result['viewrun']['iframe_height'] = $_iframe_height; |
---|
903 | if ($_resume) $result['resume']=tra('Resume this exception instance'); |
---|
904 | if ($_exception) $result['exception']=tra('Exception this instance'); |
---|
905 | if ($_monitor) $result['monitor']=tra('Monitor this instance'); |
---|
906 | |
---|
907 | return $result; |
---|
908 | } |
---|
909 | |
---|
910 | |
---|
911 | } |
---|
912 | |
---|
913 | |
---|
914 | ?> |
---|