2.0.0
Scoutr 2.0.0
This is a major, major, major code refactor with multiple breaking changes. The codebase has been refactored to be much cleaner and more extensible for multiple NoSQL backend providers. It brings it up to the same level of support as the Go version.
New Features
- Added GCP Firestore (
FirestoreAPI
) and MongoDB (MongoAPI
) support sentry_sdk
is now mocked if the package is not installed- For OIDC-focused deployments, the
Config
object accepts parameters to specify the name of the OIDC headers to pull user and group information from. Note thatMongoAPI
uses aMongoConfig
class instead of theConfig
class. - Ability to use operators on user filter fields (now rebranded as read filters)
- More granular filtering per action
Changes
- BREAKING: Primary key of the group table is changing (see below)
- BREAKING: Renamed and moved
scoutr.dynamo.DynamoAPI.value_in_list
toscoutr.utils.value_in_set
. It now expects aset
as input instead of alist
- BREAKING:
DynamoAPI
constructor expects aConfig
instance as input instead of a series of parameters - BREAKING: Deleted
scoutr.dynamo.DynamoAPI
. It is replaced byscoutr.providers.aws.DynamoAPI
. - BREAKING: Moved
scoutr.api_gateway
toscoutr.helpers.api_gateway
- BREAKING: Moved
scoutr.flask
toscoutr.helpers.flask
- BREAKING: Renamed
filter_fields
toread_filters
- Modified how multiple filters targeting the same field are combined together (see below)
Group Table
- BREAKING: The primary key used in the groups table has been changed from
group_id
toid
to remain consistent with the auth table. This may require the underlying table to be recreated.
Validation
- An additional argument
required_fields
is now available onCREATE
operations. Validation will be performed using thevalidation
object like before. If a field in thevalidation
object does not exist in the request body, that validation is skipped - Each validation function runs in a separate thread to improve performance
Error output
In the event of any validation failures on creates/updates, an error dictionary will be returned that maps the failed key to the error message:
{
"errors": {
"date": "Date must be formatted as YYYY-MM-DD",
"id": "Id is not valid",
"name": "Name is not valid"
}
}
If there is a single error otherwise, the response will be the same as before:
{
"error": "User is not authorized"
}
Filtering
-
Added
create_filters
,update_filters
, anddelete_filters
to allow for more granular filtering based on the action being performed by the user. -
FilterField
objects now support anoperator
where you can specify a specific operation to perform. The supported operations are the same as the operators used in filtering (i.e.eq
,ne
,in
,not in
, etc.)
{
"field": "type",
"operator": "ne",
"value": "Test"
}
Filter Merging
The way that filters are merged together has changed. Now, for all filters that target the same key
, those filters will be run together with an OR
operation. Then, those filters will be combined with filters for other keys using an AND
operation. For example:
{
"read_filters": [
// Inherited from group1
{
"field": "product",
"operator": "contains",
"value": "ABC"
},
// Inherited from group2
{
"field": "product",
"operator": "ne",
"value": "ABCDEF"
},
// Inherited from group3
{
"field": "status",
"value": "Active"
}
]
}
Previously, this would have generated a query expression that would return no results because the two product
filters would be conflicting:
status = "Active" AND product CONTAINS "ABC" AND product != "ABCDEF"
However, using the new filter merging methodology, the generated query will be:
status = "Active" AND (
product CONTAINS "ABC" OR product != "ABCDEF"
)
Granular Filtering
Create Filters
When performing a CREATE
action, the filtering is applied as follows:
- Ensure user has permission to set the fields they have included in the body. This checks against the values in
exclude_fields
. If any matches are found, an exception is thrown. - Run field validation
- Run
create_filters
. If the filter criteria fails, the request will be denied with an Unauthorized error.
Update Filters
When performing an UPDATE
action, the filtering is applied as follows:
- Use the
update_filters
to determine if the user has permissions to access the item. If no item is found (i.e. it does not exist or user does not have permissions to access the item), then a Not Found error will be thrown. - Use
update_fields_permitted
,update_fields_restricted
, andexclude_fields
to determine if the user has permissions to update the fields they specified in the request body. If either of these criteria fail, an Unauthorized exception will be thrown - Run
update_filters
against the existing item merged together with the user’s desired updates. If the filter criteria fails, the request will be denied with an Unauthorized error. - Run field validation as before.
Example
User permissions:
{
"update_filters": [
{
"field": "product",
"operator": "eq",
"value": "a"
}
]
}
Record in the database:
{
"product": "a",
"approved": false
}
If a user tried to perform the below update:
{
"product": "b",
"approved": true
}
This update would be denied because the update_filters
do not permit the user to modify the product
key to any value that is not equal to “a”. However, this update would be permitted:
{
"approved": true,
"reason": "approved by user"
}
Similarly, if the user wanted to update the below record:
{
"product": "b",
"approved": false
}
This would be denied because they do not have permissions to update an item where the product
key is not equal to “a”.
Delete Filters
When performing a DELETE
action, the filtering is applied as follows:
- Use the
delete_filters
to determine if the user has permissions to delete the item. If no item is found (i.e. it does not exist or the user does not have permissions to delete it), then the request will be denied with a Bad Request error.