Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: deploytargetconfig permission for projects in organizations #3558

Merged
merged 1 commit into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 5 additions & 16 deletions services/api/src/resources/deploytargetconfig/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { Sql as EnvironmentSql } from '../environment/sql'
import { Helpers as projectHelpers } from '../project/helpers';
import { Helpers as organizationHelpers } from '../organization/helpers';


export const getDeployTargetConfigById = async (
root,
args,
Expand All @@ -26,9 +25,7 @@ export const getDeployTargetConfigById = async (
// since deploytargetconfigs are associated to a project
// re-use the existing `project:view` permissions check, since the same sorts of fields
// are viewable by the same permissions at the project scope
await hasPermission('project', 'view', {
project: deployTargetConfig.project
});
await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, deployTargetConfig.project)

return deployTargetConfig;
};
Expand All @@ -47,9 +44,7 @@ export const getDeployTargetConfigsByProjectId: ResolverFn = async (
// since deploytargetconfigs are associated to a project
// re-use the existing `project:view` permissions check, since the same sorts of fields
// are viewable by the same permissions at the project scope
await hasPermission('project', 'view', {
project: pid
});
await projectHelpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, pid)

const rows = await query(sqlClientPool, Sql.selectDeployTargetConfigsByProjectId(pid));

Expand Down Expand Up @@ -196,9 +191,7 @@ export const addDeployTargetConfig: ResolverFn = async (
// since deploytargetconfigs are associated to a project
// re-use the existing `project:update` permissions check, since the same sorts of fields
// are updateable by the same permissions at the project scope
await hasPermission('project', 'update', {
project: project
});
await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project)

// check the project has an organization id, if it does, check that the organization supports the requested deploytarget
await checkProjectDeployTargetByOrg(project, deployTarget, sqlClientPool)
Expand Down Expand Up @@ -246,9 +239,7 @@ export const deleteDeployTargetConfig: ResolverFn = async (
// re-use the existing `project:update` permissions check, since the same sorts of fields
// are updateable by the same permissions at the project scope
// deleting a deploytargetconfig from a project is classed as updating the project
await hasPermission('project', 'update', {
project: project
});
await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, project)

try {
await query(sqlClientPool, 'DELETE FROM deploy_target_config WHERE id = :id', {
Expand Down Expand Up @@ -292,9 +283,7 @@ export const updateDeployTargetConfig: ResolverFn = async (
// since deploytargetconfigs are associated to a project
// re-use the existing `project:update` permissions check, since the same sorts of fields
// are updateable by the same permissions at the project scope
await hasPermission('project', 'update', {
project: deployTargetConfig.project
});
await projectHelpers(sqlClientPool).checkOrgProjectUpdatePermission(hasPermission, deployTargetConfig.project)

// check the project has an organization id, if it does, check that the organization supports the requested deploytarget
await checkProjectDeployTargetByOrg(deployTargetConfig.project, deployTarget, sqlClientPool)
Expand Down
43 changes: 43 additions & 0 deletions services/api/src/resources/project/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,47 @@ import { Sql } from './sql';
// import { logger } from '../../loggers/logger';

export const Helpers = (sqlClientPool: Pool) => {
const checkOrgProjectViewPermission = async (hasPermission, pid) => {
// helper that allows fall through of permission check
// for viewProject:organization to view:project
// this allows queries to be performed by organization owners
// then falling through to the default project view for general users
const rows = await query(sqlClientPool, Sql.selectProject(pid));
const project = rows[0];
if (project.organization != null) {
try {
await hasPermission('organization', 'viewProject', {
organization: project.organization
});
// if the organization owner has permission to view project, return
return
} catch (err) {
// otherwise fall through to project view permission check
}
}
// finally check the user view:project permission
await hasPermission('project', 'view', {
project: project.id
});
}
const checkOrgProjectUpdatePermission = async (hasPermission, pid) => {
// helper checks the permission to updateProject:organization
// or the update:project permission
const rows = await query(sqlClientPool, Sql.selectProject(pid));
const project = rows[0];
if (project.organization != null) {
// if the project is in an organization, only the organization owner should be able to do this
await hasPermission('organization', 'updateProject', {
organization: project.organization
});
} else {
// if not in a project, follow the standard rbac
await hasPermission('project', 'update', {
project: project.id
});
}
}

const aliasOpenshiftToK8s = (projects: any[]) => {
return projects.map(project => {
return {
Expand Down Expand Up @@ -52,6 +93,8 @@ export const Helpers = (sqlClientPool: Pool) => {
query(sqlClientPool, Sql.selectProjectsByIds(projectIds));

return {
checkOrgProjectViewPermission,
checkOrgProjectUpdatePermission,
aliasOpenshiftToK8s,
getProjectById,
getProjectsByIds,
Expand Down
56 changes: 4 additions & 52 deletions services/api/src/resources/project/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,7 @@ export const getProjectByEnvironmentId: ResolverFn = async (

const project = withK8s[0];

try {
await hasPermission('project', 'view', {
project: project.id
});
} catch (err) {
// if the user hasn't got permission to view the project, but the project is in the organization
// allow the user to get the project
if (project.organization != null) {
await hasPermission('organization', 'viewProject', {
organization: project.organization
});
}
}
await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id)

return project;
};
Expand All @@ -152,19 +140,7 @@ export const getProjectById: ResolverFn = async (

const project = withK8s[0];

try {
await hasPermission('project', 'view', {
project: project.id
});
} catch (err) {
// if the user hasn't got permission to view the project, but the project is in the organization
// allow the user to get the project
if (project.organization != null) {
await hasPermission('organization', 'viewProject', {
organization: project.organization
});
}
}
await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id)

return project;
};
Expand All @@ -180,19 +156,7 @@ export const getProjectByGitUrl: ResolverFn = async (

const project = withK8s[0];

try {
await hasPermission('project', 'view', {
project: project.id
});
} catch (err) {
// if the user hasn't got permission to view the project, but the project is in the organization
// allow the user to get the project
if (project.organization != null) {
await hasPermission('organization', 'viewProject', {
organization: project.organization
});
}
}
await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id)

return project;
};
Expand All @@ -211,19 +175,7 @@ export const getProjectByName: ResolverFn = async (
return null;
}

try {
await hasPermission('project', 'view', {
project: project.id
});
} catch (err) {
// if the user hasn't got permission to view the project, but the project is in the organization
// allow the user to get the project
if (project.organization != null) {
await hasPermission('organization', 'viewProject', {
organization: project.organization
});
}
}
await Helpers(sqlClientPool).checkOrgProjectViewPermission(hasPermission, project.id)

return project;
};
Expand Down