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

Permissions creating items #11775

Open
3 tasks done
stefanovalentegva opened this issue Feb 21, 2022 · 24 comments
Open
3 tasks done

Permissions creating items #11775

stefanovalentegva opened this issue Feb 21, 2022 · 24 comments
Labels

Comments

@stefanovalentegva
Copy link

Preflight Checklist

Describe the Bug

Unable to set permissions check on create

To Reproduce

Suppose you've got 2 collections

Products
    - id
    - name
    - code 

Prices
    - id
    - product_id
    - price

Relation between the two collections is Many to One (Many price refers to One Product)

{
    "data": [
        {
            "collection": "prices",
            "field": "product_id",
            "related_collection": "products",
            "schema": {
                "table": "prices",
                "column": "product_id",
                "foreign_key_table": "products",
                "foreign_key_column": "id",
                "constraint_name": "fk_prices_products",
                "on_update": "NO ACTION",
                "on_delete": "NO ACTION"
            },
            "meta": {
                "id": 171,
                "many_collection": "prices",
                "many_field": "product_id",
                "one_collection": "products",
                "one_field": "prices",
                "one_collection_field": null,
                "one_allowed_collections": null,
                "junction_field": null,
                "sort_field": null,
                "one_deselect_action": "nullify"
            }
        }
    ]
}

and Suppose you've got 2 roles:

SocksUsers should add price only for product with code === 'socks'
TrousersUser should add price only for product with code === 'trousers'.

Create Permission on Prices Table for SocksUsers is

{
    "data": {
        "id": 53655,
        "role": "b99478ec-b777-4652-a4ae-6db2bc47bbcd",
        "collection": "prices",
        "action": "create",
        "permissions": null,
        "validation": {
            "_and": [
                {
                    "products": {
                        "code": {
                            "_eq": "socks"
                        }
                    }
                }
            ]
        },
        "presets": null,
        "fields": [
            "*"
        ]
    }
}

Trying to add a new price within API

POST /items/prices
with body:

{
    "product_id": 1
    "price": 10
}

response is:

{
    "errors": [
        {
            "message": "\"product_id\" must be of type object",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "product_id",
                "stack": "Error: \"product_id\" must be of type object\n    at /path/to/datahub/node_modules/directus/dist/services/authorization.js:210:174\n    at Array.map (<anonymous>)\n    at /path/to/datahub/node_modules/directus/dist/services/authorization.js:210:157\n    at Array.map (<anonymous>)\n    at AuthorizationService.validatePayload (/path/to/datahub/node_modules/directus/dist/services/authorization.js:210:128)\n    at /path/to/datahub/node_modules/directus/dist/services/items.js:84:46\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
            }
        }
    ]
}

POST /items/prices
with body:

{
    "product_id": {
        "id": 1
    }
    "price": 10
   "por
}

response is:

{
    "errors": [
        {
            "message": "\"product_id.id\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "product_id",
                "stack": "Error: \"product_id.id\" is not allowed\n    at /path/to/datahub/node_modules/directus/dist/services/authorization.js:210:174\n    at Array.map (<anonymous>)\n    at /path/to/datahub/node_modules/directus/dist/services/authorization.js:210:157\n    at Array.map (<anonymous>)\n    at AuthorizationService.validatePayload (/path/to/datahub/node_modules/directus/dist/services/authorization.js:210:128)\n    at /path/to/datahub/node_modules/directus/dist/services/items.js:84:46\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)"
            }
        }
    ]
}

I would expect the user belonging to group SocksUsers to receive an error if product 1 (to which the price is related) has "code" other than "socks". Instead I receive error at any calls.

Errors Shown

No response

What version of Directus are you using?

9.5.2 / 9.5.1

What version of Node.js are you using?

16.14.0

What database are you using?

mysql

What browser are you using?

API Call

What operating system are you using?

macOS

How are you deploying Directus?

running locally

