Skip to content

Commit

Permalink
Creates Access Control Section for Analyses
Browse files Browse the repository at this point in the history
  • Loading branch information
gbdubs committed Jan 22, 2024
1 parent 7840197 commit ea9ab64
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 20 deletions.
2 changes: 1 addition & 1 deletion cmd/server/pactasrv/conv/pacta_to_oapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ func AnalysisArtifactToOAPI(aa *pacta.AnalysisArtifact) (*api.AnalysisArtifact,
return &api.AnalysisArtifact{
Id: string(aa.ID),
AdminDebugEnabled: aa.AdminDebugEnabled,
SharedToPublic: aa.AdminDebugEnabled,
SharedToPublic: aa.SharedToPublic,
Blob: *blob,
}, nil
}
Expand Down
5 changes: 3 additions & 2 deletions frontend/components/AdminDebugEnabledToggleButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,14 @@ const visible = newModalVisibilityState('AdminDebugEnabledWarning')
:sub-header="tt('ModalSubheading')"
>
<p>
TODO - add good copy for this
{{ tt('Paragraph1' ) }}
</p>
<p>
{{ tt('Paragraph1' ) }}
You're enabling administrator access to this resource. If you do so, site administrators will be able to
access the content of this data.
</p>
<div class="flex gap-2 justify-content-between align-items-center flex-wrap">
<div class="flex gap-2 pt-3 justify-content-between align-items-center flex-wrap">
<PVButton
:label="tt('No Ack')"
icon="pi pi-arrow-left"
Expand Down
77 changes: 77 additions & 0 deletions frontend/components/SharedToPublicToggleButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script setup lang="ts">
const { newModalVisibilityState } = useModal()
const { computedBooleanLocalStorageValue } = useLocalStorage()
const { t } = useI18n()
interface Props {
value: boolean
}
const props = defineProps<Props>()
interface Emits {
(e: 'update:value', value: boolean): void
}
const emit = defineEmits<Emits>()
const prefix = 'components/SharedToPublicToggleButton'
const tt = (s: string) => t(`${prefix}.${s}`)
const everAcked = computedBooleanLocalStorageValue(`${prefix}.everAcked`, false)
const model = computed({
get: () => props.value,
set: (value: boolean) => {
if (value && !everAcked.value) {
visible.value = true
return
} else {
emit('update:value', value)
}
},
})
const ack = () => {
everAcked.value = true
model.value = true
visible.value = false
}
const noAck = () => {
model.value = false
}
const visible = newModalVisibilityState('SharedToPublicWarning')
</script>

