From fe68c1232fa287e6c49e34b258734d2afece5907 Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Tue, 15 Feb 2022 23:59:23 -0800 Subject: [PATCH 1/7] /me api --- superset/initialization/__init__.py | 2 ++ superset/users/api.py | 54 +++++++++++++++++++++++++++++ superset/users/schemas.py | 27 +++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 superset/users/api.py create mode 100644 superset/users/schemas.py diff --git a/superset/initialization/__init__.py b/superset/initialization/__init__.py index f2bce87afbc5b..d2b67d95b62af 100644 --- a/superset/initialization/__init__.py +++ b/superset/initialization/__init__.py @@ -50,6 +50,7 @@ ) from superset.security import SupersetSecurityManager from superset.typing import FlaskResponse +from superset.users.api import CurrentUserRestApi from superset.utils.core import pessimistic_connection_handling from superset.utils.log import DBEventLogger, get_event_logger_from_cfg_value @@ -205,6 +206,7 @@ def init_views(self) -> None: appbuilder.add_api(ChartRestApi) appbuilder.add_api(ChartDataRestApi) appbuilder.add_api(CssTemplateRestApi) + appbuilder.add_api(CurrentUserRestApi) appbuilder.add_api(DashboardFilterStateRestApi) appbuilder.add_api(DashboardRestApi) appbuilder.add_api(DatabaseRestApi) diff --git a/superset/users/api.py b/superset/users/api.py new file mode 100644 index 0000000000000..1c8e0ce81869f --- /dev/null +++ b/superset/users/api.py @@ -0,0 +1,54 @@ +# 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. +from flask import g +from flask_appbuilder.api import BaseApi, expose, safe + +from .schemas import UserResponseSchema + +user_response_schema = UserResponseSchema() + + +class CurrentUserRestApi(BaseApi): + """ An api to get information about the current user """ + + resource_name = "me" + openapi_spec_tag = "Current User" + openapi_spec_component_schemas = (UserResponseSchema,) + + @expose("/", methods=["GET"]) + @safe + def me(self): + """Get the user object corresponding to the agent making the request + --- + get: + description: >- + Returns the user object corresponding to the agent making the request, + or returns a 401 error if the request is unauthenticated. + responses: + 200: + description: The current user + content: + application/json: + schema: + type: object + properties: + result: + $ref: '#/components/schemas/UserResponseSchema' + 401: + $ref: '#/components/responses/401' + """ + return self.response(200, result=user_response_schema.dump(g.user)) diff --git a/superset/users/schemas.py b/superset/users/schemas.py new file mode 100644 index 0000000000000..9b28bb65b322e --- /dev/null +++ b/superset/users/schemas.py @@ -0,0 +1,27 @@ +# 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. +from marshmallow import Schema +from marshmallow.fields import Boolean, Integer, String + + +class UserResponseSchema(Schema): + id = Integer() + username = String() + email = String() + first_name = String() + last_name = String() + is_anonymous = Boolean() From 0ff9e5699a76d48a9ff2d36f5e3c09e281ed8190 Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Wed, 16 Feb 2022 16:43:07 -0800 Subject: [PATCH 2/7] test it --- superset/users/api.py | 8 ++-- superset/users/schemas.py | 1 + superset/views/utils.py | 1 + tests/integration_tests/users/api_tests.py | 48 ++++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 tests/integration_tests/users/api_tests.py diff --git a/superset/users/api.py b/superset/users/api.py index 1c8e0ce81869f..7d52056a8f070 100644 --- a/superset/users/api.py +++ b/superset/users/api.py @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from flask import g +from flask import g, Response from flask_appbuilder.api import BaseApi, expose, safe from .schemas import UserResponseSchema @@ -31,13 +31,13 @@ class CurrentUserRestApi(BaseApi): @expose("/", methods=["GET"]) @safe - def me(self): + def me(self) -> Response: """Get the user object corresponding to the agent making the request --- get: description: >- Returns the user object corresponding to the agent making the request, - or returns a 401 error if the request is unauthenticated. + or returns a 401 error if the user is unauthenticated. responses: 200: description: The current user @@ -51,4 +51,6 @@ def me(self): 401: $ref: '#/components/responses/401' """ + if g.user is None or g.user.is_anonymous: + return self.response_401() return self.response(200, result=user_response_schema.dump(g.user)) diff --git a/superset/users/schemas.py b/superset/users/schemas.py index 9b28bb65b322e..021209edafb8e 100644 --- a/superset/users/schemas.py +++ b/superset/users/schemas.py @@ -24,4 +24,5 @@ class UserResponseSchema(Schema): email = String() first_name = String() last_name = String() + is_active = Boolean() is_anonymous = Boolean() diff --git a/superset/views/utils.py b/superset/views/utils.py index eda24863e1059..c318c38cbf17d 100644 --- a/superset/views/utils.py +++ b/superset/views/utils.py @@ -81,6 +81,7 @@ def bootstrap_user_data(user: User, include_perms: bool = False) -> Dict[str, An "lastName": user.last_name, "userId": user.id, "isActive": user.is_active, + "isAnonymous": user.is_anonymous, "createdOn": user.created_on.isoformat(), "email": user.email, } diff --git a/tests/integration_tests/users/api_tests.py b/tests/integration_tests/users/api_tests.py new file mode 100644 index 0000000000000..6f5e474f8b899 --- /dev/null +++ b/tests/integration_tests/users/api_tests.py @@ -0,0 +1,48 @@ +# 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. +"""Unit tests for Superset""" +import json +from unittest.mock import patch + +from superset import security_manager +from tests.integration_tests.base_tests import SupersetTestCase + +meUri = "/api/v1/me/" + + +class TestCurrentUserApi(SupersetTestCase): + def test_get_me_logged_in(self): + self.login(username="admin") + + rv = self.client.get(meUri) + + self.assertEqual(200, rv.status_code) + response = json.loads(rv.data.decode("utf-8")) + self.assertEqual("admin", response["result"]["username"]) + self.assertEqual(True, response["result"]["is_active"]) + self.assertEqual(False, response["result"]["is_anonymous"]) + + def test_get_me_unauthorized(self): + self.logout() + rv = self.client.get(meUri) + self.assertEqual(401, rv.status_code) + + @patch("superset.security.manager.g") + def test_get_me_anonymous(self, mock_g): + mock_g.user = security_manager.get_anonymous_user + rv = self.client.get(meUri) + self.assertEqual(401, rv.status_code) From d99a5c03b1d9b647adca6cf8560179a66b9fcb9d Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Wed, 16 Feb 2022 16:44:09 -0800 Subject: [PATCH 3/7] watch for window activation and check auth --- .../useWindowActivatedAuthCheck.test.tsx | 78 +++++++++++++++++++ .../src/hooks/useWindowActivatedAuthCheck.ts | 52 +++++++++++++ superset-frontend/src/preamble.ts | 3 +- superset-frontend/src/views/App.tsx | 11 +++ 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx create mode 100644 superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts diff --git a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx new file mode 100644 index 0000000000000..a3a27ec38930d --- /dev/null +++ b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx @@ -0,0 +1,78 @@ +/** + * 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 React from 'react'; +import { fireEvent, waitFor } from '@testing-library/dom'; +import { render } from '@testing-library/react'; +import fetchMock from 'fetch-mock'; + +jest.useFakeTimers(); + +import { useWindowActivatedAuthCheck } from './useWindowActivatedAuthCheck'; + +const HookTester = () => { + useWindowActivatedAuthCheck(); + return <>hook tester; +}; + +describe('useWindowActivatedAuthCheck', () => { + beforeEach(() => { + // jsdom doesn't support window location, so just gonna fake it here real simple-like + Object.defineProperty(window, 'location', { + value: { + href: 'http://example.com/fake-test-page', + pathname: '/fake-test-page', + search: '?foo=bar', + }, + writable: true, + }); + }); + + afterEach(() => { + fetchMock.restore(); + }); + + it('redirects when the user tabs back after logging out elsewhere', async () => { + fetchMock.get('glob:*/api/v1/me/', { status: 401 }); + + render(); + + fireEvent(document, new Event('visibilitychange')); + + await waitFor(() => { + expect(window.location.href).toEqual( + '/login?next=/fake-test-page?foo=bar', + ); + }); + }); + + it('does not redirect if the user is still logged in', async () => { + fetchMock.get('glob:*/api/v1/me/', { + status: 200, + json: { username: 'test_user' }, + }); + + render(); + + fireEvent(document, new Event('visibilitychange')); + + await jest.runAllTimers(); + + expect(window.location.href).toEqual('http://example.com/fake-test-page'); + }); +}); diff --git a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts new file mode 100644 index 0000000000000..cdd02e3f35461 --- /dev/null +++ b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts @@ -0,0 +1,52 @@ +/** + * 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 { useEffect } from 'react'; +import { makeApi } from '@superset-ui/core'; +import { User } from 'src/types/bootstrapTypes'; + +const getMe = makeApi({ + method: 'GET', + endpoint: '/api/v1/me/', +}); + +/** + * When the window becomes visible, checks for the current auth state. + * If we get a 401, we are no longer logged in and the SupersetClient will redirect us. + * This ensures that if you log out in browser tab A, and click to tab B, + * tab B will also display as logged out. + */ +export function useWindowActivatedAuthCheck() { + useEffect(() => { + const listener = () => { + // we only care about the tab becoming visible, not vice versa + if (document.visibilityState !== 'visible') return; + + getMe().catch(() => { + // ignore error, SupersetClient will redirect to login on a 401 + }); + }; + + document.addEventListener('visibilitychange', listener); + + return () => { + document.removeEventListener('visibilitychange', listener); + }; + }, []); +} diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts index 0547e8a6abcca..5a268c3febcd6 100644 --- a/superset-frontend/src/preamble.ts +++ b/superset-frontend/src/preamble.ts @@ -26,13 +26,14 @@ import setupClient from './setup/setupClient'; import setupColors from './setup/setupColors'; import setupFormatters from './setup/setupFormatters'; import setupDashboardComponents from './setup/setupDasboardComponents'; +import { User } from './types/bootstrapTypes'; if (process.env.WEBPACK_MODE === 'development') { setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false }); } // eslint-disable-next-line import/no-mutable-exports -export let bootstrapData: any; +export let bootstrapData: { user?: User | undefined; common?: any } = {}; // Configure translation if (typeof window !== 'undefined') { const root = document.getElementById('app'); diff --git a/superset-frontend/src/views/App.tsx b/superset-frontend/src/views/App.tsx index 4e193bbf776aa..379a38e8d2867 100644 --- a/superset-frontend/src/views/App.tsx +++ b/superset-frontend/src/views/App.tsx @@ -34,6 +34,7 @@ import setupApp from 'src/setup/setupApp'; import { routes, isFrontendRoute } from 'src/views/routes'; import { Logger } from 'src/logger/LogUtils'; import { RootContextProviders } from './RootContextProviders'; +import { useWindowActivatedAuthCheck } from 'src/hooks/useWindowActivatedAuthCheck'; setupApp(); @@ -55,8 +56,18 @@ const LocationPathnameLogger = () => { return <>; }; +/** This component is just here so we can conditionally call this hook */ +const AuthVigilance = () => { + useWindowActivatedAuthCheck(); + return <>; +}; + const App = () => ( + { + // only check auth on window visibility change if the user is actually logged in in the first place + user?.isActive && + } From 40c40edc75123e3eb9d4ddb5d0aaa959c0bbf280 Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Wed, 16 Feb 2022 18:04:06 -0800 Subject: [PATCH 4/7] simplify --- .../src/dashboard/util/findPermission.test.ts | 1 + .../useWindowActivatedAuthCheck.test.tsx | 78 ------------------- .../src/hooks/useWindowActivatedAuthCheck.ts | 52 ------------- superset-frontend/src/preamble.ts | 28 ++++++- superset-frontend/src/types/bootstrapTypes.ts | 1 + superset-frontend/src/views/App.tsx | 11 --- 6 files changed, 28 insertions(+), 143 deletions(-) delete mode 100644 superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx delete mode 100644 superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts diff --git a/superset-frontend/src/dashboard/util/findPermission.test.ts b/superset-frontend/src/dashboard/util/findPermission.test.ts index f90c2800f4a37..8930549f4a7e9 100644 --- a/superset-frontend/src/dashboard/util/findPermission.test.ts +++ b/superset-frontend/src/dashboard/util/findPermission.test.ts @@ -75,6 +75,7 @@ describe('canUserEditDashboard', () => { email: 'user@example.com', firstName: 'Test', isActive: true, + isAnonymous: false, lastName: 'User', userId: 1, username: 'owner', diff --git a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx deleted file mode 100644 index a3a27ec38930d..0000000000000 --- a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -/** - * 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 React from 'react'; -import { fireEvent, waitFor } from '@testing-library/dom'; -import { render } from '@testing-library/react'; -import fetchMock from 'fetch-mock'; - -jest.useFakeTimers(); - -import { useWindowActivatedAuthCheck } from './useWindowActivatedAuthCheck'; - -const HookTester = () => { - useWindowActivatedAuthCheck(); - return <>hook tester; -}; - -describe('useWindowActivatedAuthCheck', () => { - beforeEach(() => { - // jsdom doesn't support window location, so just gonna fake it here real simple-like - Object.defineProperty(window, 'location', { - value: { - href: 'http://example.com/fake-test-page', - pathname: '/fake-test-page', - search: '?foo=bar', - }, - writable: true, - }); - }); - - afterEach(() => { - fetchMock.restore(); - }); - - it('redirects when the user tabs back after logging out elsewhere', async () => { - fetchMock.get('glob:*/api/v1/me/', { status: 401 }); - - render(); - - fireEvent(document, new Event('visibilitychange')); - - await waitFor(() => { - expect(window.location.href).toEqual( - '/login?next=/fake-test-page?foo=bar', - ); - }); - }); - - it('does not redirect if the user is still logged in', async () => { - fetchMock.get('glob:*/api/v1/me/', { - status: 200, - json: { username: 'test_user' }, - }); - - render(); - - fireEvent(document, new Event('visibilitychange')); - - await jest.runAllTimers(); - - expect(window.location.href).toEqual('http://example.com/fake-test-page'); - }); -}); diff --git a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts b/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts deleted file mode 100644 index cdd02e3f35461..0000000000000 --- a/superset-frontend/src/hooks/useWindowActivatedAuthCheck.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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 { useEffect } from 'react'; -import { makeApi } from '@superset-ui/core'; -import { User } from 'src/types/bootstrapTypes'; - -const getMe = makeApi({ - method: 'GET', - endpoint: '/api/v1/me/', -}); - -/** - * When the window becomes visible, checks for the current auth state. - * If we get a 401, we are no longer logged in and the SupersetClient will redirect us. - * This ensures that if you log out in browser tab A, and click to tab B, - * tab B will also display as logged out. - */ -export function useWindowActivatedAuthCheck() { - useEffect(() => { - const listener = () => { - // we only care about the tab becoming visible, not vice versa - if (document.visibilityState !== 'visible') return; - - getMe().catch(() => { - // ignore error, SupersetClient will redirect to login on a 401 - }); - }; - - document.addEventListener('visibilitychange', listener); - - return () => { - document.removeEventListener('visibilitychange', listener); - }; - }, []); -} diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts index 5a268c3febcd6..976016fec20be 100644 --- a/superset-frontend/src/preamble.ts +++ b/superset-frontend/src/preamble.ts @@ -20,7 +20,7 @@ import { setConfig as setHotLoaderConfig } from 'react-hot-loader'; import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'; import moment from 'moment'; // eslint-disable-next-line no-restricted-imports -import { configure, supersetTheme } from '@superset-ui/core'; +import { configure, makeApi, supersetTheme } from '@superset-ui/core'; import { merge } from 'lodash'; import setupClient from './setup/setupClient'; import setupColors from './setup/setupColors'; @@ -33,7 +33,11 @@ if (process.env.WEBPACK_MODE === 'development') { } // eslint-disable-next-line import/no-mutable-exports -export let bootstrapData: { user?: User | undefined; common?: any } = {}; +export let bootstrapData: { + user?: User | undefined; + common?: any; + config?: any; +} = {}; // Configure translation if (typeof window !== 'undefined') { const root = document.getElementById('app'); @@ -68,3 +72,23 @@ export const theme = merge( supersetTheme, bootstrapData?.common?.theme_overrides ?? {}, ); + +const getMe = makeApi({ + method: 'GET', + endpoint: '/api/v1/me/', +}); + +/** + * When you re-open the window, we check if you are still logged in. + * If your session expired or you signed out, we'll redirect to login. + */ +if (bootstrapData.user?.isActive) { + document.addEventListener('visibilitychange', () => { + // we only care about the tab becoming visible, not vice versa + if (document.visibilityState !== 'visible') return; + + getMe().catch(() => { + // ignore error, SupersetClient will redirect to login on a 401 + }); + }); +} diff --git a/superset-frontend/src/types/bootstrapTypes.ts b/superset-frontend/src/types/bootstrapTypes.ts index 15e7eba0a0313..dc41eb5878f81 100644 --- a/superset-frontend/src/types/bootstrapTypes.ts +++ b/superset-frontend/src/types/bootstrapTypes.ts @@ -23,6 +23,7 @@ export type User = { email: string; firstName: string; isActive: boolean; + isAnonymous: boolean; lastName: string; userId: number; username: string; diff --git a/superset-frontend/src/views/App.tsx b/superset-frontend/src/views/App.tsx index 379a38e8d2867..4e193bbf776aa 100644 --- a/superset-frontend/src/views/App.tsx +++ b/superset-frontend/src/views/App.tsx @@ -34,7 +34,6 @@ import setupApp from 'src/setup/setupApp'; import { routes, isFrontendRoute } from 'src/views/routes'; import { Logger } from 'src/logger/LogUtils'; import { RootContextProviders } from './RootContextProviders'; -import { useWindowActivatedAuthCheck } from 'src/hooks/useWindowActivatedAuthCheck'; setupApp(); @@ -56,18 +55,8 @@ const LocationPathnameLogger = () => { return <>; }; -/** This component is just here so we can conditionally call this hook */ -const AuthVigilance = () => { - useWindowActivatedAuthCheck(); - return <>; -}; - const App = () => ( - { - // only check auth on window visibility change if the user is actually logged in in the first place - user?.isActive && - } From f86de51f8d30b272bb42e987cbee339174cba6a3 Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Wed, 16 Feb 2022 18:08:02 -0800 Subject: [PATCH 5/7] more comment --- superset-frontend/src/preamble.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts index 976016fec20be..7bd6dbe29b365 100644 --- a/superset-frontend/src/preamble.ts +++ b/superset-frontend/src/preamble.ts @@ -81,6 +81,7 @@ const getMe = makeApi({ /** * When you re-open the window, we check if you are still logged in. * If your session expired or you signed out, we'll redirect to login. + * If you aren't logged in in the first place (!isActive), then we shouldn't do this. */ if (bootstrapData.user?.isActive) { document.addEventListener('visibilitychange', () => { From 1be3e0cb54fea713f26b5c44a670602e317e906d Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Thu, 17 Feb 2022 13:38:34 -0800 Subject: [PATCH 6/7] making ci happy --- superset-frontend/src/profile/components/fixtures.tsx | 1 + tests/integration_tests/security_tests.py | 1 + 2 files changed, 2 insertions(+) diff --git a/superset-frontend/src/profile/components/fixtures.tsx b/superset-frontend/src/profile/components/fixtures.tsx index 90e97da22c762..e721b6dfa7510 100644 --- a/superset-frontend/src/profile/components/fixtures.tsx +++ b/superset-frontend/src/profile/components/fixtures.tsx @@ -40,6 +40,7 @@ export const user: UserWithPermissionsAndRoles = { userId: 5, email: 'alpha@alpha.com', isActive: true, + isAnonymous: false, permissions: { datasource_access: ['table1', 'table2'], database_access: ['db1', 'db2', 'db3'], diff --git a/tests/integration_tests/security_tests.py b/tests/integration_tests/security_tests.py index 9dca5ac51375c..0cdf0d786b0e2 100644 --- a/tests/integration_tests/security_tests.py +++ b/tests/integration_tests/security_tests.py @@ -907,6 +907,7 @@ def test_views_are_secured(self): ["LocaleView", "index"], ["AuthDBView", "login"], ["AuthDBView", "logout"], + ["CurrentUserRestApi", "me"], ["Dashboard", "embedded"], ["R", "index"], ["Superset", "log"], From e17686b9afe532d9e488d455c1b86de3310a1490 Mon Sep 17 00:00:00 2001 From: David Aaron Suddjian Date: Thu, 17 Feb 2022 14:03:28 -0800 Subject: [PATCH 7/7] mypy should ignore tests --- tests/integration_tests/users/api_tests.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration_tests/users/api_tests.py b/tests/integration_tests/users/api_tests.py index 6f5e474f8b899..ee965f6f2bf01 100644 --- a/tests/integration_tests/users/api_tests.py +++ b/tests/integration_tests/users/api_tests.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +# type: ignore """Unit tests for Superset""" import json from unittest.mock import patch