source: branches/1.2/workflow/inc/engine/src/common/WfSecurity.php @ 1349

Revision 1349, 39.8 KB checked in by niltonneto, 15 years ago (diff)

Ticket #561 - Inclusão do módulo Workflow faltante nessa versão.

  • Property svn:executable set to *
Line 
1<?php
2require_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 */
10class 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?>
Note: See TracBrowser for help on using the repository browser.