From fa7c2376870c98f995b48537c3fbe369bf22e602 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Thu, 24 Jan 2019 10:25:53 +0200 Subject: [PATCH 1/8] Refine Auth service: remove dead code and fix race condition --- client/app/services/auth.js | 91 +++++++++++++++---------------------- 1 file changed, 36 insertions(+), 55 deletions(-) diff --git a/client/app/services/auth.js b/client/app/services/auth.js index 86f7ddaabc..29a9e87a50 100644 --- a/client/app/services/auth.js +++ b/client/app/services/auth.js @@ -1,32 +1,37 @@ import debug from 'debug'; +import { includes, extend } from 'lodash'; -const logger = debug('redash:auth'); -const SESSION_ITEM = 'session'; -const session = { loaded: false }; +const currentUser = { + canEdit(object) { + const userId = object.user_id || (object.user && object.user.id); + return this.hasPermission('admin') || (userId && (userId === this.id)); + }, -function storeSession(sessionData) { - logger('Updating session to be:', sessionData); - Object.assign(session, sessionData, { loaded: true }); -} + hasPermission(permission) { + return includes(this.permissions, permission); + }, -function getLocalSessionData() { - if (session.loaded) { - return session; - } + get isAdmin() { + return this.hasPermission('admin'); + }, +}; - const sessionData = window.sessionStorage.getItem(SESSION_ITEM); - if (sessionData) { - storeSession(JSON.parse(sessionData)); - } +const clientConfig = {}; - return session; +const logger = debug('redash:auth'); +const session = { loaded: false }; + +function updateSession(sessionData) { + logger('Updating session to be:', sessionData); + extend(session, sessionData, { loaded: true }); + extend(currentUser, session.user); + extend(clientConfig, session.client_config); } function AuthService($window, $location, $q, $http) { - const Auth = { + return { isAuthenticated() { - const sessionData = getLocalSessionData(); - return sessionData.loaded && sessionData.user.id; + return session.loaded && session.user.id; }, login() { const next = encodeURI($location.url()); @@ -35,27 +40,25 @@ function AuthService($window, $location, $q, $http) { }, logout() { logger('Logout.'); - window.sessionStorage.removeItem(SESSION_ITEM); $window.location.href = 'logout'; }, loadSession() { logger('Loading session'); - const sessionData = getLocalSessionData(); - if (sessionData.loaded && sessionData.user.id) { + if (session.loaded && session.user.id) { logger('Resolving with local value.'); - return $q.resolve(sessionData); + return $q.resolve(session); } this.setApiKey(null); return $http.get('api/session').then((response) => { - storeSession(response.data); + updateSession(response.data); return session; }); }, loadConfig() { logger('Loading config'); return $http.get('/api/config').then((response) => { - storeSession({ client_config: response.data.client_config, user: { permissions: [] } }); + updateSession({ client_config: response.data.client_config, user: { permissions: [] } }); return response.data; }); }, @@ -68,14 +71,14 @@ function AuthService($window, $location, $q, $http) { }, requireSession() { logger('Requested authentication'); - if (Auth.isAuthenticated()) { - return $q.when(getLocalSessionData()); + if (this.isAuthenticated()) { + return $q.when(session); } - return Auth.loadSession() + return this.loadSession() .then(() => { - if (Auth.isAuthenticated()) { + if (this.isAuthenticated()) { logger('Loaded session'); - return getLocalSessionData(); + return session; } logger('Need to login, redirecting'); this.login(); @@ -86,33 +89,12 @@ function AuthService($window, $location, $q, $http) { }); }, }; - - return Auth; -} - -function CurrentUserService() { - const sessionData = getLocalSessionData(); - Object.assign(this, sessionData.user); - - this.canEdit = (object) => { - const userId = object.user_id || (object.user && object.user.id); - return this.hasPermission('admin') || (userId && userId === this.id); - }; - - this.hasPermission = permission => this.permissions.indexOf(permission) !== -1; - - this.isAdmin = this.hasPermission('admin'); -} - -function ClientConfigService() { - Object.assign(this, getLocalSessionData().client_config); } function apiKeyHttpInterceptor($injector) { return { request(config) { - const Auth = $injector.get('Auth'); - const apiKey = Auth.getApiKey(); + const apiKey = $injector.get('Auth').getApiKey(); if (apiKey) { config.headers.Authorization = `Key ${apiKey}`; } @@ -124,8 +106,8 @@ function apiKeyHttpInterceptor($injector) { export default function init(ngModule) { ngModule.factory('Auth', AuthService); - ngModule.service('currentUser', CurrentUserService); - ngModule.service('clientConfig', ClientConfigService); + ngModule.value('currentUser', currentUser); + ngModule.value('clientConfig', clientConfig); ngModule.factory('apiKeyHttpInterceptor', apiKeyHttpInterceptor); ngModule.config(($httpProvider) => { @@ -134,4 +116,3 @@ export default function init(ngModule) { } init.init = true; - From dd8968f3113186e0f30674e7f3e0f1e466c172c8 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Thu, 24 Jan 2019 10:48:38 +0200 Subject: [PATCH 2/8] Export services in CommonJS style --- client/app/lib/recordEvent.js | 2 +- client/app/services/alert-dialog.js | 15 +++++---- client/app/services/alert-subscription.js | 14 +++++--- client/app/services/alert.js | 15 +++++---- client/app/services/auth.js | 17 +++++++--- client/app/services/dashboard.js | 11 ++++-- client/app/services/data-source.js | 11 ++++-- client/app/services/destination.js | 16 +++++---- client/app/services/events.js | 14 +++++--- client/app/services/getTags.js | 2 +- client/app/services/group.js | 14 +++++--- client/app/services/http.js | 11 ------ client/app/services/keyboard-shortcuts.js | 10 ++++-- client/app/services/ng.js | 14 ++++++++ client/app/services/notifications.js | 10 ++++-- client/app/services/organization-status.js | 10 ++++-- client/app/services/query-snippet.js | 10 ++++-- client/app/services/query.js | 39 ++++++++++++---------- client/app/services/toastr.js | 10 ------ client/app/services/user.js | 12 +++++-- client/app/services/widget.js | 20 +++++++---- 21 files changed, 175 insertions(+), 102 deletions(-) delete mode 100644 client/app/services/http.js create mode 100644 client/app/services/ng.js delete mode 100644 client/app/services/toastr.js diff --git a/client/app/lib/recordEvent.js b/client/app/lib/recordEvent.js index f4c2e2ff51..0913fd9a09 100644 --- a/client/app/lib/recordEvent.js +++ b/client/app/lib/recordEvent.js @@ -1,5 +1,5 @@ import { debounce } from 'lodash'; -import { $http } from '@/services/http'; +import { $http } from '@/services/ng'; let events = []; diff --git a/client/app/services/alert-dialog.js b/client/app/services/alert-dialog.js index c64b48c30c..84b1c410a2 100644 --- a/client/app/services/alert-dialog.js +++ b/client/app/services/alert-dialog.js @@ -1,3 +1,5 @@ +export let AlertDialog = null; // eslint-disable-line import/no-mutable-exports + const AlertDialogComponent = { template: ` @@ -134,8 +129,7 @@ export default function init(ngModule) { dismiss: '&', }, }); - ngModule.component('editParameterMappingsDialogImpl', react2angular(EditParameterMappingsDialog, null, [ - 'Query', 'clientConfig'])); + ngModule.component('editParameterMappingsDialogImpl', react2angular(EditParameterMappingsDialog)); } init.init = true; diff --git a/client/app/components/tags-control/DashboardTagsControl.jsx b/client/app/components/tags-control/DashboardTagsControl.jsx index 1a535d4994..7337964308 100644 --- a/client/app/components/tags-control/DashboardTagsControl.jsx +++ b/client/app/components/tags-control/DashboardTagsControl.jsx @@ -6,7 +6,7 @@ export class DashboardTagsControl extends ModelTagsControl { } export default function init(ngModule) { - ngModule.component('dashboardTagsControl', react2angular(DashboardTagsControl, null, ['$uibModal'])); + ngModule.component('dashboardTagsControl', react2angular(DashboardTagsControl)); } init.init = true; diff --git a/client/app/components/tags-control/QueryTagsControl.jsx b/client/app/components/tags-control/QueryTagsControl.jsx index cd071e2308..9b56c26700 100644 --- a/client/app/components/tags-control/QueryTagsControl.jsx +++ b/client/app/components/tags-control/QueryTagsControl.jsx @@ -6,7 +6,7 @@ export class QueryTagsControl extends ModelTagsControl { } export default function init(ngModule) { - ngModule.component('queryTagsControl', react2angular(QueryTagsControl, null, ['$uibModal'])); + ngModule.component('queryTagsControl', react2angular(QueryTagsControl)); } init.init = true; diff --git a/client/app/components/tags-control/TagsControl.jsx b/client/app/components/tags-control/TagsControl.jsx index c486ac78af..0a28c0f98d 100644 --- a/client/app/components/tags-control/TagsControl.jsx +++ b/client/app/components/tags-control/TagsControl.jsx @@ -1,6 +1,7 @@ import { map, trim } from 'lodash'; import React from 'react'; import PropTypes from 'prop-types'; +import { $uibModal } from '@/services/ng'; export default class TagsControl extends React.Component { static propTypes = { @@ -20,7 +21,7 @@ export default class TagsControl extends React.Component { }; editTags() { - const { getAvailableTags, onEdit, $uibModal } = this.props; // eslint-disable-line react/prop-types + const { getAvailableTags, onEdit } = this.props; // eslint-disable-line react/prop-types const tags = map(this.props.tags, trim); getAvailableTags().then((availableTags) => { From 1d421dd7bff2d91dcbcb312d115f74c0d6ef3209 Mon Sep 17 00:00:00 2001 From: Levko Kravets Date: Thu, 24 Jan 2019 13:06:57 +0200 Subject: [PATCH 6/8] Fix Footer tests --- client/app/components/Footer.test.js | 15 +++++++++------ client/app/services/auth.js | 10 ++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/client/app/components/Footer.test.js b/client/app/components/Footer.test.js index 81a157ebfc..519a9e5732 100644 --- a/client/app/components/Footer.test.js +++ b/client/app/components/Footer.test.js @@ -1,16 +1,19 @@ +import { extend } from 'lodash'; import React from 'react'; import renderer from 'react-test-renderer'; +import { clientConfig, currentUser } from '../services/auth'; import { Footer } from './Footer'; test('Footer renders', () => { - const clientConfig = { + // TODO: Properly mock this + extend(clientConfig, { version: '5.0.1', newVersionAvailable: true, - }; - const currentUser = { - isAdmin: true, - }; - const component = renderer.create(