8000 🐛 Bug Report: users cannot update documents where they have update permissions when there is a relationship · Issue #5852 · appwrite/appwrite · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

🐛 Bug Report: users cannot update documents where they have update permissions when there is a relationship #5852

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

Closed
2 tasks done
t0mm4rx opened this issue Jul 20, 2023 · 10 comments
Assignees
Labels
bug Something isn't working feature / relationships Fixes and upgrades for database relationships product / databases Fixes and upgrades for the Appwrite Database.

Comments

@t0mm4rx
Copy link
t0mm4rx commented Jul 20, 2023

👟 Reproduction steps

Hello,

I have collections A and B. Both have document security enabled.
A has a one-to-many relationship attribute with B.

X is a document in A, the user has update rights.
Z is a document in B, the user has update rights.

Z is inside of X.

When updating a field of X, the user gets a 401. Here are the logs of the appwrite container:

[Error] Method: PATCH
[Error] URL: /v1/databases/:databaseId/collections/:collectionId/documents/:documentId
[Error] Type: Appwrite\Extend\Exception
[Error] Message: The current user is not authorized to perform the requested action.
[Error] File: /usr/src/code/app/controllers/api/databases.php
[Error] Line: 3295

The error is thrown in this block code:

throw new Exception(Exception::USER_UNAUTHORIZED);

This block is check for !$documentSecurity, and both collection has documentSecurity enabled, so it might be an error done while computing this variable.

Here is a Discord post discussing the issue: https://ptb.discord.com/channels/564160730845151244/1125081645322604554

👍 Expected behavior

The user should be able to update its document.

👎 Actual Behavior

The user cannot update the document where he has the rights to.

🎲 Appwrite version

Version 1.3.x

💻 Operating system

Linux

🧱 Your Environment

Self-deployed 1.3.7.

👀 Have you spent some time to check if this issue has been raised before?

  • I checked and didn't find similar issue

🏢 Have you read the Code of Conduct?

@t0mm4rx t0mm4rx added the bug Something isn't working label Jul 20, 2023
@stnguyen90 stnguyen90 added the product / databases Fixes and upgrades for the Appwrite Database. label Jul 20, 2023
@stnguyen90 stnguyen90 self-assigned this Jul 20, 2023
@stnguyen90
Copy link
Contributor
stnguyen90 commented Jul 20, 2023

@t0mm4rx, thanks for raising this issue! 🙏🏼 Unfortunately, I was unable to reproduce your issue.

I have a document with document-level permissions enabled:

{
  "total": 1,
  "documents": [
    {
      "name": "asdf",
      "$id": "level1",
      "$createdAt": "2023-06-01T18:49:33.582+00:00",
      "$updatedAt": "2023-07-20T03:12:56.879+00:00",
      "$permissions": [
        "read(\"user:joe\")",
        "update(\"user:joe\")",
        "delete(\"user:joe\")"
      ],
      "level2": [
        {
          "$id": "level2",
          "$createdAt": "2023-06-01T18:49:33.583+00:00",
          "$updatedAt": "2023-07-20T03:12:56.871+00:00",
          "$permissions": [
            "read(\"user:joe\")",
            "update(\"user:joe\")",
            "delete(\"user:joe\")"
          ],
          "level3": [
            {
              "$id": "level3",
              "$createdAt": "2023-06-01T18:49:33.586+00:00",
              "$updatedAt": "2023-07-20T03:12:56.861+00:00",
              "$permissions": [
                "read(\"user:joe\")",
                "update(\"user:joe\")",
                "delete(\"user:joe\")"
              ],
              "$databaseId": "one-to-many",
              "$collectionId": "level3"
            }
          ],
          "$databaseId": "one-to-many",
          "$collectionId": "level2"
        }
      ],
      "$databaseId": "one-to-many",
      "$collectionId": "level1"
    }
  ]
}

I created an update request and it was successful:

image

Are you able to reproduce the errors when following your reproduction steps from a fresh project?

@t0mm4rx
Copy link
Author
t0mm4rx commented Jul 20, 2023

Have you disabled all permissions on the collection rights?
Also in my setup, the user doesn't have the delete right on the documents, not sure if that could change something.

I have also forgotten to mention that this is a two-way relationship.

This bug (and all the other relationship bugs I created yesterday) start happening more and more as the database gets heavier (> 50k documents, > 10 relationships for each doc). Maybe you could populate a database with a random dataset to perform these tests.

@t0mm4rx
Copy link
Author
t0mm4rx commented Jul 20, 2023

I just reproduced a similar bug from a fresh install. The bug appears with an admin API key, so it seems unrelated to document security.

Here is a backup of my instance: https://drive.google.com/file/d/1QI3YN3Nze_rCXuzl8rKmMgUbcz42YJEv/view?usp=sharing

Here is the code I run that triggers the 500 error:

const a = await db.createDocument("test", "connections", "unique()", { 
    name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    description: "lorem"
})

 console.log(a)

await db.createDocument("test", "profiles", "unique()", {
    name: "ab",
    description: "lorem",
    connections: [a],
    connections2: [a],
})

Here is the appwrite container logs:

[Error] Method: POST
[Error] URL: /v1/databases/:databaseId/collections/:collectionId/documents
[Error] Type: Appwrite\Extend\Exception
[Error] Message: The current user is not authorized to perform the requested action.
[Error] File: /usr/src/code/app/controllers/api/databases.php
[Error] Line: 2726

@t0mm4rx
Copy link
Author
t0mm4rx commented Jul 20, 2023

All of these 500 errors seem to appear after a certain set of operations (delete, update) on relationships. They might be related to the same underlying issue.
Try to play with collections with 2 or more relationships inside of them, it helped me reproduce the errors quicker.
After a certain amount of these operations, the db starts "breaking" and all these random 500 appear. It's also impossible for me to create a new one-way relationship field with the same collection, it also throws a 500 error without any logs, and the newly created field is tagged as Failed.

@sammichael25
Copy link
sammichael25 commented Aug 10, 2023

I can confirm this issue also.
I'm trying to create a function that modifies multiple collections.
I created an API key with all permissions to use with the function.
From the function I can create documents in my collections that do not have relationships
However when I try to create documents in my collection that has a relationship, I get the undermentioned:
AppwriteException: user_unauthorized, The current user is not authorized to perform the requested action. (401)

The issue seems specific to the use of the API key because I can modify all collections fine when using a logged in user from my Flutter application.

@stnguyen90 stnguyen90 assigned joeyouss and unassigned stnguyen90 Aug 16, 2023
@t0mm4rx
Copy link
Author
t0mm4rx commented Aug 23, 2023

Hello,

I found a fix to this error, and it's quite strange.

First, I need to mention that updating a document with a lot of relationships inside could be very long. It takes almost 30 seconds to update a string attribute with 2000 relationships.
It also randomly yields a 401 as stated in this issue.

When send the whole array of relationships as IDs when updating a doc, both issues disapear.

For example:

/**
* Either takes a very long time, or yields a 401.
*/
database.updateDocument('my-db', 'collection-a', 'document-a', {
    randomAttribute: 'xyz'
})

/**
* No 401, and 100x faster
*/
database.updateDocument('my-db', 'collection-a', 'document-a', {
    randomAttribute: 'xyz',
    relations: ['doc-1', 'doc-2', ...]
})

@abnegate
Copy link
Member
abnegate commented Sep 8, 2023

Hey @t0mm4rx, there were changes to the permissions logic relating to your original issue in 1.4.x, are you still able to reproduce the root issue in the latest version?

@abnegate abnegate assigned abnegate and unassigned joeyouss Sep 8, 2023
@abnegate abnegate added the feature / relationships Fixes and upgrades for database relationships label Sep 8, 2023
@abnegate
Copy link
Member

