chore: Migrate /superset/schemas_access_for_file_upload to v1 (#23227)

Co-authored-by: Diego Medina <diegomedina24@gmail.com>
This commit is contained in:
Hugh A. Miles II 2023-03-14 19:21:10 -06:00 committed by GitHub
parent 1874f9a3b6
commit 9920ab3fd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 227 additions and 70 deletions

View File

@ -1834,10 +1834,10 @@
"type": "string"
},
"last_saved_by": {
"$ref": "#/components/schemas/ChartDataRestApi.get_list.User3"
"$ref": "#/components/schemas/ChartDataRestApi.get_list.User1"
},
"owners": {
"$ref": "#/components/schemas/ChartDataRestApi.get_list.User1"
"$ref": "#/components/schemas/ChartDataRestApi.get_list.User3"
},
"params": {
"nullable": true,
@ -1925,16 +1925,11 @@
"last_name": {
"maxLength": 64,
"type": "string"
},
"username": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name",
"username"
"last_name"
],
"type": "object"
},
@ -1972,11 +1967,16 @@
"last_name": {
"maxLength": 64,
"type": "string"
},
"username": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
"last_name",
"username"
],
"type": "object"
},
@ -2627,10 +2627,10 @@
"type": "string"
},
"last_saved_by": {
"$ref": "#/components/schemas/ChartRestApi.get_list.User3"
"$ref": "#/components/schemas/ChartRestApi.get_list.User1"
},
"owners": {
"$ref": "#/components/schemas/ChartRestApi.get_list.User1"
"$ref": "#/components/schemas/ChartRestApi.get_list.User3"
},
"params": {
"nullable": true,
@ -2718,16 +2718,11 @@
"last_name": {
"maxLength": 64,
"type": "string"
},
"username": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name",
"username"
"last_name"
],
"type": "object"
},
@ -2765,11 +2760,16 @@
"last_name": {
"maxLength": 64,
"type": "string"
},
"username": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
"last_name",
"username"
],
"type": "object"
},
@ -3420,7 +3420,7 @@
"readOnly": true
},
"created_by": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.User2"
"$ref": "#/components/schemas/DashboardRestApi.get_list.User1"
},
"created_on_delta_humanized": {
"readOnly": true
@ -3446,7 +3446,7 @@
"type": "string"
},
"owners": {
"$ref": "#/components/schemas/DashboardRestApi.get_list.User1"
"$ref": "#/components/schemas/DashboardRestApi.get_list.User2"
},
"position_json": {
"nullable": true,
@ -3519,6 +3519,27 @@
"type": "object"
},
"DashboardRestApi.get_list.User1": {
"properties": {
"first_name": {
"maxLength": 64,
"type": "string"
},
"id": {
"format": "int32",
"type": "integer"
},
"last_name": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
],
"type": "object"
},
"DashboardRestApi.get_list.User2": {
"properties": {
"email": {
"maxLength": 64,
@ -3549,27 +3570,6 @@
],
"type": "object"
},
"DashboardRestApi.get_list.User2": {
"properties": {
"first_name": {
"maxLength": 64,
"type": "string"
},
"id": {
"format": "int32",
"type": "integer"
},
"last_name": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
],
"type": "object"
},
"DashboardRestApi.post": {
"properties": {
"certification_details": {
@ -4293,6 +4293,18 @@
},
"type": "object"
},
"DatabaseSchemaAccessForFileUploadResponse": {
"properties": {
"schemas": {
"description": "The list of schemas allowed for the database to upload information",
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"DatabaseTablesResponse": {
"properties": {
"extra": {
@ -4917,7 +4929,7 @@
"$ref": "#/components/schemas/DatasetRestApi.get.TableColumn"
},
"created_by": {
"$ref": "#/components/schemas/DatasetRestApi.get.User2"
"$ref": "#/components/schemas/DatasetRestApi.get.User1"
},
"created_on": {
"format": "date-time",
@ -4981,7 +4993,7 @@
"type": "integer"
},
"owners": {
"$ref": "#/components/schemas/DatasetRestApi.get.User1"
"$ref": "#/components/schemas/DatasetRestApi.get.User2"
},
"schema": {
"maxLength": 255,
@ -5190,6 +5202,23 @@
"type": "object"
},
"DatasetRestApi.get.User1": {
"properties": {
"first_name": {
"maxLength": 64,
"type": "string"
},
"last_name": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
],
"type": "object"
},
"DatasetRestApi.get.User2": {
"properties": {
"first_name": {
"maxLength": 64,
@ -5215,23 +5244,6 @@
],
"type": "object"
},
"DatasetRestApi.get.User2": {
"properties": {
"first_name": {
"maxLength": 64,
"type": "string"
},
"last_name": {
"maxLength": 64,
"type": "string"
}
},
"required": [
"first_name",
"last_name"
],
"type": "object"
},
"DatasetRestApi.get_list": {
"properties": {
"changed_by": {
@ -6971,7 +6983,7 @@
"type": "integer"
},
"created_by": {
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User2"
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User1"
},
"created_on": {
"format": "date-time",
@ -7021,7 +7033,7 @@
"type": "string"
},
"owners": {
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User1"
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.User2"
},
"recipients": {
"$ref": "#/components/schemas/ReportScheduleRestApi.get_list.ReportRecipients"
@ -7082,10 +7094,6 @@
"maxLength": 64,
"type": "string"
},
"id": {
"format": "int32",
"type": "integer"
},
"last_name": {
"maxLength": 64,
"type": "string"
@ -7103,6 +7111,10 @@
"maxLength": 64,
"type": "string"
},
"id": {
"format": "int32",
"type": "integer"
},
"last_name": {
"maxLength": 64,
"type": "string"
@ -15301,6 +15313,50 @@
]
}
},
"/api/v1/database/{pk}/schemas_access_for_file_upload/": {
"get": {
"parameters": [
{
"in": "path",
"name": "pk",
"required": true,
"schema": {
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DatabaseSchemaAccessForFileUploadResponse"
}
}
},
"description": "The list of the database schemas where to upload information"
},
"401": {
"$ref": "#/components/responses/401"
},
"404": {
"$ref": "#/components/responses/404"
},
"500": {
"$ref": "#/components/responses/500"
}
},
"security": [
{
"jwt": []
}
],
"summary": "The list of the database schemas where to upload information",
"tags": [
"Database"
]
}
},
"/api/v1/database/{pk}/select_star/{table_name}/": {
"get": {
"description": "Get database select star for table",

View File

@ -145,6 +145,7 @@ MODEL_API_RW_METHOD_PERMISSION_MAP = {
"delete_ssh_tunnel": "write",
"get_updated_since": "read",
"stop_query": "read",
"schemas_access_for_file_upload": "read",
"get_objects": "read",
"get_all_objects": "read",
"add_objects": "write",

View File

@ -65,6 +65,7 @@ from superset.databases.schemas import (
DatabasePostSchema,
DatabasePutSchema,
DatabaseRelatedObjectsResponse,
DatabaseSchemaAccessForFileUploadResponse,
DatabaseTablesResponse,
DatabaseTestConnectionSchema,
DatabaseValidateParametersSchema,
@ -120,6 +121,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
"validate_parameters",
"validate_sql",
"delete_ssh_tunnel",
"schemas_access_for_file_upload",
}
resource_name = "database"
class_permission_name = "Database"
@ -222,6 +224,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
openapi_spec_tag = "Database"
openapi_spec_component_schemas = (
DatabaseFunctionNamesResponse,
DatabaseSchemaAccessForFileUploadResponse,
DatabaseRelatedObjectsResponse,
DatabaseTablesResponse,
DatabaseTestConnectionSchema,
@ -814,7 +817,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
)
except NoSuchTableError:
self.incr_stats("error", self.select_star.__name__)
return self.response(404, message="Table not found in the database")
return self.response(404, message="Table not found on the database")
self.incr_stats("success", self.select_star.__name__)
return self.response(200, result=result)
@ -1453,3 +1456,52 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
exc_info=True,
)
return self.response_400(message=str(ex))
@expose("/<int:pk>/schemas_access_for_file_upload/")
@protect()
@safe
@statsd_metrics
@event_logger.log_this_with_context(
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
f".schemas_access_for_file_upload",
log_to_statsd=False,
)
def schemas_access_for_file_upload(self, pk: int) -> Response:
"""The list of the database schemas where to upload information
---
get:
summary:
The list of the database schemas where to upload information
parameters:
- in: path
name: pk
schema:
type: integer
responses:
200:
description: The list of the database schemas where to upload information
content:
application/json:
schema:
$ref: "#/components/schemas/DatabaseSchemaAccessForFileUploadResponse"
401:
$ref: '#/components/responses/401'
404:
$ref: '#/components/responses/404'
500:
$ref: '#/components/responses/500'
"""
database = DatabaseDAO.find_by_id(pk)
if not database:
return self.response_404()
schemas_allowed = database.get_schema_access_for_file_upload()
# the list schemas_allowed should not be empty here
# and the list schemas_allowed_processed returned from security_manager
# should not be empty either,
# otherwise the database should have been filtered out
# in CsvToDatabaseForm
schemas_allowed_processed = security_manager.get_schemas_accessible_by_user(
database, schemas_allowed, True
)
return self.response(200, schemas=schemas_allowed_processed)

View File

@ -808,3 +808,10 @@ def encrypted_field_properties(self, field: Any, **_) -> Dict[str, Any]: # type
if self.openapi_version.major > 2:
ret["x-encrypted-extra"] = True
return ret
class DatabaseSchemaAccessForFileUploadResponse(Schema):
schemas = fields.List(
fields.String(),
description="The list of schemas allowed for the database to upload information",
)

View File

@ -32,12 +32,11 @@ under the License.
function update_schemas_allowed_for_csv_upload(db_id) {
$.ajax({
method: "GET",
url: "/superset/schemas_access_for_file_upload",
data: {db_id: db_id},
url: `/api/v1/database/${db_id}/schemas_access_for_file_upload/`,
dataType: 'json',
contentType: "application/json; charset=utf-8"
}).done(function (data) {
change_schema_field_in_formview(data)
change_schema_field_in_formview(data ? data.schemas : [])
}).fail(function (error) {
var errorMsg = error.responseJSON.error;
alert("ERROR: " + errorMsg);

View File

@ -2747,6 +2747,7 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
@has_access_api
@event_logger.log_this
@expose("/schemas_access_for_file_upload")
@deprecated()
def schemas_access_for_file_upload(self) -> FlaskResponse:
"""
This method exposes an API endpoint to

View File

@ -3619,3 +3619,44 @@ class TestDatabaseApi(SupersetTestCase):
return
self.assertEqual(rv.status_code, 422)
self.assertIn("Kaboom!", response["errors"][0]["message"])
@mock.patch(
"superset.security.SupersetSecurityManager.get_schemas_accessible_by_user"
)
@mock.patch("superset.security.SupersetSecurityManager.can_access_database")
@mock.patch("superset.security.SupersetSecurityManager.can_access_all_datasources")
def test_schemas_access_for_csv_upload_not_found_endpoint(
self,
mock_can_access_all_datasources,
mock_can_access_database,
mock_schemas_accessible,
):
self.login(username="gamma")
self.create_fake_db()
mock_can_access_database.return_value = False
mock_schemas_accessible.return_value = ["this_schema_is_allowed_too"]
rv = self.client.get(f"/api/v1/database/120ff/schemas_access_for_file_upload")
self.assertEqual(rv.status_code, 404)
self.delete_fake_db()
@mock.patch(
"superset.security.SupersetSecurityManager.get_schemas_accessible_by_user"
)
@mock.patch("superset.security.SupersetSecurityManager.can_access_database")
@mock.patch("superset.security.SupersetSecurityManager.can_access_all_datasources")
def test_schemas_access_for_csv_upload_endpoint(
self,
mock_can_access_all_datasources,
mock_can_access_database,
mock_schemas_accessible,
):
self.login(username="admin")
dbobj = self.create_fake_db()
mock_can_access_all_datasources.return_value = False
mock_can_access_database.return_value = False
mock_schemas_accessible.return_value = ["this_schema_is_allowed_too"]
data = self.get_json_resp(
url=f"/api/v1/database/{dbobj.id}/schemas_access_for_file_upload"
)
assert data == {"schemas": ["this_schema_is_allowed_too"]}
self.delete_fake_db()