ACL Extension  0.3
 All Data Structures Namespaces Files Functions Variables
RestrictedActiveRecord.php
Go to the documentation of this file.
1 <?php
2 
17 abstract class RestrictedActiveRecord extends CActiveRecord{
18 
24  public static $byPassCheck = false;
25 
31  public static $inAttendance = NULL;
32 
36  public static $model = 'User';
37 
44  public static $possibleActions = NULL;
45 
46  public static $defaultOptions = array(
47  'disableInheritance' => false, // If true, no aco will inherit the permissions of it's parent
48  );
49 
58  protected function generateAccessCheck($conditions = '', $params = array(), $options = array()){
59  if(is_object($conditions) && get_class($conditions) == 'CDbCriteria'){
60  $criteria = $conditions;
61  }
62  else{
63  $criteria = new CDbCriteria;
64  $criteria->mergeWith(array(
65  'condition' => $conditions,
66  'params' => $params
67  ));
68  }
69 
70 
71  $options = array_merge(RestrictedActiveRecord::$defaultOptions, $options);
72 
73  //If the check is bypassed, return criteria without check
75  return $criteria;
76 
77  $criteria->distinct = true; //Important: there can be multiple locations which grant permission
78 
79  //Inner join to get the collection associated with this content
80  $acoClass = Strategy::getClass('Aco');
81  $collection = 'INNER JOIN `'.$acoClass::model()->tableName().'` AS acoC ON acoC.model = :RAR_model AND acoC.foreign_key = t.id';
82  $criteria->params[':RAR_model'] = get_class($this);
83 
84  //Inner join to the associated aco-nodes themselves to get the positions
85  $acoNodeClass = Strategy::getClass('AcoNode');
86  $nodes = ' INNER JOIN `'.$acoNodeClass::model()->tableName().'` AS aco ON aco.collection_id = acoC.id';
87 
88  //But before: fetch the positions of the current user
89  $aroClass = Strategy::getClass('Aro');
91  $aro = $aroClass::model()->find('model = :model AND foreign_key = :foreign_key',
92  array(':model'=> static::$model, ':foreign_key' => $user->id));
93 
94  //If we are nobody... we are a guest^^
95  $guest = Strategy::get('guestGroup');
96  if(!$aro && $guest){
97  $aro = $aroClass::model()->find('alias = :alias',
98  array(':alias' => $guest));
99 
100  //If there's no guest group... we are nobody and we may nothing ;)
101  if(!$aro)
102  return array();
103  }
104 
105 
106  $aroPositions = $aro->fetchComprisedPositions();
107  $aroPositionCheck = $aro->addPositionCheck($aroPositions, "aro", "map");
108 
109  //Get our action :)
110  $action = Action::model()->find('name = :name', array(':name' => 'read'));
111 
112  if($action === NULL)
113  throw new RuntimeException('Unable to find action read');
114 
115  //Now, join connecting table
116  $acoCondition = $acoClass::buildTreeQueryCondition(
117  array('table' => 'aco'),
118  array('table' => 'map', 'field' => 'aco'),
119  $options['disableInheritance']
120  );
121  $connection = ' INNER JOIN `'.Permission::model()->tableName().'` AS map ON '.$acoCondition.' AND '.$aroPositionCheck.' AND map.action_id = :acl_action_id';
122  $criteria->params[':acl_action_id'] = $action->id;
123 
124  $joins = array($collection, $nodes, $connection);
125 
126  foreach($joins as $join){
127  $criteria->mergeWith(array('join' => $join), true);
128  }
129 
130 
131  return $criteria;
132  }
133 
134 
135  public function find($conditions = '', $params = array()){
136  return parent::find($this->generateAccessCheck($conditions, $params));
137  }
138 
139  public function findByAttributes($attributes, $conditions = '', $params = array()){
140  return parent::findByAttributes($attributes, $this->generateAccessCheck($conditions, $params));
141  }
142 
143  public function findByPk($pk, $conditions = '', $params = array()){
144  return parent::findByPk($pk, $this->generateAccessCheck($conditions, $params));
145  }
146 
147  public function findBySQL($sql, $params = array()){
148  return parent::find($this->generateAccessCheck($sql, $params));
149  }
150 
151 
152  public function findAll($conditions = '', $params = array()){
153  return parent::findAll($this->generateAccessCheck($conditions, $params));
154  }
155 
156  public function findAllByAttributes($attributes, $conditions = '', $params = array()){
157  return parent::findAllByAttributes($attributes, $this->generateAccessCheck($conditions, $params));
158  }
159 
160  public function findAllByPk($pk, $conditions = '', $params = array()){
161  return parent::findAllByPk($pk, $this->generateAccessCheck($conditions, $params));
162  }
163 
164  public function findAllBySQL($sql, $params = array()){
165  return parent::findAll($this->generateAccessCheck($sql, $params));
166  }
167 
168 
175  public function getDirectlyPermitted($actions = '*'){
176  //First, fetch all of the action Ids
177  $actions = Action::translateActions($this, $actions);
178  $actionCondition = Util::generateInStatement($actions);
179  $actions = Action::model()->findAll('name '.$actionCondition);
180 
181  $actionIds = array();
182  foreach($actions as $action){
183  $actionIds[] = $action->id;
184  }
185  $actionIdCondition = Util::generateInStatement($actionIds);
186 
187  //Get the associated Aco first
188  $aco = AclObject::loadObjectStatic($this, 'Aco');
189  //Fetch all of the own positions and build condition
190  $positions = $aco->fetchComprisedPositions();
191  $acoCondition = Util::generateInStatement($positions);
192 
193  $aroNodeClass = Strategy::getClass('AroNode');
194 
195  $rGroupTable = RGroup::model()->tableName();
196  $nodeTable = $aroNodeClass::model()->tableName();
197  $permTable = Permission::model()->tableName();
198  return Yii::app()->db->createCommand()
199  ->selectDistinct('t.id AS collection_id, t.foreign_key, t.model, p.action_id')
200  ->from($rGroupTable.' t')
201  ->join($nodeTable.' n', 'n.collection_id = t.id')
202  ->join($permTable.' p',
203  'p.aro_id = n.id AND p.aco_path '.$acoCondition.' AND p.action_id '. $actionIdCondition)
204  ->queryAll()
205  ;
206  }
207 
212  public function beforeSave(){
214  //The Record is updated
215  if(!$this->isNewRecord){
216  $aro = self::getUser();
217 
218  if(!$aro->may($this, 'update'))
219  throw new RuntimeException('You are not allowed to update this record');
220  }
221 
222  return true;
223  }
224 
229  public function beforeDelete(){
231  $aro = self::getUser();
232  if(!$aro->may($this, 'delete'))
233  throw new RuntimeException('You are not allowed to delete this record');
234 
235  //Ok he has the right to do that - remove all the ACL-objects associated with this object
236  $class = Strategy::getClass('Aco');
237  $aco = $class::model()->find('model = :model AND foreign_key = :key', array(':model' => get_class($this), ':key' => $this->id));
238  if(!$aco)
239  throw new RuntimeException('No associated Aco!');
240 
241  if(!$aco->delete())
242  throw new RuntimeException('Unable to delete associated Aco');
243 
244  return true;
245  }
246 
247 
253  public function afterSave(){
255  if($this->isNewRecord){
256  $aro = self::getUser();
257  //As the object is newly created, it needs a representation
258  //If strict mode is disabled, this is not necessary
259  $class = Strategy::getClass('Aco');
260  $aco = new $class();
261  $aco->model = get_class($this);
262  $aco->foreign_key = $this->getPrimaryKey();
263 
264  if(!$aco->save()){
265  throw new RuntimeException('Unable to create corresponding Aco for new '.get_class($this));
266  }
267 
268  $aro->grant($aco, '*');
269  }
270  }
271 
272 
277  public function grants($permission){
278  $aro = self::getUser();
279  return $aro->may($this, $permission);
280  }
281 
289  public static function getUser(){
290 
291  if(self::$inAttendance !== NULL)
292  return self::$inAttendance;
293 
294  $user = Yii::app()->user;
295  $class = Strategy::getClass('Aro');
296  $aro = $class::model()->find('model = :model AND foreign_key = :foreign_key',
297  array('model' => static::$model, 'foreign_key' => $user->id));
298  if(!$aro)
299  throw new RuntimeException('Invalid Aro');
300  return $aro;
301  }
302 
303 
304 }
305 ?>