screwdriver-api 8.0.132 → 8.0.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/plugins/events/create.js +1 -1
- package/plugins/pipelines/update.js +109 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-api",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.133",
|
|
4
4
|
"description": "API server for the Screwdriver.cd service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"screwdriver-config-parser": "^12.2.3",
|
|
115
115
|
"screwdriver-coverage-bookend": "^3.0.0",
|
|
116
116
|
"screwdriver-coverage-sonar": "^5.0.0",
|
|
117
|
-
"screwdriver-data-schema": "^
|
|
117
|
+
"screwdriver-data-schema": "^26.1.0",
|
|
118
118
|
"screwdriver-datastore-sequelize": "^10.0.0",
|
|
119
119
|
"screwdriver-executor-base": "^11.0.0",
|
|
120
120
|
"screwdriver-executor-docker": "^8.0.1",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"screwdriver-executor-queue": "^6.0.0",
|
|
124
124
|
"screwdriver-executor-router": "^5.0.0",
|
|
125
125
|
"screwdriver-logger": "^3.0.0",
|
|
126
|
-
"screwdriver-models": "^
|
|
126
|
+
"screwdriver-models": "^34.1.0",
|
|
127
127
|
"screwdriver-notifications-email": "^5.0.0",
|
|
128
128
|
"screwdriver-notifications-slack": "^7.0.0",
|
|
129
129
|
"screwdriver-request": "^3.0.0",
|
package/plugins/events/create.js
CHANGED
|
@@ -129,7 +129,7 @@ module.exports = () => ({
|
|
|
129
129
|
if (!pipeline) {
|
|
130
130
|
throw boom.notFound();
|
|
131
131
|
} else if (pipeline.state !== 'ACTIVE') {
|
|
132
|
-
// INACTIVE or
|
|
132
|
+
// INACTIVE, DELETING, or DISABLED pipeline
|
|
133
133
|
throw boom.badRequest(`Cannot create an event for a(n) ${pipeline.state} pipeline`);
|
|
134
134
|
}
|
|
135
135
|
|
|
@@ -39,6 +39,30 @@ function getPermissionsForOldPipeline({ scmContexts, pipeline, user }) {
|
|
|
39
39
|
return user.getPermissions(pipeline.scmUri);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Apply state change fields to a pipeline
|
|
44
|
+
* @method applyStateChange
|
|
45
|
+
* @param {Object} pipeline Pipeline object to mutate
|
|
46
|
+
* @param {String} newState New state ('ACTIVE' or 'DISABLED')
|
|
47
|
+
* @param {String} username Username making the change
|
|
48
|
+
* @param {String} stateChangeMessage Optional message for state change
|
|
49
|
+
*/
|
|
50
|
+
function applyStateChange(pipeline, newState, username, stateChangeMessage) {
|
|
51
|
+
const currentState = pipeline.state;
|
|
52
|
+
|
|
53
|
+
if (
|
|
54
|
+
(currentState === 'ACTIVE' && newState === 'DISABLED') ||
|
|
55
|
+
(currentState === 'DISABLED' && newState === 'ACTIVE')
|
|
56
|
+
) {
|
|
57
|
+
pipeline.state = newState;
|
|
58
|
+
pipeline.stateChanger = username;
|
|
59
|
+
pipeline.stateChangeTime = new Date().toISOString();
|
|
60
|
+
pipeline.stateChangeMessage = stateChangeMessage || null;
|
|
61
|
+
} else {
|
|
62
|
+
throw boom.conflict(`Pipeline state cannot be transitioned from ${currentState} to ${newState}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
42
66
|
module.exports = () => ({
|
|
43
67
|
method: 'PUT',
|
|
44
68
|
path: '/pipelines/{id}',
|
|
@@ -52,10 +76,16 @@ module.exports = () => ({
|
|
|
52
76
|
},
|
|
53
77
|
|
|
54
78
|
handler: async (request, h) => {
|
|
55
|
-
const { checkoutUrl, rootDir, settings, badges } = request.payload;
|
|
79
|
+
const { checkoutUrl, rootDir, settings, badges, state, stateChangeMessage } = request.payload;
|
|
80
|
+
const hasNonStateFields = !!(
|
|
81
|
+
checkoutUrl ||
|
|
82
|
+
rootDir ||
|
|
83
|
+
(settings && Object.keys(settings).length > 0) ||
|
|
84
|
+
badges
|
|
85
|
+
);
|
|
56
86
|
const { id } = request.params;
|
|
57
|
-
const { pipelineFactory, userFactory, secretFactory } = request.server.app;
|
|
58
|
-
const { scmContext, username } = request.auth.credentials;
|
|
87
|
+
const { pipelineFactory, userFactory, secretFactory, bannerFactory } = request.server.app;
|
|
88
|
+
const { scmContext, username, scmUserId } = request.auth.credentials;
|
|
59
89
|
const scmContexts = pipelineFactory.scm.getScmContexts();
|
|
60
90
|
const { isValidToken } = request.server.plugins.pipelines;
|
|
61
91
|
const deployKeySecret = 'SD_SCM_DEPLOY_KEY';
|
|
@@ -82,10 +112,53 @@ module.exports = () => ({
|
|
|
82
112
|
oldPipeline.adminUserIds = [];
|
|
83
113
|
}
|
|
84
114
|
|
|
115
|
+
// Detect if user is a screwdriver cluster admin
|
|
116
|
+
const scmDisplayName = bannerFactory.scm.getDisplayName({ scmContext });
|
|
117
|
+
const adminDetails = request.server.plugins.banners.screwdriverAdminDetails(
|
|
118
|
+
username,
|
|
119
|
+
scmDisplayName,
|
|
120
|
+
scmUserId
|
|
121
|
+
);
|
|
122
|
+
const isScrewdriverAdmin = adminDetails.isAdmin;
|
|
123
|
+
|
|
124
|
+
// If the pipeline is a child pipeline, only allow state/stateChangeMessage updates
|
|
85
125
|
if (oldPipeline.configPipelineId) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
126
|
+
// get pipeline admin permissions for child pipeline check
|
|
127
|
+
let childPermissions;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
childPermissions = await getPermissionsForOldPipeline({
|
|
131
|
+
scmContexts,
|
|
132
|
+
pipeline: oldPipeline,
|
|
133
|
+
user
|
|
134
|
+
});
|
|
135
|
+
} catch (err) {
|
|
136
|
+
childPermissions = { admin: false };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const isPipelineAdmin = childPermissions.admin;
|
|
140
|
+
|
|
141
|
+
if (!isPipelineAdmin && !isScrewdriverAdmin) {
|
|
142
|
+
throw boom.forbidden(
|
|
143
|
+
`Child pipeline can only be modified by config pipeline ${oldPipeline.configPipelineId}`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (hasNonStateFields) {
|
|
148
|
+
throw boom.forbidden('Only state fields can be updated for a child pipeline');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!state) {
|
|
152
|
+
throw boom.forbidden(
|
|
153
|
+
`Child pipeline can only be modified by config pipeline ${oldPipeline.configPipelineId}`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Apply state change and return early
|
|
158
|
+
applyStateChange(oldPipeline, state, username, stateChangeMessage);
|
|
159
|
+
const updatedPipeline = await oldPipeline.update();
|
|
160
|
+
|
|
161
|
+
return h.response(updatedPipeline.toJson()).code(200);
|
|
89
162
|
}
|
|
90
163
|
|
|
91
164
|
// get the user permissions for the repo
|
|
@@ -98,7 +171,31 @@ module.exports = () => ({
|
|
|
98
171
|
user
|
|
99
172
|
});
|
|
100
173
|
} catch (err) {
|
|
101
|
-
|
|
174
|
+
if (isScrewdriverAdmin) {
|
|
175
|
+
oldPermissions = { admin: false };
|
|
176
|
+
} else {
|
|
177
|
+
throw boom.forbidden(
|
|
178
|
+
`User ${user.getFullDisplayName()} does not have admin permission for this repo`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const isPipelineAdmin = oldPermissions.admin;
|
|
184
|
+
|
|
185
|
+
// Screwdriver admin without pipeline admin access: only allow state updates
|
|
186
|
+
if (!isPipelineAdmin && isScrewdriverAdmin) {
|
|
187
|
+
if (hasNonStateFields) {
|
|
188
|
+
throw boom.forbidden(`User ${username} is only allowed to update the state of this pipeline`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!state) {
|
|
192
|
+
throw boom.forbidden(`User ${username} is not an admin of these repos`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
applyStateChange(oldPipeline, state, username, stateChangeMessage);
|
|
196
|
+
const updatedPipeline = await oldPipeline.update();
|
|
197
|
+
|
|
198
|
+
return h.response(updatedPipeline.toJson()).code(200);
|
|
102
199
|
}
|
|
103
200
|
|
|
104
201
|
let token;
|
|
@@ -143,7 +240,7 @@ module.exports = () => ({
|
|
|
143
240
|
oldPipeline.name = scmRepo.name;
|
|
144
241
|
}
|
|
145
242
|
|
|
146
|
-
if (!
|
|
243
|
+
if (!isPipelineAdmin) {
|
|
147
244
|
throw boom.forbidden(`User ${username} is not an admin of these repos`);
|
|
148
245
|
}
|
|
149
246
|
|
|
@@ -159,6 +256,10 @@ module.exports = () => ({
|
|
|
159
256
|
oldPipeline.settings = { ...oldPipeline.settings, ...settings };
|
|
160
257
|
}
|
|
161
258
|
|
|
259
|
+
if (state) {
|
|
260
|
+
applyStateChange(oldPipeline, state, username, stateChangeMessage);
|
|
261
|
+
}
|
|
262
|
+
|
|
162
263
|
if (checkoutUrl || rootDir) {
|
|
163
264
|
logger.info(
|
|
164
265
|
`[Audit] user ${user.username}:${scmContext} updates the scmUri for pipelineID:${id} to ${oldPipeline.scmUri} from ${oldPipelineConfig.scmUri}.`
|