@licitdev
Copy link
Member

Test notes

The relational field product_id is not populated when validating in the API.

If manually populated (just for testing), it has to be in the following format where the field key is somehow duplicated.

{
    "product_id": {
        "product_id": {
            "id": 1,
            "code": "socks"
        }
    }
    "price": 10
}

With the manually populated fields, the API now returns a Forbidden Error instead of the above validation error.

@supermx1
Copy link

I have a very similar issue. I created a role lets say "Admin", and I wanted to set the permissions to only allow that role (Admin) to create specific roles i.e "Account Holders" and "Guards". So the user with an admin account can only create users that belong to the roles "Account Holders" and "Guards" only and cannot create users in other roles like "Super Admin" and "Directors". So in the permissions I set the roles IDs the Admin can create.

But I get the same error as above:

{
"errors": [
{
"message": ""role" must be of type object",
"extensions": {
"code": "FAILED_VALIDATION",
.........

@Attacler
Copy link

I have the exact same issue.

@schuellc
Copy link

schuellc commented Mar 10, 2022

I think this also happens if you update (edit) an existing item. The validation will throw an exception since the relational field is not populated.

@HitomiTenshi
Copy link
Contributor

I just got this issue as well. It happens because the field validation rules use nested checks. The PATCH API then wants to check your input but since you just provided an ID it complains about needing an object.

This also prevents nullable fields from setting them to null because the field validator expects an object. (I experienced this myself)

For example see this field validator:

chrome_2022-03-12_00-03-07

If I just pass the ID of the customer there it complains about not being an object, because the field validation rule does not query anything, it expects the customer object to be there, along with the user object and its ID for the check.

@rijkvanzanten The field validation does not currently query references when just the ID is passed, that's the gist of this issue.

Another flaw that I noticed is that you cannot setup an is null check on references. What you could do is check for ID is null but field validation isn't smart and complains about needing an object again.

@rijkvanzanten
Copy link
Member

@rijkvanzanten The field validation does not currently query references when just the ID is passed, that's the gist of this issue.

Yup! There's a handful of discussions floating around on the same as well..

You're right. The validation check is a little "dumb", in that it won't auto-expand passed in relational values. If you configure the validation to check for { "role": { "id": "abc" }}, but you pass { "role": "abc" }, the validation will fail even though you might expect that to go through, as the referenced item is the same.

Ideally the app will expand the value using the data from the API before passing it to the API, that way you can configure a single rule that checks for both. In the meantime, you can make this work as expected by using an _or that checks for both approaches:

_or
  customers_id
    user
      id
        _eq: $CURRENT_USER
  customers_id
    user
      _eq: $CURRENT_USER

@HitomiTenshi
Copy link
Contributor

@rijkvanzanten I have to set this in the database right? Because the filter UI does not allow me to write anything by hand (it used to before the new very nice UI rework landed)

@rijkvanzanten
Copy link
Member

You can use an or group in the UI as well, or click on the field label -> edit raw value to do it in JSON if you prefer that approach 🙂

(my little example above was just pseudocode to give an example)

@HitomiTenshi
Copy link
Contributor

@rijkvanzanten Ah didn't know you can click on the label for the raw value. Thanks! 👍 It's just that it was not possible to check for $CURRENT_USER on a reference field directly using the filter UI. It forces you to pick an underlying property. With the raw value editing it is possible to implement this check now.

@rijkvanzanten
Copy link
Member

It forces you to pick an underlying property.

Ahh yeah, #12040 🙂

@schuellc
Copy link

schuellc commented Mar 16, 2022

The proposed solution only works for the case where the validation is done directly on the foreign key of the linked table but does not work for any actual relational fields. As describe in the initial issue report from @stefanovalentegva it is currently not possible to create validation rules based on field values of linked tables (e.g. M2O relations) to the best of my knowledge. Here is another simplified example of the case:

table_a
- id (PK)
- table_b_id (FK)

table_b
- id (PK)
- name (String)

Validation rule on create/edit of entry in table_a:

{
  "_and": [
    {
      "table_b_id": {
        "name": {
          "_eq": "a_value"
        }
      }
    }
  ]
}

This validation rule will always fail, both in the web interface and using the REST API.

image

POST /items/table_a

{
  "table_b_id": 1
}

1 being the foreign key to the item in table_b with the value "a_value" in the field name.

{
  "errors": [
    {
      "message": "\"table_b_id\" must be of type object",
      "extensions": {
        "code": "FAILED_VALIDATION",
        "field": "table_b_id"
      }
    }
  ]
}

Here is a little Postgres example DB to reproduce it:
directus-test-db-backup.zip
user: user@test.com / pw: 123
admin: admin@test.com / pw: 12345

@rijkvanzanten
Copy link
Member

In that example, you tell the API to expect a value that is an object with a key called name that has to equal "a_value". When you submit the numeric value 1, that does not match.

@schuellc
Copy link

schuellc commented Mar 16, 2022

No, what I try to do is to create a new item in table_a that has as value the foreign key 1 to an item in table_b with the value "a_value" in the field name. I added an explanation above to make it more clear. Also the following does not and should not work, since it would circumvent the validation that is suppose to happen on the actual expansion of the items in tabel_b:

POST /items/table_a

{
  "table_b_id": {
    "id": 1,
    "name": "a_value"
  }
}

and results in the following error:

{
  "errors": [
    {
      "message": "\"table_b_id.id\" is not allowed",
      "extensions": {
        "code": "FAILED_VALIDATION",
        "field": "table_b_id"
      }
    },
    {
      "message": "\"table_b_id.name\" is not allowed",
      "extensions": {
        "code": "FAILED_VALIDATION",
        "field": "table_b_id"
      }
    }
  ]
}

@zakwear
Copy link

zakwear commented Mar 21, 2022

I edited raw JSON to allow both validation rules suggested above. This is to create an item in the Meeting collection:

{
	"_and": [
		{
			"_or": [
				{
					"organization": {
						"active_owners_junction": {
							"user_id": {
								"id": {
									"_eq": "$CURRENT_USER"
								}
							}
						}
					}
				},
				{
					"organization": {
						"active_owners_junction": {
							"user_id": {
								"_eq": "$CURRENT_USER"
							}
						}
					}
				}
			]
		}
	]
}

I also dropped the _and condition above and no changes in the results.

Ran the following POST requests:

{
    "meeting_title": "Trying to make a meeting",
    "organization": {
        "id": "xxx-xxx-xxx"
    }
}

Response body:

{
    "errors": [
        {
            "message": "\"organization.id\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        },
        {
            "message": "\"organization.id\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        }
    ]
}

Attempt 2:

{
    "meeting_title": "Trying to make a meeting",
    "organization": {
        "id": "xxx-xxxx-xxx",
        "active_owners_junction": {
            "id": "xxx-xxx-xxx",
            "user_id": {
                "id": "xxx-xxx-xxx"
                }
        }
    }
}

Response:

{
    "errors": [
        {
            "message": "\"organization.id\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        },
        {
            "message": "\"organization.active_owners_junction\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        },
        {
            "message": "\"organization.id\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        },
        {
            "message": "\"organization.active_owners_junction\" is not allowed",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        }
    ]
}

And the empty object case discussed already:

{
    "meeting_title": "Trying to make a meeting",
    "organization": "xx-xx-xx"
}

Response:

{
    "errors": [
        {
            "message": "\"organization\" must be of type object",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        },
        {
            "message": "\"organization\" must be of type object",
            "extensions": {
                "code": "FAILED_VALIDATION",
                "field": "organization"
            }
        }
    ]
}

@icrotz
Copy link

icrotz commented May 3, 2022

I looked at the code, this features is not developed yet. Considering that anyone creating an app with permission requirements will need this feature, I'm currently working on a PR to add it. I keep you updated.

@Dominic-Marcelino
Copy link
Contributor

@rijkvanzanten sorry for tagging, but is there a solution for creating new (nested) items?

Right now we have collection "a" with a many to many relation to collection "b" (junction table: "a_b").
Collection "a" has a field "platform" which is set by preset to "$CURRENT_USER.platform"

Table "a_b":

id a_id b_id

Permissions
Now we need to make sure that users can only add and update relations to "a_b" that are related to "a" items with the platform equals the users platform ("$CURRENT_USER.platform") .
Relations are always managed deeply from a.

We would do this with the following rule on "a_b":

{
	"_and": [
		{
			"a_id": {
				"platform": {
					"_eq": "$CURRENT_USER.PLATFORM"
				}
			}
		}
	]
}

And then do a POST on "a"

{
	"title": "A title for a",
	"b":[
		{
			"b_id":{
				"id":"47f9a47d-d3ed-40d4-bfc9-cc087e83ec60"
			}
		},
		{
			"b_id": {
				"id":"a75c800e-8dae-48a5-8e1f-9a60075f4249"}
		}
	]
}

Generating the following Error:
"a_id" must be of type object

That's because the rule tries to check the "role" of the related a_id which isn't fetched.
Can this currently archived somehow else?

Thanks!

@rijkvanzanten
Copy link
Member

I don't think there's a good workaround currently besides making a custom hook that runs the validation logic to your exact spec. What you're running into is exactly what's described in this issue thread, which is something we're looking to properly support 🙂

@Dominic-Marcelino
Copy link
Contributor

I don't think there's a good workaround currently besides making a custom hook that runs the validation logic to your exact spec. What you're running into is exactly what's described in this issue thread, which is something we're looking to properly support 🙂

Thanks for your response! We’ll then create a custom hook for validation.

Can you say if it’s already planned for the next few versions or is this more a “we want to support this one day in the future☺️

@rijkvanzanten
Copy link
Member

Can you say if it’s already planned for the next few versions or is this more a “we want to support this one day in the future” ☺️

That's near impossible for me to accurately predict. What's being worked on next is a combination of community demand, sponsored work, (emergency) bugfixes that have to happen first, and other things. That being said, we are tracking this as an Issue (rather than a Discussion which are more the "This would be cool to support at some point" things), and it's something that affects/confuses more people, so it's definitely high on my personal priorities list

@Dominic-Marcelino
Copy link
Contributor

Dominic-Marcelino commented May 24, 2022

That's near impossible for me to accurately predict. What's being worked on next is a combination of community demand, sponsored work, (emergency) bugfixes that have to happen first, and other things. That being said, we are tracking this as an Issue (rather than a Discussion which are more the "This would be cool to support at some point" things), and it's something that affects/confuses more people, so it's definitely high on my personal priorities list

totally understand that it’s impossible to give an eta or exact plans for oss projects ☺️
Awesome, that’s everything I need to know. Thanks again! 👍🏼

@NilsBaumgartner1994
Copy link
Contributor

Facing the same problem for field validation upon creation. Any solutions that work?

@dstoyanoff
Copy link
Contributor

Hey guys, any ideas when we could have this resolved? Or at least a temporary alternative?

@rijkvanzanten
Copy link
Member

Hey guys, any ideas when we could have this resolved? Or at least a temporary alternative?

Alternative would be a custom hook. As for a resolution, this is a very large lift, but it is something we're currently actively working on. @qborisb and myself actually just spent over an hour discussing different implementation strategies for this 🙂

Long story short, the API will have to smartly expand the input IDs to the resolved related data to run the validation on the final "pre-insert" modified object, but that comes with a lot of tricky edge cases

@rijkvanzanten
Copy link
Member

Linear: ENG-268

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: 📋 Backlog
Development

No branches or pull requests