Skip to content

Commit

Permalink
Hide the JIRA plugin if user is not authenticated with a configurable…
Browse files Browse the repository at this point in the history
… realm (#1119)

* Configure which realms Jira plugin should show for
* Preserve existing behaviour when no realms specified
  • Loading branch information
nicwells authored Jun 2, 2022
1 parent 5705749 commit 03ab697
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 54 deletions.
3 changes: 3 additions & 0 deletions src/server/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ app.get('*', async (req: express.Request, res: express.Response) => {
jiraProjectCustomFieldName: process.env.JIRA_PROJECT_FIELD_NAME || '',
jiraProjectCustomFieldLabel:
process.env.JIRA_PROJECT_FIELD_LABEL || 'Nexus Project',
...(process.env.JIRA_SUPPORTED_REALMS && {
jiraSupportedRealms: process.env.JIRA_SUPPORTED_REALMS.split(','),
}),
},
uiSettings: DEFAULT_UI_SETTINGS,
oidc: {
Expand Down
54 changes: 20 additions & 34 deletions src/shared/containers/JIRA/JIRAPluginContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { Collapse } from 'antd';
import * as React from 'react';
import { Resource } from '@bbp/nexus-sdk';
import JIRAPluginUI, { AuthorizeJiraUI } from '../../components/JIRA/JIRA';
Expand All @@ -8,16 +7,12 @@ type JIRAPluginContainerProps = {
resource: Resource;
projectLabel: string;
orgLabel: string;
collapsed: boolean;
handleCollapseChanged: () => void;
};

const JIRAPluginContainer = ({
resource,
projectLabel,
orgLabel,
collapsed,
handleCollapseChanged,
}: JIRAPluginContainerProps) => {
const {
projects,
Expand Down Expand Up @@ -45,35 +40,26 @@ const JIRAPluginContainer = ({
commentCount: issue.commentCount,
}));

return (
<Collapse
onChange={handleCollapseChanged}
activeKey={collapsed ? 'jira' : undefined}
>
<Collapse.Panel header="JIRA" key="jira">
{!isJiraConnected ? (
<AuthorizeJiraUI
jiraAuthUrl={jiraAuthUrl}
onSubmitVerificationCode={verificationCode => {
connectJira(verificationCode);
}}
/>
) : (
<JIRAPluginUI
displayType="resource"
projects={projects}
issues={tableIssues}
onCreateIssue={(project, summary, description) =>
createIssue(project, summary, description)
}
onLinkIssue={issueUrl => linkIssue(issueUrl)}
onUnlinkIssue={issueKey => unlinkIssue(issueKey)}
searchJiraLink={`${jiraWebBaseUrl}/issues/?jql=`}
isLoading={isLoading}
/>
)}
</Collapse.Panel>
</Collapse>
return !isJiraConnected ? (
<AuthorizeJiraUI
jiraAuthUrl={jiraAuthUrl}
onSubmitVerificationCode={verificationCode => {
connectJira(verificationCode);
}}
/>
) : (
<JIRAPluginUI
displayType="resource"
projects={projects}
issues={tableIssues}
onCreateIssue={(project, summary, description) =>
createIssue(project, summary, description)
}
onLinkIssue={issueUrl => linkIssue(issueUrl)}
onUnlinkIssue={issueKey => unlinkIssue(issueKey)}
searchJiraLink={`${jiraWebBaseUrl}/issues/?jql=`}
isLoading={isLoading}
/>
);
};

Expand Down
35 changes: 24 additions & 11 deletions src/shared/containers/ResourceViewContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import JIRAPluginContainer from './JIRA/JIRAPluginContainer';
import { useSelector } from 'react-redux';
import { RootState } from '../store/reducers';
import { StudioResource } from '../../subapps/studioLegacy/containers/StudioContainer';
import { useJiraPlugin } from '../hooks/useJIRA';

export type PluginMapping = {
[pluginKey: string]: object;
Expand Down Expand Up @@ -480,17 +481,29 @@ const ResourceViewContainer: React.FunctionComponent<{
}}
/>
);
const jiraPlugin = resource && deltaPlugins && 'jira' in deltaPlugins && (
<JIRAPluginContainer
resource={resource}
orgLabel={orgLabel}
projectLabel={projectLabel}
collapsed={openPlugins.includes('jira')}
handleCollapseChanged={() => {
pluginCollapsedToggle('jira');
}}
/>
);
const { isUserInSupportedJiraRealm } = useJiraPlugin();

const jiraPlugin = resource &&
deltaPlugins &&
'jira' in deltaPlugins &&
isUserInSupportedJiraRealm && (
<Collapse
onChange={() => {
pluginCollapsedToggle('jira');
}}
activeKey={openPlugins.includes('jira') ? 'jira' : undefined}
>
<Collapse.Panel header="JIRA" key="jira">
{openPlugins.includes('jira') && (
<JIRAPluginContainer
resource={resource}
orgLabel={orgLabel}
projectLabel={projectLabel}
/>
)}
</Collapse.Panel>
</Collapse>
);

const builtInPlugins = [
{ key: 'preview', name: 'preview', pluginComponent: previewPlugin },
Expand Down
20 changes: 19 additions & 1 deletion src/shared/hooks/useJIRA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@ import { useNexusContext } from '@bbp/react-nexus';
import * as React from 'react';
import { useSelector } from 'react-redux';
import { RootState } from '../store/reducers';
import { getResourceLabel, labelOf } from '../utils';
import { getResourceLabel, isUserInAtLeastOneRealm, labelOf } from '../utils';
import useLocalStorage from './useLocalStorage';
import useNotification from './useNotification';

export function useJiraPlugin() {
const { jiraSupportedRealms } = useSelector(
(state: RootState) => state.config
);

if (jiraSupportedRealms === undefined) {
return { isUserInSupportedJiraRealm: true };
}

const { identities } = useSelector((state: RootState) => state.auth);
const isUserInSupportedJiraRealm =
identities?.data &&
jiraSupportedRealms &&
isUserInAtLeastOneRealm(identities.data.identities, jiraSupportedRealms);

return { isUserInSupportedJiraRealm };
}

/**
* Manages our JIRA data model
*
Expand Down
1 change: 1 addition & 0 deletions src/shared/store/reducers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface ConfigState {
jiraResourceCustomFieldLabel: string;
jiraProjectCustomFieldName: string;
jiraProjectCustomFieldLabel: string;
jiraSupportedRealms?: string[];
}

const initialState: ConfigState = {
Expand Down
9 changes: 9 additions & 0 deletions src/shared/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,15 @@ export const labelOf = (inputString: string) => {

export const isBrowser = typeof window !== 'undefined';

export function isUserInAtLeastOneRealm(
userIdentities: Identity[],
realm: string[]
) {
return (
userIdentities.filter(i => i.realm && realm.includes(i.realm)).length > 0
);
}

/**
* Returns the logout URL of the realm the user is authenticated with
*
Expand Down
22 changes: 14 additions & 8 deletions src/subapps/admin/views/ProjectView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import './ProjectView.less';
import ResourceCreateUploadContainer from '../../../shared/containers/ResourceCreateUploadContainer';
import { makeOrganizationUri } from '../../../shared/utils';
import JiraPluginProjectContainer from '../containers/JiraContainer';
import { useJiraPlugin } from '../../../shared/hooks/useJIRA';

const ProjectView: React.FunctionComponent = () => {
const notification = useNotification();
Expand Down Expand Up @@ -262,6 +263,9 @@ const ProjectView: React.FunctionComponent = () => {
if (activeKey === 'studios' || activeKey === 'workflows') return;
history.push(pathFromTab(activeKey));
};

const { isUserInSupportedJiraRealm } = useJiraPlugin();

return (
<div className="project-view">
{!!project && (
Expand Down Expand Up @@ -401,14 +405,16 @@ const ProjectView: React.FunctionComponent = () => {
<br />
</>
</TabPane>
{deltaPlugins && 'jira' in deltaPlugins && (
<TabPane tab="Jira" key="jira">
<JiraPluginProjectContainer
orgLabel={orgLabel}
projectLabel={projectLabel}
/>
</TabPane>
)}
{deltaPlugins &&
'jira' in deltaPlugins &&
isUserInSupportedJiraRealm && (
<TabPane tab="Jira" key="jira">
<JiraPluginProjectContainer
orgLabel={orgLabel}
projectLabel={projectLabel}
/>
</TabPane>
)}
{deltaPlugins && 'graph-analytics' in deltaPlugins && (
<TabPane tab="Graph Analytics" key="graph-analytics">
<ProjectStatsContainer
Expand Down

0 comments on commit 03ab697

Please sign in to comment.