Skip to content
Commits on Source (10)
<?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
......@@ -242,6 +243,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 = [];
......
......@@ -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.
......
......@@ -95,6 +95,7 @@ class Manager
'filter_hashtags' => false,
'pinned_guids' => null,
'as_activities' => false,
'exclude' => null,
], $opts);
if (isset($opts['query']) && $opts['query']) {
......
......@@ -53,6 +53,7 @@ class Repository
'exclude_moderated' => false,
'moderation_reservations' => null,
'pinned_guids' => null,
'exclude' => null,
], $opts);
if (!$opts['type']) {
......@@ -67,6 +68,8 @@ class Repository
throw new \Exception('Unsupported period');
}
$type = $opts['type'];
$body = [
'_source' => array_unique([
'guid',
......@@ -75,7 +78,7 @@ class Repository
'time_created',
'access_id',
'moderator_guid',
$this->getSourceField($opts['type']),
$this->getSourceField($type),
]),
'query' => [
'function_score' => [
......@@ -98,7 +101,7 @@ class Repository
'sort' => [],
];
/*if ($opts['type'] === 'group' && false) {
/*if ($type === 'group' && false) {
if (!isset($body['query']['function_score']['query']['bool']['must_not'])) {
$body['query']['function_score']['query']['bool']['must_not'] = [];
}
......@@ -107,7 +110,7 @@ class Repository
'access_id' => ['0', '1', '2'],
],
];
} elseif ($opts['type'] === 'user') {
} elseif ($type === 'user') {
$body['query']['function_score']['query']['bool']['must'][] = [
'term' => [
'access_id' => '2',
......@@ -236,7 +239,7 @@ class Repository
}
}
if ($opts['type'] !== 'group' && $opts['access_id'] !== null) {
if ($type !== 'group' && $opts['access_id'] !== null) {
$body['query']['function_score']['query']['bool']['must'][] = [
'terms' => [
'access_id' => Text::buildArray($opts['access_id']),
......@@ -294,6 +297,14 @@ class Repository
}
}
if ($opts['exclude']) {
$body['query']['function_score']['query']['bool']['must_not'][] = [
'terms' => [
'guid' => Text::buildArray($opts['exclude']),
],
];
}
// firehose options
......@@ -338,9 +349,15 @@ class Repository
//
$esType = $opts['type'];
if ($esType === 'all') {
$esType = 'object:image,object:video,object:blog';
}
$query = [
'index' => $this->index,
'type' => $opts['type'],
'type' => $esType,
'body' => $body,
'size' => $opts['limit'],
'from' => $opts['offset'],
......
<?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();
}
}