Skip to content

Instantly share code, notes, and snippets.

@bummzack
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bummzack/2a6c5a3109a4ada75b1c to your computer and use it in GitHub Desktop.
Save bummzack/2a6c5a3109a4ada75b1c to your computer and use it in GitHub Desktop.
Extension for member based API access (SilverStripe RESTfulAPI)
<?php
/**
* Extension that adds permissions for object owners
*
* The desired permissions have to be set as extension parameters.
* Allowed permission values are:
* - view
* - edit
* - delete
*
* 'create' cannot be set as permission, since this extensions needs
* to have an owner present (eg, the object has to be created first).
*
* Example for yml config
* MyDataObject:
* extensions:
* - OwnerCanExtension('view', 'edit')
*
* @author bummzack
*/
class OwnerCanExtension extends DataExtension
{
private static $has_one = array(
'DataOwner' => 'Member'
);
private static $class_perms = array();
private static $perms = array(
'view', 'edit', 'delete'
);
// Cache the class arguments for later access
public static function add_to_class($class, $extensionClass, $args = null) {
if($args && is_array($args)){
$key = $class . '_' . $extensionClass;
foreach($args as $arg){
if(in_array($arg, self::$perms)){
self::$class_perms[$key][] = $arg;
}
}
}
}
/**
* Restful API onAfterSerialize hook.
* Remove dataOwner ID from the serialized output
* @param $data
*/
public function onAfterSerialize(&$data){
unset($data['dataOwner']);
}
/**
* Restful API onAfterDeserialize hook.
* Don't allow setting of the DataOwner via REST API
* @param $data
*/
public function onAfterDeserialize(&$data){
unset($data['DataOwner']);
}
/**
* Upon writing, set the owner to the current member (if not already set)
*/
public function onBeforeWrite() {
if(!$this->owner->DataOwnerID){
$this->owner->DataOwnerID = Member::currentUserID();
}
}
public function canView($member){
return $this->checkAccess($member, 'view');
}
public function canEdit($member){
return $this->checkAccess($member, 'edit');
}
public function canDelete($member){
return $this->checkAccess($member, 'delete');
}
protected function checkAccess($member, $perm)
{
$key = $this->ownerBaseClass . '_' . $this->class;
// check if the extension should check the given permission, if not, don't influence the outcome
if(!in_array($perm, self::$class_perms[$key])){
return null;
}
$ownerID = $this->owner->DataOwnerID;
// check if there's no owner of this object
if(!$ownerID){
if($this->owner->exists()){
// when the object is persisted, require elevated permissions!
return Permission::check('ADMIN', 'any', $member);
} else {
// if the object isn't written yet, don't influence the outcome
return null;
}
}
$memberId = 0;
if($member instanceof Member && $member->ID){
$memberId = $member->ID;
} else if(is_numeric($member)){
$memberId = $member;
} else {
$memberId = Member::currentUserID();
}
if(!$memberId){
// we can't determine ownership, so don't influence the outcome
return null;
}
return $memberId == $ownerID;
}
public function validate(ValidationResult $validationResult){
$ownerID = $this->owner->DataOwnerID;
if($ownerID && $this->owner->DataOwnerID != Member::currentUserID()){
$validationResult->error('Only owner is allowed to write');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment