Convert AccessList

Signed-off-by: Jonas Rittershofer <jotoeri@users.noreply.github.com>
This commit is contained in:
Jonas Rittershofer 2020-04-09 18:21:59 +02:00
parent c80c65901c
commit 68f2d9f2c6
8 changed files with 122 additions and 280 deletions

View file

@ -34,7 +34,6 @@ return [
['name' => 'page#goto_form', 'url' => '/{hash}', 'verb' => 'GET'],
['name' => 'page#insert_submission', 'url' => '/insert/submission', 'verb' => 'POST'],
['name' => 'page#search', 'url' => '/search', 'verb' => 'POST'],
['name' => 'page#get_display_name', 'url' => '/get/displayname', 'verb' => 'POST'],
['name' => 'api#write_form', 'url' => '/write/form', 'verb' => 'POST'],

View file

@ -94,108 +94,6 @@ class ApiController extends Controller {
$this->logger = $logger;
}
/**
* Transforms a string with user and group names to an array
* of nextcloud users and groups
* @param string $item
* @return Array
*/
private function convertAccessList($item) : array {
$split = [];
if (strpos($item, 'user_') === 0) {
$user = $this->userManager->get(substr($item, 5));
$split = [
'id' => $user->getUID(),
'user' => $user->getUID(),
'type' => 'user',
'desc' => 'user',
'icon' => 'icon-user',
'displayName' => $user->getDisplayName(),
'avatarURL' => '',
'lastLogin' => $user->getLastLogin(),
'cloudId' => $user->getCloudId()
];
} elseif (strpos($item, 'group_') === 0) {
$group = substr($item, 6);
$group = $this->groupManager->get($group);
$split = [
'id' => $group->getGID(),
'user' => $group->getGID(),
'type' => 'group',
'desc' => 'group',
'icon' => 'icon-group',
'displayName' => $group->getDisplayName(),
'avatarURL' => '',
];
}
return($split);
}
/**
* Check if current user is in the access list
* @param Array $accessList
* @return Boolean
*/
private function checkUserAccess($accessList) {
foreach ($accessList as $accessItem ) {
if ($accessItem['type'] === 'user' && $accessItem['id'] === \OC::$server->getUserSession()->getUser()->getUID()) {
return true;
}
}
return false;
}
/**
* Check If current user is member of a group in the access list
* @param Array $accessList
* @return Boolean
*/
private function checkGroupAccess($accessList) {
foreach ($accessList as $accessItem ) {
if ($accessItem['type'] === 'group' && $this->groupManager->isInGroup(\OC::$server->getUserSession()->getUser()->getUID(),$accessItem['id'])) {
return true;
}
}
return false;
}
/**
* Set the access right of the current user for the form
* @param Array $form
* @param Array $shares
* @return String
*/
private function grantAccessAs($form, $shares) {
if (!\OC::$server->getUserSession()->getUser() instanceof IUser) {
$currentUser = '';
} else {
$currentUser = \OC::$server->getUserSession()->getUser()->getUID();
}
$grantAccessAs = 'none';
if ($form['ownerId'] === $currentUser) {
$grantAccessAs = 'owner';
} elseif ($form['access'] === 'public') {
$grantAccessAs = 'public';
} elseif ($form['access'] === 'registered' && \OC::$server->getUserSession()->getUser() instanceof IUser) {
$grantAccessAs = 'registered';
} elseif ($form['access'] === 'hidden' && ($form['ownerId'] === \OC::$server->getUserSession()->getUser())) {
$grantAccessAs = 'hidden';
} elseif ($this->checkUserAccess($shares)) {
$grantAccessAs = 'userInvitation';
} elseif ($this->checkGroupAccess($shares)) {
$grantAccessAs = 'groupInvitation';
} elseif ($this->groupManager->isAdmin($currentUser)) {
$grantAccessAs = 'admin';
}
return $grantAccessAs;
}
/**
* Read an entire form based on form id
* @NoAdminRequired
@ -215,31 +113,6 @@ class ApiController extends Controller {
}
/**
* Read all shares (users and groups with access) of a form based on the form id
* @NoAdminRequired
* @param Integer $formId
* @return Array
*/
public function getShares($formId) {
$accessList = array();
try {
$form = $this->formMapper->find($formId);
if (!strpos('|public|hidden|registered', $form->getAccess())) {
$accessList = explode(';', $form->getAccess());
$accessList = array_filter($accessList);
$accessList = array_map(array($this, 'convertAccessList'), $accessList);
}
} catch (DoesNotExistException $e) {
// return silently
} finally {
return $accessList;
}
}
public function getQuestions($formId) : array {
$questionList = [];
try{
@ -310,10 +183,8 @@ class ApiController extends Controller {
$data = [
'id' => $form['id'],
'result' => $result,
'grantedAs' => $this->grantAccessAs($form, $shares),
'mode' => $mode,
'form' => $form,
'shares' => $shares,
'questions' => $this->getQuestions($form['id']),
];
} catch (DoesNotExistException $e) {
@ -335,7 +206,6 @@ class ApiController extends Controller {
'id' => $form->getId(),
'form' => $form->read(),
'mode' => 'edit',
'shares' => $this->getShares($form->getId()),
'questions' => $this->getQuestions($form->getId())
];
}
@ -394,19 +264,12 @@ class ApiController extends Controller {
$newForm->setIsAnonymous($form['isAnonymous']);
$newForm->setSubmitOnce($form['submitOnce']);
if ($form['access'] === 'select') {
$shareAccess = '';
foreach ($shares as $shareElement) {
if ($shareElement['type'] === 'user') {
$shareAccess = $shareAccess . 'user_' . $shareElement['id'] . ';';
} elseif ($shareElement['type'] === 'group') {
$shareAccess = $shareAccess . 'group_' . $shareElement['id'] . ';';
}
}
$newForm->setAccess(rtrim($shareAccess, ';'));
} else {
$newForm->setAccess($form['access']);
// Only write Users/Groups-Arrays if necessary.
if($form['access']['type'] !== 'selected') {
unset($form['access']['users']);
unset($form['access']['groups']);
}
$newForm->setAccess($form['access']);
if ($form['expires']) {
$newForm->setExpirationDate(date('Y-m-d H:i:s', strtotime($form['expirationDate'])));
@ -466,7 +329,9 @@ class ApiController extends Controller {
));
$form->setTitle('New form');
$form->setDescription('');
$form->setAccess('public');
$form->setAccess([
'type' => 'public'
]);
$this->formMapper->insert($form);

View file

@ -47,6 +47,7 @@ use OCP\AppFramework\Http\TemplateResponse;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\User; //To do: replace according to API
@ -66,6 +67,9 @@ class PageController extends Controller {
private $userMgr;
private $groupManager;
/** @var ILogger */
private $logger;
public function __construct(
IRequest $request,
IUserManager $userMgr,
@ -77,7 +81,8 @@ class PageController extends Controller {
QuestionMapper $questionMapper,
OptionMapper $optionMapper,
SubmissionMapper $SubmissionMapper,
AnswerMapper $AnswerMapper
AnswerMapper $AnswerMapper,
ILogger $logger
) {
parent::__construct(Application::APP_ID, $request);
$this->userMgr = $userMgr;
@ -90,6 +95,7 @@ class PageController extends Controller {
$this->optionMapper = $optionMapper;
$this->submissionMapper = $SubmissionMapper;
$this->answerMapper = $AnswerMapper;
$this->logger = $logger;
}
/**
@ -304,131 +310,55 @@ class PageController extends Controller {
/**
* @NoAdminRequired
* @param string $searchTerm
* @param string $groups
* @param string $users
* @return array
*/
public function search($searchTerm, $groups, $users) {
return array_merge($this->searchForGroups($searchTerm, $groups), $this->searchForUsers($searchTerm, $users));
}
/**
* @NoAdminRequired
* @param string $searchTerm
* @param string $groups
* @return array
*/
public function searchForGroups($searchTerm, $groups) {
$selectedGroups = json_decode($groups);
$groups = $this->groupManager->search($searchTerm);
$gids = [];
$sgids = [];
foreach ($selectedGroups as $sg) {
$sgids[] = str_replace('group_', '', $sg);
}
foreach ($groups as $g) {
$gids[] = $g->getGID();
}
$diffGids = array_diff($gids, $sgids);
$gids = [];
foreach ($diffGids as $g) {
$gids[] = ['gid' => $g, 'isGroup' => true];
}
return $gids;
}
/**
* @NoAdminRequired
* @param string $searchTerm
* @param string $users
* @return array
*/
public function searchForUsers($searchTerm, $users) {
$selectedUsers = json_decode($users);
Util::writeLog('forms', print_r($selectedUsers, true), Util::ERROR);
$userNames = $this->userMgr->searchDisplayName($searchTerm);
$users = [];
$sUsers = [];
foreach ($selectedUsers as $su) {
$sUsers[] = str_replace('user_', '', $su);
}
foreach ($userNames as $u) {
$allreadyAdded = false;
foreach ($sUsers as &$su) {
if ($su === $u->getUID()) {
unset($su);
$allreadyAdded = true;
break;
}
}
if (!$allreadyAdded) {
$users[] = ['uid' => $u->getUID(), 'displayName' => $u->getDisplayName(), 'isGroup' => false];
} else {
continue;
}
}
return $users;
}
/**
* @return \OCP\IGroup[]
*/
private function getGroups() {
$groups = $this->groupManager->getUserGroups(\OC::$server->getUserSession()->getUser());
return array_map(function(IGroup $group) {
return $group->getGID();
}, $groups);
}
/**
* Check if user has access to this form
*
* @param Form $form
* @return bool
*/
private function hasUserAccess($form) {
private function hasUserAccess(Form $form): bool {
$access = $form->getAccess();
$ownerId = $form->getOwnerId();
if ($access === 'public' || $access === 'hidden') {
if ($access['type'] === 'public') {
return true;
}
// Refuse access, if not public and no user logged in.
if ($this->userId === null) {
return false;
}
if ($access === 'registered') {
if ($form->getSubmitOnce()) {
$participants = $this->submissionMapper->findParticipantsByForm($form->getId());
foreach($participants as $participant) {
// Don't allow access if user has already taken part
if ($participant->getUserId() === $this->userId) return false;
}
}
return true;
}
// Always grant access to owner.
if ($ownerId === $this->userId) {
return true;
}
Util::writeLog('forms', $this->userId, Util::ERROR);
$userGroups = $this->getGroups();
$arr = explode(';', $access);
foreach ($arr as $item) {
if (strpos($item, 'group_') === 0) {
$grp = substr($item, 6);
foreach ($userGroups as $userGroup) {
if ($userGroup === $grp) {
return true;
}
}
} else {
if (strpos($item, 'user_') === 0) {
$usr = substr($item, 5);
if ($usr === $this->userId) {
return true;
}
// Refuse access, if SubmitOnce is set and user already has taken part.
if ($form->getSubmitOnce()) {
$participants = $this->submissionMapper->findParticipantsByForm($form->getId());
foreach($participants as $participant) {
if ($participant === $this->userId) {
return false;
}
}
}
// Now all remaining users are allowed, if access-type 'registered'.
if ($access['type'] === 'registered') {
return true;
}
// Selected Access remains.
// Grant Access, if user is in users-Array.
if (in_array($this->userId, $access['users'])) {
return true;
}
// Check if access granted by group.
foreach ($access['groups'] as $group) {
if( $this->groupManager->isInGroup($this->userId, $group) ) {
return true;
}
}
// None of the possible access-options matched.
return false;
}
}

View file

@ -37,8 +37,8 @@ use OCP\AppFramework\Db\Entity;
* @method void setDescription(string $value)
* @method string getOwnerId()
* @method void setOwnerId(string $value)
* @method string getAccess()
* @method void setAccess(string $value)
* @method array getAccess()
* @method void setAccess(array $value)
* @method string getCreated()
* @method void setCreated(string $value)
* @method string getExpirationDate()
@ -54,7 +54,7 @@ class Form extends Entity {
protected $title;
protected $description;
protected $ownerId;
protected $access;
protected $accessJson;
protected $created;
protected $expirationDate;
protected $isAnonymous;
@ -68,11 +68,22 @@ class Form extends Entity {
$this->addType('submitOnce', 'bool');
}
/**
* JSON-Decoding of access-column.
*/
public function getAccess(): array {
return json_decode($this->getAccessJson(), true); // assoc=true, => Convert to associative Array
}
/**
* JSON-Encoding of access-column.
*/
public function setAccess(array $access) {
$this->setAccessJson(json_encode($access));
}
public function read() {
$accessType = $this->getAccess();
if (!strpos('|public|hidden|registered', $accessType)) {
$accessType = 'select';
}
if ($this->getExpirationDate() === null) {
$expired = false;
$expires = false;
@ -89,7 +100,7 @@ class Form extends Entity {
'ownerId' => $this->getOwnerId(),
'ownerDisplayName' => \OC_User::getDisplayName($this->getOwnerId()),
'created' => $this->getCreated(),
'access' => $accessType,
'access' => $this->getAccess(),
'expires' => $expires,
'expired' => $expired,
'expirationDate' => $this->getExpirationDate(),

View file

@ -66,7 +66,7 @@ class SubmissionMapper extends QBMapper {
* @throws \OCP\AppFramework\Db\DoesNotExistException if not found
* @return array
*/
public function findParticipantsByForm(int $formId, $limit = null, $offset = null): array {
public function findParticipantsByForm(int $formId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('user_id')
@ -75,7 +75,14 @@ class SubmissionMapper extends QBMapper {
$qb->expr()->eq('form_id', $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT))
);
return $this->findEntities($qb);
$submissionEntities = $this->findEntities($qb);
// From array of submissionEntities produce array of userIds.
$userIds = array_map(function($submissionEntity) {
return $submissionEntity->getUserId();
}, $submissionEntities);
return $userIds;
}
/**

View file

@ -87,9 +87,8 @@ class Version010200Date2020323141300 extends SimpleMigrationStep {
'notnull' => true,
'length' => 64,
]);
$table->addColumn('access', Type::STRING, [
$table->addColumn('access_json', Type::JSON, [
'notnull' => false,
'length' => 1024,
]);
$table->addColumn('created', Type::DATETIME, [
'notnull' => false,
@ -212,13 +211,15 @@ class Version010200Date2020323141300 extends SimpleMigrationStep {
->from('forms_events');
$cursor = $qb_fetch->execute();
while( $event = $cursor->fetch() ){
$newAccessJSON = $this->convertAccessList($event['access']);
$qb_restore->insert('forms_v2_forms')
->values([
'hash' => $qb_restore->createNamedParameter($event['hash'], IQueryBuilder::PARAM_STR),
'title' => $qb_restore->createNamedParameter($event['title'], IQueryBuilder::PARAM_STR),
'description' => $qb_restore->createNamedParameter($event['description'], IQueryBuilder::PARAM_STR),
'owner_id' => $qb_restore->createNamedParameter($event['owner'], IQueryBuilder::PARAM_STR),
'access' => $qb_restore->createNamedParameter($event['access'], IQueryBuilder::PARAM_STR),
'access_json' => $qb_restore->createNamedParameter($newAccessJSON, IQueryBuilder::PARAM_STR),
'created' => $qb_restore->createNamedParameter($event['created'], IQueryBuilder::PARAM_STR),
'expiration_date' => $qb_restore->createNamedParameter($event['expire'], IQueryBuilder::PARAM_STR),
'is_anonymous' => $qb_restore->createNamedParameter($event['is_anonymous'], IQueryBuilder::PARAM_BOOL),
@ -358,4 +359,34 @@ class Version010200Date2020323141300 extends SimpleMigrationStep {
}
}
}
/**
* Convert old Access-String into JSON of new Access-Structure.
* @param $accessString Old access-String
*/
private function convertAccessList($accessString) : string {
$accessArray = [];
if ($accessString === 'public' || $accessString === 'registered') {
// Store type and return with empty users/groups.
$accessArray['type'] = $accessString;
return json_encode($accessArray);
}
// Access 'selected'
$accessArray['type'] = 'selected';
$accessArray['users'] = [];
$accessArray['groups'] = [];
$stringExplode = explode(';', $accessString);
foreach($stringExplode as $string) {
if (strpos($string, 'user_') === 0) {
$accessArray['users'][] = substr($string, 5);
} elseif (strpos($string, 'group_') === 0) {
$accessArray['groups'][] = substr($string, 6);
}
}
return json_encode($accessArray);
}
}

View file

@ -38,7 +38,7 @@
<input id="submitOnce"
v-model="form.form.submitOnce"
:disabled="form.form.access !== 'registered' || form.form.isAnonymous"
:disabled="form.form.access.type === 'public' || form.form.isAnonymous"
type="checkbox"
class="checkbox">
<label for="submitOnce" class="title">
@ -66,19 +66,19 @@
<label class="title icon-user">
{{ t('forms', 'Access') }}
</label>
<input id="private"
v-model="form.form.access"
<input id="registered"
v-model="form.form.access.type"
type="radio"
value="registered"
class="radio">
<label for="private" class="title">
<label for="registered" class="title">
<div class="title icon-group" />
<span>{{ t('forms', 'Registered users only') }}</span>
</label>
<input id="public"
v-model="form.form.access"
<input id="public"
v-model="form.form.access.type"
type="radio"
value="public"
class="radio">
@ -86,19 +86,19 @@
<div class="title icon-link" />
<span>{{ t('forms', 'Public access') }}</span>
</label>
<input id="select"
v-model="form.form.access"
<input id="selected"
v-model="form.form.access.type"
type="radio"
value="select"
value="selected"
class="radio">
<label for="select" class="title">
<label for="selected" class="title">
<div class="title icon-shared" />
<span>{{ t('forms', 'Only shared') }}</span>
</label>
</div>
<ShareDiv v-show="form.form.access === 'select'"
<ShareDiv v-show="form.form.access.type === 'selected'"
:active-shares="form.shares"
:placeholder="t('forms', 'Name of user or group')"
:hide-names="true"

View file

@ -33,7 +33,6 @@
/** @var OCA\Forms\Db\Question[] $questions */
$questions = $_['questions'];
$access = $form->getAccess();
?>
<?php if ($form->getIsAnonymous()):?>