Add permission checks for delegates and authors

This commit is contained in:
Sebastian Martinez
2023-11-20 22:13:51 +01:00
parent ffe7bbba17
commit eeba82017d
5 changed files with 110 additions and 29 deletions
+9 -4
View File
@@ -22,9 +22,9 @@
<script lang="ts" strictEvents>
import type { Embed } from "@app/lib/file";
import { tick } from "svelte";
import { partial } from "lodash";
import * as utils from "@app/lib/utils";
import { partial } from "lodash";
import { tick } from "svelte";
import CommentComponent from "@app/components/Comment.svelte";
import CommentToggleInput from "@app/components/CommentToggleInput.svelte";
@@ -36,6 +36,7 @@
};
export let rawPath: string;
export let enableAttachments: boolean = false;
export let canEditComment: (author: string) => true | undefined;
export let editComment:
| ((commentId: string, body: string, embeds: Embed[]) => Promise<void>)
| undefined;
@@ -96,7 +97,9 @@
timestamp={root.timestamp}
disableEdit={root.embeds.length > 0}
body={root.body}
editComment={editComment && partial(editComment, root.id)}
editComment={editComment &&
canEditComment(root.author.id) &&
partial(editComment, root.id)}
handleReaction={handleReaction && partial(handleReaction, root.id)}>
<IconSmall name="chat" slot="icon" />
</CommentComponent>
@@ -116,7 +119,9 @@
timestamp={reply.timestamp}
disableEdit={reply.embeds.length > 0}
body={reply.body}
editComment={editComment && partial(editComment, reply.id)}
editComment={editComment &&
canEditComment(reply.author.id) &&
partial(editComment, reply.id)}
handleReaction={handleReaction &&
partial(handleReaction, reply.id)} />
{/each}
+36
View File
@@ -0,0 +1,36 @@
import { parseNodeId } from "@app/lib/utils";
export function isDelegate(
publicKey: string | undefined,
delegates: string[],
): true | undefined {
if (!publicKey) {
return undefined;
}
return (
delegates.some(delegate => parseNodeId(delegate)?.pubkey === publicKey) ||
undefined
);
}
function matchAuthor(
publicKey: string | undefined,
nid: string,
): true | undefined {
// Normalize the passed in NID
const parsedNid = parseNodeId(nid);
return (
(publicKey && parsedNid && parsedNid.pubkey === publicKey) || undefined
);
}
// All restricted actions are a combination of either:
// - the user is a delegate
// - the user is an author of the comment, issue, patch, etc.
export function isDelegateOrAuthor(
publicKey: string | undefined,
delegates: string[],
author: string,
) {
return isDelegate(publicKey, delegates) || matchAuthor(publicKey, author);
}
+2
View File
@@ -45,6 +45,7 @@
export let previousRevId: string | undefined = undefined;
export let previousRevOid: string | undefined = undefined;
export let first: boolean;
export let canEditComment: (author: string) => true | undefined;
export let editComment:
| ((commentId: string, body: string, embeds: Embed[]) => Promise<void>)
| undefined;
@@ -431,6 +432,7 @@
enableAttachments
rawPath={utils.getRawBasePath(projectId, baseUrl, projectHead)}
thread={element.inner}
{canEditComment}
{editComment}
{createReply}
{handleReaction} />
+29 -11
View File
@@ -6,6 +6,7 @@
import { isEqual, uniqBy, partial } from "lodash";
import * as modal from "@app/lib/modal";
import * as role from "@app/lib/roles";
import * as router from "@app/lib/router";
import * as utils from "@app/lib/utils";
import { HttpdClient } from "@httpd-client";
@@ -442,7 +443,13 @@
<CobHeader
id={issue.id}
title={issue.title}
editTitle={session && partial(editTitle, session.id)}
editTitle={session &&
role.isDelegateOrAuthor(
session.publicKey,
project.delegates,
issue.author.id,
) &&
partial(editTitle, session.id)}
on:refresh={refreshIssue}>
<svelte:fragment slot="icon">
<div
@@ -465,7 +472,7 @@
{/if}
</svelte:fragment>
<div slot="description">
{#if session && descriptionState !== "read"}
{#if descriptionState !== "read"}
<ExtendedTextarea
enableAttachments
body={issue.discussion[0].body}
@@ -492,7 +499,7 @@
baseUrl,
project.head,
)} />
{#if session}
{#if role.isDelegateOrAuthor(session?.publicKey, project.delegates, issue.author.id)}
<IconButton
title="edit description"
on:click={() => (descriptionState = "edit")}>
@@ -537,6 +544,11 @@
enableAttachments
{thread}
{rawPath}
canEditComment={partial(
role.isDelegateOrAuthor,
session?.publicKey,
project.delegates,
)}
editComment={session && partial(editComment, session.id)}
createReply={session && partial(createReply, session.id)}
handleReaction={session && partial(handleReaction, session)} />
@@ -547,19 +559,23 @@
<CommentToggleInput
placeholder="Leave your comment"
enableAttachments
submit={session && partial(createComment, session.id)} />
submit={partial(createComment, session.id)} />
<div style:display="flex">
<CobStateButton
items={items.filter(([, state]) => !isEqual(state, issue.state))}
{selectedItem}
state={issue.state}
save={session && partial(saveStatus, session.id)} />
{#if role.isDelegateOrAuthor(session.publicKey, project.delegates, issue.author.id)}
<CobStateButton
items={items.filter(([, state]) => !isEqual(state, issue.state))}
{selectedItem}
state={issue.state}
save={partial(saveStatus, session.id)} />
{/if}
</div>
{/if}
</div>
<div class="metadata">
<AssigneeInput
locallyAuthenticated={Boolean(session)}
locallyAuthenticated={Boolean(
role.isDelegate(session?.publicKey, project.delegates),
)}
assignees={issue.assignees}
submitInProgress={assigneeState === "submit"}
on:save={async ({ detail: newAssignees }) => {
@@ -574,7 +590,9 @@
await refreshIssue();
}} />
<LabelInput
locallyAuthenticated={Boolean(session)}
locallyAuthenticated={Boolean(
role.isDelegate(session?.publicKey, project.delegates),
)}
labels={issue.labels}
submitInProgress={labelState === "submit"}
on:save={async ({ detail: newLabels }) => {
+34 -14
View File
@@ -42,6 +42,7 @@
import type { ComponentProps } from "svelte";
import * as modal from "@app/lib/modal";
import * as role from "@app/lib/roles";
import * as router from "@app/lib/router";
import * as utils from "@app/lib/utils";
import { HttpdClient } from "@httpd-client";
@@ -650,7 +651,13 @@
<CobHeader
id={patch.id}
title={patch.title}
editTitle={session && partial(editTitle, session.id)}
editTitle={session &&
role.isDelegateOrAuthor(
session.publicKey,
project.delegates,
patch.author.id,
) &&
partial(editTitle, session.id)}
on:refresh={refreshPatch}>
<svelte:fragment slot="icon">
<div
@@ -697,7 +704,7 @@
{:else}
<span class="txt-missing">No description available</span>
{/if}
{#if session && descriptionState === "read"}
{#if role.isDelegateOrAuthor(session?.publicKey, project.delegates, patch.author.id) && descriptionState === "read"}
<div class="edit-buttons">
<IconButton
title="edit description"
@@ -755,12 +762,12 @@
</Radio>
<div style:margin-left="auto">
{#if session && view.name === "activity"}
{#if session && role.isDelegateOrAuthor(session.publicKey, project.delegates, patch.author.id) && view.name === "activity"}
<CobStateButton
items={items.filter(([, state]) => !isEqual(state, patch.state))}
{selectedItem}
state={patch.state}
save={session && partial(saveStatus, session.id)} />
save={partial(saveStatus, session.id)} />
{/if}
{#if view.name === "commits" || view.name === "changes"}
<div style="margin-left: auto;">
@@ -846,6 +853,11 @@
projectHead={project.head}
{...revision}
first={index === 0}
canEditComment={partial(
role.isDelegateOrAuthor,
session?.publicKey,
project.delegates,
)}
editComment={session &&
partial(editComment, session.id, revision.revisionId)}
handleReaction={session &&
@@ -863,17 +875,22 @@
<CommentToggleInput
enableAttachments
placeholder="Leave your comment"
submit={session &&
partial(createComment, session.id, revision.revisionId)} />
submit={partial(
createComment,
session.id,
revision.revisionId,
)} />
<div class="connector" />
<div style="display: flex;">
<CobStateButton
items={items.filter(
([, state]) => !isEqual(state, patch.state),
)}
{selectedItem}
state={patch.state}
save={session && partial(saveStatus, session.id)} />
{#if role.isDelegateOrAuthor(session.publicKey, project.delegates, patch.author.id)}
<CobStateButton
items={items.filter(
([, state]) => !isEqual(state, patch.state),
)}
{selectedItem}
state={patch.state}
save={partial(saveStatus, session.id)} />
{/if}
</div>
{/if}
{/if}
@@ -932,7 +949,10 @@
</div>
</div>
<LabelInput
locallyAuthenticated={Boolean(session)}
locallyAuthenticated={role.isDelegate(
session?.publicKey,
project.delegates,
)}
submitInProgress={labelState === "submit"}
labels={patch.labels}
on:save={async ({ detail: newLabels }) => {