diff --git a/CHANGELOG-workspace-globus-group.md b/CHANGELOG-workspace-globus-group.md new file mode 100644 index 0000000000..bcd0c19c4f --- /dev/null +++ b/CHANGELOG-workspace-globus-group.md @@ -0,0 +1 @@ +- Allow access to workspaces to users in workspaces globus group. \ No newline at end of file diff --git a/context/app/main.py b/context/app/main.py index fef51a2e6f..47845d278b 100755 --- a/context/app/main.py +++ b/context/app/main.py @@ -98,6 +98,7 @@ def inject_template_globals(): 'groups_token': session.get('groups_token'), 'user_email': session.get('user_email'), 'workspaces_token': session.get('workspaces_token'), + 'user_groups': session.get('user_groups'), } @app.before_request @@ -107,8 +108,8 @@ def set_default_groups_token(): groups_token='', user_email='', is_authenticated=False, - workspaces_token='') - + workspaces_token='', + user_groups=[]) return app diff --git a/context/app/routes_auth.py b/context/app/routes_auth.py index 1aacadaf7c..5cb46fc261 100644 --- a/context/app/routes_auth.py +++ b/context/app/routes_auth.py @@ -23,7 +23,7 @@ def load_app_client(): current_app.config['APP_CLIENT_ID'], current_app.config['APP_CLIENT_SECRET']) -def has_hubmap_group(groups_token): +def get_globus_groups(groups_token): # Mostly copy-and-paste from # https://github.com/hubmapconsortium/commons/blob/641d03b0dc/hubmap_commons/hm_auth.py#L626-L646 headers = { @@ -36,7 +36,11 @@ def has_hubmap_group(groups_token): headers=headers) response.raise_for_status() groups = response.json() - return any([group['id'] == current_app.config['GROUP_ID'] for group in groups]) + return groups + + +def has_globus_group(groups, group_id): + return any([group['id'] == group_id for group in groups]) def get_ip(): @@ -127,16 +131,22 @@ def login(): headers=user_info_request_headers).json() user_email = user_info['email'] if 'email' in user_info else '' - if not has_hubmap_group(groups_token): + user_globus_groups = get_globus_groups(groups_token) + + if not has_globus_group(user_globus_groups, current_app.config['GROUP_ID']): # Globus institution login worked, but user does not have HuBMAP group! log('7: 401') abort(401) + additional_groups = {'Workspaces': current_app.config['WORKSPACES_GROUP_ID']} + session.update( groups_token=groups_token, is_authenticated=True, user_email=user_email, - workspaces_token=workspaces_token + workspaces_token=workspaces_token, + user_groups=[k for k, group_id in additional_groups.items( + ) if has_globus_group(user_globus_groups, group_id)] ) previous_url = unquote(request.cookies.get('urlBeforeLogin')) diff --git a/context/app/static/js/components/App.jsx b/context/app/static/js/components/App.jsx index 5885095235..4b8bdf9821 100644 --- a/context/app/static/js/components/App.jsx +++ b/context/app/static/js/components/App.jsx @@ -11,12 +11,32 @@ import { StyledAlert, FlexContainer } from './style'; // Importing Search styles here so the CSS import order is correct. import 'js/components/searchPage/Search.scss'; +// TODO: Delete this when workspaces are publicly released. +// If we stay in limbo for a long time, this configuration could be moved out of code. +const workspacesUsers = [ + 'nils@hms.harvard.edu', + 'john_conroy@hms.harvard.edu', + 'tiffany_liaw@hms.harvard.edu', + 'tony_hsiao@hms.harvard.edu', + 'morgan_turner@hms.harvard.edu', + 'lisa_choy@hms.harvard.edu', + 'tsmits@hms.harvard.edu', + 'pdblood@andrew.cmu.edu', + 'blood@psc.edu', + 'jpuerto@andrew.cmu.edu', + 'gphillip@andrew.cmu.edu', + 'ivlachos@bidmc.harvard.edu', + 'geremy.clair@pnnl.gov', +]; + function App(props) { - const { flaskData, groupsToken, isAuthenticated, userEmail, workspacesToken } = props; + const { flaskData, groupsToken, isAuthenticated, userEmail, workspacesToken, userGroups = [] } = props; const { endpoints, globalAlertMd } = flaskData; delete flaskData.endpoints; delete flaskData.globalAlertMd; + const isWorkspacesUser = userGroups.includes('Workspaces') || workspacesUsers.includes(userEmail); + return (
{globalAlertMd && ( diff --git a/context/app/static/js/components/Header/UserLinks/UserLinks.jsx b/context/app/static/js/components/Header/UserLinks/UserLinks.jsx index 8123cf180e..4b1a7b96ad 100644 --- a/context/app/static/js/components/Header/UserLinks/UserLinks.jsx +++ b/context/app/static/js/components/Header/UserLinks/UserLinks.jsx @@ -8,12 +8,12 @@ import { StyledDivider } from '../HeaderContent/style'; import { TruncatedSpan, WarningDropdownLink } from './style'; function UserLinks({ isAuthenticated, userEmail }) { - const { workspacesUsers } = useContext(AppContext); + const { isWorkspacesUser } = useContext(AppContext); return ( {isAuthenticated ? userEmail || 'User' : 'User Profile'}}> My Lists - {workspacesUsers.includes(userEmail) && My Workspaces} + {isWorkspacesUser && My Workspaces} {isAuthenticated ? ( Log Out diff --git a/context/app/static/js/components/Providers.jsx b/context/app/static/js/components/Providers.jsx index 54b8e053e0..d591de865b 100644 --- a/context/app/static/js/components/Providers.jsx +++ b/context/app/static/js/components/Providers.jsx @@ -16,24 +16,15 @@ const generateClassName = createGenerateClassName({ const AppContext = React.createContext({}); -function Providers({ endpoints, groupsToken, isAuthenticated, userEmail, children, workspacesToken }) { - // TODO: Delete this when workspaces are publicly released. - // If we stay in limbo for a long time, this configuration could be moved out of code. - const workspacesUsers = [ - 'nils@hms.harvard.edu', - 'john_conroy@hms.harvard.edu', - 'tiffany_liaw@hms.harvard.edu', - 'tony_hsiao@hms.harvard.edu', - 'morgan_turner@hms.harvard.edu', - 'lisa_choy@hms.harvard.edu', - 'tsmits@hms.harvard.edu', - 'pdblood@andrew.cmu.edu', - 'blood@psc.edu', - 'jpuerto@andrew.cmu.edu', - 'gphillip@andrew.cmu.edu', - 'ivlachos@bidmc.harvard.edu', - 'geremy.clair@pnnl.gov', - ]; +function Providers({ + endpoints, + groupsToken, + isAuthenticated, + userEmail, + children, + workspacesToken, + isWorkspacesUser, +}) { // injectFirst ensures styled-components takes priority over mui for styling return ( @@ -41,7 +32,7 @@ function Providers({ endpoints, groupsToken, isAuthenticated, userEmail, childre diff --git a/context/app/static/js/index.jsx b/context/app/static/js/index.jsx index 247fc5246e..ffdc76bffe 100644 --- a/context/app/static/js/index.jsx +++ b/context/app/static/js/index.jsx @@ -23,6 +23,7 @@ ReactDOM.render( isAuthenticated={isAuthenticated} userEmail={userEmail} workspacesToken={workspacesToken} + userGroups={userGroups} /> ), document.getElementById('react-content'), diff --git a/context/app/static/js/pages/Dataset/Dataset.jsx b/context/app/static/js/pages/Dataset/Dataset.jsx index 19ba439ee4..338d7fb838 100644 --- a/context/app/static/js/pages/Dataset/Dataset.jsx +++ b/context/app/static/js/pages/Dataset/Dataset.jsx @@ -51,7 +51,7 @@ function SummaryDataChildren({ hubmap_id, uuid, }) { - const { workspacesUsers, userEmail } = useContext(AppContext); + const { isWorkspacesUser } = useContext(AppContext); const createNotebook = useCallback( async ({ workspaceName }) => { @@ -91,7 +91,7 @@ function SummaryDataChildren({ doi:{registered_doi} )} - {workspacesUsers.includes(userEmail) && ( + {isWorkspacesUser && ( <> @@ -19,7 +19,7 @@ function Workspaces() { My Workspaces - {!isAuthenticated ? ( + {!(isAuthenticated && isWorkspacesUser) ? ( The workspaces feature is only available if logged in. Log in to view saved workspaces or to begin a new workspace. diff --git a/context/app/templates/base-pages/base.html b/context/app/templates/base-pages/base.html index fc3704d927..659a21a677 100644 --- a/context/app/templates/base-pages/base.html +++ b/context/app/templates/base-pages/base.html @@ -15,6 +15,7 @@ const groupsToken = '{{ groups_token }}'; const userEmail = '{{ user_email }}'; const workspacesToken = '{{ workspaces_token }}'; + const userGroups = {{ user_groups | tojson }}; diff --git a/context/test-utils/functions.js b/context/test-utils/functions.js index f02fca06b6..175cc7f4a4 100644 --- a/context/test-utils/functions.js +++ b/context/test-utils/functions.js @@ -10,11 +10,12 @@ const appProviderEndpoints = { assetsEndpoint: 'fakeAssetsEndpoint', }; +const isWorkspacesUser = false; const appProviderToken = 'fakeGroupsToken'; const AllTheProviders = ({ children }) => { return ( - + {children} );