Closing as fixed in 1.4.2

@sammichael25
Copy link

I can confirm this issue is still present and can be replicated.
I created a fresh install of Appwrite on another computer and used the attached appwrite.json file to deploy the collections with their relationships.
I then created an API key and deployed the function below
When attempting to run the function I still get the undermentioned error when attempting to create a document in a collection that has relationships even tho the API key has full permissions:
AppwriteException: user_unauthorized, The current user is not authorized to perform the requested action. (401)

While my initial function was more complex, to replicate the issue I have a barebones version of the function code below.
This function is triggered by an excel document being uploaded to the bucket in the appwrite.json
and the attached .env file has to be filled out.

import 'dart:convert';
import 'dart:io';
import 'package:dart_appwrite/dart_appwrite.dart';

bool checkEnvVariables() {
  if (Platform.environment['APPWRITE_FUNCTION_ENDPOINT'] == null ||
      Platform.environment['APPWRITE_FUNCTION_PROJECT_ID'] == null ||
      Platform.environment['APPWRITE_FUNCTION_API_KEY'] == null ||
      Platform.environment['DATABASEID'] == null ||
      Platform.environment['VULNERABILITIESCOLLECTIONID'] == null ||
      Platform.environment['APPLICATIONSCOLLECTIONID'] == null ||
      Platform.environment['SERVERSCOLLECTIONID'] == null ||
      Platform.environment['PORTSCOLLECTIONID'] == null ||
      Platform.environment['CVECOLLECTIONID'] == null ||
      Platform.environment['TIERCOLLECTIONID'] == null ||
      Platform.environment['TERRITORYCOLLECTIONID'] == null) {
    return false;
  }
  return true;
}

bool checkPayload(Map<String, dynamic> payload) {
  if (payload['\$id'] == null || payload['name'] == null || payload['bucketId'] == null) {
    return false;
  }
  return true;
}

bool hasPayload(String payload) {
  if (payload == "") {
    return false;
  }
  return true;
}

