Webylon 3.2 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • 1c
    • exchange
      • catalog
  • auth
  • Booking
  • building
    • company
  • cart
    • shipping
    • steppedcheckout
  • Catalog
    • monument
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • Authenticator
  • BasicAuth
  • ChangePasswordForm
  • Group
  • GroupCsvBulkLoader
  • LoginAttempt
  • LoginForm
  • Member
  • Member_ChangePasswordEmail
  • Member_ForgotPasswordEmail
  • Member_GroupSet
  • Member_ProfileForm
  • Member_SignupEmail
  • Member_Validator
  • MemberAuthenticator
  • MemberCsvBulkLoader
  • MemberLoginForm
  • MemberPassword
  • NZGovtPasswordValidator
  • PasswordEncryptor
  • PasswordEncryptor_LegacyPHPHash
  • PasswordEncryptor_MySQLOldPassword
  • PasswordEncryptor_MySQLPassword
  • PasswordEncryptor_None
  • PasswordEncryptor_PHPHash
  • PasswordValidator
  • Permission
  • Permission_Group
  • PermissionCheckboxSetField
  • PermissionCheckboxSetField_Readonly
  • PermissionRole
  • PermissionRoleCode
  • Security

Interfaces

  • PermissionProvider

Exceptions

  • PasswordEncryptor_NotFoundException
  1 <?php
  2 /**
  3  * Represents a permission assigned to a group.
  4  * @package sapphire
  5  * @subpackage security
  6  */
  7 class Permission extends DataObject {
  8 
  9   // the (1) after Type specifies the DB default value which is needed for
 10     // upgrades from older SilverStripe versions
 11     static $db = array(
 12         "Code" => "Varchar",
 13         "Arg" => "Int",
 14         "Type" => "Int(1)"
 15     );
 16     static $has_one = array(
 17         "Group" => "Group"
 18     );
 19     static $indexes = array(
 20         "Code" => true
 21     );
 22     static $defaults = array(
 23         "Type" => 1
 24     );
 25     static $has_many = array();
 26     
 27     static $many_many = array();
 28     
 29     static $belongs_many_many = array();
 30 
 31     /**
 32      * This is the value to use for the "Type" field if a permission should be
 33      * granted.
 34      */
 35     const GRANT_PERMISSION = 1;
 36 
 37     /**
 38      * This is the value to use for the "Type" field if a permission should be
 39      * denied.
 40      */
 41     const DENY_PERMISSION = -1;
 42 
 43     /**
 44      * This is the value to use for the "Type" field if a permission should be
 45      * inherited.
 46      */
 47     const INHERIT_PERMISSION = 0;
 48 
 49 
 50     /**
 51      * Method to globally disable "strict" checking, which means a permission
 52      * will be granted if the key does not exist at all.
 53      *
 54      * @var bool
 55      */
 56     static $declared_permissions = null;
 57 
 58   /**
 59      * Linear list of declared permissions in the system.
 60      *
 61      * @var array
 62      */
 63     protected static $declared_permissions_list = null;
 64 
 65     /**
 66      * @var $strict_checking Boolean Method to globally disable "strict" checking,
 67      * which means a permission will be granted if the key does not exist at all.
 68      */
 69     static $strict_checking = true;
 70     
 71     /**
 72      * If this setting is set, then permissions can imply other permissions
 73      *
 74      * @var bool
 75      */
 76     static $implied_permissions = false;
 77 
 78     /**
 79      * Set to false to prevent the 'ADMIN' permission from implying all
 80      * permissions in the system
 81      *
 82      * @var bool
 83      */
 84     static $admin_implies_all = true;
 85     
 86     /**
 87      * a list of permission codes which doesn't appear in the Permission list
 88      * when make the {@link PermissionCheckboxSetField}
 89      * @var array;
 90      */
 91     static $hidden_permissions = array();
 92 
 93     /**
 94      * Check that the current member has the given permission.
 95      * 
 96      * @param string $code Code of the permission to check (case-sensitive)
 97      * @param string $arg Optional argument (e.g. a permissions for a specific page)
 98      * @param int|Member $member Optional member instance or ID. If set to NULL, the permssion
 99      *  will be checked for the current user
100      * @param bool $strict Use "strict" checking (which means a permission
101      *  will be granted if the key does not exist at all)?
102      * @return int|bool The ID of the permission record if the permission
103      *  exists; FALSE otherwise. If "strict" checking is
104      *  disabled, TRUE will be returned if the permission does not exist at all.
105      */
106     public static function check($code, $arg = "any", $member = null, $strict = true) {
107         if(!$member) {
108             if(!Member::currentUserID()) {
109                 return false;
110             }
111             $member = Member::currentUserID();
112         }
113 
114         return self::checkMember($member, $code, $arg, $strict);
115     }
116 
117     /**
118      * Permissions cache.  The format is a map, where the keys are member IDs, and the values are
119      * arrays of permission codes.
120      */
121     private static $cache_permissions = array();
122 
123     /**
124      * Flush the permission cache, for example if you have edited group membership or a permission record.
125      * @todo Call this whenever Group_Members is added to or removed from
126      */
127     public static function flush_permission_cache() {
128         self::$cache_permissions = array();
129     }
130 
131     /**
132      * Check that the given member has the given permission.
133      * 
134      * @param int|Member memberID The ID of the member to check. Leave blank for the current member. 
135      *  Alternatively you can use a member object.
136      * @param string|array $code Code of the permission to check (case-sensitive)
137      * @param string $arg Optional argument (e.g. a permissions for a specific page)
138      * @param bool $strict Use "strict" checking (which means a permission
139      *  will be granted if the key does not exist at all)?
140      * @return int|bool The ID of the permission record if the permission
141      *  exists; FALSE otherwise. If "strict" checking is
142      *  disabled, TRUE will be returned if the permission does not exist at all.
143      */
144     public static function checkMember($member, $code, $arg = "any", $strict = true) {
145         if(!$member) {
146             $memberID = $member = Member::currentUserID();
147         } else {
148             $memberID = (is_object($member)) ? $member->ID : $member; 
149         }
150         
151         if($arg == 'any') {
152             // Cache the permissions in memory
153             if(!isset(self::$cache_permissions[$memberID])) {
154                 self::$cache_permissions[$memberID] = self::permissions_for_member($memberID);
155             }
156             
157             // If $admin_implies_all was false then this would be inefficient, but that's an edge
158             // case and this keeps the code simpler
159             if(!is_array($code)) $code = array($code);
160             if(self::$admin_implies_all) $code[] = "ADMIN";
161 
162             // Multiple $code values - return true if at least one matches, ie, intersection exists
163             return (bool)array_intersect($code, self::$cache_permissions[$memberID]);
164         } 
165 
166         // The following code should only be used if you're not using the "any" arg.  This is kind
167         // of obselete functionality and could possibly be deprecated.
168 
169         $groupList = self::groupList($memberID);
170         if(!$groupList) return false;
171         
172         $groupCSV = implode(", ", $groupList);
173         
174         // Arg component
175         switch($arg) {
176             case "any":
177                 $argClause = "";
178                 break;
179             case "all":
180                 $argClause = " AND \"Arg\" = -1";
181                 break;
182             default:
183                 if(is_numeric($arg)) {
184                     $argClause = "AND \"Arg\" IN (-1, $arg) ";
185                 } else {
186                     user_error("Permission::checkMember: bad arg '$arg'", E_USER_ERROR);
187                 }
188         }
189         
190         if(is_array($code)) {
191             $SQL_codeList = "'" . implode("', '", Convert::raw2sql($code)) . "'";
192         } else {
193             $SQL_codeList = "'" . Convert::raw2sql($code) . "'";
194         }
195         
196         $SQL_code = Convert::raw2sql($code);
197         
198         $adminFilter = (self::$admin_implies_all) ?  ",'ADMIN'" : '';
199 
200         // Raw SQL for efficiency
201         $permission = DB::query("
202             SELECT \"ID\"
203             FROM \"Permission\"
204             WHERE (
205                 \"Code\" IN ($SQL_codeList $adminFilter)
206                 AND \"Type\" = " . self::GRANT_PERMISSION . "
207                 AND \"GroupID\" IN ($groupCSV)
208                 $argClause
209             )
210         ")->value();
211 
212         if($permission) return $permission;
213 
214         // Strict checking disabled?
215         if(!self::$strict_checking || !$strict) {
216             $hasPermission = DB::query("
217                 SELECT COUNT(*) 
218                 FROM \"Permission\"
219                 WHERE (
220                     (\"Code\" IN '$SQL_code')' 
221                     AND (\"Type\" = " . self::GRANT_PERMISSION . ")
222                 )
223             ")->value();
224 
225             if(!$hasPermission) return;
226         }
227         
228         return false;
229     }
230 
231     /**
232      * Get all the 'any' permission codes available to the given member.
233      * @return array();
234      */
235     public static function permissions_for_member($memberID) {
236         $groupList = self::groupList($memberID);
237         if($groupList) {
238             $groupCSV = implode(", ", $groupList);
239 
240             // Raw SQL for efficiency
241             return array_unique(DB::query("
242                 SELECT \"Code\"
243                 FROM \"Permission\"
244                 WHERE \"Type\" = " . self::GRANT_PERMISSION . " AND \"GroupID\" IN ($groupCSV)
245                 
246                 UNION
247                 
248                 SELECT \"Code\"
249                 FROM \"PermissionRoleCode\" AS PRC
250                 INNER JOIN \"PermissionRole\" AS PR ON PRC.\"RoleID\" = PR.\"ID\"
251                 INNER JOIN \"Group_Roles\" AS GR ON GR.\"PermissionRoleID\" = PR.\"ID\"
252                 WHERE \"GroupID\" IN ($groupCSV)
253             ")->column());
254 
255         } else {
256             return array();
257         }
258     }
259 
260 
261     /**
262      * Get the list of groups that the given member belongs to.
263      *
264      * Call without an argument to get the groups that the current member
265      * belongs to. In this case, the results will be session-cached.
266      *
267      * @param int $memberID The ID of the member. Leave blank for the current
268      *                      member.
269      * @return array Returns a list of group IDs to which the member belongs
270      *               to or NULL.
271      */
272     public static function groupList($memberID = null) {
273         // Default to current member, with session-caching
274         if(!$memberID) {
275             $member = Member::currentUser();
276             if($member && isset($_SESSION['Permission_groupList'][$member->ID]))
277                 return $_SESSION['Permission_groupList'][$member->ID];
278         } else {
279             $member = DataObject::get_by_id("Member", $memberID);
280         }
281 
282         if($member) {
283             // Build a list of the IDs of the groups.  Most of the heavy lifting
284             // is done by Member::Groups
285             // NOTE: This isn't effecient; but it's called once per session so
286             // it's a low priority to fix.
287             $groups = $member->Groups();
288             $groupList = array();
289 
290             if($groups) {
291                 foreach($groups as $group)
292                     $groupList[] = $group->ID;
293             }
294 
295 
296             // Session caching
297             if(!$memberID) {
298                 $_SESSION['Permission_groupList'][$member->ID] = $groupList;
299             }
300             
301             return isset($groupList) ? $groupList : null;
302         }
303     }
304 
305 
306     /**
307      * Grant the given permission code/arg to the given group
308      *
309      * @param int $groupID The ID of the group
310      * @param string $code The permission code
311      * @param string Optional: The permission argument (e.g. a page ID).
312      * @returns Permission Returns the new permission object.
313      */
314     public static function grant($groupID, $code, $arg = "any") {
315         $perm = new Permission();
316         $perm->GroupID = $groupID;
317         $perm->Code = $code;
318         $perm->Type = self::GRANT_PERMISSION;
319 
320         // Arg component
321         switch($arg) {
322             case "any":
323                 break;
324             case "all":
325                 $perm->Arg = -1;
326             default:
327                 if(is_numeric($arg)) {
328                     $perm->Arg = $arg;
329                 } else {
330                     user_error("Permission::checkMember: bad arg '$arg'",
331                                         E_USER_ERROR);
332                 }
333         }
334 
335         $perm->write();
336         return $perm;
337     }
338 
339 
340     /**
341      * Deny the given permission code/arg to the given group
342      *
343      * @param int $groupID The ID of the group
344      * @param string $code The permission code
345      * @param string Optional: The permission argument (e.g. a page ID).
346      * @returns Permission Returns the new permission object.
347      */
348     public static function deny($groupID, $code, $arg = "any") {
349         $perm = new Permission();
350         $perm->GroupID = $groupID;
351         $perm->Code = $code;
352         $perm->Type = self::DENY_PERMISSION;
353 
354         // Arg component
355         switch($arg) {
356             case "any":
357                 break;
358             case "all":
359                 $perm->Arg = -1;
360             default:
361                 if(is_numeric($arg)) {
362                     $perm->Arg = $arg;
363                 } else {
364                     user_error("Permission::checkMember: bad arg '$arg'",
365                                         E_USER_ERROR);
366                 }
367         }
368 
369         $perm->write();
370         return $perm;
371     }
372 
373     /**
374      * Returns all members for a specific permission.
375      * 
376      * @param $code String|array Either a single permission code, or a list of permission codes
377      * @return DataObjectSet Returns a set of member that have the specified
378      *                       permission.
379      */
380     public static function get_members_by_permission($code) {
381         $toplevelGroups = self::get_groups_by_permission($code);
382         if (!$toplevelGroups) return false;
383 
384         $groupIDs = array();
385         foreach($toplevelGroups as $group) {
386             $familyIDs = $group->collateFamilyIDs();
387             if(is_array($familyIDs)) {
388                 $groupIDs = array_merge($groupIDs, array_values($familyIDs));
389             }
390         }
391 
392         if(!count($groupIDs))
393             return false;
394             
395         $members = DataObject::get(
396             Object::getCustomClass('Member'),
397             $_filter = "\"Group\".\"ID\" IN (" . implode(",",$groupIDs) . ")",
398             $_sort = "",
399             $_join = "LEFT JOIN \"Group_Members\" ON \"Member\".\"ID\" = \"Group_Members\".\"MemberID\" " . 
400                 "LEFT JOIN \"Group\" ON \"Group_Members\".\"GroupID\" = \"Group\".\"ID\" "
401         );
402         
403         return $members;
404     }
405 
406     /**
407      * Return all of the groups that have one of the given permission codes
408      * @param $codes array|string Either a single permission code, or an array of permission codes
409      * @return DataObjectSet The matching group objects
410      */
411     static function get_groups_by_permission($codes) {
412         if(!is_array($codes)) $codes = array($codes);
413         
414         $SQLa_codes = Convert::raw2sql($codes);
415         $SQL_codes = join("','", $SQLa_codes);
416         
417         // Via Roles are groups that have the permission via a role
418         return DataObject::get('Group',
419             "\"PermissionRoleCode\".\"Code\" IN ('$SQL_codes') OR \"Permission\".\"Code\" IN ('$SQL_codes')",
420             "",
421             "LEFT JOIN \"Permission\" ON \"Permission\".\"GroupID\" = \"Group\".\"ID\"
422             LEFT JOIN \"Group_Roles\" ON \"Group_Roles\".\"GroupID\" = \"Group\".\"ID\"
423             LEFT JOIN \"PermissionRole\" ON \"Group_Roles\".\"PermissionRoleID\" = \"PermissionRole\".\"ID\"
424             LEFT JOIN \"PermissionRoleCode\" ON \"PermissionRoleCode\".\"RoleID\" = \"PermissionRole\".\"ID\"");
425     }
426 
427 
428     /**
429      * Get a list of all available permission codes, both defined through the
430      * {@link PermissionProvider} interface, and all not explicitly defined codes existing
431      * as a {@link Permission} database record. By default, the results are
432      * grouped as denoted by {@link Permission_Group}.
433      *
434      * @param bool $grouped Group results into an array of permission groups.
435      * @return array Returns an array of all available permission codes. The
436      *  array indicies are the permission codes as used in
437      *  {@link Permission::check()}. The value is a description
438      *  suitable for using in an interface.
439      */
440     public static function get_codes($grouped = true) {
441         $classes = ClassInfo::implementorsOf('PermissionProvider');
442 
443         $allCodes = array();
444         $adminCategory = _t('Permission.AdminGroup', 'Administrator');
445         $allCodes[$adminCategory]['ADMIN'] = array(
446             'name' => _t('Permission.FULLADMINRIGHTS', 'Full administrative rights'),
447             'help' => _t(
448                 'Permission.FULLADMINRIGHTS_HELP',
449                 'Implies and overrules all other assigned permissions.'
450             ),
451             'sort' => 100000
452         );
453         
454         if($classes) foreach($classes as $class) {
455             $SNG = singleton($class);
456             if($SNG instanceof TestOnly) continue;
457             
458             $someCodes = $SNG->providePermissions();
459             if($someCodes) {
460                 foreach($someCodes as $k => $v) {
461                     if (is_array($v)) {
462                         // There must be a category and name key.
463                         if (!isset($v['category'])) user_error("The permission $k must have a category key", E_USER_WARNING);
464                         if (!isset($v['name'])) user_error("The permission $k must have a name key", E_USER_WARNING);
465                         
466                         if (!isset($allCodes[$v['category']])) $allCodes[$v['category']] = array();
467                         
468                         $allCodes[$v['category']][$k] = array(
469                             'name' => $v['name'],
470                             'help' => isset($v['help']) ? $v['help'] : null,
471                             'sort' => isset($v['sort']) ? $v['sort'] : 0
472                         );
473                         
474                     } else {
475                         $allCodes[_t('Permission.OtherGroup', 'Other')][$k] = array(
476                             'name' => $v,
477                             'help' => null,
478                             'sort' => 0
479                         );
480                     }
481                 }
482             }
483         }
484 
485         $flatCodeArray = array();
486         foreach($allCodes as $category) foreach($category as $code => $permission) $flatCodeArray[] = $code;
487         $otherPerms = DB::query("SELECT DISTINCT \"Code\" From \"Permission\" WHERE \"Code\" != ''")->column();
488             
489         if($otherPerms) foreach($otherPerms as $otherPerm) {
490             if(!in_array($otherPerm, $flatCodeArray))
491                 $allCodes[_t('Permission.OtherGroup', 'Other')][$otherPerm] = array(
492                     'name' => _t('PermissionCode.' . $otherPerm, $otherPerm),
493                     'help' => null,
494                     'sort' => 0
495                 );
496         }
497 
498         // Don't let people hijack ADMIN rights
499         if(!Permission::check("ADMIN")) unset($allCodes['ADMIN']);
500         
501         ksort($allCodes);
502 
503         $returnCodes = array();
504         foreach($allCodes as $category => $permissions) {
505             if($grouped) {
506                 uasort($permissions, array(__CLASS__, 'sort_permissions'));
507                 $returnCodes[$category] = $permissions;
508             } else {
509                 $returnCodes = array_merge($returnCodes, $permissions);
510             }
511         }
512         
513         return $returnCodes;
514     }
515     
516     /**
517      * Sort permissions based on their sort value, or name
518      *
519      */
520     static function sort_permissions($a, $b) {
521         if ($a['sort'] == $b['sort']) {
522             // Same sort value, do alpha instead
523             return strcmp($a['name'], $b['name']);
524         } else {
525             // Just numeric.
526             return $a['sort'] < $b['sort'] ? -1 : 1;
527         }
528     }
529     
530     /**
531      * add a permission represented by the $code to the {@link slef::$hidden_permissions} list
532      *
533      * @param $code string - the permissions code
534      * @return void
535      */
536     static function add_to_hidden_permissions($code){
537         self::$hidden_permissions[] = $code;
538     }
539     
540     /**
541      * remove a permission represented by the $code from the {@link slef::$hidden_permissions} list
542      *
543      * @param $code string - the permissions code
544      * @return void
545      */
546     static function remove_from_hidden_permissions($code){
547         self::$hidden_permissions = array_diff(self::$hidden_permissions, array($code));
548     }
549 
550     /**
551      * Declare an array of permissions for the system.
552      *
553      * Permissions can be grouped by nesting arrays. Scalar values are always
554      * treated as permissions.
555      *
556      * @param array $permArray A (possibly nested) array of permissions to
557      *                         declare for the system.
558      */
559     static function declare_permissions($permArray) {
560         if(is_array(self::$declared_permissions)) {
561             self::$declared_permissions =
562                 array_merge_recursive(self::$declared_permissions, $permArray);
563         }
564         else {
565             self::$declared_permissions = $permArray;
566         }
567     }
568 
569 
570     /**
571      * Get a linear list of the permissions in the system.
572      *
573      * @return array Linear list of declared permissions in the system.
574      */
575     public static function get_declared_permissions_list() {
576         if(!self::$declared_permissions)
577             return null;
578 
579         if(self::$declared_permissions_list)
580             return self::$declared_permissions_list;
581 
582         self::$declared_permissions_list = array();
583 
584         self::traverse_declared_permissions(self::$declared_permissions,
585                                                                                 self::$declared_permissions_list);
586 
587         return self::$declared_permissions_list;
588     }
589 
590     /**
591      * Look up the human-readable title for the permission as defined by <code>Permission::declare_permissions</code>
592      * 
593      * @param $perm Permission code
594      * @return Label for the given permission, or the permission itself if the label doesn't exist
595      */
596     public static function get_label_for_permission($perm) {
597         $list = self::get_declared_permissions_list();
598         if(array_key_exists($perm, $list)) return $list[$perm];
599         return $perm;
600     }
601 
602     /**
603      * Recursively traverse the nested list of declared permissions and create
604      * a linear list.
605      *
606      * @param aeeay $declared Nested structure of permissions.
607      * @param $list List of permissions in the structure. The result will be
608      *              written to this array.
609      */
610     protected static function traverse_declared_permissions($declared,
611                                                                                                                     &$list) {
612         if(!is_array($declared))
613             return;
614 
615         foreach($declared as $perm => $value) {
616             if($value instanceof Permission_Group) {
617                 $list[] = $value->getName();
618                 self::traverse_declared_permissions($value->getPermissions(), $list);
619             } else {
620                 $list[$perm] = $value;
621             }
622         }
623     }
624     
625     public function onBeforeWrite() {
626         parent::onBeforeWrite();
627         
628         // Just in case we've altered someone's permissions
629         Permission::flush_permission_cache();
630     }
631 }
632 
633 
634 /**
635  * Permission_Group class
636  *
637  * This class is used to group permissions together for showing on an
638  * interface.
639  * @package sapphire
640  * @subpackage security
641  */
642 class Permission_Group {
643 
644     /**
645      * Name of the permission group (can be used as label in an interface)
646      * @var string
647      */
648     protected $name;
649 
650     /**
651      * Associative array of permissions in this permission group. The array
652      * indicies are the permission codes as used in
653      * {@link Permission::check()}. The value is suitable for using in an
654      * interface.
655      * @var string
656      */
657     protected $permissions = array();
658 
659 
660     /**
661      * Constructor
662      *
663      * @param string $name Text that could be used as label used in an
664      *                     interface
665      * @param array $permissions Associative array of permissions in this
666      *                           permission group. The array indicies are the
667      *                           permission codes as used in
668      *                           {@link Permission::check()}. The value is
669      *                           suitable for using in an interface.
670      */
671     public function __construct($name, $permissions) {
672         $this->name = $name;
673         $this->permissions = $permissions;
674     }
675 
676     /**
677      * Get the name of the permission group
678      *
679      * @return string Name (label) of the permission group
680      */
681     public function getName() {
682         return $this->name;
683     }
684 
685 
686     /**
687      * Get permissions
688      *
689      * @return array Associative array of permissions in this permission
690      *               group. The array indicies are the permission codes as
691      *               used in {@link Permission::check()}. The value is
692      *               suitable for using in an interface.
693      */
694     public function getPermissions() {
695         return $this->permissions;
696     }
697 }
698 
699 ?>
700 
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.2 API Docs API documentation generated by ApiGen 2.8.0