Skip to content

Commit

Permalink
Copy permissions and encrypted to new key
Browse files Browse the repository at this point in the history
  • Loading branch information
hiroasano committed Sep 11, 2024
1 parent 3449793 commit f4ef0e9
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CopyButton } from "@/components/dashboard/copy-button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Code } from "@/components/ui/code";
import { getTenantId } from "@/lib/auth";
import { type Key, and, db, eq, isNull, schema } from "@/lib/db";
import { type EncryptedKey, Key, Permission, Role, and, db, eq, isNull, schema } from "@/lib/db";
import { getLastUsed } from "@/lib/tinybird";
import { ArrowLeft } from "lucide-react";
import Link from "next/link";
Expand Down Expand Up @@ -34,7 +34,6 @@ export default async function SettingsPage(props: Props) {
with: {
workspace: true,
encrypted: true,
identity: true,
roles: true,
permissions: true,
},
Expand Down Expand Up @@ -79,7 +78,7 @@ export default async function SettingsPage(props: Props) {
</Card>
<RerollKey
apiId={props.params.apiId}
apiKey={key as unknown as Key & { roles: [] }}
apiKey={key as unknown as Key & { roles: Role[], permissions: Permission[], encrypted: EncryptedKey }}
lastUsed={lastUsed}
/>
<DeleteKey apiKey={key} keyAuthId={key.keyAuthId} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { trpc } from "@/lib/trpc/client";
import { parseTrpcError } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
import type { Key } from "@unkey/db";
import type { EncryptedKey, Key, Permission, Role } from "@unkey/db";
import ms from "ms";
import { useState } from "react";
import { useForm } from "react-hook-form";
Expand All @@ -32,7 +32,9 @@ import { RerollNewKeyDialog } from "./reroll-new-key-dialog";
type Props = {
apiId: string;
apiKey: Key & {
roles: [];
roles: Role[];
permissions: Permission[];
encrypted: EncryptedKey;
};
lastUsed: number;
};
Expand Down Expand Up @@ -71,7 +73,23 @@ export const RerollKey: React.FC<Props> = ({ apiKey, apiId, lastUsed }: Props) =
},
});