Future<dynamic> main(final context) async {
  if (!checkEnvVariables()) {
    return Communication.returnFailure(context, "Some Environment variables are not set");
  }

  final client = Client();
  client
      .setEndpoint(Platform.environment['APPWRITE_FUNCTION_ENDPOINT']!)
      .setProject(Platform.environment['APPWRITE_FUNCTION_PROJECT_ID']!)
      .setKey(Platform.environment['APPWRITE_FUNCTION_API_KEY']!)
      .setSelfSigned(status: true);

  final database = Databases(client);

  context.log(jsonEncode(context.req.headers));

  if (!hasPayload(context.req.body)) {
    return Communication.returnFailure(context, "No payload data");
  }

  final payload = jsonDecode(context.req.body);

  if (!checkPayload(payload)) {
    return Communication.returnFailure(context, "Payload has incorrect data");
  }

  context.log(payload);

  final String fileId = payload['\$id'];
  final String fileName = payload['name'];
  final String bucketId = payload['bucketId'];
  final String applicationName = fileName.split('_')[4];

  try {
    await database.createDocument(
      databaseId: Platform.environment['DATABASEID']!,
      collectionId: Platform.environment['APPLICATIONSCOLLECTIONID']!,
      documentId: "6499af4d385699723b3b",
      data: {"appName": "CRM"},
    );
  } on AppwriteException catch (e, s) {
    return Communication.returnFailure(context, 'Error Creating New Application', e: e.toString(), s: s.toString());
  } catch (e, s) {
    return Communication.returnFailure(context, 'Unknown Error Creating New Application', e: e.toString(), s: s.toString());
  }

  try {
    await database.createDocument(
      databaseId: Platform.environment['DATABASEID']!,
      collectionId: Platform.environment['TERRITORYCOLLECTIONID']!,
      documentId: "649326a4684872634a46",
      data: {"name": "US"},
    );
  } on AppwriteException catch (e, s) {
    return Communication.returnFailure(context, 'Error Creating New Territory', e: e.toString(), s: s.toString());
  } catch (e, s) {
    return Communication.returnFailure(context, 'Unknown Error Creating New Territory', e: e.toString(), s: s.toString());
  }

  try {
    await database.createDocument(
      databaseId: Platform.environment['DATABASEID']!,
      collectionId: Platform.environment['SERVERSCOLLECTIONID']!,
      documentId: "64a6d0fba36f48ebe963",
      data: {
        "ip": "192.168.0.1",
        "hostType": "Linux",
        "territories": {"\$id": "649326a4684872634a46", "name": "US"},
        "applications": {"\$id": "6499af4d385699723b3b", "appName": "CRM"},
        "name": "us-crm-dbs"
      },
    );
  } on AppwriteException catch (e, s) {
    return Communication.returnFailure(context, 'Error Creating New Server', e: e.toString(), s: s.toString());
  } catch (e, s) {
    return Communication.returnFailure(context, 'Unknown Error Creating New Server', e: e.toString(), s: s.toString());
  }

  try {
    await database.createDocument(
      databaseId: Platform.environment['DATABASEID']!,
      collectionId: Platform.environment['PORTSCOLLECTIONID']!,
      documentId: "64d3c52984c0f768ac83",
      data: {"portNo": 3389, "connectionType": "tcp"},
    );
  } on AppwriteException catch (e, s) {
    return Communication.returnFailure(context, 'Error Creating New Port', e: e.toString(), s: s.toString());
  } catch (e, s) {
    return Communication.returnFailure(context, 'Unknown Error Creating New Port', e: e.toString(), s: s.toString());
  }

  try {
    await database.createDocument(
      databaseId: Platform.environment['DATABASEID']!,
      collectionId: Platform.environment['VULNERABILITIESCOLLECTIONID']!,
      documentId: ID.unique(),
      data: {
        "servers": {
          "\$id": "64a6d0fba36f48ebe963",
          "ip": "192.168.0.1",
          "hostType": "Linux",
          "territories": {"\$id": "649326a4684872634a46", "name": "T"},
          "applications": {"\$id": "6499af4d385699723b3b", "appName": "CRM"},
          "name": "tt-crm-dbs"
        },
        "ports": [
          {"\$id": "64d3c52984c0f768ac83", "portNo": 3389, "connectionType": "tcp"}
        ],
        "classType": null,
        "applications": {"\$id": "6499af4d385699723b3b", "appName": "CRM"},
        "severity": "Low",
        "cve": [],
        "detail": "SSL certificate is self signed"
      },
    );
  } on AppwriteException catch (e, s) {
    return Communication.returnFailure(context, 'Error Creating New Vulnerability', e: e.toString(), s: s.toString());
  } catch (e, s) {
    return Communication.returnFailure(context, 'Unknown Error Creating New Vulnerability', e: e.toString(), s: s.toString());
  }

  return Communication.returnSuccess(context, "Success");
}

class Communication {
  static void returnSuccess(final context, final message) {
    context.log(message);
    context.res.json({
      'success': true,
      'message': message,
    });
  }

  static dynamic returnFailure(final context, final String message, {String e = '', String s = ''}) {
    context.log(message);
    if (e.isNotEmpty) {
      context.error(e);
    }
    if (s.isNotEmpty) {
      context.error(s);
    }
    return context.res.json({
      'success': false,
      'message': message,
    });
  }
}

appwrite.json.txt
Q1_T1_ProdSupport_US_CRM_BCP4.xlsx
env.txt

@Ajmaljalal
Copy link

Adding same permissions on the relationship(related docs) solved it for me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working feature / relationships Fixes and upgrades for database relationships product / databases Fixes and upgrades for the Appwrite Database.
Projects
None yet
Development

No branches or pull requests

6 participants
0