def apply(self, query: Query, value: Any) -> Query: return query.filter( or_( Dashboard.created_by_fk # pylint: disable=comparison-with-callable == get_user_id(), Dashboard.changed_by_fk # pylint: disable=comparison-with-callable == get_user_id(), ))
def run(self) -> str: self.validate() try: datasource_id = self._cmd_params.datasource_id datasource_type = self._cmd_params.datasource_type chart_id = self._cmd_params.chart_id tab_id = self._cmd_params.tab_id form_data = self._cmd_params.form_data check_access(datasource_id, chart_id, datasource_type) contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) key = cache_manager.explore_form_data_cache.get(contextual_key) if not key or not tab_id: key = random_key() if form_data: state: TemporaryExploreState = { "owner": get_user_id(), "datasource_id": datasource_id, "datasource_type": DatasourceType(datasource_type), "chart_id": chart_id, "form_data": form_data, } cache_manager.explore_form_data_cache.set(key, state) cache_manager.explore_form_data_cache.set(contextual_key, key) return key except SQLAlchemyError as ex: logger.exception("Error running create command") raise TemporaryCacheCreateFailedError() from ex
def populate_owners( owner_ids: Optional[List[int]], default_to_user: bool, ) -> List[User]: """ Helper function for commands, will fetch all users from owners id's :param owner_ids: list of owners by id's :param default_to_user: make user the owner if `owner_ids` is None or empty :raises OwnersNotFoundValidationError: if at least one owner id can't be resolved :returns: Final list of owners """ owner_ids = owner_ids or [] owners = [] if not owner_ids and default_to_user: return [g.user] if not (security_manager.is_admin() or get_user_id() in owner_ids): # make sure non-admins can't remove themselves as owner by mistake owners.append(g.user) for owner_id in owner_ids: owner = security_manager.get_user_by_id(owner_id) if not owner: raise OwnersNotFoundValidationError() owners.append(owner) return owners
def validate_session(response: Response) -> Response: user_id = get_user_id() reset_token = ( not request.cookies.get(self._jwt_cookie_name) or "async_channel_id" not in session or "async_user_id" not in session or user_id != session["async_user_id"] ) if reset_token: async_channel_id = str(uuid.uuid4()) session["async_channel_id"] = async_channel_id session["async_user_id"] = user_id sub = str(user_id) if user_id else None token = jwt.encode( {"channel": async_channel_id, "sub": sub}, self._jwt_secret, algorithm="HS256", ) response.set_cookie( self._jwt_cookie_name, value=token, httponly=True, secure=self._jwt_cookie_secure, domain=self._jwt_cookie_domain, ) return response
def update(self, cmd_params: CommandParameters) -> Optional[str]: resource_id = cmd_params.resource_id key = cmd_params.key value = cast( str, cmd_params.value) # schema ensures that value is not optional check_access(resource_id) entry: Entry = cache_manager.filter_state_cache.get( cache_key(resource_id, key)) owner = get_user_id() if entry: if entry["owner"] != owner: raise TemporaryCacheAccessDeniedError() # Generate a new key if tab_id changes or equals 0 contextual_key = cache_key(session.get("_id"), cmd_params.tab_id, resource_id) key = cache_manager.filter_state_cache.get(contextual_key) if not key or not cmd_params.tab_id: key = random_key() cache_manager.filter_state_cache.set(contextual_key, key) new_entry: Entry = {"owner": owner, "value": value} cache_manager.filter_state_cache.set(cache_key(resource_id, key), new_entry) return key
def _run_async(self, form_data: Dict[str, Any], command: ChartDataCommand) -> Response: """ Execute command as an async query. """ # First, look for the chart query results in the cache. result = None try: result = command.run(force_cached=True) if result is not None: return self._send_chart_response(result) except ChartDataCacheLoadError: pass # Otherwise, kick off a background job to run the chart query. # Clients will either poll or be notified of query completion, # at which point they will call the /data/<cache_key> endpoint # to retrieve the results. async_command = CreateAsyncChartDataJobCommand() try: async_command.validate(request) except AsyncQueryTokenException: return self.response_401() result = async_command.run(form_data, get_user_id()) return self.response(202, **result)
def favorited_ids(charts: List[Slice]) -> List[FavStar]: ids = [chart.id for chart in charts] return [ star.obj_id for star in db.session.query(FavStar.obj_id).filter( FavStar.class_name == FavStarClassName.CHART, FavStar.obj_id.in_(ids), FavStar.user_id == get_user_id(), ).all() ]
def test_get_user_id( app_context: AppContext, mocker: MockFixture, username: Optional[str], user_id: Optional[int], ) -> None: mock_g = mocker.patch("superset.utils.core.g", spec={}) mock_g.user = security_manager.find_user(username) assert get_user_id() == user_id
def favorited_ids(dashboards: List[Dashboard]) -> List[FavStar]: ids = [dash.id for dash in dashboards] return [ star.obj_id for star in db.session.query(FavStar.obj_id).filter( FavStar.class_name == FavStarClassName.DASHBOARD, FavStar.obj_id.in_(ids), FavStar.user_id == get_user_id(), ).all() ]
def apply(self, query: BaseQuery, value: Any) -> BaseQuery: """ Filter queries to only those owned by current user. If can_access_all_queries permission is set a user can list all queries :returns: query """ if not security_manager.can_access_all_queries(): query = query.filter(Query.user_id == get_user_id()) return query
def set_owners(instance: Model, owners: List[int]) -> None: owner_objs = [] user_id = get_user_id() if user_id and user_id not in owners: owners.append(user_id) for owner_id in owners: user = current_app.appbuilder.get_session.query( current_app.appbuilder.sm.user_model).get(owner_id) owner_objs.append(user) instance.owners = owner_objs
def apply(self, query: Query, value: Any) -> Query: # If anonymous user filter nothing if security_manager.current_user is None: return query users_favorite_query = db.session.query(FavStar.obj_id).filter( and_( FavStar.user_id == get_user_id(), FavStar.class_name == self.class_name, )) if value: return query.filter(and_(self.model.id.in_(users_favorite_query))) return query.filter(and_(~self.model.id.in_(users_favorite_query)))
def apply(self, query: Query, value: Any) -> Query: if security_manager.is_admin(): return query filter_set_ids_by_dashboard_owners = ( # pylint: disable=C0103 query.from_self(FilterSet.id).join( dashboard_user, FilterSet.owner_id == dashboard_user.c.dashboard_id).filter( and_( FilterSet.owner_type == DASHBOARD_OWNER_TYPE, dashboard_user.c.user_id == get_user_id(), ))) return query.filter( or_( and_( FilterSet.owner_type == USER_OWNER_TYPE, FilterSet.owner_id == get_user_id(), ), FilterSet.id.in_(filter_set_ids_by_dashboard_owners), ))
def delete(self, cmd_params: CommandParameters) -> bool: resource_id = cmd_params.resource_id key = cache_key(resource_id, cmd_params.key) check_access(resource_id) entry: Entry = cache_manager.filter_state_cache.get(key) if entry: if entry["owner"] != get_user_id(): raise TemporaryCacheAccessDeniedError() tab_id = cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, resource_id) cache_manager.filter_state_cache.delete(contextual_key) return cache_manager.filter_state_cache.delete(key) return False
def create(self, cmd_params: CommandParameters) -> str: resource_id = cmd_params.resource_id tab_id = cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, resource_id) key = cache_manager.filter_state_cache.get(contextual_key) if not key or not tab_id: key = random_key() value = cast(str, cmd_params.value) # schema ensures that value is not optional check_access(resource_id) entry: Entry = {"owner": get_user_id(), "value": value} cache_manager.filter_state_cache.set(cache_key(resource_id, key), entry) cache_manager.filter_state_cache.set(contextual_key, key) return key
def current_user_id(self, add_to_cache_keys: bool = True) -> Optional[int]: """ Return the user ID of the user who is currently logged in. :param add_to_cache_keys: Whether the value should be included in the cache key :returns: The user ID """ if hasattr(g, "user") and g.user: id_ = get_user_id() if add_to_cache_keys: self.cache_key_wrapper(id_) return id_ return None
def validate_unique_creation_method( dashboard_id: Optional[int] = None, chart_id: Optional[int] = None ) -> bool: """ Validate if the user already has a chart or dashboard with a report attached form the self subscribe reports """ query = db.session.query(ReportSchedule).filter_by(created_by_fk=get_user_id()) if dashboard_id is not None: query = query.filter(ReportSchedule.dashboard_id == dashboard_id) if chart_id is not None: query = query.filter(ReportSchedule.chart_id == chart_id) return not db.session.query(query.exists()).scalar()
def apply(self, query: Query, value: Any) -> Query: if security_manager.can_access_all_datasources(): return query datasource_perms = security_manager.user_view_menu_names("datasource_access") schema_perms = security_manager.user_view_menu_names("schema_access") owner_ids_query = ( db.session.query(models.SqlaTable.id) .join(models.SqlaTable.owners) .filter(security_manager.user_model.id == get_user_id()) ) return query.filter( or_( self.model.perm.in_(datasource_perms), self.model.schema_perm.in_(schema_perms), models.SqlaTable.id.in_(owner_ids_query), ) )
def filter_sets_lst(self) -> Dict[int, FilterSet]: if security_manager.is_admin(): return self._filter_sets filter_sets_by_owner_type: Dict[str, List[Any]] = { "Dashboard": [], "User": [] } for fs in self._filter_sets: filter_sets_by_owner_type[fs.owner_type].append(fs) user_filter_sets = list( filter( lambda filter_set: filter_set.owner_id == get_user_id(), filter_sets_by_owner_type["User"], )) return { fs.id: fs for fs in user_filter_sets + filter_sets_by_owner_type["Dashboard"] }
def update(self) -> Optional[Key]: filter_ = get_filter(self.resource, self.key) entry: KeyValueEntry = ( db.session.query(KeyValueEntry) .filter_by(**filter_) .autoflush(False) .first() ) if entry: entry.value = pickle.dumps(self.value) entry.expires_on = self.expires_on entry.changed_on = datetime.now() entry.changed_by_fk = get_user_id() db.session.merge(entry) db.session.commit() return Key(id=entry.id, uuid=entry.uuid) return None
def create(self) -> Key: entry = KeyValueEntry( resource=self.resource.value, value=pickle.dumps(self.value), created_on=datetime.now(), created_by_fk=get_user_id(), expires_on=self.expires_on, ) if self.key is not None: try: if isinstance(self.key, UUID): entry.uuid = self.key else: entry.id = self.key except ValueError as ex: raise KeyValueCreateFailedError() from ex db.session.add(entry) db.session.commit() return Key(id=entry.id, uuid=entry.uuid)
def run(self) -> str: self.validate() try: DashboardDAO.get_by_id_or_slug(self.dashboard_id) value = { "dashboardId": self.dashboard_id, "state": self.state, } user_id = get_user_id() key = UpsertKeyValueCommand( resource=self.resource, key=get_deterministic_uuid(self.salt, (user_id, value)), value=value, ).run() assert key.id # for type checks return encode_permalink_key(key=key.id, salt=self.salt) except SQLAlchemyError as ex: logger.exception("Error running create command") raise DashboardPermalinkCreateFailedError() from ex
def upsert(self) -> Key: filter_ = get_filter(self.resource, self.key) entry: KeyValueEntry = ( db.session.query(KeyValueEntry) .filter_by(**filter_) .autoflush(False) .first() ) if entry: entry.value = pickle.dumps(self.value) entry.expires_on = self.expires_on entry.changed_on = datetime.now() entry.changed_by_fk = get_user_id() db.session.merge(entry) db.session.commit() return Key(entry.id, entry.uuid) return CreateKeyValueCommand( resource=self.resource, value=self.value, key=self.key, expires_on=self.expires_on, ).run()
def check_ownership(self) -> None: try: if not security_manager.is_admin(): filter_set: FilterSet = cast(FilterSet, self._filter_set) if filter_set.owner_type == USER_OWNER_TYPE: if get_user_id() != filter_set.owner_id: raise FilterSetForbiddenError( str(self._filter_set_id), "The user is not the owner of the filter_set", ) elif not security_manager.is_owner(self._dashboard): raise FilterSetForbiddenError( str(self._filter_set_id), "The user is not an owner of the filter_set's dashboard", ) except NotAuthorizedException as err: raise FilterSetForbiddenError( str(self._filter_set_id), "user not authorized to access the filterset", ) from err except FilterSetForbiddenError as err: raise err
def run(self) -> bool: try: key = self._cmd_params.key state: TemporaryExploreState = cache_manager.explore_form_data_cache.get( key) if state: datasource_id: int = state["datasource_id"] chart_id: Optional[int] = state["chart_id"] datasource_type = DatasourceType(state["datasource_type"]) check_access(datasource_id, chart_id, datasource_type) if state["owner"] != get_user_id(): raise TemporaryCacheAccessDeniedError() tab_id = self._cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) cache_manager.explore_form_data_cache.delete(contextual_key) return cache_manager.explore_form_data_cache.delete(key) return False except SQLAlchemyError as ex: logger.exception("Error running delete command") raise TemporaryCacheDeleteFailedError() from ex
def run(self) -> Optional[str]: self.validate() try: datasource_id = self._cmd_params.datasource_id chart_id = self._cmd_params.chart_id datasource_type = self._cmd_params.datasource_type key = self._cmd_params.key form_data = self._cmd_params.form_data check_access(datasource_id, chart_id, datasource_type) state: TemporaryExploreState = cache_manager.explore_form_data_cache.get( key) owner = get_user_id() if state and form_data: if state["owner"] != owner: raise TemporaryCacheAccessDeniedError() # Generate a new key if tab_id changes or equals 0 tab_id = self._cmd_params.tab_id contextual_key = cache_key(session.get("_id"), tab_id, datasource_id, chart_id, datasource_type) key = cache_manager.explore_form_data_cache.get(contextual_key) if not key or not tab_id: key = random_key() cache_manager.explore_form_data_cache.set( contextual_key, key) new_state: TemporaryExploreState = { "owner": owner, "datasource_id": datasource_id, "datasource_type": DatasourceType(datasource_type), "chart_id": chart_id, "form_data": form_data, } cache_manager.explore_form_data_cache.set(key, new_state) return key except SQLAlchemyError as ex: logger.exception("Error running update command") raise TemporaryCacheUpdateFailedError() from ex
def log_with_context( # pylint: disable=too-many-locals self, action: str, duration: Optional[timedelta] = None, object_ref: Optional[str] = None, log_to_statsd: bool = True, **payload_override: Optional[Dict[str, Any]], ) -> None: # pylint: disable=import-outside-toplevel from superset.views.core import get_form_data referrer = request.referrer[:1000] if request and request.referrer else None duration_ms = int(duration.total_seconds() * 1000) if duration else None # Initial try and grab user_id via flask.g.user user_id = get_user_id() # Whenever a user is not bounded to a session we # need to add them back before logging to capture user_id if user_id is None: try: session = current_app.appbuilder.get_session session.add(g.user) user_id = get_user_id() except Exception as ex: # pylint: disable=broad-except logging.warning(ex) user_id = None payload = collect_request_payload() if object_ref: payload["object_ref"] = object_ref if payload_override: payload.update(payload_override) dashboard_id: Optional[int] = None try: dashboard_id = int(payload.get("dashboard_id")) # type: ignore except (TypeError, ValueError): dashboard_id = None if "form_data" in payload: form_data, _ = get_form_data() payload["form_data"] = form_data slice_id = form_data.get("slice_id") else: slice_id = payload.get("slice_id") try: slice_id = int(slice_id) # type: ignore except (TypeError, ValueError): slice_id = 0 if log_to_statsd: self.stats_logger.incr(action) try: # bulk insert explode_by = payload.get("explode") records = json.loads(payload.get(explode_by)) # type: ignore except Exception: # pylint: disable=broad-except records = [payload] self.log( user_id, action, records=records, dashboard_id=dashboard_id, slice_id=slice_id, duration_ms=duration_ms, referrer=referrer, )
def _validate_owner_id_exists(self) -> None: owner_id = self._properties[OWNER_ID_FIELD] if not (get_user_id() == owner_id or security_manager.get_user_by_id(owner_id)): raise FilterSetCreateFailedError( str(self._dashboard_id), "owner_id does not exists" )
def apply(self, query: Query, value: Any) -> Query: if security_manager.is_admin(): return query datasource_perms = security_manager.user_view_menu_names( "datasource_access") schema_perms = security_manager.user_view_menu_names("schema_access") is_rbac_disabled_filter = [] dashboard_has_roles = Dashboard.roles.any() if is_feature_enabled("DASHBOARD_RBAC"): is_rbac_disabled_filter.append(~dashboard_has_roles) datasource_perm_query = (db.session.query(Dashboard.id).join( Dashboard.slices, isouter=True).filter( and_( Dashboard.published.is_(True), *is_rbac_disabled_filter, or_( Slice.perm.in_(datasource_perms), Slice.schema_perm.in_(schema_perms), security_manager.can_access_all_datasources(), ), ))) users_favorite_dash_query = db.session.query(FavStar.obj_id).filter( and_( FavStar.user_id == get_user_id(), FavStar.class_name == "Dashboard", )) owner_ids_query = (db.session.query(Dashboard.id).join( Dashboard.owners).filter( security_manager.user_model.id == get_user_id())) feature_flagged_filters = [] if is_feature_enabled("DASHBOARD_RBAC"): roles_based_query = (db.session.query(Dashboard.id).join( Dashboard.roles).filter( and_( Dashboard.published.is_(True), dashboard_has_roles, Role.id.in_( [x.id for x in security_manager.get_user_roles()]), ), )) feature_flagged_filters.append(Dashboard.id.in_(roles_based_query)) if is_feature_enabled("EMBEDDED_SUPERSET" ) and security_manager.is_guest_user(g.user): guest_user: GuestUser = g.user embedded_dashboard_ids = [ r["id"] for r in guest_user.resources if r["type"] == GuestTokenResourceType.DASHBOARD.value ] # TODO (embedded): only use uuid filter once uuids are rolled out condition = (Dashboard.embedded.any( EmbeddedDashboard.uuid.in_(embedded_dashboard_ids)) if any( is_uuid(id_) for id_ in embedded_dashboard_ids) else Dashboard.id.in_(embedded_dashboard_ids)) feature_flagged_filters.append(condition) query = query.filter( or_( Dashboard.id.in_(owner_ids_query), Dashboard.id.in_(datasource_perm_query), Dashboard.id.in_(users_favorite_dash_query), *feature_flagged_filters, )) return query
def __init__(self, query_params: Dict[str, Any]): self.create_table_as_select = None self.database = None self._init_from_query_params(query_params) self.user_id = get_user_id() self.client_id_or_short_id = cast(str, self.client_id or utils.shortid()[:10])