<template>
<div>
<ExplicitInputSwitch
v-model:value="model"
:on-label="tt('Shared to Public')"
:off-label="tt('Not Shared')"
/>
<StandardModal
v-model:visible="visible"
:header="tt('ModalHeading')"
:sub-header="tt('ModalSubheading')"
>
<p>
{{ tt('Paragraph1') }}
</p>
<p>
{{ tt('Paragraph2') }}
</p>
<div class="flex pt-3 gap-2 justify-content-between align-items-center flex-wrap">
<PVButton
:label="tt('No Ack')"
icon="pi pi-arrow-left"
class="p-button-secondary"
@click="noAck"
/>
<PVButton
:label="tt('Ack')"
icon="pi pi-arrow-right"
icon-pos="right"
@click="ack"
/>
</div>
</StandardModal>
</div>
</template>
72 changes: 70 additions & 2 deletions frontend/components/analysis/ListView.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<script setup lang="ts">
import { analysisEditor } from '@/lib/editor'
import { type Portfolio, type PortfolioGroup, type Initiative, type Analysis } from '@/openapi/generated/pacta'
import { AuditLogQuerySortBy, type Portfolio, type AnalysisArtifact, type AnalysisArtifactChanges, type PortfolioGroup, type Initiative, type Analysis } from '@/openapi/generated/pacta'
import { selectedCountSuffix } from '@/lib/selection'
import { createURLAuditLogQuery } from '@/lib/auditlogquery'
const { humanReadableTimeFromStandardString } = useTime()
const pactaClient = usePACTA()
const { loading: { withLoading } } = useModal()
const i18n = useI18n()
const localePath = useLocalePath()
const { t } = i18n
interface Props {
Expand Down Expand Up @@ -36,6 +38,8 @@ const selectedAnalysisIDs = computed({
interface EditorObject extends ReturnType<typeof analysisEditor> {
id: string
artifactsADEState: boolean
artifactsSTPState: boolean
}
const prefix = 'components/analysis/ListView'
Expand All @@ -53,7 +57,12 @@ const selectedRows = computed<EditorObject[]>({
},
})
const editorObjects = computed<EditorObject[]>(() => props.analyses.map((item) => ({ ...analysisEditor(item, i18n), id: item.id })))
const editorObjects = computed<EditorObject[]>(() => props.analyses.map((item: Analysis) => ({
...analysisEditor(item, i18n),
id: item.id,
artifactsADEState: item.artifacts.every(a => a.adminDebugEnabled),
artifactsSTPState: item.artifacts.every(a => a.sharedToPublic),
})))
const selectedAnalyses = computed<Analysis[]>(() => selectedRows.value.map((row) => row.currentValue.value))
Expand All @@ -66,6 +75,32 @@ const saveChanges = (id: string) => {
)
}
const auditLogURL = (id: string) => {
return createURLAuditLogQuery(
localePath,
{
sorts: [{ by: AuditLogQuerySortBy.AUDIT_LOG_QUERY_SORT_BY_CREATED_AT, ascending: false }],
wheres: [{ inTargetId: [id] }],
},
)
}
const changeAllArtifacts = (id: string, changes: AnalysisArtifactChanges) => {
const artifacts: AnalysisArtifact[] = editorObjects.value.find((eo) => eo.id === id)?.currentValue.value.artifacts ?? []
void withLoading(async () => {
for (const artifact of artifacts) {
await pactaClient.updateAnalysisArtifact(artifact.id, changes)
}
refresh()
}, `${prefix}.changeAllArtifacts`)
}
const setAllADEOnArtifacts = (id: string, value: boolean) => {
changeAllArtifacts(id, { adminDebugEnabled: value })
}
const setAllSTPOnArtifacts = (id: string, value: boolean) => {
changeAllArtifacts(id, { sharedToPublic: value })
}
const deleteAnalysis = (id: string) => withLoading(
() => pactaClient.deleteAnalysis(id),
`${prefix}.deleteAnalysis`,
Expand Down Expand Up @@ -174,6 +209,39 @@ const deleteSpecificAnalysis = async (id: string) => {
/>
</div>
</div>
<h2 class="mt-5">
{{ tt('Access Controls') }}
</h2>
<FormField
:label="tt('Admin Debugging Enabled')"
:help-text="tt('ADEHelpText')"
>
<AdminDebugEnabledToggleButton
:value="slotProps.data.artifactsADEState"
@update:value="(newValue: boolean) => setAllADEOnArtifacts(slotProps.data.id, newValue)"
/>
</FormField>
<FormField
:label="tt('Shared To Public')"
:help-text="tt('STPHelpText')"
>
<SharedToPublicToggleButton
:value="slotProps.data.artifactsSTPState"
@update:value="(newValue: boolean) => setAllSTPOnArtifacts(slotProps.data.id, newValue)"
/>
</FormField>
<FormField
:label="tt('Audit Logs')"
:help-text="tt('AuditLogsHelpText')"
>
<LinkButton
:label="tt('View Audit Logs')"
:to="auditLogURL(slotProps.data.id)"
icon="pi pi-arrow-right"
class="p-button-outlined align-self-start"
icon-pos="right"
/>
</formfield>
</div>
</template>
</PVDataTable>
Expand Down
4 changes: 2 additions & 2 deletions frontend/components/standard/Nav.vue
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const userMenuItems = computed(() => {
<LinkButton
v-if="mi.to"
:key="index"
:class="mi.to === router.currentRoute.value.fullPath ? 'border-noround sm:border-round' : 'p-button-text'"
:class="mi.to === router.currentRoute.value.path ? 'border-noround sm:border-round' : 'p-button-text'"
:to="mi.to"
:external="mi.external"
:label="`${mi.label}`"
Expand All @@ -138,7 +138,7 @@ const userMenuItems = computed(() => {
/>
</template>
<PVButton
v-show="maybeMe !== undefined"
v-if="isAuthenticated"
v-tooltip.left="tt('Settings')"
icon="pi pi-user"
class="hidden sm:flex ml-2 flex-shrink-0"
Expand Down
22 changes: 21 additions & 1 deletion frontend/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,15 @@
"Save Changes": "Save Changes",
"Refresh": "Refresh",
"View": "View",
"Status": "Status"
"Status": "Status",
"Access Controls": "Access Controls",
"Admin Debugging Enabled": "Admin Debugging",
"ADEHelpText": "If enabled, this analysis will be accessible to administrators for debugging purposes. When disabled, only you, the owner of this analysis, can access it.",
"Shared To Public": "Sharing Status",
"STPHelpText": "If enabled, anyone with the link can see this analysis. If disabled, folks visiting the link for this report will not be able to see it.",
"Audit Logs":"Audit Logs",
"AuditLogsHelpText": "Audit logs are a record of who has accessed or modified this analysis, and when. This is useful for debugging, for understanding who has seen this analysis, and for establishing peace of mind that your data's security is being upheld.",
"View Audit Logs": "View Audit Logs"
},
"components/initiative/Toolbar": {
"Edit": "Edit",
Expand Down Expand Up @@ -164,6 +172,18 @@
"No Administrator Access Enabled": "No Administrator Access Enabled",
"ModalHeading": "Administrator Debugging Access",
"ModalSubheading": "Exercise caution - this changes who can see the data that you upload",
"Paragraph1": "You're enabling administrator access to this resource. If you continue, site administrators will be able to access the content of this data.",
"Paragraph2": "Enabling this will not grant access to this data to anyone besides site administrators. Additionally, you will be able to see who accessed this data, and when, via the audit logs for this resource.",
"Ack": "I Understand",
"No Ack": "Nevermind"
},
"components/SharedToPublicToggleButton": {
"Shared to Public": "Shared to Public - anyone with the link can see this analysis",
"Not Shared": "Not Shared - only you can see this analysis",
"ModalHeading": "Sharing Status Change Requested",
"ModalSubheading": "Exercise caution - this changes who can see this report",
"Paragraph1": "This report is currently only visible to it's owner (you). If you set the visibility to public, then anyone with the link will be able to access it. You can change this setting at any time.",
"Paragraph2": "Note this is distinct from enabling administrator debugging access - if the report is not shared, but administrator debugging access is enabled, then administrators will be able to see the report, but it will not be visible to the public.",
"Ack": "I Understand",
"No Ack": "Nevermind"
},
Expand Down
6 changes: 2 additions & 4 deletions frontend/lib/auditlogquery/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ const wheresQP = 'w'
const limitQP = 'l'
const limitDefault = 100
const cursorQP = 'c'
const pageURLBase = '/auditlog'
const pageURLBase = '/audit-logs'

export const urlReactiveAuditLogQuery = (fromQueryReactiveWithDefault: (key: string, defaultValue: string) => WritableComputedRef<string>): WritableComputedRef<AuditLogQueryReq> => {
const qSorts = fromQueryReactiveWithDefault(sortsQP, '')
Expand Down Expand Up @@ -194,7 +194,5 @@ export const createURLAuditLogQuery = (localePath: LocalePathFunction, req: Audi
if (qCursor) {
q.set(cursorQP, qCursor)
}
const url = new URL(pageURLBase)
url.search = q.toString()
return localePath(url.toString())
return localePath(pageURLBase + '?' + q.toString())
}
19 changes: 11 additions & 8 deletions frontend/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,8 @@ const tt = (s: string) => t(`pages/index.${s}`)
>
<PVImage
preview
class="img-max-w-full"
src="/img/how-it-works.jpg"
:pt="{
root: {
'class': 'max-w-full',
},
image : {
'class': 'max-w-full',
}
}"
/>
</div>
</div>
Expand Down Expand Up @@ -98,3 +91,13 @@ const tt = (s: string) => t(`pages/index.${s}`)
</div>
</StandardContent>
</template>

<style lang="scss">
.img-max-w-full {
max-width: 100%;
img {
max-width: 100%;
}
}
</style>

0 comments on commit ea9ab64

Please sign in to comment.