diff --git a/graylog2-web-interface/src/@types/graylog-web-plugin/index.d.ts b/graylog2-web-interface/src/@types/graylog-web-plugin/index.d.ts index a1cdf6bfcbc5..dd5da4d02769 100644 --- a/graylog2-web-interface/src/@types/graylog-web-plugin/index.d.ts +++ b/graylog2-web-interface/src/@types/graylog-web-plugin/index.d.ts @@ -193,7 +193,9 @@ export type FieldValueProvider = { } interface PluginDataWarehouse { - StreamDataWarehouse: React.ComponentType, + StreamDataWarehouse: React.ComponentType<{ + permissions: Immutable.List, + }>, DataWarehouseStatus: React.ComponentType<{ datawareHouseEnabled: boolean; }>, @@ -201,6 +203,7 @@ interface PluginDataWarehouse { nodeId: string, }>, DataWarehouseJobs: React.ComponentType<{ + permissions: Immutable.List, streamId: string, }>, StreamIlluminateProcessingSection: React.ComponentType<{ diff --git a/graylog2-web-interface/src/components/streams/MatchingTypeSwitcher.tsx b/graylog2-web-interface/src/components/streams/MatchingTypeSwitcher.tsx index 4ce8a8b22392..fbefd1f4c3dc 100644 --- a/graylog2-web-interface/src/components/streams/MatchingTypeSwitcher.tsx +++ b/graylog2-web-interface/src/components/streams/MatchingTypeSwitcher.tsx @@ -18,10 +18,11 @@ import React, { useState } from 'react'; import styled, { css } from 'styled-components'; import { Input } from 'components/bootstrap'; +import ConfirmDialog from 'components/common/ConfirmDialog'; import UserNotification from 'util/UserNotification'; +import useCurrentUser from 'hooks/useCurrentUser'; import StreamsStore, { type Stream } from 'stores/streams/StreamsStore'; - -import ConfirmDialog from '../common/ConfirmDialog'; +import { isPermitted } from 'util/PermissionsMixin'; const StreamRuleConnector = styled.div(({ theme }) => css` margin-top: 10px; @@ -53,7 +54,8 @@ type Props = { const MatchingTypeSwitcher = ({ stream, onChange }: Props) => { const [matchingType, setMatchingType] = useState<'AND'|'OR'|undefined>(undefined); - const disabled = stream.is_default || !stream.is_editable; + const currentUser = useCurrentUser(); + const disabled = stream.is_default || !stream.is_editable || !isPermitted(currentUser.permissions, `streams:edit:${stream.id}`); const handleTypeChange = (newValue: 'AND'|'OR') => { StreamsStore.update(stream.id, { matching_type: newValue }, (response) => { diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/DestinationPermissionAlert.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/DestinationPermissionAlert.tsx new file mode 100644 index 000000000000..a44460603eea --- /dev/null +++ b/graylog2-web-interface/src/components/streams/StreamDetails/DestinationPermissionAlert.tsx @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2020 Graylog, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * . + */ +import * as React from 'react'; + +import { Alert } from 'components/bootstrap'; + +type Props = { + sectionName: string, +}; + +const DestinationPermissionAlert = ({ sectionName }: Props) => ( + + {`You currently do not have the permissions to view or manage ${sectionName} destination.`} + +); + +export default DestinationPermissionAlert; diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/StreamDataRoutingDestinations.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/StreamDataRoutingDestinations.tsx index ef56425faccf..d4d1f6e6c904 100644 --- a/graylog2-web-interface/src/components/streams/StreamDetails/StreamDataRoutingDestinations.tsx +++ b/graylog2-web-interface/src/components/streams/StreamDetails/StreamDataRoutingDestinations.tsx @@ -19,11 +19,13 @@ import * as React from 'react'; import { PluginStore } from 'graylog-web-plugin/plugin'; import styled, { css } from 'styled-components'; -import { IfPermitted } from 'components/common'; import type { Stream } from 'stores/streams/StreamsStore'; -import useSingleIndexSet from 'components/indices/hooks/useSingleIndexSet'; import DestinationOutputs from 'components/streams/StreamDetails/routing-destination/DestinationOutputs'; import DestinationIndexSetSection from 'components/streams/StreamDetails/routing-destination/DestinationIndexSetSection'; +import useCurrentUser from 'hooks/useCurrentUser'; +import { isPermitted } from 'util/PermissionsMixin'; + +import DestinationPermissionAlert from './DestinationPermissionAlert'; type Props = { stream: Stream; @@ -36,17 +38,17 @@ const Container = styled.div(({ theme }) => css` `); const StreamDataRoutingDestinations = ({ stream }: Props) => { - const { index_set_id: indexSetId } = stream; - const { data: indexSet, isSuccess } = useSingleIndexSet(indexSetId); + const currentUser = useCurrentUser(); const StreamDataWarehouseComponent = PluginStore.exports('dataWarehouse')?.[0]?.StreamDataWarehouse; + const destinationIndexset = isPermitted(currentUser.permissions, ['indexsets:read']) ? : ; + const destinationOutput = isPermitted(currentUser.permissions, ['output:read']) ? : ; + return ( - {isSuccess && } - {StreamDataWarehouseComponent && } - - - + {destinationIndexset} + {StreamDataWarehouseComponent && } + {destinationOutput} ); }; diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/StreamDetails.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/StreamDetails.tsx index 4ef10b3ca686..3d0e008def20 100644 --- a/graylog2-web-interface/src/components/streams/StreamDetails/StreamDetails.tsx +++ b/graylog2-web-interface/src/components/streams/StreamDetails/StreamDetails.tsx @@ -41,6 +41,7 @@ import useHistory from 'routing/useHistory'; import useSendTelemetry from 'logic/telemetry/useSendTelemetry'; import useQuery from 'routing/useQuery'; import { TELEMETRY_EVENT_TYPE } from 'logic/telemetry/Constants'; +import useCurrentUser from 'hooks/useCurrentUser'; import StreamDataRoutingIntake from './StreamDataRoutingIntake'; import StreamDataRoutingProcessing from './StreamDataRoutingProcessing'; @@ -163,6 +164,7 @@ const StreamDetails = ({ stream }: Props) => { const { indexSets } = useStore(IndexSetsStore); const queryClient = useQueryClient(); const history = useHistory(); + const currentUser = useCurrentUser(); const sendTelemetry = useSendTelemetry(); const updateURLStepQueryParam = (nextSegment: DetailsSegment) => { @@ -199,7 +201,7 @@ const StreamDetails = ({ stream }: Props) => { return ( <> - {DataWarehouseJobComponent && } + {DataWarehouseJobComponent && }
diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/output-filter/FilterRuleList.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/output-filter/FilterRuleList.tsx index 2593586a9f31..696fd3f3c00f 100644 --- a/graylog2-web-interface/src/components/streams/StreamDetails/output-filter/FilterRuleList.tsx +++ b/graylog2-web-interface/src/components/streams/StreamDetails/output-filter/FilterRuleList.tsx @@ -71,26 +71,31 @@ type Props = { destinationType: string, paginatedFilters: PaginatedListType, onPaginationChange: (newPage: number, newPerPage: number) => void, + requiredPermissions: Array, }; const _headerCellFormatter = (header: string) => ({header}); -const buildFilterItem = (destinationType: string) => (filter: StreamOutputFilterRule) => ( +const buildFilterItem = (destinationType: string, requiredPermissions: Array) => (filter: StreamOutputFilterRule) => ( {filter.title} {filter.description} - + + + + + ); -const FilterRulesList = ({ streamId, destinationType, paginatedFilters, onPaginationChange }: Props) => { +const FilterRulesList = ({ streamId, destinationType, paginatedFilters, onPaginationChange, requiredPermissions }: Props) => { const { list: filters, pagination: { total } } = paginatedFilters; return ( + @@ -112,7 +117,7 @@ const FilterRulesList = ({ streamId, destinationType, paginatedFilters, onPagina sortByKey="title" noDataText={No filter have been found.} rows={filters.toJS()} - dataRowFormatter={buildFilterItem(destinationType)} /> + dataRowFormatter={buildFilterItem(destinationType, requiredPermissions)} /> ); diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/DestinationIndexSetSection.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/DestinationIndexSetSection.tsx index dfa2bc9621b9..6fe6bfe8d9b7 100644 --- a/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/DestinationIndexSetSection.tsx +++ b/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/DestinationIndexSetSection.tsx @@ -20,7 +20,7 @@ import styled, { css } from 'styled-components'; import { ARCHIVE_RETENTION_STRATEGY } from 'stores/indices/IndicesStore'; import { Icon, Section, Spinner } from 'components/common'; -import { IndexSetsStore, type IndexSet } from 'stores/indices/IndexSetsStore'; +import { IndexSetsStore } from 'stores/indices/IndexSetsStore'; import { Table, Button, Alert } from 'components/bootstrap'; import { LinkContainer } from 'components/common/router'; import Routes from 'routing/Routes'; @@ -36,11 +36,11 @@ import SectionCountLabel from 'components/streams/StreamDetails/SectionCountLabe import useIndexSetStats from 'hooks/useIndexSetStats'; import { DEFAULT_PAGINATION } from 'stores/PaginationTypes'; import useIndexerOverview from 'hooks/useIndexerOverview'; +import useSingleIndexSet from 'components/indices/hooks/useSingleIndexSet'; import IndexSetOldestMessageCell from './IndexSetOldestMessageCell'; type Props = { - indexSet: IndexSet, stream: Stream, }; @@ -48,17 +48,18 @@ const ActionButtonsWrap = styled.span(() => css` float: right; `); -const DestinationIndexSetSection = ({ indexSet, stream }: Props) => { +const DestinationIndexSetSection = ({ stream }: Props) => { const [pagination, setPagination] = useState(DEFAULT_PAGINATION); - const archivingEnabled = indexSet.retention_strategy_class === ARCHIVE_RETENTION_STRATEGY || indexSet?.data_tiering?.archive_before_deletion; + const { data: indexSet, isInitialLoading: isLoadingIndexSet } = useSingleIndexSet(stream.index_set_id); + const archivingEnabled = indexSet?.retention_strategy_class === ARCHIVE_RETENTION_STRATEGY || indexSet?.data_tiering?.archive_before_deletion; const { indexSets } = useStore(IndexSetsStore); const { data: streamOutputFilters, isLoading: isLoadingStreamOutputFilters } = useStreamOutputFilters(stream.id, 'indexer', pagination); - const { data: indexerOverview, isSuccess: isLoadingIndexerOverviewSuccess } = useIndexerOverview(indexSet.id); + const { data: indexerOverview, isSuccess: isLoadingIndexerOverviewSuccess } = useIndexerOverview(stream.index_set_id); /* eslint-disable no-constant-condition */ const title = true ? 'Enabled' : 'Disabled'; // TODO use api to check if enabled - const { data: indexSetStats, isSuccess: isStatsLoaded } = useIndexSetStats(indexSet.id); + const { data: indexSetStats, isSuccess: isStatsLoaded } = useIndexSetStats(stream.index_set_id); - if (isLoadingStreamOutputFilters) { + if (isLoadingStreamOutputFilters || isLoadingIndexSet) { ; } @@ -84,7 +85,7 @@ const DestinationIndexSetSection = ({ indexSet, stream }: Props) => { )} actions={( - )}> @@ -103,26 +104,28 @@ const DestinationIndexSetSection = ({ indexSet, stream }: Props) => { - - {indexSet?.title} - {(isStatsLoaded && indexSetStats?.size) ? NumberUtils.formatBytes(indexSetStats.size) : 0} - {isLoadingIndexerOverviewSuccess && } - - - - - - - - - - - + {indexSet && ( + + {indexSet?.title} + {(isStatsLoaded && indexSetStats?.size) ? NumberUtils.formatBytes(indexSetStats.size) : 0} + {isLoadingIndexerOverviewSuccess && } + + + + + + + + + + + + )} {streamOutputFilters && ()} diff --git a/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/IndexSetFilters.tsx b/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/IndexSetFilters.tsx index c9b9d42e676f..823cd8057d38 100644 --- a/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/IndexSetFilters.tsx +++ b/graylog2-web-interface/src/components/streams/StreamDetails/routing-destination/IndexSetFilters.tsx @@ -30,7 +30,8 @@ const IndexSetFilters = ({ streamId, paginatedFilters, onPaginationChange }: Pro + onPaginationChange={onPaginationChange} + requiredPermissions={['indexsets:edit']} /> ); diff --git a/graylog2-web-interface/src/components/streams/StreamsOverview/StreamActions.tsx b/graylog2-web-interface/src/components/streams/StreamsOverview/StreamActions.tsx index 34d2dcaa44a1..655940337f62 100644 --- a/graylog2-web-interface/src/components/streams/StreamsOverview/StreamActions.tsx +++ b/graylog2-web-interface/src/components/streams/StreamsOverview/StreamActions.tsx @@ -159,7 +159,7 @@ const StreamActions = ({ return ( - +