Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Issue #27: Fix permissions #29

Open
wants to merge 19 commits into
base: main
Choose a base branch
from

Conversation

donquixote
Copy link

No description provided.


if (!$res) {
\Drupal::messenger()->addError(t('Cannot fetch from @url.', ['@url' => $discovery_url]));
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually happened to me all the time and slowed things down.
Not sure if we can safely include it in this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The question is which version of Collabora Online are you using. I'm currently tracking down an issue with a recent change that the Drupal module seem to trigger. And maybe it is related.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whichever version we get from using the 'collabora/code' docker image.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed it's recent enough to have the issue.

If I apply this patch alone with the two extra lines for the SSL verification, it actually solve the problem in CollaboraOnline/online#10042

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will isolate the patch from this PR, and make a separate PR.
The tests no (longer) depend on the connection to collabora, so it does not impact them.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed from the PR for now.

@donquixote donquixote force-pushed the issue-27-fix-permissions branch 2 times, most recently from b969749 to 3707404 Compare September 13, 2024 16:54
AccountInterface $account,
): AccessResultInterface {
// @todo Should it make a difference whether a media is published or not?
// @todo Should a user be allowed to preview/edit a media in Collabora even
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These points are TBD.

Copy link
Author

@donquixote donquixote Sep 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For regular media access in core:

  • With 'administer media' you can do anything.
  • (Generic "update any media" and "update media" and others are being faded out, I don't mention them below, see https://www.drupal.org/project/drupal/issues/2925459.)
  • (Create and delete are not relevant for us, I don't mention them below.)
  • Otherwise:
    • To view published media, it needs "view media" permission.
    • To view own unpublished media, it needs "view own unpublished media" permission to view.
    • To view other people's unpublished media, you need "administer media".
    • To update any media (published or not), you need to:
      • "edit any media", OR.
      • "edit own media", and be the owner.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually reveals a lack in Drupal core.
See https://www.drupal.org/project/drupal/issues/2936652
Currently there are permissions to "edit any media" which also applies to unpublished, and does not require ownership.
But to view the same media, you need to be the owner or have "administer media".

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am removing these @todo after talking with my colleagues.
In the current iteration, we make no distinction of published vs not published.
Also we treat these operations as isolated from other media operations.
Instead, we could add description to the permission itself.

@donquixote
Copy link
Author

Drupal conventions set the indentation level to 2 spaces.
For now I am keeping the 4 spaces even for new code, to keep it consistent within this module.
In the future this can be fixed for the entire module with a separate PR.

"edit own $type_id in collabora" => [
'title' => $this->t('%type_name: Edit own media file in Collabora', $type_params),
],
"edit any $type_id in collabora" => [
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had internal spec which said "edit all", but "edit any" is more aligned with core permissions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, "edit any xyz" works better with singular "xyz". With "edit all xyz", xyz would have to be plura.

$res = curl_exec($curl);

if (!$res) {
\Drupal::messenger()->addError(t('Cannot fetch from @url.', ['@url' => $discovery_url]));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I just log the error but then proceed to return false.
The calling code already does have a condition for return value false.
We could improve the error handling in the future.


The user role `anonymous` by default disallows editing and is the
minimal permission for viewing documents in Collabora Online.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can rethink this section. For now I just removed it to avoid confusion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now it is unclear how these new permissions work. Is it possible to have a short write up here?

@donquixote donquixote force-pushed the issue-27-fix-permissions branch 2 times, most recently from e154762 to 0972d4a Compare September 16, 2024 10:11
@donquixote
Copy link
Author

donquixote commented Sep 16, 2024

How to run the tests.

For now I am not adding a phpunit.xml(.dist) file.
This would only make sense if we also have a recommended/prepared way to set up a local development instance.

I personally ran the tests with ddev-drupal-contrib.
Unfortunately, I was not able to make the Collabora instance work, this needs more tweaking. But the tests don't need it.

I assume that Francesco will probably run the tests from a custom project where this module is pulled in as a dependency.
I actually replicated this setup, but can't get the tests to run..

So it is up to the developer/reviewer how they want to run the tests.

}
public function __construct(
private readonly RendererInterface $renderer,
) {}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change simplifies the class by using constructor property promotion.
It is now a bit unnecessary in the final version of the PR, but it made more sense with prior commits in this PR where the constructor parameters were changed. I suggest to keep it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you did not do it for CollaboraMediaPermissions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so true, bad copy paste

// stream_context_create(), the 'verify_peer' and 'verify_peer_name'
// options were set.
// @todo Check if an equivalent to 'verify_peer_name' exists for curl.
CURLOPT_SSL_VERIFYPEER => !$disable_checks,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding

        CURLOPT_SSL_VERIFYSTATUS => !$disable_checks,
        CURLOPT_SSL_VERIFYHOST => !$disable_checks,

seems to work.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you tell me what is different with or without these additions?
I am happy to add it but I like to understand why.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or how we could test it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my setup use self signed certificates (it's local to my workstation) and without these two option it go through.

I think maybe of the two just CURLOPT_SSL_VERIFYHOST is needed (it doesn't work without,a nd removing CURLOPT_SSL_VERIFYSTATUS seems to be fine). It seems to be the equivalent of verify_peer_name.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually it should be

        CURLOPT_SSL_VERIFYHOST => $disable_checks ? 0 : 2,

as per https://www.php.net/manual/en/curl.constants.php#constant.curlopt-ssl-verifyhost

/**
* Provides permissions for Collabora per media type.
*
* This class is registered in collabora_online.permissions.yml.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for me the '@see' is enough

protected $entityTypeManager;

/**
* MediaPermissions constructor.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* MediaPermissions constructor.
* CollaboraMediaPermissions constructor.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops lazy copy paste.
Personally I like just "Constructor." but ok for me.
See https://www.drupal.org/project/coding_standards/issues/3457897 which has different opinions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked core and oe_* modules. We have a mix of "Constructs a *** instance" and "*** constructor". I also see the occasional "Constructor." which could have been me and was accepted in a review at the time it happened.

@@ -11,13 +12,16 @@ collabora-online.view:
edit:
type: boolean
requirements:
_permission: 'access content'
# Media id must be an integer.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's evident from the code, I would not add this comment

}
public function __construct(
private readonly RendererInterface $renderer,
) {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you did not do it for CollaboraMediaPermissions

\Drupal::logger('cool')->error('Token and user permissions do not match.');
return static::permissionDenied();
}

$user_picture = $user->user_picture->entity;
$user_picture = $user->user_picture?->entity;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might actually split it out of the PR.

'verify_peer_name' => !$disable_checks,
]]);
$res = file_get_contents($discovery_url, false, $stream_context);
// Previously, file_get_contents() was used to fetch the discovery xml data.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's have a clean PR only with stuff related to our issue

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, see comments below.
It first seemed necessary to make things work, but now we can skip it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, the problem was addressed in COOL so it shouldn't be necessary per see.

$media = $items->getEntity();
if (!$media instanceof MediaInterface) {
// The formatter is being used on the wrong entity type.
// @todo Log this.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todos become wont dos, think its better to have these todos grouped in issues

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There will need to be an issue about error handling, then we can clean this up.
To me, the @todo indicates that the code as it is now is not intentionally this way but because we did not have the capacity or scope to do it properly.
But I can drop it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* New media entity.
*/
protected function createMediaEntity(string $type, array $values = []): MediaInterface {
file_put_contents('public://test.txt', 'Hello test');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use \Drupal::service('file_system')->copy(

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not copying, we just put file content.. to copy we have to put an example file. or maybe use one that already exists.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I would go with the whole sh'bang, put a fixtures folder under test etc etc

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually find a bunch of file_put_contents('public://' . in core tests.
I would prefer to keep this simple snippet unless you can point out what could possibly go wrong.

* @return array
* Report to compare against in assertions.
*/
protected function buildAccessReport(array $paths, array $users): array {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is too cryptic for me (and prob most people), in the test I need to see clearly that role x and y have or not permission for routes. I can guess what this '/cool/view/<document>' => ['reader', 'editor'], means but dont really know until I compare what buildAccessReport is actually composing

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benefit of the "compare big array" is that you see in one big overview what you need to fix.
if we do separate assertions, we only see the first failure which would say "user x has no access to path y".
knowing which users have access and which don't is quite useful for troubleshooting.

Could we resolve this confusion with comments or improved assertion message?
Or is the problem in the "magicalness" of the buildAccessReport()?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magicalness yes, need to be more readable without debuging

/**
* Tests a scenario where specific permissions are given to users.
*/
public function testCollaboraMediaPermissions(): void {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could rather build a simpler test that receives data from a yeilding function (doesn't need to be marked as a provider, we can just loop)

],
$accounts,
$paths,
);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drishu I think this is simpler, is it not?

$media = \Drupal::entityTypeManager()->getStorage('media')->load($id);
if (!$media) {
// @todo Use default mechanism for access denied response.
return static::permissionDenied();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be changed.

@donquixote donquixote force-pushed the issue-27-fix-permissions branch 3 times, most recently from 5ceeeac to d34d7d0 Compare September 18, 2024 12:58
@donquixote
Copy link
Author

@hfiguiere or whoever wants to merge this:
Please do not squash, keep the history. The commits are very intentionally crafted.

use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;
use Drupal\user\Entity\Role;
use Drupal\user\PermissionHandlerInterface;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the classes aren't used, please removed unused.

* {@inheritdoc}
*/
protected static $modules = [
'media',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed since its a module dependency.
Will be added on install.

@AaronGilMartinez
Copy link

Changes about permissions are ready @hfiguiere


/* Make sure that the user is a collaborator if edit is true */
$edit = $edit && $permissions['is_collaborator'];

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we check about the write permission? The code above make sure that if you get an "editor" but don't have write permission as per drupal, then you get bounced back to a read-only. It also reject if you don't have viewing permission. This is better than WOPI errors in Collabora online.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permissions are set at the entity level and can be found in the collabora_online.module. They handle actions like viewing and editing documents. These permissions are not tied to specific roles, allowing sites to configure which roles can access or modify documents.

donquixote and others added 17 commits October 14, 2024 14:39
…upal permissions.

Also remove roles configuration which is no longer needed now.
…README.md, for now.

A new version of this section may be re-added later.
…sionDenied(), drop the `@todo` in the controller method.
@AaronGilMartinez
Copy link

Hi @hfiguiere, added license for the files, and rebased branch.
Can you check, please?
Hope is clearer about permissions, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants