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

[table] feat: REST API #9034

Closed
wants to merge 9 commits into from
Closed

Conversation

dpgaspar
Copy link
Member

@dpgaspar dpgaspar commented Jan 28, 2020

CATEGORY

  • Bug Fix
  • Enhancement (new features, refinement)
  • Refactor
  • Add tests
  • Build / Development Environment
  • Documentation

SUMMARY

Following CRUD MVC deprecation. CRUD REST API for tables

image

ADDITIONAL INFORMATION

  • Has associated issue:
  • Changes UI
  • Requires DB Migration.
  • Confirm DB Migration upgrade and downgrade tested.
  • Introduces new feature or API
  • Removes existing feature or API

REVIEWERS

@nytai

@dpgaspar dpgaspar marked this pull request as ready for review January 28, 2020 16:58
logger = logging.getLogger(__name__)


def validate_database(value):
Copy link
Member

Choose a reason for hiding this comment

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

Nit: value->db_id . The name value is a bit ambiguous

Copy link
Member Author

Choose a reason for hiding this comment

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

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)
Copy link
Member

Choose a reason for hiding this comment

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

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)

Copy link
Member

Choose a reason for hiding this comment

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

Agreed, good catch.

Copy link
Member Author

@dpgaspar dpgaspar Jan 30, 2020

Choose a reason for hiding this comment

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

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.

Copy link
Member

Choose a reason for hiding this comment

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

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:

  1. Can the user access the route? If yes, continue, if no 403
  2. Run marshmallow validation to validate the shape of the request. If OK, continue, if not 400
  3. Look up the resource being modified. Can the user modify it? If so, continue, if not 403
  4. 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 :)

Copy link
Member

@suddjian suddjian Feb 4, 2020

Choose a reason for hiding this comment

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

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

@codecov-io
Copy link

Codecov Report

Merging #9034 into master will decrease coverage by 0.33%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff            @@
##           master   #9034      +/-   ##
=========================================
- Coverage   59.43%   59.1%   -0.34%     
=========================================
  Files         369     372       +3     
  Lines       11743   11920     +177     
  Branches     2884    2917      +33     
=========================================
+ Hits         6980    7045      +65     
- Misses       4584    4693     +109     
- Partials      179     182       +3
Impacted Files Coverage Δ
superset/assets/src/components/Checkbox.jsx
.../assets/src/dashboard/reducers/dashboardFilters.js
...ets/src/dashboard/components/dnd/DragDroppable.jsx
superset/assets/src/components/EditableTitle.jsx
...src/explore/components/controls/VizTypeControl.jsx
...t/assets/src/components/InfoTooltipWithTrigger.jsx
.../src/dashboard/components/UndoRedoKeylisteners.jsx
.../src/dashboard/components/FilterTooltipWrapper.jsx
...sets/src/dashboard/components/DashboardBuilder.jsx
...ssets/src/dashboard/components/PublishedStatus.jsx
... and 709 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1867f06...e737516. Read the comment docs.

@dpgaspar dpgaspar mentioned this pull request Feb 11, 2020
12 tasks
@dandanhub
Copy link
Contributor

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?

Copy link
Member

@villebro villebro left a comment

Choose a reason for hiding this comment

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

Some minor non-blocking comments.

@@ -21,7 +21,7 @@ croniter==0.3.31
cryptography==2.8
decorator==4.4.1 # via retry
defusedxml==0.6.0 # via python3-openid
flask-appbuilder==2.2.2
flask-appbuilder==2.2.3rc5
Copy link
Member

Choose a reason for hiding this comment

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

I think this needs a rebase

include_route_methods = RouteMethod.REST_MODEL_VIEW_CRUD_SET | {RouteMethod.RELATED}
openapi_spec_tag = "Datasets"

list_columns = ["metric_name", "verbose_name", "metric_type"]
Copy link
Member

Choose a reason for hiding this comment

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

Might be overkill (some metrics might be very long) and probably depends on where this is used in practice, but for a listing I think I would prefer to see the expression over metric_type here. Looking at the datasource editor, it seems it's also doing the same:

image

"type": "VARCHAR",
"groupby": False,
"filterable": True,
"expression": "col expression",
Copy link
Member

Choose a reason for hiding this comment

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

nit: perhaps put some ANSI SQL here: case when 1 = 1 then 'abc' else 'qwerty' end

Comment on lines +64 to +71
verbose_name: str = "",
description: str = "",
type: str = "",
groupby: bool = True,
filterable: bool = True,
is_dttm: bool = True,
expression: str = "",
python_date_format: str = "",
Copy link
Member

Choose a reason for hiding this comment

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

Is there some reason the strs aren't Optional[str] = None? In the datamodel these seem to be nullable.

@dpgaspar
Copy link
Member Author

Replaced by #9268

@dpgaspar dpgaspar closed this Mar 11, 2020
@willbarrett willbarrett deleted the feature/api-tables branch March 11, 2020 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants