diff --git a/server/db/queries/verifySessionQueries.ts b/server/db/queries/verifySessionQueries.ts index 7be2426d5..88c97abbb 100644 --- a/server/db/queries/verifySessionQueries.ts +++ b/server/db/queries/verifySessionQueries.ts @@ -36,7 +36,8 @@ import { ResourcePolicyHeaderAuth } from "@server/db"; import { alias } from "drizzle-orm/sqlite-core"; -import { and, eq, inArray, or, sql } from "drizzle-orm"; +import { and, eq, inArray, isNull, or, sql } from "drizzle-orm"; +import logger from "@server/logger"; export type ResourceWithAuth = { resource: Resource | null; @@ -44,6 +45,7 @@ export type ResourceWithAuth = { password: ResourcePassword | ResourcePolicyPassword | null; headerAuth: ResourceHeaderAuth | ResourcePolicyHeaderAuth | null; headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null; + applyRules: boolean; org: Org; }; @@ -213,9 +215,14 @@ export async function getResourceByDomain( const effectivePolicyHeaderAuth = hasSharedPolicy ? result.sharedPolicyHeaderAuth : (result.defaultPolicyHeaderAuth ?? null); + const effectiveApplyRules = + (hasSharedPolicy + ? (result.sharedPolicy?.applyRules ?? false) + : (result.defaultPolicy?.applyRules ?? false)) || + result.resources.applyRules; return { - resource: result.resources, + resource: { ...result.resources, applyRules: effectiveApplyRules }, // doing this for backward compatability so the remote nodes get the value as part of the resource struct pincode: effectivePolicyPincode ?? result.resourcePincode, password: effectivePolicyPassword ?? result.resourcePassword, headerAuth: effectivePolicyHeaderAuth ?? result.resourceHeaderAuth, @@ -227,6 +234,7 @@ export async function getResourceByDomain( effectivePolicyHeaderAuth.extendedCompatibility } as ResourceHeaderAuthExtendedCompatibility) : result.resourceHeaderAuthExtendedCompatibility, + applyRules: effectiveApplyRules, org: result.orgs }; } @@ -290,7 +298,21 @@ export async function getRoleResourceAccess( .from(rolePolicies) .innerJoin( resources, - eq(resources.resourcePolicyId, rolePolicies.resourcePolicyId) + // Shared policy wins; only use default policy when no shared + // policy is assigned to the resource. + or( + eq( + resources.resourcePolicyId, + rolePolicies.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + rolePolicies.resourcePolicyId + ) + ) + ) ) .where( and( @@ -330,7 +352,21 @@ export async function getUserResourceAccess( .from(userPolicies) .innerJoin( resources, - eq(resources.resourcePolicyId, userPolicies.resourcePolicyId) + // Shared policy wins; only use default policy when no shared + // policy is assigned to the resource. + or( + eq( + resources.resourcePolicyId, + userPolicies.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + userPolicies.resourcePolicyId + ) + ) + ) ) .where( and( @@ -368,9 +404,20 @@ export async function getResourceRules( .from(resourcePolicyRules) .innerJoin( resources, - eq( - resources.resourcePolicyId, - resourcePolicyRules.resourcePolicyId + // Shared policy wins; only use default policy when no shared + // policy is assigned to the resource. + or( + eq( + resources.resourcePolicyId, + resourcePolicyRules.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + resourcePolicyRules.resourcePolicyId + ) + ) ) ) .where(eq(resources.resourceId, resourceId)) diff --git a/server/private/routers/hybrid.ts b/server/private/routers/hybrid.ts index 4528bc4ba..88ba8d26b 100644 --- a/server/private/routers/hybrid.ts +++ b/server/private/routers/hybrid.ts @@ -60,7 +60,7 @@ import { userOrgRoles, roles } from "@server/db"; -import { eq, and, inArray, isNotNull, ne, or, sql } from "drizzle-orm"; +import { eq, and, inArray, isNotNull, isNull, ne, or, sql } from "drizzle-orm"; import { alias } from "drizzle-orm/sqlite-core"; import { response } from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -670,21 +670,28 @@ hybridRouter.get( }); } - const effectivePolicyPincode = - result.sharedPolicyPincode ?? - result.defaultPolicyPincode ?? - null; - const effectivePolicyPassword = - result.sharedPolicyPassword ?? - result.defaultPolicyPassword ?? - null; - const effectivePolicyHeaderAuth = - result.sharedPolicyHeaderAuth ?? - result.defaultPolicyHeaderAuth ?? - null; + const hasSharedPolicy = result.sharedPolicy !== null; + + const effectivePolicyPincode = hasSharedPolicy + ? result.sharedPolicyPincode + : (result.defaultPolicyPincode ?? null); + const effectivePolicyPassword = hasSharedPolicy + ? result.sharedPolicyPassword + : (result.defaultPolicyPassword ?? null); + const effectivePolicyHeaderAuth = hasSharedPolicy + ? result.sharedPolicyHeaderAuth + : (result.defaultPolicyHeaderAuth ?? null); + const effectiveApplyRules = + (hasSharedPolicy + ? (result.sharedPolicy?.applyRules ?? false) + : (result.defaultPolicy?.applyRules ?? false)) || + result.resources.applyRules; const resourceWithAuth: ResourceWithAuth = { - resource: result.resources, + resource: { + ...result.resources, + applyRules: effectiveApplyRules + }, pincode: effectivePolicyPincode ?? result.resourcePincode, password: effectivePolicyPassword ?? result.resourcePassword, headerAuth: @@ -1272,9 +1279,18 @@ hybridRouter.get( .from(rolePolicies) .innerJoin( resources, - eq( - resources.resourcePolicyId, - rolePolicies.resourcePolicyId + or( + eq( + resources.resourcePolicyId, + rolePolicies.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + rolePolicies.resourcePolicyId + ) + ) ) ) .where( @@ -1386,9 +1402,18 @@ hybridRouter.get( .from(rolePolicies) .innerJoin( resources, - eq( - resources.resourcePolicyId, - rolePolicies.resourcePolicyId + or( + eq( + resources.resourcePolicyId, + rolePolicies.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + rolePolicies.resourcePolicyId + ) + ) ) ) .where( @@ -1477,19 +1502,52 @@ hybridRouter.get( ); } - const userResourceAccess = await db - .select() - .from(userResources) - .where( - and( - eq(userResources.userId, userId), - eq(userResources.resourceId, resourceId) - ) - ) - .limit(1); + const [directUserAccess, viaPoliciesUserAccess] = await Promise.all( + [ + db + .select() + .from(userResources) + .where( + and( + eq(userResources.userId, userId), + eq(userResources.resourceId, resourceId) + ) + ) + .limit(1), + db + .select({ + userId: userPolicies.userId, + resourcePolicyId: userPolicies.resourcePolicyId + }) + .from(userPolicies) + .innerJoin( + resources, + or( + eq( + resources.resourcePolicyId, + userPolicies.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + userPolicies.resourcePolicyId + ) + ) + ) + ) + .where( + and( + eq(resources.resourceId, resourceId), + eq(userPolicies.userId, userId) + ) + ) + .limit(1) + ] + ); const result = - userResourceAccess.length > 0 ? userResourceAccess[0] : null; + directUserAccess[0] ?? viaPoliciesUserAccess[0] ?? null; return response(res, { data: result, @@ -1580,9 +1638,18 @@ hybridRouter.get( .from(resourcePolicyRules) .innerJoin( resources, - eq( - resources.resourcePolicyId, - resourcePolicyRules.resourcePolicyId + or( + eq( + resources.resourcePolicyId, + resourcePolicyRules.resourcePolicyId + ), + and( + isNull(resources.resourcePolicyId), + eq( + resources.defaultResourcePolicyId, + resourcePolicyRules.resourcePolicyId + ) + ) ) ) .where(eq(resources.resourceId, resourceId)) diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 9bdfdfa53..0b03aef58 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -144,6 +144,7 @@ export async function verifyResourceSession( | ResourcePolicyHeaderAuth | null; headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null; + applyRules: boolean; org: Org; } | undefined = localCache.get(resourceCacheKey); @@ -175,6 +176,7 @@ export async function verifyResourceSession( const { resource, + applyRules, pincode, password, headerAuth, @@ -220,7 +222,7 @@ export async function verifyResourceSession( } // check the rules - if (resource.applyRules) { + if (applyRules) { const action = await checkRules( resource.resourceId, clientIp, @@ -876,10 +878,7 @@ function allowed( message: "Access allowed", status: HttpCode.OK }; - logger.debug( - "++++++++++++++++++++++++++++++++++Access allowed, response data:", - data - ); + logger.debug("Access allowed, response data:", data); return response(res, data); }