Restrict content to the content authors

I want my users to be able to view and edit only collection posts they have created. Is this something I would implement in the config file or programmatically? If so, any help would be much appreciated. Also, I will have a hidden boolean field created on each collection post only accessible by admions. Once that field is set to true only admins should be able to see/edit that post. This access to author’s own content will be more restrictive than just user groups.

2 Likes

Add this code to the read permissions of your collection to restrict entries by owner:

<?php
if ($context->user) {
    $context->options['filter']['_by'] = $context->user['_id'];
}

Add this code to the read permissions of your collection to restrict entries by owner, except admins:

<?php
if ($context->user && $context->user['group'] != 'admin') {
    $context->options['filter']['_by'] = $context->user['_id'];
}

Filtering + Hiding fields for non-admins can be done this way:

<?php
if ($context->user && $context->user['group'] != 'admin') {
    // filter by admin-boolean
    $context->options['filter']['admin_boolean'] = false;
    // hide field for non-admin --> or use field acl instead
    $context->options['fields']['admin_boolean'] = false;
}

I tried to test the solution above, but I have some weird bugs with boolean fields right now. I have to check in the next days, if I broke my test cockpit or if it is a real bug…


edit: I tried it again with a fresh installation and it worked.

Instead of adding $context->options['fields']['admin_boolean'] = false; to the read permissions, you can add permissions in the context menu of your field to group “admin”. It has the same effect.

For some more permission options, you may have a look at

and

1 Like

Thank you for your explanation. For some reason, I thought that code had to go into the update permissions as well which ended up disabling the desired functionality. With regards to the boolean field, I have already restricted the viewing and editing of it to admins. The boolean field name is published. Once published is set to true, I would like for all collection posts with a value of published: true to only be edited by admins. Is there a way to implement this functionality? Thank you so much for your help!

Hmm…, I’m not sure, if this is possible with collections permissions.

You could add this code to config/bootstrap.php to interrupt the saving process. But it feels a bit wrong…

<?php

$app->on('collections.save.before.yourcollectionname', function($name, &$entry, $isUpdate) {
    if ($isUpdate && $entry['published'] == true && $this->module('cockpit')->getGroup() != 'admin') {
        die;
    }
});
1 Like

Thank you for your help. That code did the trick. I like the way you put it that “it feels a bit wrong.” Could the problem be in the way I am imagining how to handle published content? Is there a way to hide content with published:true from all users except admins? I could create a new collection only visible and editable by admins, but unless I am mistaken that would involve an admin manually porting content from one collection to another which doesn’t seem right. I am trying to use published:true as a way to designate which collection posts will generate website content. Once the content has been “approved” by admin to be published to the website, I do not want anyone but an admin to be able to edit it in any way. I am building a Vue app that uses axios to get content from Cockpit with the filter:{published:true}. This code does what I asked, and I really appreciate it. I may just replace the saving failed message with a custom one to alert users as to why they cannot update the specific post. Cheers!

This was a hard one, but I’m sure, I’ll need it myself in the future.

And I also found another way while fiddling. You don’t need the extra bootstrap.php.

read permissions:

<php
if ($context->user && $context->user['group'] != 'admin') {
    
    // filter by content owner
    $context->options['filter']['_by'] = $context->user['_id'];
    
    // return error when trying to edit published entries
    if (isset($context->entry) && (!isset($context->entry['published']) || $context->entry['published']) == true) {
        return cockpit()->stop('{"error": "You can\'t edit published entries."}', 401);
    }
}

delete permissions:

if ($context->user && $context->user['group'] != 'admin') {
    
    $entries = cockpit()->module('collections')->find($collection['name'], $context->options);
    
    foreach ($entries as $entry) {
        if ($entry['published'] == true) {
            return cockpit()->stop('{"error": "You can\'t delete published entries."}', 401);
        }
    }
    
}

Wait with the delete permissions until this PR got merged. Right now, the delete permission has no effect.

Full example with annotations

4 Likes

Just a hint:

Instead of

return cockpit()->stop('{"error": "You can\'t delete published entries."}', 401);

you can also do this:

return 401;
1 Like

These tips were awesome. Thank you so much for all the help!

Sorry to revive this old thread, but I would need the code to restrict update permissions. Is it the same as for delete?

@artur is there any equivalent of this for Cockpit v2?

CC: @raffaelj, I saw that all your snippets are for v1 only and supposedly do not work with v2

Just sharing what I used to do in Cockpit V2.

  • If the request is from admin, he/she can perform delete and update.
  • If the request is from user, allow to perform create but restrict delete and update (especially protect from overwriting other user’s content)

Note: this affects all models.


// Before remove 
$this->on('content.remove.before', function ($modelName, &$filter, $collection) {

    $user = $this->helper('auth')->getUser();
    if (!$user) {
        return $this->stop(404);
    }

    $account = $this->dataStorage->findOne('system/users', ['user' => $user['user']]);

    // Protect only when role is user
    if ($user['role'] === 'user') {

        if ($account['_id'] !== $item['_cby']) {
            return $this->stop('{"error": "You can\'t perform this action."}', 401);
        }
    }
});

// Before create or update
$this->on('content.item.save.before', function ($modelName, &$item, $isUpdate, $collection) {

    $user = $this->helper('auth')->getUser();
    if (!$user) {
        return $this->stop(404);
    }

    $account = $this->dataStorage->findOne('system/users', ['user' => $user['user']]);

    // Protect only when role is user and update
    if ($user['role'] === 'user' && $isUpdate === true) {

        if ($account['_id'] !== $item['_mby']) {
            return $this->stop('{"error": "You can\'t perform this action."}', 401);
        }
    }


    // Just make sure when creating a new item, it has _cby and _mby
    $item['_cby'] = $account['_id'];
    $item['_mby'] = $account['_id'];
});

Hope this helps someone.

1 Like