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