Skip to content

Commit

Permalink
feat(quartz,rbac): apply RBAC to Quartz plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
tadayosi committed Sep 11, 2023
1 parent 21b2b4d commit 5dace1b
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 33 deletions.
13 changes: 12 additions & 1 deletion packages/hawtio/src/plugins/quartz/scheduler/Scheduler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { CheckCircleIcon, PauseCircleIcon } from '@patternfly/react-icons'
import React, { useContext, useEffect, useState } from 'react'
import { QuartzContext } from '../context'
import { log } from '../globals'
import { quartzService } from '../quartz-service'
import { QUARTZ_OPERATIONS, quartzService } from '../quartz-service'
import './Scheduler.css'

export const Scheduler: React.FunctionComponent = () => {
Expand Down Expand Up @@ -78,11 +78,20 @@ export const Scheduler: React.FunctionComponent = () => {

const { name, objectName } = selectedNode

const canStartPauseScheduler = () => {
return selectedNode.hasInvokeRights(QUARTZ_OPERATIONS.start, QUARTZ_OPERATIONS.standby)
}

const handleSchedulerSwitchChange = async (start: boolean) => {
await (start ? quartzService.start(name, objectName) : quartzService.pause(name, objectName))
setReload(true)
}

const canUpdateSampleStatisticsEnabled = () => {
// TODO: currently using the same criteria as start/pause scheduler, as it requires writing to an attribute
return selectedNode.hasInvokeRights(QUARTZ_OPERATIONS.start, QUARTZ_OPERATIONS.standby)
}

const handleSampledStatisticsSwitchChange = async (value: boolean) => {
await quartzService.updateSampleStatisticsEnabled(name, objectName, value)
setReload(true)
Expand Down Expand Up @@ -126,6 +135,7 @@ export const Scheduler: React.FunctionComponent = () => {
label='Started'
labelOff='Paused'
isChecked={scheduler.started}
isDisabled={!canStartPauseScheduler()}
onChange={handleSchedulerSwitchChange}
isReversed
/>
Expand Down Expand Up @@ -180,6 +190,7 @@ export const Scheduler: React.FunctionComponent = () => {
label='Enabled'
labelOff='Disabled'
isChecked={scheduler.sampledStatisticsEnabled}
isDisabled={!canUpdateSampleStatisticsEnabled()}
onChange={handleSampledStatisticsSwitchChange}
isReversed
/>
Expand Down
6 changes: 2 additions & 4 deletions packages/hawtio/src/plugins/quartz/triggers/Triggers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ import { SearchIcon } from '@patternfly/react-icons'
import { TableComposable, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'
import React, { useContext, useEffect, useRef, useState } from 'react'
import { QuartzContext } from '../context'
import { log } from '../globals'
import { Trigger, TriggerFilter, quartzService } from '../quartz-service'
import { TriggersTableRow } from './TriggersTableRow'
import { log } from '../globals'

export const Triggers: React.FunctionComponent = () => {
const { selectedNode } = useContext(QuartzContext)
Expand Down Expand Up @@ -87,8 +87,6 @@ export const Triggers: React.FunctionComponent = () => {
return null
}

const { objectName } = selectedNode

if (isReading) {
return <HawtioLoadingCard />
}
Expand Down Expand Up @@ -199,7 +197,7 @@ export const Triggers: React.FunctionComponent = () => {
</Thead>
<Tbody>
{filteredTriggers.map((trigger, index) => (
<TriggersTableRow key={index} mbean={objectName} trigger={trigger} reload={() => setReload(true)} />
<TriggersTableRow key={index} trigger={trigger} reload={() => setReload(true)} />
))}
{filteredTriggers.length === 0 && (
<Tr>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import { Button, Form, FormGroup, Modal, TextArea } from '@patternfly/react-core'
import React, { useState } from 'react'
import React, { useContext, useState } from 'react'
import { QuartzContext } from '../context'
import { log } from '../globals'
import { Trigger, quartzService } from '../quartz-service'

export const TriggersManualModal: React.FunctionComponent<{
isOpen: boolean
onClose: () => void
mbean: string
input: Trigger
}> = ({ isOpen, onClose, mbean, input }) => {
}> = ({ isOpen, onClose, input }) => {
const { selectedNode } = useContext(QuartzContext)
const [parameters, setParameters] = useState('{}')

if (!selectedNode || !selectedNode.objectName) {
return null
}

const { objectName } = selectedNode
const { name, group } = input

const fireTrigger = () => {
log.info('Manually fire trigger:', mbean, input, parameters)
quartzService.triggerJob(mbean, name, group, parameters)
log.info('Manually fire trigger:', objectName, input, parameters)
quartzService.triggerJob(objectName, name, group, parameters)
clear()
}

Expand Down
62 changes: 45 additions & 17 deletions packages/hawtio/src/plugins/quartz/triggers/TriggersTableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,66 @@
import { Button, Icon } from '@patternfly/react-core'
import { CheckCircleIcon, PauseCircleIcon } from '@patternfly/react-icons'
import { ActionsColumn, Td, Tr } from '@patternfly/react-table'
import React, { useState } from 'react'
import { Trigger, misfireInstructions, quartzService } from '../quartz-service'
import { TriggersUpdateModal } from './TriggersUpdateModal'
import React, { useContext, useState } from 'react'
import { QuartzContext } from '../context'
import {
QUARTZ_FACADE_OPERATIONS,
QUARTZ_OPERATIONS,
Trigger,
misfireInstructions,
quartzService,
} from '../quartz-service'
import { TriggersManualModal } from './TriggersManualModal'
import { TriggersUpdateModal } from './TriggersUpdateModal'

export const TriggersTableRow: React.FunctionComponent<{
mbean: string
trigger: Trigger
reload: () => void
}> = ({ mbean, trigger, reload }) => {
}> = ({ trigger, reload }) => {
const { selectedNode } = useContext(QuartzContext)
const [isUpdateOpen, setIsUpdateOpen] = useState(false)
const [isManualOpen, setIsManualOpen] = useState(false)

if (!selectedNode || !selectedNode.objectName) {
return null
}

const { objectName } = selectedNode

const canUpdateTrigger = () => {
return selectedNode.hasInvokeRights(
QUARTZ_FACADE_OPERATIONS.updateCronTrigger,
QUARTZ_FACADE_OPERATIONS.updateSimpleTrigger,
)
}

const handleUpdateToggle = () => {
setIsUpdateOpen(!isUpdateOpen)
}

const canTriggerJob = () => {
return selectedNode.hasInvokeRights(QUARTZ_OPERATIONS.triggerJob)
}

const handleManualToggle = () => {
setIsManualOpen(!isManualOpen)
}

const canPauseTrigger = () => {
return selectedNode.hasInvokeRights(QUARTZ_OPERATIONS.pauseTrigger)
}

const pauseTrigger = async () => {
await quartzService.pauseTrigger(mbean, trigger.name, trigger.group)
await quartzService.pauseTrigger(objectName, trigger.name, trigger.group)
reload()
}

const canResumeTrigger = () => {
return selectedNode.hasInvokeRights(QUARTZ_OPERATIONS.resumeTrigger)
}

const resumeTrigger = async () => {
await quartzService.resumeTrigger(mbean, trigger.name, trigger.group)
await quartzService.resumeTrigger(objectName, trigger.name, trigger.group)
reload()
}

Expand Down Expand Up @@ -62,11 +94,11 @@ export const TriggersTableRow: React.FunctionComponent<{
<Td dataLabel='finalFire'>{trigger.finalFireTime?.toString()}</Td>
<Td dataLabel='resume/pause' modifier='fitContent'>
{normalState ? (
<Button variant='danger' isSmall onClick={pauseTrigger}>
<Button variant='danger' isSmall onClick={pauseTrigger} isDisabled={!canPauseTrigger()}>
Pause
</Button>
) : (
<Button variant='primary' isSmall onClick={resumeTrigger}>
<Button variant='primary' isSmall onClick={resumeTrigger} isDisabled={!canResumeTrigger()}>
Resume
</Button>
)}
Expand All @@ -76,24 +108,20 @@ export const TriggersTableRow: React.FunctionComponent<{
items={[
{
title: 'Update Trigger',
isDisabled: !canUpdateTrigger(),
onClick: handleUpdateToggle,
},
{
title: 'Trigger Manually',
isDisabled: !canTriggerJob(),
onClick: handleManualToggle,
},
]}
/>
</Td>
</Tr>
<TriggersUpdateModal
isOpen={isUpdateOpen}
onClose={handleUpdateToggle}
mbean={mbean}
input={trigger}
reload={reload}
/>
<TriggersManualModal isOpen={isManualOpen} onClose={handleManualToggle} mbean={mbean} input={trigger} />
<TriggersUpdateModal isOpen={isUpdateOpen} onClose={handleUpdateToggle} input={trigger} reload={reload} />
<TriggersManualModal isOpen={isManualOpen} onClose={handleManualToggle} input={trigger} />
</React.Fragment>
)
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import { Button, Form, FormGroup, FormSelect, FormSelectOption, Modal, TextInput } from '@patternfly/react-core'
import React, { useState } from 'react'
import { Trigger, misfireInstructions, quartzService } from '../quartz-service'
import React, { useContext, useState } from 'react'
import { QuartzContext } from '../context'
import { log } from '../globals'
import { Trigger, misfireInstructions, quartzService } from '../quartz-service'

export const TriggersUpdateModal: React.FunctionComponent<{
isOpen: boolean
onClose: () => void
mbean: string
input: Trigger
reload: () => void
}> = ({ isOpen, onClose, mbean, input, reload }) => {
}> = ({ isOpen, onClose, input, reload }) => {
const { selectedNode } = useContext(QuartzContext)
const [trigger, setTrigger] = useState(input)

if (!selectedNode || !selectedNode.objectName) {
return null
}

const { objectName } = selectedNode
const isCron = input.type === 'cron'
const isSimple = input.type === 'simple'

const updateTrigger = async () => {
log.info('Update trigger:', mbean, trigger)
await quartzService.updateTrigger(mbean, trigger)
log.info('Update trigger:', objectName, trigger)
await quartzService.updateTrigger(objectName, trigger)
reload()
onClose()
}
Expand Down

0 comments on commit 5dace1b

Please sign in to comment.