diff --git a/apps/api/src/pkg/keys/service.ts b/apps/api/src/pkg/keys/service.ts index 8ff72f4def..124cf0cf54 100644 --- a/apps/api/src/pkg/keys/service.ts +++ b/apps/api/src/pkg/keys/service.ts @@ -277,9 +277,9 @@ export class KeyService { * through a role. */ const permissions = new Set([ - ...dbRes.permissions.filter((p) => p.permission).map((p) => p.permission.name), + ...dbRes.permissions.filter((p) => p.permission).map((p) => p.permission.slug), ...dbRes.roles.flatMap((r) => - r.role.permissions.filter((p) => p.permission).map((p) => p.permission.name), + r.role.permissions.filter((p) => p.permission).map((p) => p.permission.slug), ), ]); diff --git a/apps/api/src/routes/v1_apis_listKeys.ts b/apps/api/src/routes/v1_apis_listKeys.ts index f9ef7e78a3..70a875919d 100644 --- a/apps/api/src/routes/v1_apis_listKeys.ts +++ b/apps/api/src/routes/v1_apis_listKeys.ts @@ -219,8 +219,8 @@ export const registerV1ApisListKeys = (app: App) => return { keys: keySpace.keys.map((k) => { const permissions = new Set([ - ...k.permissions.map((p) => p.permission.name), - ...k.roles.flatMap((r) => r.role.permissions.map((p) => p.permission.name)), + ...k.permissions.map((p) => p.permission.slug), + ...k.roles.flatMap((r) => r.role.permissions.map((p) => p.permission.slug)), ]); return { ...k, diff --git a/apps/api/src/routes/v1_keys_removePermissions.happy.test.ts b/apps/api/src/routes/v1_keys_removePermissions.happy.test.ts index 2659d41a16..38f3612624 100644 --- a/apps/api/src/routes/v1_keys_removePermissions.happy.test.ts +++ b/apps/api/src/routes/v1_keys_removePermissions.happy.test.ts @@ -16,10 +16,11 @@ test("removes permission by name", async (t) => { const { keyId } = await h.createKey(); + const slug = randomUUID(); const permission = { id: newId("test"), - name: randomUUID(), - slug: randomUUID(), + name: slug, + slug: slug, workspaceId: h.resources.userWorkspace.id, }; diff --git a/apps/api/src/routes/v1_keys_removePermissions.ts b/apps/api/src/routes/v1_keys_removePermissions.ts index 1ba5d54c84..2a98f8508d 100644 --- a/apps/api/src/routes/v1_keys_removePermissions.ts +++ b/apps/api/src/routes/v1_keys_removePermissions.ts @@ -27,11 +27,16 @@ const route = createRoute({ z.object({ id: z.string().min(3).optional().openapi({ description: - "The id of the permission. Provide either `id` or `name`. If both are provided `id` is used.", + "The id of the permission. Provide either `id` or `slug`. If both are provided `id` is used.", }), name: z.string().min(1).optional().openapi({ + deprecated: true, description: - "Identify the permission via its name. Provide either `id` or `name`. If both are provided `id` is used.", + "This field is deprecated and will be removed in a future release. please use `slug` instead.", + }), + slug: z.string().min(1).optional().openapi({ + description: + "Identify the permission via its slug. Provide either `id` or `slug`. If both are provided `id` is used.", }), }), ) @@ -113,8 +118,11 @@ export const registerV1KeysRemovePermissions = (app: App) => if ("id" in deleteRequest) { return cr.permissionId === deleteRequest.id; } + if ("slug" in deleteRequest) { + return cr.permission.slug === deleteRequest.slug; + } if ("name" in deleteRequest) { - return cr.permission.name === deleteRequest.name; + return cr.permission.slug === deleteRequest.name; } } }); diff --git a/apps/api/src/routes/v1_keys_setPermissions.ts b/apps/api/src/routes/v1_keys_setPermissions.ts index 1694a64fe2..b76dee829f 100644 --- a/apps/api/src/routes/v1_keys_setPermissions.ts +++ b/apps/api/src/routes/v1_keys_setPermissions.ts @@ -31,8 +31,13 @@ const route = createRoute({ "The id of the permission. Provide either `id` or `name`. If both are provided `id` is used.", }), name: z.string().min(1).optional().openapi({ + deprecated: true, description: - "Identify the permission via its name. Provide either `id` or `name`. If both are provided `id` is used.", + "This field is deprecated and will be removed in a future release. please use `slug` instead.", + }), + slug: z.string().min(1).optional().openapi({ + description: + "Identify the permission via its slug. Provide either `id` or `slug`. If both are provided `id` is used.", }), create: z .boolean() @@ -118,15 +123,16 @@ export async function setPermissions( requested: Array<{ id?: string; name?: string; + slug?: string; create?: boolean; }>, ): Promise> { const { db, cache, rbac } = c.get("services"); const requestedIds = requested.filter(({ id }) => !!id).map(({ id }) => id!); - const requestedNames = requested - .filter(({ name }) => !!name) - .map(({ name, create }) => ({ name: name!, create: create! })); + const requestedSlugs = requested + .filter(({ name, slug }) => !!name || !!slug) + .map(({ name, slug, create }) => ({ slug: slug ?? name!, create: create! })); const [key, existingPermissions, connectedPermissions] = await Promise.all([ db.primary.query.keys.findFirst({ @@ -143,10 +149,10 @@ export async function setPermissions( eq(table.workspaceId, auth.authorizedWorkspaceId), or( requestedIds.length > 0 ? inArray(table.id, requestedIds) : undefined, - requestedNames.length > 0 + requestedSlugs.length > 0 ? inArray( - table.name, - requestedNames.map((n) => n.name), + table.slug, + requestedSlugs.map((r) => r.slug), ) : undefined, ), @@ -159,6 +165,7 @@ export async function setPermissions( permission: { columns: { name: true, + slug: true, }, }, }, @@ -176,7 +183,10 @@ export async function setPermissions( if (requestedIds.includes(r.permissionId)) { return false; } - if (requestedNames.some(({ name }) => name === r.permission.name)) { + if (requestedSlugs.some(({ slug }) => slug === r.permission.slug)) { + return false; + } + if (!r.permission.slug && !r.permission.name) { return false; } return true; @@ -212,7 +222,7 @@ export async function setPermissions( ); } - const missingPermissionNames: string[] = []; + const missingPermissionSlugs: string[] = []; for (const id of requestedIds) { if (!existingPermissions.some((r) => r.id === id)) { throw new UnkeyApiError({ @@ -221,23 +231,23 @@ export async function setPermissions( }); } } - for (const { create, name } of requestedNames) { - if (!existingPermissions.some((r) => r.name === name)) { + for (const { create, slug } of requestedSlugs) { + if (!existingPermissions.some((r) => r.slug === slug)) { if (!create) { throw new UnkeyApiError({ code: "NOT_FOUND", - message: `permission ${name} not found and not allowed to create`, + message: `permission ${slug} not found and not allowed to create`, }); } - missingPermissionNames.push(name); + missingPermissionSlugs.push(slug); } } - const createPermissions = missingPermissionNames.map((name) => ({ + const createPermissions = missingPermissionSlugs.map((slug) => ({ id: newId("permission"), workspaceId: auth.authorizedWorkspaceId, - name, - slug: name, + name: slug, + slug: slug, })); if (createPermissions.length > 0) { const rbacResp = rbac.evaluatePermissions( diff --git a/apps/dashboard/lib/trpc/routers/rbac.ts b/apps/dashboard/lib/trpc/routers/rbac.ts index 5ce2a7e8b7..bb98d99da9 100644 --- a/apps/dashboard/lib/trpc/routers/rbac.ts +++ b/apps/dashboard/lib/trpc/routers/rbac.ts @@ -796,7 +796,7 @@ export const rbacRouter = t.router({ export async function upsertPermissions( ctx: Context, workspaceId: string, - names: string[], + slugs: string[], ): Promise<{ permissions: Permission[]; auditLogs: UnkeyAuditLog[]; @@ -804,14 +804,14 @@ export async function upsertPermissions( return await db.transaction(async (tx) => { const existingPermissions = await tx.query.permissions.findMany({ where: (table, { inArray, and, eq }) => - and(eq(table.workspaceId, workspaceId), inArray(table.name, names)), + and(eq(table.workspaceId, workspaceId), inArray(table.slug, slugs)), }); const newPermissions: InsertPermission[] = []; const auditLogs: UnkeyAuditLog[] = []; - const permissions = names.map((name) => { - const existingPermission = existingPermissions.find((p) => p.name === name); + const permissions = slugs.map((slug) => { + const existingPermission = existingPermissions.find((p) => p.slug === slug); if (existingPermission) { return existingPermission; @@ -820,8 +820,8 @@ export async function upsertPermissions( const permission: Permission = { id: newId("permission"), workspaceId, - name, - slug: name, + name: slug, + slug: slug, description: null, updatedAtM: null, createdAtM: Date.now(), @@ -837,7 +837,7 @@ export async function upsertPermissions( { type: "permission", id: permission.id, - name: permission.name, + name: permission.slug, }, ], context: { diff --git a/go/pkg/tls/doc.go b/go/pkg/tls/doc.go index 96a6696b04..53a826dbfb 100644 --- a/go/pkg/tls/doc.go +++ b/go/pkg/tls/doc.go @@ -9,6 +9,4 @@ // - Loading TLS certificates and keys from files // - Configuring HTTPS servers with sensible security defaults // - Enabling HTTPS in command-line applications -// -// The package enforces secure defaults such as requiring TLS 1.2 or higher. package tls diff --git a/go/pkg/tls/tls.go b/go/pkg/tls/tls.go index de23e6d6cc..311f838303 100644 --- a/go/pkg/tls/tls.go +++ b/go/pkg/tls/tls.go @@ -84,7 +84,7 @@ func New(certPEMBlock, keyPEMBlock []byte) (*tls.Config, error) { // The keyFile parameter should be the path to a PEM-encoded private key file. // // The function reads both files, verifies their content, and creates a properly -// configured TLS configuration with secure defaults (TLS 1.2+). +// configured TLS configuration with secure defaults (TLS 1.3+). // // If the files cannot be read or contain invalid certificate/key data, the function // returns a descriptive error. Common error cases include non-existent files, permission diff --git a/go/pkg/tls/tls_test.go b/go/pkg/tls/tls_test.go index dffd32d08a..446977b56c 100644 --- a/go/pkg/tls/tls_test.go +++ b/go/pkg/tls/tls_test.go @@ -100,8 +100,8 @@ func TestNewWithValidCertificateAndKey(t *testing.T) { require.NoError(t, err) // A valid TLS config should have certificates require.NotEmpty(t, tlsConfig.Certificates) - // Check TLS version is at least 1.2 - require.Equal(t, uint16(0x0303), tlsConfig.MinVersion) // TLS 1.2 + // Check TLS version is at least 1.3 + require.Equal(t, uint16(0x0304), tlsConfig.MinVersion) // TLS 1.3 } // TestNewWithEmptyCertificate verifies that the New function returns an appropriate @@ -177,8 +177,8 @@ func TestNewFromFilesWithValidFiles(t *testing.T) { require.NoError(t, err) // A valid TLS config should have certificates require.NotEmpty(t, tlsConfig.Certificates) - // Check TLS version is at least 1.2 - require.Equal(t, uint16(0x0303), tlsConfig.MinVersion) // TLS 1.2 + // Check TLS version is at least 1.3 + require.Equal(t, uint16(0x0304), tlsConfig.MinVersion) // TLS 1.3 } // TestNewFromFilesWithNonExistentCertificate verifies that the NewFromFiles function diff --git a/internal/db/src/schema/rbac.ts b/internal/db/src/schema/rbac.ts index f9a650a5f3..84932016d7 100644 --- a/internal/db/src/schema/rbac.ts +++ b/internal/db/src/schema/rbac.ts @@ -17,9 +17,8 @@ export const permissions = mysqlTable( id: varchar("id", { length: 256 }).primaryKey(), workspaceId: varchar("workspace_id", { length: 256 }).notNull(), name: varchar("name", { length: 512 }).notNull(), - slug: varchar("slug", { length: 128 }), + slug: varchar("slug", { length: 128 }).notNull(), description: varchar("description", { length: 512 }), - createdAtM: bigint("created_at_m", { mode: "number" }) .notNull() .default(0)