mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-07 00:39:53 +00:00
♻️ create table for resource policies associations with users
This commit is contained in:
@@ -1251,7 +1251,8 @@
|
||||
"sidebarResources": "Resources",
|
||||
"sidebarProxyResources": "Public",
|
||||
"sidebarClientResources": "Private",
|
||||
"sidebarResourcePolicies": "Policies",
|
||||
"sidebarPolicies": "Policies",
|
||||
"sidebarResourcePolicies": "Resources",
|
||||
"sidebarAccessControl": "Access Control",
|
||||
"sidebarLogsAndAnalytics": "Logs & Analytics",
|
||||
"sidebarUsers": "Users",
|
||||
|
||||
@@ -94,8 +94,10 @@ export const sites = pgTable("sites", {
|
||||
|
||||
export const resources = pgTable("resources", {
|
||||
resourceId: serial("resourceId").primaryKey(),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId").references(
|
||||
() => resourcePolicies.resourcePolicyId,
|
||||
{ onDelete: "cascade" }
|
||||
),
|
||||
resourceGuid: varchar("resourceGuid", { length: 36 })
|
||||
.unique()
|
||||
.notNull()
|
||||
@@ -420,10 +422,7 @@ export const roleResources = pgTable("roleResources", {
|
||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||
resourceId: integer("resourceId")
|
||||
.notNull()
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||
});
|
||||
|
||||
export const userResources = pgTable("userResources", {
|
||||
@@ -432,10 +431,29 @@ export const userResources = pgTable("userResources", {
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
resourceId: integer("resourceId")
|
||||
.notNull()
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
||||
});
|
||||
|
||||
export const rolePolicies = pgTable("rolePolicies", {
|
||||
roleId: integer("roleId")
|
||||
.notNull()
|
||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const userPolicies = pgTable("userPolicies", {
|
||||
userId: varchar("userId")
|
||||
.notNull()
|
||||
.references(() => users.userId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const userInvites = pgTable("userInvites", {
|
||||
@@ -460,7 +478,9 @@ export const resourcePincode = pgTable("resourcePincode", {
|
||||
digitLength: integer("digitLength").notNull(),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const resourcePassword = pgTable("resourcePassword", {
|
||||
@@ -471,7 +491,9 @@ export const resourcePassword = pgTable("resourcePassword", {
|
||||
passwordHash: varchar("passwordHash").notNull(),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const resourceHeaderAuth = pgTable("resourceHeaderAuth", {
|
||||
@@ -482,7 +504,9 @@ export const resourceHeaderAuth = pgTable("resourceHeaderAuth", {
|
||||
headerAuthHash: varchar("headerAuthHash").notNull(),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const resourceHeaderAuthExtendedCompatibility = pgTable(
|
||||
@@ -496,7 +520,9 @@ export const resourceHeaderAuthExtendedCompatibility = pgTable(
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
extendedCompatibilityIsActivated: boolean(
|
||||
"extendedCompatibilityIsActivated"
|
||||
)
|
||||
@@ -571,7 +597,9 @@ export const resourceWhitelist = pgTable("resourceWhitelist", {
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
});
|
||||
|
||||
export const resourceOtp = pgTable("resourceOtp", {
|
||||
@@ -581,7 +609,9 @@ export const resourceOtp = pgTable("resourceOtp", {
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
email: varchar("email").notNull(),
|
||||
otpHash: varchar("otpHash").notNull(),
|
||||
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
|
||||
@@ -599,7 +629,9 @@ export const resourceRules = pgTable("resourceRules", {
|
||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, { onDelete: "cascade" }),
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
priority: integer("priority").notNull(),
|
||||
action: varchar("action").notNull(), // ACCEPT, DROP, PASS
|
||||
@@ -607,21 +639,40 @@ export const resourceRules = pgTable("resourceRules", {
|
||||
value: varchar("value").notNull()
|
||||
});
|
||||
|
||||
export const policyRules = pgTable("policyRules", {
|
||||
ruleId: serial("ruleId").primaryKey(),
|
||||
resourcePolicyId: integer("resourcePolicyId")
|
||||
.notNull()
|
||||
.references(() => resourcePolicies.resourcePolicyId, {
|
||||
onDelete: "cascade"
|
||||
}),
|
||||
enabled: boolean("enabled").notNull().default(true),
|
||||
priority: integer("priority").notNull(),
|
||||
action: varchar("action").$type<"ACCEPT" | "DROP" | "PASS">().notNull(),
|
||||
match: varchar("match").$type<"CIDR" | "PATH" | "IP">().notNull(),
|
||||
value: varchar("value").notNull()
|
||||
});
|
||||
|
||||
export const resourcePolicies = pgTable("resourcePolicies", {
|
||||
resourcePolicyId: serial('resourcePolicyId').primaryKey(),
|
||||
resourcePolicyId: serial("resourcePolicyId").primaryKey(),
|
||||
sso: boolean("sso").notNull().default(true),
|
||||
emailWhitelistEnabled: boolean("emailWhitelistEnabled").notNull().default(false),
|
||||
scope: varchar("scope")
|
||||
.$type<"global" | "resource">()
|
||||
.notNull()
|
||||
.default("global"),
|
||||
emailWhitelistEnabled: boolean("emailWhitelistEnabled")
|
||||
.notNull()
|
||||
.default(false),
|
||||
idpId: integer("idpId").references(() => idp.idpId, {
|
||||
onDelete: "set null"
|
||||
}),
|
||||
niceId: text("niceId").notNull(),
|
||||
isDefault: boolean("isDefault").notNull().default(true),
|
||||
name: varchar("name").notNull(),
|
||||
orgId: varchar("orgId")
|
||||
.references(() => orgs.orgId, {
|
||||
onDelete: "cascade"
|
||||
})
|
||||
.notNull(),
|
||||
.notNull()
|
||||
});
|
||||
|
||||
export const supporterKey = pgTable("supporterKey", {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* This file is not licensed under the AGPLv3.
|
||||
*/
|
||||
|
||||
import { db, resourcePolicies, roleResources, userResources } from "@server/db";
|
||||
import { db, resourcePolicies, rolePolicies, userPolicies } from "@server/db";
|
||||
import response from "@server/lib/response";
|
||||
import logger from "@server/logger";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
@@ -51,8 +51,7 @@ function queryResourcePoliciesBase() {
|
||||
resourcePolicyId: resourcePolicies.resourcePolicyId,
|
||||
name: resourcePolicies.name,
|
||||
niceId: resourcePolicies.niceId,
|
||||
orgId: resourcePolicies.orgId,
|
||||
isDefault: resourcePolicies.isDefault
|
||||
orgId: resourcePolicies.orgId
|
||||
})
|
||||
.from(resourcePolicies);
|
||||
}
|
||||
@@ -124,20 +123,20 @@ export async function listResourcePolicies(
|
||||
if (req.user) {
|
||||
accessibleResourcePolicies = await db
|
||||
.select({
|
||||
resourcePolicyId: sql<number>`COALESCE(${userResources.resourcePolicyId}, ${roleResources.resourcePolicyId})`
|
||||
resourcePolicyId: sql<number>`COALESCE(${userPolicies.resourcePolicyId}, ${rolePolicies.resourcePolicyId})`
|
||||
})
|
||||
.from(userResources)
|
||||
.from(userPolicies)
|
||||
.fullJoin(
|
||||
roleResources,
|
||||
rolePolicies,
|
||||
eq(
|
||||
userResources.resourcePolicyId,
|
||||
roleResources.resourcePolicyId
|
||||
userPolicies.resourcePolicyId,
|
||||
rolePolicies.resourcePolicyId
|
||||
)
|
||||
)
|
||||
.where(
|
||||
or(
|
||||
eq(userResources.userId, req.user!.userId),
|
||||
eq(roleResources.roleId, req.userOrgRoleId!)
|
||||
eq(userPolicies.userId, req.user!.userId),
|
||||
eq(rolePolicies.roleId, req.userOrgRoleId!)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@@ -159,7 +158,8 @@ export async function listResourcePolicies(
|
||||
resourcePolicies.resourcePolicyId,
|
||||
accessibleResourceIds
|
||||
),
|
||||
eq(resourcePolicies.orgId, orgId)
|
||||
eq(resourcePolicies.orgId, orgId),
|
||||
eq(resourcePolicies.scope, "global")
|
||||
)
|
||||
];
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@ export type GetMaintenanceInfoResponse = {
|
||||
|
||||
export type ListResourcePoliciesResponse = PaginatedResponse<{
|
||||
policies: Array<
|
||||
Pick<
|
||||
ResourcePolicy,
|
||||
"resourcePolicyId" | "niceId" | "name" | "orgId" | "isDefault"
|
||||
>
|
||||
Pick<ResourcePolicy, "resourcePolicyId" | "niceId" | "name" | "orgId">
|
||||
>;
|
||||
}>;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
CreditCard,
|
||||
Fingerprint,
|
||||
Globe,
|
||||
GlobeIcon,
|
||||
GlobeLock,
|
||||
KeyRound,
|
||||
Laptop,
|
||||
@@ -63,18 +64,7 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
|
||||
title: "sidebarClientResources",
|
||||
href: "/{orgId}/settings/resources/client",
|
||||
icon: <GlobeLock className="size-4 flex-none" />
|
||||
},
|
||||
...(build !== "oss"
|
||||
? [
|
||||
{
|
||||
title: "sidebarResourcePolicies",
|
||||
href: "/{orgId}/settings/resources/policies",
|
||||
icon: (
|
||||
<ShieldIcon className="size-4 flex-none" />
|
||||
)
|
||||
}
|
||||
]
|
||||
: [])
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -133,6 +123,24 @@ export const orgNavSections = (env?: Env): SidebarNavSection[] => [
|
||||
href: "/{orgId}/settings/access/roles",
|
||||
icon: <Users className="size-4 flex-none" />
|
||||
},
|
||||
...(build !== "oss"
|
||||
? [
|
||||
{
|
||||
title: "sidebarPolicies",
|
||||
|
||||
icon: <ShieldIcon className="size-4 flex-none" />,
|
||||
items: [
|
||||
{
|
||||
title: "sidebarResourcePolicies",
|
||||
href: "/{orgId}/settings/policies/resources",
|
||||
icon: (
|
||||
<GlobeIcon className="size-4 flex-none" />
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
: []),
|
||||
// PaidFeaturesAlert
|
||||
...((build === "oss" && !env?.flags.disableEnterpriseFeatures) ||
|
||||
build === "saas" ||
|
||||
|
||||
@@ -72,24 +72,7 @@ export function ResourcePoliciesTable({
|
||||
enableHiding: false,
|
||||
friendlyName: t("name"),
|
||||
header: () => <span className="p-3">{t("name")}</span>,
|
||||
cell({ row }) {
|
||||
const r = row.original;
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>{r.name}</span>
|
||||
{r.isDefault && (
|
||||
<>
|
||||
<Badge
|
||||
variant="outlinePrimary"
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{t("resourcePoliciesDefaultBadgeText")}
|
||||
</Badge>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
cell: ({ row }) => <span>{row.original.name}</span>
|
||||
},
|
||||
{
|
||||
id: "niceId",
|
||||
@@ -183,7 +166,7 @@ export function ResourcePoliciesTable({
|
||||
onAdd={() =>
|
||||
startNavigation(() =>
|
||||
router.push(
|
||||
`/${orgId}/settings/resources/policies/create`
|
||||
`/${orgId}/settings/policies/resources/create`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user