const updateNewKey = trpc.rbac.connectRoleToKey.useMutation({
const copyRolesToNewKey = trpc.rbac.connectRoleToKey.useMutation({
onError(err) {
console.error(err);
const message = parseTrpcError(err);
toast.error(message);
},
});

const copyPermissionsToNewKey = trpc.rbac.addPermissionToKey.useMutation({
onError(err) {
console.error(err);
const message = parseTrpcError(err);
toast.error(message);
},
});

const copyEncryptedToNewKey = trpc.key.update.encrypted.useMutation({
onError(err) {
console.error(err);
const message = parseTrpcError(err);
Expand Down Expand Up @@ -119,10 +137,18 @@ export const RerollKey: React.FC<Props> = ({ apiKey, apiId, lastUsed }: Props) =
refill,
});

apiKey.roles?.forEach(async (role: { roleId: string }) => {
await updateNewKey.mutateAsync({ roleId: role.roleId, keyId: newKey.keyId });
apiKey.roles?.forEach(async (role) => {
await copyRolesToNewKey.mutateAsync({ roleId: role.id, keyId: newKey.keyId });
});

apiKey.permissions?.forEach(async (permission) => {
await copyPermissionsToNewKey.mutateAsync({ permission: permission.name, keyId: newKey.keyId, });
})

if (apiKey.encrypted) {
await copyEncryptedToNewKey.mutateAsync({ encrypted: apiKey.encrypted.encrypted, encryptiodKeyId: apiKey.encrypted.encryptionKeyId, keyId: newKey.keyId })
}

const miliseconds = values.expiresIn === "now" ? 0 : ms(values.expiresIn);
const deletedAt = new Date(Date.now() + miliseconds);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
"use client";

import { CopyButton } from "@/components/dashboard/copy-button";
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { toast } from "@/components/ui/toaster";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { trpc } from "@/lib/trpc/client";
import { parseTrpcError } from "@/lib/utils";
import { Loader2 } from "lucide-react";
Expand Down Expand Up @@ -32,7 +30,7 @@ export const PermissionToggle: React.FC<Props> = ({
const router = useRouter();

const [optimisticChecked, setOptimisticChecked] = useState(checked);
const addPermission = trpc.rbac.addPermissionToRootKey.useMutation({
const addPermission = trpc.rbac.addPermissionToKey.useMutation({
onMutate: () => {
setOptimisticChecked(true);
},
Expand Down Expand Up @@ -60,7 +58,7 @@ export const PermissionToggle: React.FC<Props> = ({
cancel: {
label: "Undo",
onClick: () => {
addPermission.mutate({ rootKeyId, permission: permissionName });
addPermission.mutate({ keyId: rootKeyId, permission: permissionName });
},
},
});
Expand Down Expand Up @@ -98,7 +96,7 @@ export const PermissionToggle: React.FC<Props> = ({
}
} else {
if (!preventEnabling) {
addPermission.mutate({ rootKeyId, permission: permissionName });
addPermission.mutate({ keyId: rootKeyId, permission: permissionName });
}
}
}}
Expand Down
6 changes: 4 additions & 2 deletions apps/dashboard/lib/trpc/routers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { deleteKeys } from "./key/delete";
import { deleteRootKeys } from "./key/deleteRootKey";
import { updateKeyDeletedAt } from "./key/updateDeletedAt";
import { updateKeyEnabled } from "./key/updateEnabled";
import { updateKeyEncrypted } from "./key/updateEncrypted";
import { updateKeyExpiration } from "./key/updateExpiration";
import { updateKeyMetadata } from "./key/updateMetadata";
import { updateKeyName } from "./key/updateName";
Expand All @@ -27,7 +28,7 @@ import { deleteNamespace } from "./ratelimit/deleteNamespace";
import { deleteOverride } from "./ratelimit/deleteOverride";
import { updateNamespaceName } from "./ratelimit/updateNamespaceName";
import { updateOverride } from "./ratelimit/updateOverride";
import { addPermissionToRootKey } from "./rbac/addPermissionToRootKey";
import { addPermissionToKey } from "./rbac/addPermissionToKey";
import { connectPermissionToRole } from "./rbac/connectPermissionToRole";
import { connectRoleToKey } from "./rbac/connectRoleToKey";
import { createPermission } from "./rbac/createPermission";
Expand Down Expand Up @@ -68,6 +69,7 @@ export const router = t.router({
update: t.router({
deletedAt: updateKeyDeletedAt,
enabled: updateKeyEnabled,
encrypted: updateKeyEncrypted,
expiration: updateKeyExpiration,
metadata: updateKeyMetadata,
name: updateKeyName,
Expand Down Expand Up @@ -110,7 +112,7 @@ export const router = t.router({
createIssue: createPlainIssue,
}),
rbac: t.router({
addPermissionToRootKey: addPermissionToRootKey,
addPermissionToKey: addPermissionToKey,
connectPermissionToRole: connectPermissionToRole,
connectRoleToKey: connectRoleToKey,
createPermission: createPermission,
Expand Down
70 changes: 70 additions & 0 deletions apps/dashboard/lib/trpc/routers/key/updateEncrypted.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { db, schema } from "@/lib/db";
import { ingestAuditLogs } from "@/lib/tinybird";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { auth, t } from "../../trpc";

export const updateKeyEncrypted = t.procedure
.use(auth)
.input(
z.object({
keyId: z.string(),
encrypted: z.string(),
encryptiodKeyId: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
const key = await db.query.keys.findFirst({
where: (table, { eq, isNull, and }) =>
and(eq(table.id, input.keyId), isNull(table.deletedAt)),
with: {
workspace: true,
},
});
if (!key || key.workspace.tenantId !== ctx.tenant.id) {
throw new TRPCError({
message:
"We are unable to find the correct key. Please contact support using [email protected].",
code: "NOT_FOUND",
});
}

const tuple = {
keyId: input.keyId,
encrypted: input.encrypted,
encryptionKeyId: input.encryptiodKeyId,
workspaceId: ctx.tenant.id,
}
await db
.insert(schema.encryptedKeys)
.values({ ...tuple })
.onDuplicateKeyUpdate({ set: { ...tuple }})
.catch((_err) => {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message:
"We are unable to update key encrypted. Please contact support using [email protected]",
});
});

await ingestAuditLogs({
workspaceId: key.workspace.id,
actor: {
type: "user",
id: ctx.user.id,
},
event: "key.update",
description: `Created a encrypted relation to ${key.id}`,
resources: [
{
type: "key",
id: key.id,
},
],
context: {
location: ctx.audit.location,
userAgent: ctx.audit.userAgent,
},
});
return true;
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { z } from "zod";
import { auth, t } from "../../trpc";
import { upsertPermissions } from "../rbac";

export const addPermissionToRootKey = t.procedure
export const addPermissionToKey = t.procedure
.use(auth)
.input(
z.object({
rootKeyId: z.string(),
keyId: z.string(),
permission: z.string(),
}),
)
Expand All @@ -35,9 +35,9 @@ export const addPermissionToRootKey = t.procedure
});
}

const rootKey = await db.query.keys.findFirst({
const key = await db.query.keys.findFirst({
where: (table, { eq, and }) =>
and(eq(table.forWorkspaceId, workspace.id), eq(table.id, input.rootKeyId)),
and(eq(table.forWorkspaceId, workspace.id), eq(table.id, input.keyId)),
with: {
permissions: {
with: {
Expand All @@ -46,22 +46,22 @@ export const addPermissionToRootKey = t.procedure
},
},
});
if (!rootKey) {
if (!key) {
throw new TRPCError({
code: "NOT_FOUND",
message:
"We are unable to find the correct root key. Please contact support using [email protected].",
});
}

const { permissions, auditLogs } = await upsertPermissions(ctx, rootKey.workspaceId, [
const { permissions, auditLogs } = await upsertPermissions(ctx, key.workspaceId, [
permission.data,
]);
const p = permissions[0];
await db
.insert(schema.keysPermissions)
.values({
keyId: rootKey.id,
keyId: key.id,
permissionId: p.id,
workspaceId: p.workspaceId,
})
Expand All @@ -80,11 +80,11 @@ export const addPermissionToRootKey = t.procedure
workspaceId: workspace.id,
actor: { type: "user", id: ctx.user.id },
event: "authorization.connect_permission_and_key",
description: `Attached ${p.id} to ${rootKey.id}`,
description: `Attached ${p.id} to ${key.id}`,
resources: [
{
type: "key",
id: rootKey.id,
id: key.id,
},
{
type: "permission",
Expand Down

0 comments on commit f4ef0e9

Please sign in to comment.