Fix: Make API keys compatible with old endpoints

This commit is contained in:
Daniel Hougaard
2024-03-13 10:51:07 +01:00
parent 258c9e45d4
commit 6e2f3800d4

View File

@@ -13,6 +13,7 @@ import { conditionsMatcher } from "@app/lib/casl";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { TOrgRoleDALFactory } from "@app/services/org/org-role-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectRoleDALFactory } from "@app/services/project-role/project-role-dal";
import { TServiceTokenDALFactory } from "@app/services/service-token/service-token-dal";
@@ -33,6 +34,7 @@ type TPermissionServiceFactoryDep = {
orgRoleDAL: Pick<TOrgRoleDALFactory, "findOne">;
projectRoleDAL: Pick<TProjectRoleDALFactory, "findOne">;
serviceTokenDAL: Pick<TServiceTokenDALFactory, "findById">;
projectDAL: Pick<TProjectDALFactory, "findById">;
permissionDAL: TPermissionDALFactory;
};
@@ -42,7 +44,8 @@ export const permissionServiceFactory = ({
permissionDAL,
orgRoleDAL,
projectRoleDAL,
serviceTokenDAL
serviceTokenDAL,
projectDAL
}: TPermissionServiceFactoryDep) => {
const buildOrgPermission = (role: string, permission?: unknown) => {
switch (role) {
@@ -99,7 +102,7 @@ export const permissionServiceFactory = ({
/*
* Get user permission in an organization
* */
*/
const getUserOrgPermission = async (
userId: string,
orgId: string,
@@ -112,8 +115,13 @@ export const permissionServiceFactory = ({
throw new BadRequestError({ name: "Custom permission not found" });
}
if (membership.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not a member of this organization" });
// If the org ID is API_KEY, the request is being made with an API Key.
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not logged into this organization" });
}
validateOrgSAML(authMethod, membership.orgAuthEnforced);
@@ -179,8 +187,13 @@ export const permissionServiceFactory = ({
throw new BadRequestError({ name: "Custom permission not found" });
}
if (membership.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not a member of this organization" });
// If the org ID is API_KEY, the request is being made with an API Key.
// Since we can't scope API keys to an organization, we'll need to do an arbitrary check to see if the user is a member of the organization.
// Extra: This means that when users are using API keys to make requests, they can't use slug-based routes.
// Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization.
if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) {
throw new UnauthorizedError({ name: "You are not logged into this organization" });
}
validateOrgSAML(authMethod, membership.orgAuthEnforced);
@@ -195,7 +208,8 @@ export const permissionServiceFactory = ({
const getIdentityProjectPermission = async (
identityId: string,
projectId: string
projectId: string,
identityOrgId: string | undefined
): Promise<TProjectPermissionRT<ActorType.IDENTITY>> => {
const identityProjectPermission = await permissionDAL.getProjectIdentityPermission(identityId, projectId);
if (!identityProjectPermission) throw new UnauthorizedError({ name: "Identity not in project" });
@@ -208,6 +222,10 @@ export const permissionServiceFactory = ({
throw new BadRequestError({ name: "Custom permission not found" });
}
if (identityProjectPermission.orgId !== identityOrgId) {
throw new UnauthorizedError({ name: "You are not a member of this organization" });
}
return {
permission: buildProjectPermission(identityProjectPermission.roles),
membership: identityProjectPermission,
@@ -218,14 +236,32 @@ export const permissionServiceFactory = ({
};
};
const getServiceTokenProjectPermission = async (serviceTokenId: string, projectId: string) => {
const getServiceTokenProjectPermission = async (
serviceTokenId: string,
projectId: string,
actorOrgId: string | undefined
) => {
const serviceToken = await serviceTokenDAL.findById(serviceTokenId);
if (!serviceToken) throw new BadRequestError({ message: "Service token not found" });
const serviceTokenProject = await projectDAL.findById(serviceToken.projectId);
if (!serviceTokenProject) throw new BadRequestError({ message: "Service token not linked to a project" });
if (serviceTokenProject.orgId !== actorOrgId) {
throw new UnauthorizedError({ message: "Service token not a part of this organization" });
}
if (serviceToken.projectId !== projectId)
throw new UnauthorizedError({
message: "Failed to find service authorization for given project"
});
if (serviceTokenProject.orgId !== actorOrgId)
throw new UnauthorizedError({
message: "Failed to find service authorization for given project"
});
const scopes = ServiceTokenScopes.parse(serviceToken.scopes || []);
return {
permission: buildServiceTokenProjectPermission(scopes, serviceToken.permissions),
@@ -260,9 +296,9 @@ export const permissionServiceFactory = ({
case ActorType.USER:
return getUserProjectPermission(id, projectId, actorAuthMethod, actorOrgId) as Promise<TProjectPermissionRT<T>>;
case ActorType.SERVICE:
return getServiceTokenProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
return getServiceTokenProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
case ActorType.IDENTITY:
return getIdentityProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
return getIdentityProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
default:
throw new UnauthorizedError({
message: "Permission not defined",