mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-17 22:44:42 +00:00
Adjust verify session queries to use policies
This commit is contained in:
@@ -17,10 +17,13 @@ import {
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
ResourceHeaderAuth,
|
ResourceHeaderAuth,
|
||||||
resourceRules,
|
resourceRules,
|
||||||
|
resourcePolicyRules,
|
||||||
resources,
|
resources,
|
||||||
roleResources,
|
roleResources,
|
||||||
|
rolePolicies,
|
||||||
sessions,
|
sessions,
|
||||||
userResources,
|
userResources,
|
||||||
|
userPolicies,
|
||||||
users,
|
users,
|
||||||
ResourceHeaderAuthExtendedCompatibility,
|
ResourceHeaderAuthExtendedCompatibility,
|
||||||
resourceHeaderAuthExtendedCompatibility
|
resourceHeaderAuthExtendedCompatibility
|
||||||
@@ -154,58 +157,126 @@ export async function getRoleName(roleId: number): Promise<string | null> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if role has access to resource
|
* Check if role has access to resource (direct or via resource policy)
|
||||||
*/
|
*/
|
||||||
export async function getRoleResourceAccess(
|
export async function getRoleResourceAccess(
|
||||||
resourceId: number,
|
resourceId: number,
|
||||||
roleIds: number[]
|
roleIds: number[]
|
||||||
) {
|
) {
|
||||||
const roleResourceAccess = await db
|
const [direct, viaPolicies] = await Promise.all([
|
||||||
.select()
|
db
|
||||||
.from(roleResources)
|
.select()
|
||||||
.where(
|
.from(roleResources)
|
||||||
and(
|
.where(
|
||||||
eq(roleResources.resourceId, resourceId),
|
and(
|
||||||
inArray(roleResources.roleId, roleIds)
|
eq(roleResources.resourceId, resourceId),
|
||||||
|
inArray(roleResources.roleId, roleIds)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
roleId: rolePolicies.roleId,
|
||||||
|
resourcePolicyId: rolePolicies.resourcePolicyId
|
||||||
|
})
|
||||||
|
.from(rolePolicies)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(resources.resourcePolicyId, rolePolicies.resourcePolicyId)
|
||||||
)
|
)
|
||||||
);
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resources.resourceId, resourceId),
|
||||||
|
inArray(rolePolicies.roleId, roleIds)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
return roleResourceAccess.length > 0 ? roleResourceAccess : null;
|
const combined = [...direct, ...viaPolicies];
|
||||||
|
return combined.length > 0 ? combined : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if user has direct access to resource
|
* Check if user has access to resource (direct or via resource policy)
|
||||||
*/
|
*/
|
||||||
export async function getUserResourceAccess(
|
export async function getUserResourceAccess(
|
||||||
userId: string,
|
userId: string,
|
||||||
resourceId: number
|
resourceId: number
|
||||||
) {
|
) {
|
||||||
const userResourceAccess = await db
|
const [direct, viaPolicies] = await Promise.all([
|
||||||
.select()
|
db
|
||||||
.from(userResources)
|
.select()
|
||||||
.where(
|
.from(userResources)
|
||||||
and(
|
.where(
|
||||||
eq(userResources.userId, userId),
|
and(
|
||||||
eq(userResources.resourceId, resourceId)
|
eq(userResources.userId, userId),
|
||||||
|
eq(userResources.resourceId, resourceId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
.limit(1),
|
||||||
.limit(1);
|
db
|
||||||
|
.select({
|
||||||
|
userId: userPolicies.userId,
|
||||||
|
resourcePolicyId: userPolicies.resourcePolicyId
|
||||||
|
})
|
||||||
|
.from(userPolicies)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(resources.resourcePolicyId, userPolicies.resourcePolicyId)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resources.resourceId, resourceId),
|
||||||
|
eq(userPolicies.userId, userId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1)
|
||||||
|
]);
|
||||||
|
|
||||||
return userResourceAccess.length > 0 ? userResourceAccess[0] : null;
|
return direct[0] ?? viaPolicies[0] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get resource rules for a given resource
|
* Get resource rules for a given resource (direct and via resource policy)
|
||||||
*/
|
*/
|
||||||
export async function getResourceRules(
|
export async function getResourceRules(
|
||||||
resourceId: number
|
resourceId: number
|
||||||
): Promise<ResourceRule[]> {
|
): Promise<ResourceRule[]> {
|
||||||
const rules = await db
|
const [directRules, policyRules] = await Promise.all([
|
||||||
.select()
|
db
|
||||||
.from(resourceRules)
|
.select()
|
||||||
.where(eq(resourceRules.resourceId, resourceId));
|
.from(resourceRules)
|
||||||
|
.where(eq(resourceRules.resourceId, resourceId)),
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
ruleId: resourcePolicyRules.ruleId,
|
||||||
|
resourceId: sql<number>`${resourceId}`,
|
||||||
|
enabled: resourcePolicyRules.enabled,
|
||||||
|
priority: resourcePolicyRules.priority,
|
||||||
|
action: resourcePolicyRules.action,
|
||||||
|
match: resourcePolicyRules.match,
|
||||||
|
value: resourcePolicyRules.value
|
||||||
|
})
|
||||||
|
.from(resourcePolicyRules)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(
|
||||||
|
resources.resourcePolicyId,
|
||||||
|
resourcePolicyRules.resourcePolicyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(eq(resources.resourceId, resourceId))
|
||||||
|
]);
|
||||||
|
|
||||||
return rules;
|
const maxDirectPriority = directRules.reduce(
|
||||||
|
(max, r) => Math.max(max, r.priority),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const offsetPolicyRules = policyRules.map((r) => ({
|
||||||
|
...r,
|
||||||
|
priority: maxDirectPriority + r.priority
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [...directRules, ...offsetPolicyRules] as ResourceRule[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -45,8 +45,11 @@ import {
|
|||||||
users,
|
users,
|
||||||
userOrgs,
|
userOrgs,
|
||||||
roleResources,
|
roleResources,
|
||||||
|
rolePolicies,
|
||||||
userResources,
|
userResources,
|
||||||
|
userPolicies,
|
||||||
resourceRules,
|
resourceRules,
|
||||||
|
resourcePolicyRules,
|
||||||
userOrgRoles,
|
userOrgRoles,
|
||||||
roles
|
roles
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
@@ -430,7 +433,10 @@ hybridRouter.get(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Decrypt and save key file
|
// Decrypt and save key file
|
||||||
const decryptedKey = decrypt(cert.keyFile!, config.getRawConfig().server.secret!);
|
const decryptedKey = decrypt(
|
||||||
|
cert.keyFile!,
|
||||||
|
config.getRawConfig().server.secret!
|
||||||
|
);
|
||||||
|
|
||||||
// Return only the certificate data without org information
|
// Return only the certificate data without org information
|
||||||
return {
|
return {
|
||||||
@@ -531,7 +537,10 @@ hybridRouter.get(
|
|||||||
wildcardCandidates.length > 0
|
wildcardCandidates.length > 0
|
||||||
? and(
|
? and(
|
||||||
eq(resources.wildcard, true),
|
eq(resources.wildcard, true),
|
||||||
inArray(resources.fullDomain, wildcardCandidates)
|
inArray(
|
||||||
|
resources.fullDomain,
|
||||||
|
wildcardCandidates
|
||||||
|
)
|
||||||
)
|
)
|
||||||
: sql`false`
|
: sql`false`
|
||||||
)
|
)
|
||||||
@@ -545,10 +554,10 @@ hybridRouter.get(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
result &&
|
result &&
|
||||||
await checkExitNodeOrg(
|
(await checkExitNodeOrg(
|
||||||
remoteExitNode.exitNodeId,
|
remoteExitNode.exitNodeId,
|
||||||
result.resources.orgId
|
result.resources.orgId
|
||||||
)
|
))
|
||||||
) {
|
) {
|
||||||
// If the exit node is not allowed for the org, return an error
|
// If the exit node is not allowed for the org, return an error
|
||||||
return next(
|
return next(
|
||||||
@@ -1132,22 +1141,43 @@ hybridRouter.get(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roleResourceAccess = await db
|
const [direct, viaPolicies] = await Promise.all([
|
||||||
.select()
|
db
|
||||||
.from(roleResources)
|
.select()
|
||||||
.where(
|
.from(roleResources)
|
||||||
and(
|
.where(
|
||||||
eq(roleResources.resourceId, resourceId),
|
and(
|
||||||
eq(roleResources.roleId, roleId)
|
eq(roleResources.resourceId, resourceId),
|
||||||
|
eq(roleResources.roleId, roleId)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
.limit(1),
|
||||||
.limit(1);
|
db
|
||||||
|
.select({
|
||||||
|
roleId: rolePolicies.roleId,
|
||||||
|
resourcePolicyId: rolePolicies.resourcePolicyId
|
||||||
|
})
|
||||||
|
.from(rolePolicies)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(
|
||||||
|
resources.resourcePolicyId,
|
||||||
|
rolePolicies.resourcePolicyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resources.resourceId, resourceId),
|
||||||
|
eq(rolePolicies.roleId, roleId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1)
|
||||||
|
]);
|
||||||
|
|
||||||
const result =
|
const result = direct[0] ?? viaPolicies[0] ?? null;
|
||||||
roleResourceAccess.length > 0 ? roleResourceAccess[0] : null;
|
|
||||||
|
|
||||||
return response<typeof roleResources.$inferSelect | null>(res, {
|
return response<typeof roleResources.$inferSelect | null>(res, {
|
||||||
data: result,
|
data: result as any,
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
message: result
|
message: result
|
||||||
@@ -1222,21 +1252,44 @@ hybridRouter.get(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const roleResourceAccess = await db
|
const [direct, viaPolicies] = await Promise.all([
|
||||||
.select({
|
db
|
||||||
resourceId: roleResources.resourceId,
|
.select({
|
||||||
roleId: roleResources.roleId
|
resourceId: roleResources.resourceId,
|
||||||
})
|
roleId: roleResources.roleId
|
||||||
.from(roleResources)
|
})
|
||||||
.where(
|
.from(roleResources)
|
||||||
and(
|
.where(
|
||||||
eq(roleResources.resourceId, resourceId),
|
and(
|
||||||
inArray(roleResources.roleId, roleIds)
|
eq(roleResources.resourceId, resourceId),
|
||||||
)
|
inArray(roleResources.roleId, roleIds)
|
||||||
);
|
)
|
||||||
|
),
|
||||||
|
roleIds.length > 0
|
||||||
|
? db
|
||||||
|
.select({
|
||||||
|
resourceId: sql<number>`${resourceId}`,
|
||||||
|
roleId: rolePolicies.roleId
|
||||||
|
})
|
||||||
|
.from(rolePolicies)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(
|
||||||
|
resources.resourcePolicyId,
|
||||||
|
rolePolicies.resourcePolicyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(resources.resourceId, resourceId),
|
||||||
|
inArray(rolePolicies.roleId, roleIds)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
: Promise.resolve([])
|
||||||
|
]);
|
||||||
|
|
||||||
const result =
|
const combined = [...direct, ...viaPolicies];
|
||||||
roleResourceAccess.length > 0 ? roleResourceAccess : null;
|
const result = combined.length > 0 ? combined : null;
|
||||||
|
|
||||||
return response<{ resourceId: number; roleId: number }[] | null>(
|
return response<{ resourceId: number; roleId: number }[] | null>(
|
||||||
res,
|
res,
|
||||||
@@ -1397,10 +1450,45 @@ hybridRouter.get(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rules = await db
|
const [directRules, policyRules] = await Promise.all([
|
||||||
.select()
|
db
|
||||||
.from(resourceRules)
|
.select()
|
||||||
.where(eq(resourceRules.resourceId, resourceId));
|
.from(resourceRules)
|
||||||
|
.where(eq(resourceRules.resourceId, resourceId)),
|
||||||
|
db
|
||||||
|
.select({
|
||||||
|
ruleId: resourcePolicyRules.ruleId,
|
||||||
|
resourceId: sql<number>`${resourceId}`,
|
||||||
|
enabled: resourcePolicyRules.enabled,
|
||||||
|
priority: resourcePolicyRules.priority,
|
||||||
|
action: resourcePolicyRules.action,
|
||||||
|
match: resourcePolicyRules.match,
|
||||||
|
value: resourcePolicyRules.value
|
||||||
|
})
|
||||||
|
.from(resourcePolicyRules)
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(
|
||||||
|
resources.resourcePolicyId,
|
||||||
|
resourcePolicyRules.resourcePolicyId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(eq(resources.resourceId, resourceId))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const maxDirectPriority = directRules.reduce(
|
||||||
|
(max, r) => Math.max(max, r.priority),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
const offsetPolicyRules = policyRules.map((r) => ({
|
||||||
|
...r,
|
||||||
|
priority: maxDirectPriority + r.priority
|
||||||
|
}));
|
||||||
|
|
||||||
|
const rules = [
|
||||||
|
...directRules,
|
||||||
|
...offsetPolicyRules
|
||||||
|
] as (typeof resourceRules.$inferSelect)[];
|
||||||
|
|
||||||
// backward compatibility: COUNTRY -> GEOIP
|
// backward compatibility: COUNTRY -> GEOIP
|
||||||
// TODO: remove this after a few versions once all exit nodes are updated
|
// TODO: remove this after a few versions once all exit nodes are updated
|
||||||
|
|||||||
Reference in New Issue
Block a user