Skip to content
Commits on Source (13)
<?php
namespace Minds\Common;
use ReflectionClass;
abstract class ChannelMode
{
const OPEN = 0;
const MODERATED = 1;
const CLOSED = 2;
final public static function toArray() : array
{
return (new ReflectionClass(static::class))->getConstants();
}
final public static function isValid($value) : bool
{
return in_array($value, static::toArray(), true);
}
}
......@@ -12,6 +12,7 @@ use Minds\Helpers;
use Minds\Interfaces;
use Minds\Entities;
use Minds\Api\Factory;
use Minds\Common\ChannelMode;
use ElggFile;
class channel implements Interfaces\Api
......@@ -255,6 +256,10 @@ class channel implements Interfaces\Api
}
}
if (isset($_POST['mode']) && ChannelMode::isValid($_POST['mode'])) {
$update['mode'] = $_POST['mode'];
}
if (isset($_POST['social_profiles']) && is_array($_POST['social_profiles'])) {
$profiles = [];
......
......@@ -88,7 +88,7 @@ class feed implements Interfaces\Api
/** @var Core\Boost\Network\Iterator $iterator */
$iterator = Core\Di\Di::_()->get('Boost\Network\Iterator');
$iterator
->setLimit(12)
->setLimit(10)
->setOffset($offset)
->setRating($rating)
->setQuality($quality)
......@@ -118,11 +118,13 @@ class feed implements Interfaces\Api
$next = $iterator->getOffset();
if (isset($boosts[1]) && !$isBoostFeed) { // Always offset to 2rd in list if in rotator
if (!$offset) {
$next = $boosts[1]->getTimestamp();
} else {
$next = 0;
}
// if (!$offset) {
// $next = $boosts[1]->getTimestamp();
// } else {
// $next = 0;
// }
$len = count($boosts);
$next = $boosts[$len -1]->getTimestamp();
} elseif ($isBoostFeed) {
$len = count($boosts);
$next = $boosts[$len -1]->getTimestamp();
......@@ -130,7 +132,7 @@ class feed implements Interfaces\Api
// $ttl = 1800; // 30 minutes
// if (($next / 1000) < strtotime('48 hours ago')) {
$ttl = 150; // 2.5 minutes;
$ttl = 300; // 5 minutes;
// }
$cacher->set(Core\Session::getLoggedinUser()->guid . ':boost-offset-rotator', $next, $ttl);
......
......@@ -57,6 +57,9 @@ class container implements Interfaces\Api
case 'blogs':
$type = 'object:blog';
break;
case 'all':
$type = 'all';
break;
}
//
......
......@@ -45,6 +45,13 @@ class comments implements Interfaces\Api
$entitiesBuilder = Di::_()->get('EntitiesBuilder');
$entity = $entitiesBuilder->single($pages[0]);
if (!$entity) {
return Factory::response([
'status' => 'error',
'message' => 'entity not found',
]);
}
if (!$entity->canEdit($owner)) {
return Factory::response([
'status' => 'error',
......@@ -53,8 +60,8 @@ class comments implements Interfaces\Api
}
/** @var PermissionsManager */
$manager = Di::_()->get('Permissions\Manager');
$permissions = new Permissions();
$manager = Di::_()->get('Permissions\Entities\Manager');
$permissions = new EntityPermissions();
$permissions->setAllowComments($allowed);
$manager->save($entity, $permissions);
......
<?php
namespace Minds\Controllers\api\v2\permissions;
use Minds\Api\Factory;
use Minds\Interfaces;
use Minds\Core\Di\Di;
class roles implements Interfaces\Api
{
public function get($pages)
{
Factory::isLoggedIn();
if (!isset($pages[0])) {
return Factory::response([
'status' => 'error',
'message' => 'User guid must be provided',
]);
}
try {
/** @var Core\Permissions\Manager $manager */
$manager = Di::_()->get('Permissions\Manager');
$opts = [
'user_guid' => $pages[0],
'guids' => $_GET['guids'],
];
$permissions = $manager->getList($opts);
return Factory::response([
'status' => 'success',
'roles' => $permissions,
]);
} catch (Exception $ex) {
return Factory::response([
'status' => 'error',
'message' => $ex->getMessage(),
]);
}
return Factory::response([]);
}
public function post($pages)
{
// TODO: Implement put() method.
}
public function put($pages)
{
// TODO: Implement put() method.
}
public function delete($pages)
{
// TODO: Implement put() method.
}
}
......@@ -175,7 +175,7 @@ class Manager
//filter to get todays offchain transactions
$offlineToday = array_filter($offchain->toArray(), function ($result) {
return $result->getCreatedTimestamp() > time() - (60 * 60 * 24);
return $result->getCreatedTimestamp() > (time() - (60 * 60 * 24)) * 1000;
});
//reduce the impressions to count the days boosts.
......
......@@ -192,7 +192,7 @@ class Push implements Interfaces\NotificationExtensionInterface
* @param mixed $entity
* @return string
*/
protected static function buildNotificationMessage(array $notification = [], $from_user, $entity)
public static function buildNotificationMessage(array $notification = [], $from_user, $entity)
{
$message = '';
......@@ -207,8 +207,22 @@ class Push implements Interfaces\NotificationExtensionInterface
$name = $from_user->name;
$isOwner = $notification['to'] == $entity->owner_guid;
$prefix = $isOwner ? 'your ': $entity->ownerObj['name']."'s ";
$desc = ($entity->type == 'activity') ? 'activity': $entity->subtype;
$prefix = '';
if ($isOwner) {
$prefix = 'your ';
} elseif (isset($entity->ownerObj['name'])) {
$prefix = $entity->ownerObj['name'].'\'s ';
}
$desc = 'a post';
if ($entity->type == 'activity') {
$desc = 'activity';
} elseif (isset($entity->subtype)) {
$desc = $entity->subtype;
} elseif ($isOwner || isset($entity->ownerObj['name'])) {
$desc = 'post';
}
$boostDescription = $entity->title ?: $entity->name ?: ($entity->type !== 'user' ? 'post' : 'channel');
......
<?php
namespace Minds\Core\Permissions\Delegates;
use Minds\Core\Permissions\Roles\Roles;
use Minds\Entities\User;
abstract class BaseRoleCalculator
{
/** @var Roles */
protected $roles;
/** @var User */
protected $user;
public function __construct(User $user, Roles $roles = null)
{
$this->roles = $roles ?: new Roles();
$this->user = $user;
}
abstract public function calculate($entity);
}
<?php
namespace Minds\Core\Permissions\Delegates;
use Minds\Traits\MagicAttributes;
use Minds\Core\Permissions\Roles\Roles;
use Minds\Core\Permissions\Roles\Role;
class ChannelRoleCalculator extends BaseRoleCalculator
{
use MagicAttributes;
private $channels = [];
/**
* Retrieves permissions for an entity relative to the user's role in a channel
* Retrieves the role from the in memory cache if we've seen this channel before during this request
* Else checks the user's membership against the channel.
*
* @param $entity an entity from a channel
*
* @return Role
*/
public function calculate($entity): Role
{
if (isset($this->channels[$entity->getOwnerGUID()])) {
return $this->channels[$entity->getOwnerGUID()];
}
$role = null;
if ($entity->getOwnerGUID() === $this->user->getGUID()) {
$role = $this->roles->getRole(Roles::ROLE_CHANNEL_OWNER);
} elseif ($this->user->isSubscribed($entity->getOwnerGUID())) {
$role = $this->roles->getRole(Roles::ROLE_CHANNEL_SUBSCRIBER);
} else {
$role = $this->roles->getRole(Roles::ROLE_CHANNEL_NON_SUBSCRIBER);
}
$this->channels[$entity->getOwnerGUID()] = $role;
return $role;
}
}
<?php
namespace Minds\Core\Permissions\Delegates;
use Minds\Traits\MagicAttributes;
use Minds\Core\Di\Di;
use Minds\Core\Permissions\Roles\Role;
use Minds\Core\EntitiesBuilder;
use Minds\Entities\User;
use Minds\Core\Permissions\Roles\Roles;
class GroupRoleCalculator extends BaseRoleCalculator
{
use MagicAttributes;
/** @var EntitiesBuilder */
private $entitiesBuilder;
/** @var array */
private $groups = [];
public function __construct(User $user, Roles $roles, EntitiesBuilder $entitiesBuilder = null)
{
parent::__construct($user, $roles);
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
}
/**
* Retrieves permissions for an entity relative to the user's role in a group
* Retrieves the role from the in memory cache if we've seen this group before during this request
* Else gets the group and checks the user's membership.
*
* @param $entity an entity belonging to a group
*
* @return Role
*/
public function calculate($entity): Role
{
if (isset($this->groups[$entity->getAccessId()])) {
return $this->groups[$entity->getAccessId()];
}
$group = $this->entitiesBuilder->single($entity->getAccessId());
$role = null;
if ($group->isCreator($this->user)) {
$role = $this->roles->getRole(Roles::ROLE_GROUP_OWNER);
} elseif ($group->isOwner($this->user)) {
$role = $this->roles->getRole(Roles::ROLE_GROUP_ADMIN);
} elseif ($group->isBanned($this->user)) {
$role = $this->roles->getRole(Roles::ROLE_BANNED);
} elseif ($group->isModerator($this->user)) {
$role = $this->roles->getRole(Roles::ROLE_GROUP_MODERATOR);
} elseif ($group->isMember($this->user)) {
$role = $this->roles->getRole(Roles::ROLE_GROUP_SUBSCRIBER);
} else {
$role = $this->roles->getRole(Roles::ROLE_GROUP_NON_SUBSCRIBER);
}
$this->groups[$entity->getAccessId()] = $role;
return $role;
}
}
<?php
namespace Minds\Core\Permissions\Entities;
use Minds\Traits\MagicAttributes;
/**
* Class Permissions
* @method Permissions setAllowComments(bool $allowComments)
* @method bool getAllowComments();
*/
class EntityPermissions
{
use MagicAttributes;
/** @var bool AllowComments */
private $allowComments = true;
}
<?php
namespace Minds\Core\Permissions\Entities;
use Minds\Core\Di\Di;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Data\Call;
use Minds\Core\Entities\Actions\Save;
/*
* Manager for managing entity specific permissions
*/
class Manager
{
/** @var EntitiesBuilder $entitiesBuilder */
protected $entitiesBuilder;
/** @var Call */
protected $db;
/** @var Save */
protected $save;
public function __construct(
EntitiesBuilder $entitiesBuilder = null,
Call $db = null,
Save $save = null)
{
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->db = $db ?: new Call('entities_by_time');
$this->save = $save ?: new Save(); //Mockable, else instantiate a new one on save.
}
/**
* Save permissions for an entity and propegate it to linked objects.
*
* @param mixed $entity a minds entity that implements the save function
* @param Permissions $permissions the flag to apply to the entity
*/
public function save($entity, EntityPermissions $permissions): void
{
$entity->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($entity)
->save();
if (method_exists($entity, 'getType')
&& $entity->getType() == 'activity'
&& $entity->get('entity_guid')
) {
$attachment = $this->entitiesBuilder->single($entity->get('entity_guid'));
$attachment->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($attachment)
->save();
}
foreach ($this->db->getRow('activity:entitylink:'.$entity->getGUID()) as $parentGuid => $ts) {
$activity = $this->entitiesBuilder->single($parentGuid);
$activity->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($activity)
->save();
}
}
}
......@@ -3,61 +3,57 @@
namespace Minds\Core\Permissions;
use Minds\Core\Di\Di;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Data\Call;
use Minds\Core\Entities\Actions\Save;
use Minds\Core\Permissions\Permissions;
/*
* Manager for managing role based permissions
*/
class Manager
{
/** @var EntitiesBuilder $entitiesBuilder */
protected $entitiesBuilder;
/** @var Call */
protected $db;
/** @var Save */
protected $save;
public function __construct(
EntitiesBuilder $entitiesBuilder = null,
Call $db = null,
Save $save = null)
/** @var EntityBuilder */
private $entityBuilder;
public function __construct($entityBuilder = null)
{
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->db = $db ?: new Call('entities_by_time');
$this->save = $save ?: new Save(); //Mockable, else instantiate a new one on save.
}
/**
* Save permissions for an entity and propegate it to linked objects
* @param mixed $entity a minds entity that implements the save function
* @param Permissions $permissions the flag to apply to the entity
*/
public function save($entity, Permissions $permissions)
* Takes a user_guid and list of entity guids
* Builds up a permissions object
* Permissions contains the user's role per entity, channel and group.
*
* @param array $opts
* - user_guid: long, the user's guid for calculating permissions
* - guids: array long, the list of entities to permit
*
* @return Permissions A map of channels, groups and entities with the user's role for each
*/
public function getList(array $opts = []): Permissions
{
$entity->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($entity)
->save();
if (method_exists($entity, 'getType')
&& $entity->getType() == 'activity'
&& $entity->get('entity_guid')
) {
$attachment = $this->entitiesBuilder->single($entity->get('entity_guid'));
$attachment->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($attachment)
->save();
$opts = array_merge([
'user_guid' => null,
'guids' => [],
], $opts);
if ($opts['user_guid'] === null) {
throw new \InvalidArgumentException('user_guid is required');
}
$user = $this->entitiesBuilder->single($opts['user_guid']);
$entities = $this->entitiesBuilder->get($opts);
if ($user->getType() !== 'user') {
throw new \InvalidArgumentException('Entity is not a user');
}
foreach ($this->db->getRow('activity:entitylink:'.$entity->getGUID()) as $parentGuid => $ts) {
$activity = $this->entitiesBuilder->single($parentGuid);
$activity->setAllowComments($permissions->getAllowComments());
$this->save
->setEntity($activity)
->save();
$roles = new Roles();
/** @var Permissions */
$permissions = new Permissions($user, null, $entitiesBuilder);
if (is_array($entities)) {
$permissions->calculate($entities);
}
return $permissions;
}
}
......@@ -3,16 +3,143 @@
namespace Minds\Core\Permissions;
use Minds\Traits\MagicAttributes;
use Minds\Entities\User;
use Minds\Core\EntitiesBuilder;
use Minds\Core\Permissions\Roles\Roles;
use Minds\Core\Permissions\Roles\Role;
use Minds\Core\Permissions\Delegates\ChannelRoleCalculator;
use Minds\Core\Permissions\Delegates\GroupRoleCalculator;
use Minds\Common\Access;
use Minds\Core\Di\Di;
use Minds\Exceptions\ImmutableException;
/**
* Class Permissions
* @method Permissions setAllowComments(bool $allowComments)
* @method bool getAllowComments();
*/
class Permissions
class Permissions implements \JsonSerializable
{
use MagicAttributes;
/** @var bool AllowComments */
private $allowComments = true;
/** @var bool */
private $isAdmin = false;
/** @var bool */
private $isBanned = false;
/** @var User */
private $user;
/** @var Roles */
private $roles;
/** @var array */
private $entities;
/** @var ChannelRoleCalculator */
private $channelRoleCalculator;
/** @var GroupRoleCalculator */
private $groupRoleCalculator;
/** @var EntitiesBuilder */
private $entitiesBuilder;
public function __construct(User $user, Roles $roles = null, EntitiesBuilder $entitiesBuilder = null)
{
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->roles = $roles ?: new Roles();
$this->user = $user;
$this->isAdmin = $user->isAdmin();
$this->isBanned = $user->isBanned();
$this->groups = [];
$this->channels = [];
$this->entities = [];
$this->entitiesBuilder = $entitiesBuilder ?: Di::_()->get('EntitiesBuilder');
$this->channels[$user->getGUID()] = $user;
$this->channelRoleCalculator = new ChannelRoleCalculator($this->user, $this->roles);
$this->groupRoleCalculator = new GroupRoleCalculator($this->user, $this->roles, $entitiesBuilder);
}
/**
* Permissions are user aware. This bomb function is to keep the user from being changed after instantiation.
*
* @throws ImmutableException
*/
public function setUser(User $user): void
{
throw new ImmutableException('User can only be set in the constructor');
}
/**
* Takes an array of entities and checks their permissions
* Builds up collections of permissions based on the user's relationships to the entity
* Any found channels and their roles are accessible in the channelRoleCalculator
* Any found groups and their roles are in the groupRoleCalculator
* All requested entities and the user's role is available in $this->entities.
*
* @param array entities an array of entities for calculating permissions
*/
public function calculate(array $entities = []): void
{
foreach ($entities as $entity) {
$this->entities[$entity->getGUID()] = $this->getRoleForEntity($entity);
}
}
private function getRoleForEntity($entity): Role
{
$role = null;
//Access id is the best way to determine what the parent entity is
//Any of the access flags are a channel
//Anything else is a group guid
switch ($entity->getAccessId()) {
case Access::UNLISTED:
case Access::LOGGED_IN:
case Access::PUBLIC:
case Access::UNKNOWN:
$role = $this->channelRoleCalculator->calculate($entity);
break;
default:
$role = $this->groupRoleCalculator->calculate($entity);
}
//Apply global overrides
if ($this->isAdmin) {
$role = $this->roles->getRole(Roles::ROLE_ADMIN);
}
if ($this->isBanned) {
$role = $this->roles->getRole(Roles::ROLE_BANNED);
}
return $role;
}
/**
* Export the nested objects.
*
* @return array serialized objects
*/
public function export(): array
{
$export = [];
$export['user'] = $this->user->export();
$export['channels'] = $this->getChannels();
$export['groups'] = $this->getGroups();
$export['entities'] = $this->entities;
return $export;
}
/**
* @return array channel guids with the user's role
*/
public function getChannels(): array
{
return $this->channelRoleCalculator->getChannels();
}
/**
* @return array group guids with the user's role
*/
public function getGroups(): array
{
return $this->groupRoleCalculator->getGroups();
}
/**
* @return array serialized objects
*/
public function jsonSerialize(): array
{
return $this->export();
}
}
......@@ -3,11 +3,17 @@
namespace Minds\Core\Permissions;
use Minds\Core\Di\Provider as DiProvider;
use Minds\Core\Permissions\Entities;
use Minds\Core\Permissions\Manager;
class Provider extends DiProvider
{
public function register()
{
$this->di->bind('Permissions\Entities\Manager', function ($di) {
return new Entities\Manager();
});
$this->di->bind('Permissions\Manager', function ($di) {
return new Manager();
});
......
<?php
namespace Minds\Core\Permissions\Roles;
class AdminRole extends BaseRole
{
public function __construct()
{
parent::__construct(Roles::ROLE_ADMIN);
$this->addPermission(Roles::FLAG_APPOINT_ADMIN);
}
}
<?php
namespace Minds\Core\Permissions\Roles;
class BannedRole extends BaseRole
{
public function __construct()
{
parent::__construct(Roles::ROLE_BANNED);
}
}
<?php
namespace Minds\Core\Permissions\Roles;
use Zend\Permissions\Rbac;
use Minds\Core\Permissions\Roles\Role;
abstract class BaseRole extends Rbac\Role implements \JsonSerializable, Role
{
public function export(): array
{
$export = [];
$export['name'] = $this->getName();
$export['permissions'] = $this->getPermissions();
return $export;
}
public function jsonSerialize(): array
{
return $this->export();
}
}