[table] feat: REST API#9034
Conversation
| logger = logging.getLogger(__name__) | ||
|
|
||
|
|
||
| def validate_database(value): |
There was a problem hiding this comment.
Nit: value->db_id . The name value is a bit ambiguous
There was a problem hiding this comment.
Was just following def __call__(self, value) from marshmallow, but yes, I'll change that
|
|
||
| @validates_schema | ||
| def validate_schema(self, data: Dict): # pylint: disable=no-self-use | ||
| validate_table_uniqueness(data) |
There was a problem hiding this comment.
Mixing API schema validation and business logic/db validation feels like a bad pattern here. Marshmallow should be concerned with keeping requests in/out of the API consistent, whereas data access rules and/or auth should live inside the ViewController.
One other interesting side note - these checks could potentially trigger BEFORE auth validation happens, which could lead to leakage (a user without rights to a DB for instance could phish around for DB names which would trigger these replies if auth logic happened downstream of this)
There was a problem hiding this comment.
The schema validation is debatable, since we are only performing basic data validation (does the relation exist?, is the fields unique?). Yet I have mixed feelings about it. The other approach would use pre_, post_ hooks. Note that using marshmallow validation gives us proper error messages for free.
It's not possible for the validation to trigger before any auth validation. Since this is done at the view decorator level. Yet, it's certainly a good point since it's possible further down the road for mashmallow validation to perform some kind of data authorization.
I would say this is a key issue. Marshmallow data validation is really useful, but has some constrains, and may easily force unnecessary db round trips (notice the use of the flask g on this case, that I'm not a big fan). We can rollback this pattern (it already implemented on previous new API PR's) and rollback to using pre_, post_ hooks that can call specific business logic API outside of the view scope.
There was a problem hiding this comment.
I don't think it's one or the other. I think marshmallow validation is great for validating the shape of the request. I do not think that marshmallow should be running database queries. My recommendation would be for the validation/auth logic to look something like:
- Can the user access the route? If yes, continue, if no 403
- Run marshmallow validation to validate the shape of the request. If OK, continue, if not 400
- Look up the resource being modified. Can the user modify it? If so, continue, if not 403
- Attempt the resource modification.
I think that can be accomplished via some combination of marshmallow and pre_ and post_ hooks, but you're the expert :)
There was a problem hiding this comment.
This can also become a security issue quite easily. If we are doing data validation inside of marshmallow schemas, an attacker could (potentially) use validation errors to gather information about a resource that they should not have access to. The schema must be very carefully written and thoroughly tested to prevent this. Much better to separate schema validation/resource checks as described by @willbarrett
|
I am also trying to add REST API for table 9118. It is good to know we've already been working on the same feature. Do we have a plan on when this PR will be merged into master? |
villebro
left a comment
There was a problem hiding this comment.
Some minor non-blocking comments.
| decorator==4.4.1 # via retry | ||
| defusedxml==0.6.0 # via python3-openid | ||
| flask-appbuilder==2.2.2 | ||
| flask-appbuilder==2.2.3rc5 |
| include_route_methods = RouteMethod.REST_MODEL_VIEW_CRUD_SET | {RouteMethod.RELATED} | ||
| openapi_spec_tag = "Datasets" | ||
|
|
||
| list_columns = ["metric_name", "verbose_name", "metric_type"] |
There was a problem hiding this comment.
| "type": "VARCHAR", | ||
| "groupby": False, | ||
| "filterable": True, | ||
| "expression": "col expression", |
There was a problem hiding this comment.
nit: perhaps put some ANSI SQL here: case when 1 = 1 then 'abc' else 'qwerty' end
| verbose_name: str = "", | ||
| description: str = "", | ||
| type: str = "", | ||
| groupby: bool = True, | ||
| filterable: bool = True, | ||
| is_dttm: bool = True, | ||
| expression: str = "", | ||
| python_date_format: str = "", |
There was a problem hiding this comment.
Is there some reason the strs aren't Optional[str] = None? In the datamodel these seem to be nullable.
|
Replaced by #9268 |

CATEGORY
SUMMARY
Following CRUD MVC deprecation. CRUD REST API for tables
ADDITIONAL INFORMATION
REVIEWERS
@nytai