mirror of
https://github.com/apache/superset.git
synced 2026-04-09 19:35:21 +00:00
* feat(dashboard): embedded dashboard UI configuration (#17175) (#17450) * setup embedded provider * update ui configuration * fix test * feat: Guest token (for embedded dashboard auth) (#17517) * generate an embed token * improve existing tests * add some auth setup, and rename token * fix the stuff for compatibility with external request loaders * docs, standard jwt claims, tweaks * black * lint * tests, and safer token decoding * linting * type annotation * prettier * add feature flag * quiet pylint * apparently typing is a problem again * Make guest role name configurable * fake being a non-anonymous user * just one log entry * customizable algo * lint * lint again * 403 works now! * get guest token from header instead of cookie * Revert "403 works now!" This reverts commitdf2f49a6d4. * fix tests * Revert "Revert "403 works now!"" This reverts commit883dff38f1. * rename method * correct import * feat: entry for embedded dashboard (#17529) * create entry for embedded dashboard in webpack * add cookies * lint * token message handshake * guestTokenHeaderName * use setupClient instead of calling configure * rename the webpack chunk * simplified handshake * embedded entrypoint: render a proper app * make the embedded page accept anonymous connections * format * lint * fix test # Conflicts: # superset-frontend/src/embedded/index.tsx # superset/views/core.py * lint * Update superset-frontend/src/embedded/index.tsx Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com> * comment out origins checks * move embedded for core to dashboard * pylint * isort Co-authored-by: David Aaron Suddjian <aasuddjian@gmail.com> Co-authored-by: David Aaron Suddjian <1858430+suddjian@users.noreply.github.com> * feat: Authorizing guest access to embedded dashboards (#17757) * helper methods and dashboard access * guest token dashboard authz * adjust csrf exempt list * eums don't work that way * Remove unnecessary import * move row level security tests to their own file * a bit of refactoring * add guest token security tests * refactor tests * clean imports * variable names can be too long apparently * missing argument to get_user_roles * don't redefine builtins * remove unused imports * fix test import * default to global user when getting roles * missing import * mock it * test get_user_roles * infer g.user for ease of tests * remove redundant check * tests for guest user security manager fns * use algo to get rid of warning messages * tweaking access checks * fix guest token security tests * missing imports * more tests * more testing and also some small refactoring * move validation out of parsing * fix dashboard access check again * add more test Co-authored-by: Lily Kuang <lily@preset.io> * feat: Row Level Security rules for guest tokens (#17836) * helper methods and dashboard access * guest token dashboard authz * adjust csrf exempt list * eums don't work that way * Remove unnecessary import * move row level security tests to their own file * a bit of refactoring * add guest token security tests * refactor tests * clean imports * variable names can be too long apparently * missing argument to get_user_roles * don't redefine builtins * remove unused imports * fix test import * default to global user when getting roles * missing import * mock it * test get_user_roles * infer g.user for ease of tests * remove redundant check * tests for guest user security manager fns * use algo to get rid of warning messages * tweaking access checks * fix guest token security tests * missing imports * more tests * more testing and also some small refactoring * move validation out of parsing * fix dashboard access check again * rls rules for guest tokens * test guest token rls rules * more flexible rls rules * lint * fix tests * fix test * defaults * fix some tests * fix some tests * lint Co-authored-by: Lily Kuang <lily@preset.io> * SupersetClient guest token test * Apply suggestions from code review Co-authored-by: Lily Kuang <lily@preset.io> Co-authored-by: Lily Kuang <lily@preset.io>
155 lines
5.0 KiB
Python
155 lines
5.0 KiB
Python
# Licensed to the Apache Software Foundation (ASF) under one
|
|
# or more contributor license agreements. See the NOTICE file
|
|
# distributed with this work for additional information
|
|
# regarding copyright ownership. The ASF licenses this file
|
|
# to you under the Apache License, Version 2.0 (the
|
|
# "License"); you may not use this file except in compliance
|
|
# with the License. You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing,
|
|
# software distributed under the License is distributed on an
|
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
# KIND, either express or implied. See the License for the
|
|
# specific language governing permissions and limitations
|
|
# under the License.
|
|
import logging
|
|
from typing import Any, Dict
|
|
|
|
from flask import request, Response
|
|
from flask_appbuilder import expose
|
|
from flask_appbuilder.api import BaseApi, safe
|
|
from flask_appbuilder.security.decorators import permission_name, protect
|
|
from flask_wtf.csrf import generate_csrf
|
|
from marshmallow import EXCLUDE, fields, post_load, Schema, ValidationError
|
|
from marshmallow_enum import EnumField
|
|
|
|
from superset.extensions import event_logger
|
|
from superset.security.guest_token import GuestTokenResourceType
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class PermissiveSchema(Schema):
|
|
"""
|
|
A marshmallow schema that ignores unexpected fields, instead of throwing an error.
|
|
"""
|
|
|
|
class Meta: # pylint: disable=too-few-public-methods
|
|
unknown = EXCLUDE
|
|
|
|
|
|
class UserSchema(PermissiveSchema):
|
|
username = fields.String()
|
|
first_name = fields.String()
|
|
last_name = fields.String()
|
|
|
|
|
|
class ResourceSchema(PermissiveSchema):
|
|
type = EnumField(GuestTokenResourceType, by_value=True, required=True)
|
|
id = fields.String(required=True)
|
|
|
|
@post_load
|
|
def convert_enum_to_value( # pylint: disable=no-self-use
|
|
self, data: Dict[str, Any], **kwargs: Any # pylint: disable=unused-argument
|
|
) -> Dict[str, Any]:
|
|
# we don't care about the enum, we want the value inside
|
|
data["type"] = data["type"].value
|
|
return data
|
|
|
|
|
|
class RlsRuleSchema(PermissiveSchema):
|
|
dataset = fields.Integer()
|
|
clause = fields.String(required=True) # todo other options?
|
|
|
|
|
|
class GuestTokenCreateSchema(PermissiveSchema):
|
|
user = fields.Nested(UserSchema)
|
|
resources = fields.List(fields.Nested(ResourceSchema), required=True)
|
|
rls = fields.List(fields.Nested(RlsRuleSchema), required=True)
|
|
|
|
|
|
guest_token_create_schema = GuestTokenCreateSchema()
|
|
|
|
|
|
class SecurityRestApi(BaseApi):
|
|
resource_name = "security"
|
|
allow_browser_login = True
|
|
openapi_spec_tag = "Security"
|
|
|
|
@expose("/csrf_token/", methods=["GET"])
|
|
@event_logger.log_this
|
|
@protect()
|
|
@safe
|
|
@permission_name("read")
|
|
def csrf_token(self) -> Response:
|
|
"""
|
|
Return the csrf token
|
|
---
|
|
get:
|
|
description: >-
|
|
Fetch the CSRF token
|
|
responses:
|
|
200:
|
|
description: Result contains the CSRF token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
result:
|
|
type: string
|
|
401:
|
|
$ref: '#/components/responses/401'
|
|
500:
|
|
$ref: '#/components/responses/500'
|
|
"""
|
|
return self.response(200, result=generate_csrf())
|
|
|
|
@expose("/guest_token/", methods=["POST"])
|
|
@event_logger.log_this
|
|
@protect()
|
|
@safe
|
|
@permission_name("grant_guest_token")
|
|
def guest_token(self) -> Response:
|
|
"""Response
|
|
Returns a guest token that can be used for auth in embedded Superset
|
|
---
|
|
post:
|
|
description: >-
|
|
Fetches a guest token
|
|
requestBody:
|
|
description: Parameters for the guest token
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: GuestTokenCreateSchema
|
|
responses:
|
|
200:
|
|
description: Result contains the guest token
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
token:
|
|
type: string
|
|
401:
|
|
$ref: '#/components/responses/401'
|
|
500:
|
|
$ref: '#/components/responses/500'
|
|
"""
|
|
try:
|
|
body = guest_token_create_schema.load(request.json)
|
|
# todo validate stuff:
|
|
# make sure the resource ids are valid
|
|
# make sure username doesn't reference an existing user
|
|
# check rls rules for validity?
|
|
token = self.appbuilder.sm.create_guest_access_token(
|
|
body["user"], body["resources"], body["rls"]
|
|
)
|
|
return self.response(200, token=token)
|
|
except ValidationError as error:
|
|
return self.response_400(message=error.messages)
|