mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Allow team admins to manage scheduled queries (#2738)
This commit is contained in:
parent
45c5e29ca0
commit
e50ca4ece7
5 changed files with 168 additions and 92 deletions
1
changes/fix-2687-team-admin-scheduled-queries
Normal file
1
changes/fix-2687-team-admin-scheduled-queries
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
* Bug fix: Allow Team Admins to manage scheduled queries
|
||||||
|
|
@ -51,6 +51,7 @@ const initialState = {
|
||||||
isAnyTeamMaintainerOrTeamAdmin: undefined,
|
isAnyTeamMaintainerOrTeamAdmin: undefined,
|
||||||
isTeamObserver: undefined,
|
isTeamObserver: undefined,
|
||||||
isTeamMaintainer: undefined,
|
isTeamMaintainer: undefined,
|
||||||
|
isTeamMaintainerOrTeamAdmin: undefined,
|
||||||
isAnyTeamAdmin: undefined,
|
isAnyTeamAdmin: undefined,
|
||||||
isTeamAdmin: undefined,
|
isTeamAdmin: undefined,
|
||||||
isOnlyObserver: undefined,
|
isOnlyObserver: undefined,
|
||||||
|
|
@ -93,6 +94,10 @@ const setPermissions = (user: IUser, config: IConfig, teamId = 0) => {
|
||||||
isTeamObserver: permissions.isTeamObserver(user, teamId),
|
isTeamObserver: permissions.isTeamObserver(user, teamId),
|
||||||
isTeamMaintainer: permissions.isTeamMaintainer(user, teamId),
|
isTeamMaintainer: permissions.isTeamMaintainer(user, teamId),
|
||||||
isTeamAdmin: permissions.isTeamAdmin(user, teamId),
|
isTeamAdmin: permissions.isTeamAdmin(user, teamId),
|
||||||
|
isTeamMaintainerOrTeamAdmin: permissions.isTeamMaintainerOrTeamAdmin(
|
||||||
|
user,
|
||||||
|
teamId
|
||||||
|
),
|
||||||
isOnlyObserver: permissions.isOnlyObserver(user),
|
isOnlyObserver: permissions.isOnlyObserver(user),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -153,6 +158,7 @@ const AppProvider = ({ children }: Props) => {
|
||||||
isTeamObserver: state.isTeamObserver,
|
isTeamObserver: state.isTeamObserver,
|
||||||
isTeamMaintainer: state.isTeamMaintainer,
|
isTeamMaintainer: state.isTeamMaintainer,
|
||||||
isTeamAdmin: state.isTeamAdmin,
|
isTeamAdmin: state.isTeamAdmin,
|
||||||
|
isTeamMaintainerOrTeamAdmin: state.isTeamMaintainer,
|
||||||
isAnyTeamAdmin: state.isAnyTeamAdmin,
|
isAnyTeamAdmin: state.isAnyTeamAdmin,
|
||||||
isOnlyObserver: state.isOnlyObserver,
|
isOnlyObserver: state.isOnlyObserver,
|
||||||
setCurrentUser: (currentUser: IUser) => {
|
setCurrentUser: (currentUser: IUser) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
/* Conditionally renders global schedule and team schedules */
|
/* Conditionally renders global schedule and team schedules */
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect } from "react";
|
import React, { useState, useCallback, useEffect, useContext } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
import { AppContext } from "context/app";
|
||||||
|
|
||||||
import { push } from "react-router-redux";
|
import { push } from "react-router-redux";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
|
@ -44,7 +45,8 @@ const renderTable = (
|
||||||
allScheduledQueriesError: { name: string; reason: string }[],
|
allScheduledQueriesError: { name: string; reason: string }[],
|
||||||
toggleScheduleEditorModal: () => void,
|
toggleScheduleEditorModal: () => void,
|
||||||
teamId: number,
|
teamId: number,
|
||||||
isTeamMaintainer: boolean
|
isTeamMaintainerOrTeamAdmin: boolean,
|
||||||
|
isOnGlobalTeam: boolean
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
if (Object.keys(allScheduledQueriesError).length !== 0) {
|
if (Object.keys(allScheduledQueriesError).length !== 0) {
|
||||||
return <TableDataError />;
|
return <TableDataError />;
|
||||||
|
|
@ -57,16 +59,18 @@ const renderTable = (
|
||||||
allScheduledQueriesList={allScheduledQueriesList}
|
allScheduledQueriesList={allScheduledQueriesList}
|
||||||
toggleScheduleEditorModal={toggleScheduleEditorModal}
|
toggleScheduleEditorModal={toggleScheduleEditorModal}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
isTeamMaintainer={isTeamMaintainer}
|
isTeamMaintainerOrTeamAdmin={isTeamMaintainerOrTeamAdmin}
|
||||||
|
isOnGlobalTeam={isOnGlobalTeam}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderAllTeamsTable = (
|
const renderAllTeamsTable = (
|
||||||
teamId: number,
|
|
||||||
isTeamMaintainer: boolean,
|
|
||||||
allTeamsScheduledQueriesList: IGlobalScheduledQuery[],
|
allTeamsScheduledQueriesList: IGlobalScheduledQuery[],
|
||||||
allTeamsScheduledQueriesError: { name: string; reason: string }[]
|
allTeamsScheduledQueriesError: { name: string; reason: string }[],
|
||||||
|
teamId: number,
|
||||||
|
isTeamMaintainerOrTeamAdmin: boolean,
|
||||||
|
isOnGlobalTeam: boolean
|
||||||
): JSX.Element => {
|
): JSX.Element => {
|
||||||
if (Object.keys(allTeamsScheduledQueriesError).length > 0) {
|
if (Object.keys(allTeamsScheduledQueriesError).length > 0) {
|
||||||
return <TableDataError />;
|
return <TableDataError />;
|
||||||
|
|
@ -78,7 +82,8 @@ const renderAllTeamsTable = (
|
||||||
inheritedQueries
|
inheritedQueries
|
||||||
allScheduledQueriesList={allTeamsScheduledQueriesList}
|
allScheduledQueriesList={allTeamsScheduledQueriesList}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
isTeamMaintainer={isTeamMaintainer}
|
isTeamMaintainerOrTeamAdmin={isTeamMaintainerOrTeamAdmin}
|
||||||
|
isOnGlobalTeam={isOnGlobalTeam}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -138,11 +143,57 @@ interface ITeamOptions {
|
||||||
const ManageSchedulePage = ({
|
const ManageSchedulePage = ({
|
||||||
params: { team_id },
|
params: { team_id },
|
||||||
}: ITeamSchedulesPageProps): JSX.Element => {
|
}: ITeamSchedulesPageProps): JSX.Element => {
|
||||||
const teamId = parseInt(team_id, 10);
|
let teamId = parseInt(team_id, 10);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { MANAGE_PACKS } = paths;
|
const { MANAGE_PACKS } = paths;
|
||||||
const handleAdvanced = () => dispatch(push(MANAGE_PACKS));
|
const handleAdvanced = () => dispatch(push(MANAGE_PACKS));
|
||||||
|
|
||||||
|
const { currentUser, isOnGlobalTeam } = useContext(AppContext);
|
||||||
|
|
||||||
|
const isTeamMaintainerOrTeamAdmin = (() => {
|
||||||
|
return !!permissionUtils.isTeamMaintainerOrTeamAdmin(currentUser, teamId);
|
||||||
|
})();
|
||||||
|
|
||||||
|
const onChangeSelectedTeam = (selectedTeamId: number) => {
|
||||||
|
if (isNaN(selectedTeamId)) {
|
||||||
|
dispatch(push(`${paths.MANAGE_SCHEDULE}`));
|
||||||
|
} else {
|
||||||
|
dispatch(push(`${paths.MANAGE_TEAM_SCHEDULE(selectedTeamId)}`));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadFirstMaintainerOrAdminTeam = () => {
|
||||||
|
if (currentUser) {
|
||||||
|
const adminOrMaintainerTeam = currentUser.teams.find((team) => {
|
||||||
|
return team.role === "admin" || team.role === "maintainer"
|
||||||
|
? team.id
|
||||||
|
: null;
|
||||||
|
});
|
||||||
|
if (adminOrMaintainerTeam) {
|
||||||
|
teamId = adminOrMaintainerTeam.id;
|
||||||
|
onChangeSelectedTeam(teamId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isOnGlobalTeam && !isTeamMaintainerOrTeamAdmin && !teamId) {
|
||||||
|
loadFirstMaintainerOrAdminTeam();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isOnGlobalTeam && !isTeamMaintainerOrTeamAdmin && teamId) {
|
||||||
|
if (currentUser) {
|
||||||
|
const canLoadTeam = currentUser.teams.find((team) => {
|
||||||
|
return (
|
||||||
|
(team.role === "admin" || team.role === "maintainer") &&
|
||||||
|
team.id === teamId
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (!canLoadTeam) {
|
||||||
|
loadFirstMaintainerOrAdminTeam();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(queryActions.loadAll());
|
dispatch(queryActions.loadAll());
|
||||||
dispatch(teamActions.loadAll());
|
dispatch(teamActions.loadAll());
|
||||||
|
|
@ -163,10 +214,6 @@ const ManageSchedulePage = ({
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isTeamMaintainer = useSelector((state: IRootState): boolean => {
|
|
||||||
return permissionUtils.isAnyTeamMaintainer(state.auth.user);
|
|
||||||
});
|
|
||||||
|
|
||||||
const allQueries = useSelector((state: IRootState) => state.entities.queries);
|
const allQueries = useSelector((state: IRootState) => state.entities.queries);
|
||||||
const allQueriesList = Object.values(allQueries.data);
|
const allQueriesList = Object.values(allQueries.data);
|
||||||
|
|
||||||
|
|
@ -197,38 +244,6 @@ const ManageSchedulePage = ({
|
||||||
|
|
||||||
const selectedTeam = isNaN(teamId) ? "global" : teamId;
|
const selectedTeam = isNaN(teamId) ? "global" : teamId;
|
||||||
|
|
||||||
const generateTeamOptionsDropdownItems = (): ITeamOptions[] => {
|
|
||||||
const teamOptions: ITeamOptions[] = [];
|
|
||||||
|
|
||||||
if (isTeamMaintainer) {
|
|
||||||
user.teams.forEach((team) => {
|
|
||||||
if (team.role === "maintainer") {
|
|
||||||
teamOptions.push({
|
|
||||||
disabled: false,
|
|
||||||
label: team.name,
|
|
||||||
value: team.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
teamOptions.push({
|
|
||||||
disabled: false,
|
|
||||||
label: "All teams",
|
|
||||||
value: "global",
|
|
||||||
});
|
|
||||||
|
|
||||||
allTeamsList.forEach((team) => {
|
|
||||||
teamOptions.push({
|
|
||||||
disabled: false,
|
|
||||||
label: team.name,
|
|
||||||
value: team.id,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return teamOptions;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [showInheritedQueries, setShowInheritedQueries] = useState<boolean>(
|
const [showInheritedQueries, setShowInheritedQueries] = useState<boolean>(
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
@ -257,6 +272,55 @@ const ManageSchedulePage = ({
|
||||||
setShowRemoveScheduledQueryModal(!showRemoveScheduledQueryModal);
|
setShowRemoveScheduledQueryModal(!showRemoveScheduledQueryModal);
|
||||||
}, [showRemoveScheduledQueryModal, setShowRemoveScheduledQueryModal]);
|
}, [showRemoveScheduledQueryModal, setShowRemoveScheduledQueryModal]);
|
||||||
|
|
||||||
|
const generateTeamOptionsDropdownItems = (): ITeamOptions[] => {
|
||||||
|
const teamOptions: ITeamOptions[] = [];
|
||||||
|
|
||||||
|
if (isTeamMaintainerOrTeamAdmin) {
|
||||||
|
user.teams.forEach((team) => {
|
||||||
|
if (team.role === "admin" || team.role === "maintainer") {
|
||||||
|
teamOptions.push({
|
||||||
|
disabled: false,
|
||||||
|
label: team.name,
|
||||||
|
value: team.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (isOnGlobalTeam) {
|
||||||
|
teamOptions.push({
|
||||||
|
disabled: false,
|
||||||
|
label: "All teams",
|
||||||
|
value: "global",
|
||||||
|
});
|
||||||
|
|
||||||
|
allTeamsList.forEach((team) => {
|
||||||
|
teamOptions.push({
|
||||||
|
disabled: false,
|
||||||
|
label: team.name,
|
||||||
|
value: team.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return teamOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTitleOrDropdown = (): JSX.Element => {
|
||||||
|
const dropDownOptions = generateTeamOptionsDropdownItems();
|
||||||
|
return dropDownOptions.length === 1 ? (
|
||||||
|
<h1>{dropDownOptions[0].label}</h1>
|
||||||
|
) : (
|
||||||
|
<Dropdown
|
||||||
|
value={selectedTeam}
|
||||||
|
className={`${baseClass}__team-dropdown`}
|
||||||
|
options={dropDownOptions}
|
||||||
|
searchable={false}
|
||||||
|
onChange={(newSelectedValue: number) =>
|
||||||
|
onChangeSelectedTeam(newSelectedValue)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const onRemoveScheduledQueryClick = (
|
const onRemoveScheduledQueryClick = (
|
||||||
selectedTableQueryIds: number[]
|
selectedTableQueryIds: number[]
|
||||||
): void => {
|
): void => {
|
||||||
|
|
@ -372,21 +436,15 @@ const ManageSchedulePage = ({
|
||||||
[dispatch, teamId, toggleScheduleEditorModal]
|
[dispatch, teamId, toggleScheduleEditorModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeSelectedTeam = (selectedTeamId: number) => {
|
if (selectedTeam === "global" && isTeamMaintainerOrTeamAdmin) {
|
||||||
if (isNaN(selectedTeamId)) {
|
|
||||||
dispatch(push(`${paths.MANAGE_SCHEDULE}`));
|
|
||||||
} else {
|
|
||||||
dispatch(push(`${paths.MANAGE_TEAM_SCHEDULE(selectedTeamId)}`));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (selectedTeam === "global" && isTeamMaintainer) {
|
|
||||||
const teamMaintainerTeams = generateTeamOptionsDropdownItems();
|
const teamMaintainerTeams = generateTeamOptionsDropdownItems();
|
||||||
dispatch(
|
if (teamMaintainerTeams.length) {
|
||||||
push(
|
dispatch(
|
||||||
`${paths.MANAGE_TEAM_SCHEDULE(Number(teamMaintainerTeams[0].value))}`
|
push(
|
||||||
)
|
`${paths.MANAGE_TEAM_SCHEDULE(Number(teamMaintainerTeams[0].value))}`
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -409,15 +467,7 @@ const ManageSchedulePage = ({
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<Dropdown
|
{renderTitleOrDropdown()}
|
||||||
value={selectedTeam}
|
|
||||||
className={`${baseClass}__team-dropdown`}
|
|
||||||
options={generateTeamOptionsDropdownItems()}
|
|
||||||
searchable={false}
|
|
||||||
onChange={(newSelectedValue: number) =>
|
|
||||||
onChangeSelectedTeam(newSelectedValue)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div className={`${baseClass}__description`}>
|
<div className={`${baseClass}__description`}>
|
||||||
{isNaN(teamId) ? (
|
{isNaN(teamId) ? (
|
||||||
<p>
|
<p>
|
||||||
|
|
@ -438,7 +488,7 @@ const ManageSchedulePage = ({
|
||||||
{allScheduledQueriesList.length !== 0 &&
|
{allScheduledQueriesList.length !== 0 &&
|
||||||
allScheduledQueriesError.length !== 0 && (
|
allScheduledQueriesError.length !== 0 && (
|
||||||
<div className={`${baseClass}__action-button-container`}>
|
<div className={`${baseClass}__action-button-container`}>
|
||||||
{!isTeamMaintainer && (
|
{!isTeamMaintainerOrTeamAdmin && (
|
||||||
<Button
|
<Button
|
||||||
variant="inverse"
|
variant="inverse"
|
||||||
onClick={handleAdvanced}
|
onClick={handleAdvanced}
|
||||||
|
|
@ -465,7 +515,8 @@ const ManageSchedulePage = ({
|
||||||
allScheduledQueriesError,
|
allScheduledQueriesError,
|
||||||
toggleScheduleEditorModal,
|
toggleScheduleEditorModal,
|
||||||
teamId,
|
teamId,
|
||||||
isTeamMaintainer
|
isTeamMaintainerOrTeamAdmin,
|
||||||
|
isOnGlobalTeam || false
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{/* must use ternary for NaN */}
|
{/* must use ternary for NaN */}
|
||||||
|
|
@ -497,10 +548,11 @@ const ManageSchedulePage = ({
|
||||||
) : null}
|
) : null}
|
||||||
{showInheritedQueries &&
|
{showInheritedQueries &&
|
||||||
renderAllTeamsTable(
|
renderAllTeamsTable(
|
||||||
teamId,
|
|
||||||
isTeamMaintainer,
|
|
||||||
allTeamsScheduledQueriesList,
|
allTeamsScheduledQueriesList,
|
||||||
allTeamsScheduledQueriesError
|
allTeamsScheduledQueriesError,
|
||||||
|
teamId,
|
||||||
|
isTeamMaintainerOrTeamAdmin,
|
||||||
|
isOnGlobalTeam || false
|
||||||
)}
|
)}
|
||||||
{showScheduleEditorModal && (
|
{showScheduleEditorModal && (
|
||||||
<ScheduleEditorModal
|
<ScheduleEditorModal
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ interface IScheduleListWrapperProps {
|
||||||
toggleScheduleEditorModal?: () => void;
|
toggleScheduleEditorModal?: () => void;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
inheritedQueries?: boolean;
|
inheritedQueries?: boolean;
|
||||||
isTeamMaintainer: boolean;
|
isTeamMaintainerOrTeamAdmin: boolean;
|
||||||
|
isOnGlobalTeam: boolean;
|
||||||
}
|
}
|
||||||
interface IRootState {
|
interface IRootState {
|
||||||
entities: {
|
entities: {
|
||||||
|
|
@ -55,7 +56,8 @@ const ScheduleListWrapper = ({
|
||||||
onEditScheduledQueryClick,
|
onEditScheduledQueryClick,
|
||||||
teamId,
|
teamId,
|
||||||
inheritedQueries,
|
inheritedQueries,
|
||||||
isTeamMaintainer,
|
isTeamMaintainerOrTeamAdmin,
|
||||||
|
isOnGlobalTeam,
|
||||||
}: IScheduleListWrapperProps): JSX.Element => {
|
}: IScheduleListWrapperProps): JSX.Element => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const { MANAGE_PACKS } = paths;
|
const { MANAGE_PACKS } = paths;
|
||||||
|
|
@ -70,28 +72,34 @@ const ScheduleListWrapper = ({
|
||||||
<div className={`${noScheduleClass}__inner-text`}>
|
<div className={`${noScheduleClass}__inner-text`}>
|
||||||
<h2>You don't have any queries scheduled.</h2>
|
<h2>You don't have any queries scheduled.</h2>
|
||||||
<p>
|
<p>
|
||||||
{!isTeamMaintainer
|
{isOnGlobalTeam &&
|
||||||
? "Schedule a query, or go to your osquery packs via the 'Advanced' button."
|
"Schedule a query, or go to your osquery packs via the 'Advanced' button."}
|
||||||
: "Schedule a query to run on hosts assigned to this team."}
|
{isTeamMaintainerOrTeamAdmin &&
|
||||||
|
"Schedule a query to run on hosts assigned to this team."}
|
||||||
|
{!isOnGlobalTeam &&
|
||||||
|
!isTeamMaintainerOrTeamAdmin &&
|
||||||
|
"There are no scheduled queries assigned to this team."}
|
||||||
</p>
|
</p>
|
||||||
<div className={`${noScheduleClass}__-cta-buttons`}>
|
{(isOnGlobalTeam || isTeamMaintainerOrTeamAdmin) && (
|
||||||
<Button
|
<div className={`${noScheduleClass}__-cta-buttons`}>
|
||||||
variant="brand"
|
|
||||||
className={`${noScheduleClass}__schedule-button`}
|
|
||||||
onClick={toggleScheduleEditorModal}
|
|
||||||
>
|
|
||||||
Schedule a query
|
|
||||||
</Button>
|
|
||||||
{!isTeamMaintainer && (
|
|
||||||
<Button
|
<Button
|
||||||
variant="inverse"
|
variant="brand"
|
||||||
onClick={handleAdvanced}
|
className={`${noScheduleClass}__schedule-button`}
|
||||||
className={`${baseClass}__advanced-button`}
|
onClick={toggleScheduleEditorModal}
|
||||||
>
|
>
|
||||||
Advanced
|
Schedule a query
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
{isOnGlobalTeam && (
|
||||||
</div>
|
<Button
|
||||||
|
variant="inverse"
|
||||||
|
onClick={handleAdvanced}
|
||||||
|
className={`${baseClass}__advanced-button`}
|
||||||
|
>
|
||||||
|
Advanced
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,14 @@ const isTeamAdmin = (user: IUser | null, teamId: number | null): boolean => {
|
||||||
return userTeamRole === "admin";
|
return userTeamRole === "admin";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isTeamMaintainerOrTeamAdmin = (
|
||||||
|
user: IUser | null,
|
||||||
|
teamId: number | null
|
||||||
|
): boolean => {
|
||||||
|
const userTeamRole = user?.teams.find((team) => team.id === teamId)?.role;
|
||||||
|
return userTeamRole === "admin" || userTeamRole === "maintainer";
|
||||||
|
};
|
||||||
|
|
||||||
// This checks against all teams
|
// This checks against all teams
|
||||||
const isAnyTeamMaintainer = (user: IUser): boolean => {
|
const isAnyTeamMaintainer = (user: IUser): boolean => {
|
||||||
if (!isOnGlobalTeam(user)) {
|
if (!isOnGlobalTeam(user)) {
|
||||||
|
|
@ -95,6 +103,7 @@ export default {
|
||||||
isOnGlobalTeam,
|
isOnGlobalTeam,
|
||||||
isTeamObserver,
|
isTeamObserver,
|
||||||
isTeamMaintainer,
|
isTeamMaintainer,
|
||||||
|
isTeamMaintainerOrTeamAdmin,
|
||||||
isAnyTeamMaintainer,
|
isAnyTeamMaintainer,
|
||||||
isAnyTeamMaintainerOrTeamAdmin,
|
isAnyTeamMaintainerOrTeamAdmin,
|
||||||
isTeamAdmin,
|
isTeamAdmin,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue