Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

John conroy/workspace globus group #3086

Merged
merged 14 commits into from
Apr 20, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG-workspace-globus-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Allow access to workspaces to users in workspaces globus group.
5 changes: 3 additions & 2 deletions context/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -107,8 +108,8 @@ def set_default_groups_token():
groups_token='',
user_email='',
is_authenticated=False,
workspaces_token='')

workspaces_token='',
user_groups=[])
return app


Expand Down
18 changes: 14 additions & 4 deletions context/app/routes_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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():
Expand Down Expand Up @@ -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'))
Expand Down
23 changes: 22 additions & 1 deletion context/app/static/js/components/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,40 @@ 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 = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
];

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 (
<Providers
endpoints={endpoints}
groupsToken={groupsToken}
isAuthenticated={isAuthenticated}
userEmail={userEmail}
workspacesToken={workspacesToken}
isWorkspacesUser={isWorkspacesUser}
>
<Header />
{globalAlertMd && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Dropdown title={<TruncatedSpan>{isAuthenticated ? userEmail || 'User' : 'User Profile'}</TruncatedSpan>}>
<DropdownLink href="/my-lists">My Lists</DropdownLink>
{workspacesUsers.includes(userEmail) && <DropdownLink href="/workspaces">My Workspaces</DropdownLink>}
{isWorkspacesUser && <DropdownLink href="/workspaces">My Workspaces</DropdownLink>}
<StyledDivider />
{isAuthenticated ? (
<WarningDropdownLink href="/logout">Log Out</WarningDropdownLink>
Expand Down
29 changes: 10 additions & 19 deletions context/app/static/js/components/Providers.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,23 @@ 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 = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
];
function Providers({
endpoints,
groupsToken,
isAuthenticated,
userEmail,
children,
workspacesToken,
isWorkspacesUser,
}) {
// injectFirst ensures styled-components takes priority over mui for styling
return (
<StylesProvider generateClassName={generateClassName} injectFirst>
<GlobalFonts />
<MuiThemeProvider theme={theme}>
<ThemeProvider theme={theme}>
<AppContext.Provider
value={{ groupsToken, workspacesToken, workspacesUsers, isAuthenticated, userEmail, ...endpoints }}
value={{ groupsToken, workspacesToken, isWorkspacesUser, isAuthenticated, userEmail, ...endpoints }}
>
<CssBaseline />
<GlobalStyles />
Expand Down
1 change: 1 addition & 0 deletions context/app/static/js/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ReactDOM.render(
isAuthenticated={isAuthenticated}
userEmail={userEmail}
workspacesToken={workspacesToken}
userGroups={userGroups}
/>
),
document.getElementById('react-content'),
Expand Down
4 changes: 2 additions & 2 deletions context/app/static/js/pages/Dataset/Dataset.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function SummaryDataChildren({
hubmap_id,
uuid,
}) {
const { workspacesUsers, userEmail } = useContext(AppContext);
const { isWorkspacesUser } = useContext(AppContext);

const createNotebook = useCallback(
async ({ workspaceName }) => {
Expand Down Expand Up @@ -91,7 +91,7 @@ function SummaryDataChildren({
doi:{registered_doi}
</OutboundIconLink>
)}
{workspacesUsers.includes(userEmail) && (
{isWorkspacesUser && (
<>
<CreateWorkspaceDialog
handleCreateWorkspace={createNotebook}
Expand Down
4 changes: 2 additions & 2 deletions context/app/static/js/pages/Workspaces/Workspaces.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import WorkspacesAuthenticated from 'js/components/workspaces/WorkspacesAuthenti
import { FlexContainer } from './style';

function Workspaces() {
const { isAuthenticated } = useContext(AppContext);
const { isAuthenticated, isWorkspacesUser } = useContext(AppContext);
return (
<>
<SectionHeader variant="h1" component="h1">
<FlexContainer>
<HeaderIcon component={WorkspacesIcon} /> My Workspaces
</FlexContainer>
</SectionHeader>
{!isAuthenticated ? (
{!(isAuthenticated && isWorkspacesUser) ? (
<Description padding="20px">
The workspaces feature is only available if logged in. <LightBlueLink href="/login">Log in</LightBlueLink> to
view saved workspaces or to begin a new workspace.
Expand Down
1 change: 1 addition & 0 deletions context/app/templates/base-pages/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
const groupsToken = '{{ groups_token }}';
const userEmail = '{{ user_email }}';
const workspacesToken = '{{ workspaces_token }}';
const userGroups = {{ user_groups | tojson }};
</script>
</head>

Expand Down
3 changes: 2 additions & 1 deletion context/test-utils/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ const appProviderEndpoints = {
assetsEndpoint: 'fakeAssetsEndpoint',
};

const isWorkspacesUser = false;
const appProviderToken = 'fakeGroupsToken';

const AllTheProviders = ({ children }) => {
return (
<Providers endpoints={appProviderEndpoints} groupsToken={appProviderToken}>
<Providers endpoints={appProviderEndpoints} groupsToken={appProviderToken} isWorkspacesUser={isWorkspacesUser}>
{children}
</Providers>
);
Expand Down