screwdriver-api
Version:
API server for the Screwdriver.cd service
287 lines (255 loc) • 11.7 kB
JavaScript
'use strict';
const boom = require('@hapi/boom');
const createRoute = require('./create');
const updateRoute = require('./update');
const removeRoute = require('./remove');
const syncRoute = require('./sync');
const syncWebhooksRoute = require('./syncWebhooks');
const syncPRsRoute = require('./syncPRs');
const getRoute = require('./get');
const listRoute = require('./list');
const badgeRoute = require('./badge');
const jobBadgeRoute = require('./jobBadge');
const listJobsRoute = require('./listJobs');
const listStagesRoute = require('./listStages');
const listTriggersRoute = require('./listTriggers');
const listSecretsRoute = require('./listSecrets');
const listEventsRoute = require('./listEvents');
const listBuildsRoute = require('./listBuilds');
const startAllRoute = require('./startAll');
const createToken = require('./tokens/create');
const updateToken = require('./tokens/update');
const refreshToken = require('./tokens/refresh');
const listTokens = require('./tokens/list');
const removeToken = require('./tokens/remove');
const removeAllTokens = require('./tokens/removeAll');
const metricsRoute = require('./metrics');
const latestBuild = require('./latestBuild');
const latestCommitEvent = require('./latestCommitEvent');
const getAdmin = require('./admins/get');
const deleteCache = require('./caches/delete');
const openPrRoute = require('./openPr');
const createTemplateRoute = require('./templates/create');
const validateTemplateRoute = require('./templates/validate');
const listTemplatesRoute = require('./templates/list');
const listTemplateVersionsRoute = require('./templates/listVersions');
const listTagsRoute = require('./templates/listTags');
const getTemplateRoute = require('./templates/get');
const getTemplateByIdRoute = require('./templates/getTemplateById');
const createTagRoute = require('./templates/createTag');
const getVersionRoute = require('./templates/getVersion');
const removeTemplateRoute = require('./templates/remove');
const removeTemplateTagRoute = require('./templates/removeTag');
const removeTemplateVersionRoute = require('./templates/removeVersion');
const updateTrustedRoute = require('./templates/updateTrusted');
const updateBuildCluster = require('./updateBuildCluster');
/**
* Pipeline API Plugin
* @method register
* @param {Hapi} server Hapi Server
*/
const pipelinesPlugin = {
name: 'pipelines',
async register(server) {
const statusColor = {
unknown: 'lightgrey',
disabled: 'lightgrey',
created: 'lightgrey',
success: 'green',
queued: 'blue',
blocked: 'blue',
running: 'blue',
collapsed: 'lightgrey',
frozen: 'lightgrey',
unstable: 'yellow',
failure: 'red',
aborted: 'red'
};
/**
* Returns an encoded string of subject based on separator of the badge service
* @method encodeBadgeSubject
* @param {String} badgeService badge service url
* @param {String} subject subject to put in the badge
* @return {String} encodedSubject
*/
server.expose('encodeBadgeSubject', ({ badgeService, subject }) => {
const separator = badgeService.match(/}}(.){{/)[1];
if (separator === '/') {
return encodeURIComponent(subject);
}
// Reference: https://shields.io/
if (separator === '-') {
return subject.replace(/-/g, '--').replace(/_/g, '__');
}
return subject;
});
/**
* Returns true if the scope does not include pipeline or includes pipeline
* and its pipelineId matches the pipeline, otherwise returns false
* @method isValidToken
* @param {String} id ID of pipeline
* @param {Object} credentials Credential object from Hapi
* @param {String} credentials.pipelineId ID of pipeline which the token is allowed to access
* @param {String} credentials.scope Scope whose token is allowed
*/
server.expose(
'isValidToken',
(id, credentials) =>
!credentials.scope.includes('pipeline') || parseInt(id, 10) === parseInt(credentials.pipelineId, 10)
);
/**
* Throws error if a credential does not have access to a pipeline
* If credential has access, returns pipeline
* @method canAccessPipeline
* @param {Object} credentials Credential object from Hapi
* @param {String} credentials.username Username of the person logged in (or build ID)
* @param {String} credentials.scmContext Scm of the person logged in (or build ID)
* @param {Array} credentials.scope Scope of the credential (user, build, admin)
* @param {String} pipelineId Target pipeline ID
* @param {String} permission Required permission level
* @param {String} app Server app object
* @return {Object} pipeline
*/
server.expose('canAccessPipeline', (credentials, pipelineId, permission, app) => {
const { username, scmContext, scope, scmUserId } = credentials;
const { userFactory, pipelineFactory } = app;
return pipelineFactory.get(pipelineId).then(pipeline => {
if (!pipeline) {
throw boom.notFound(`Pipeline ${pipelineId} does not exist`);
}
if (credentials.scope.includes('admin')) {
return pipeline;
}
if (!pipeline.scmRepo || !pipeline.scmRepo.private || (pipeline.settings && pipeline.settings.public)) {
return pipeline;
}
if (scope.includes('user')) {
return userFactory.get({ username, scmContext }).then(user => {
if (!user) {
throw boom.notFound(`User ${username} does not exist`);
}
return user
.getPermissions(pipeline.scmUri)
.then(permissions => {
if (!permissions[permission]) {
throw boom.forbidden(
`User ${username} does not have ${permission} access for this pipeline`
);
}
return pipeline;
})
.catch(() => {
const scmDisplayName = pipelineFactory.scm.getDisplayName({ scmContext });
const adminDetails = server.plugins.banners.screwdriverAdminDetails(
username,
scmDisplayName,
scmUserId
);
if (adminDetails.isAdmin) {
return pipeline;
}
throw boom.forbidden(
`User ${username} does not have ${permission} access for this pipeline`
);
});
});
}
if (
(scope.includes('pipeline') || pipelineId !== credentials.configPipelineId) &&
pipelineId !== credentials.pipelineId
) {
throw boom.forbidden('Token does not have permission for this pipeline');
}
return pipeline;
});
});
/**
* Throws error if a credential does not have permission to remove pipeline template
* If credential has access, resolves to true
* @method canRemove
* @param {Object} credentials Credential object from Hapi
* @param {String} credentials.username Username of the person logged in (or build ID)
* @param {String} credentials.scmContext Scm of the person logged in (or build ID)
* @param {Array} credentials.scope Scope of the credential (user, build, admin)
* @param {String} [credentials.pipelineId] If credential is a build, this is the pipeline ID
* @param {Object} pipelineTemplate Target pipeline template object
* @param {String} permission Required permission level
* @param {String} app Server app object
* @return {Promise}
*/
server.expose('canRemove', async (credentials, pipelineTemplate, permission, app) => {
const { username, scmContext, scope } = credentials;
const { userFactory, pipelineFactory } = app;
if (credentials.scope.includes('admin')) {
return true;
}
const pipeline = await pipelineFactory.get(pipelineTemplate.pipelineId);
if (!pipeline) {
throw boom.notFound(`Pipeline ${pipelineTemplate.pipelineId} does not exist`);
}
if (scope.includes('user')) {
const user = await userFactory.get({ username, scmContext });
if (!user) {
throw boom.notFound(`User ${username} does not exist`);
}
const permissions = await user.getPermissions(pipeline.scmUri);
if (!permissions[permission]) {
throw boom.forbidden(
`User ${username} does not have ${permission} access for this pipelineTemplate`
);
}
return true;
}
if (pipelineTemplate.pipelineId !== credentials.pipelineId || credentials.isPR) {
throw boom.forbidden('Not allowed to remove this pipelineTemplate');
}
return true;
});
server.route([
createRoute(),
removeRoute(),
updateRoute(),
syncRoute(),
syncWebhooksRoute(),
syncPRsRoute(),
getRoute(),
listRoute(),
badgeRoute({ statusColor }),
jobBadgeRoute({ statusColor }),
listJobsRoute(),
listStagesRoute(),
listTriggersRoute(),
listSecretsRoute(),
listEventsRoute(),
listBuildsRoute(),
startAllRoute(),
updateToken(),
refreshToken(),
createToken(),
listTokens(),
removeToken(),
removeAllTokens(),
metricsRoute(),
latestBuild(),
latestCommitEvent(),
getAdmin(),
deleteCache(),
openPrRoute(),
createTemplateRoute(),
validateTemplateRoute(),
listTemplatesRoute(),
listTemplateVersionsRoute(),
listTagsRoute(),
getVersionRoute(),
getTemplateByIdRoute(),
getTemplateRoute(),
createTagRoute(),
removeTemplateRoute(),
removeTemplateTagRoute(),
removeTemplateVersionRoute(),
updateTrustedRoute(),
updateBuildCluster()
]);
}
};
module.exports = pipelinesPlugin;