Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 944ccbe

Browse files
committedSep 11, 2023
fix: gitlab token renewal failure
1 parent 68aaa8c commit 944ccbe

File tree

1 file changed

+83
-69
lines changed

1 file changed

+83
-69
lines changed
 

‎services/GitlabService.js

Lines changed: 83 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,59 @@ const copilotUserSchema = Joi.object().keys({
3030
accessTokenExpiration: Joi.date().required(),
3131
refreshToken: Joi.string().required(),
3232
userProviderId: Joi.number().required(),
33-
topcoderUsername: Joi.string()
33+
topcoderUsername: Joi.string(),
34+
username: Joi.string().optional(),
35+
type: Joi.string().optional(),
36+
id: Joi.string().optional(),
37+
role: Joi.string().optional()
3438
}).required();
3539

40+
/**
41+
* Refresh the user access token if needed
42+
* @param {Object} user the copilot
43+
* @returns {Promise} the promise result of copilot with refreshed token
44+
*/
45+
async function _refreshAccessToken(user) {
46+
if (user.accessTokenExpiration && new Date().getTime() > user.accessTokenExpiration.getTime() -
47+
(config.GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION * MS_PER_SECOND)) {
48+
const refreshTokenResult = await request
49+
.post(`${config.GITLAB_API_BASE_URL}/oauth/token`)
50+
.query({
51+
client_id: config.GITLAB_CLIENT_ID,
52+
client_secret: config.GITLAB_CLIENT_SECRET,
53+
refresh_token: user.refreshToken,
54+
grant_type: 'refresh_token',
55+
redirect_uri: config.GITLAB_OWNER_USER_CALLBACK_URL
56+
})
57+
.end();
58+
// save user token data
59+
const expiresIn = refreshTokenResult.body.expires_in || config.GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION;
60+
const updates = {
61+
accessToken: refreshTokenResult.body.access_token,
62+
accessTokenExpiration: new Date(new Date().getTime() + expiresIn * MS_PER_SECOND),
63+
refreshToken: refreshTokenResult.body.refresh_token
64+
};
65+
_.assign(user, updates);
66+
await dbHelper.update(models.User, user.id, updates);
67+
}
68+
return user;
69+
}
70+
3671
/**
3772
* authenticate the gitlab using access token
38-
* @param {String} accessToken the access token of copilot
73+
* @param {Object} user the user
3974
* @private
4075
*/
41-
async function _authenticate(accessToken) {
76+
async function _getClient(user) {
4277
try {
78+
const refreshedUser = await _refreshAccessToken(user);
4379
const gitlab = new Gitlab({
4480
host: config.GITLAB_API_BASE_URL,
45-
oauthToken: accessToken
81+
oauthToken: refreshedUser.accessToken
4682
});
4783
return gitlab;
4884
} catch (err) {
49-
throw errors.handleGitLabError(err, 'Failed to during authenticate to Github using access token of copilot.');
85+
throw errors.handleGitLabError(err, 'Failed to authenticate to Gitlab using access token of copilot.', err);
5086
}
5187
}
5288

@@ -89,8 +125,7 @@ function _getIssueUrl(repoPath, issueId) {
89125
async function createComment(copilot, project, issueId, body) {
90126
const projectId = project.id;
91127
Joi.attempt({copilot, projectId, issueId, body}, createComment.schema);
92-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
93-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
128+
const gitlab = await _getClient(copilot);
94129
try {
95130
body = helper.prepareAutomatedComment(body, copilot);
96131
await gitlab.IssueNotes.create(projectId, issueId, body);
@@ -117,8 +152,7 @@ createComment.schema = {
117152
async function updateIssue(copilot, project, issueId, title) {
118153
const projectId = project.id;
119154
Joi.attempt({copilot, projectId, issueId, title}, updateIssue.schema);
120-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
121-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
155+
const gitlab = await _getClient(copilot);
122156
try {
123157
await gitlab.Issues.edit(projectId, issueId, {title});
124158
} catch (err) {
@@ -144,8 +178,7 @@ updateIssue.schema = {
144178
async function assignUser(copilot, project, issueId, userId) {
145179
const projectId = project.id;
146180
Joi.attempt({copilot, projectId, issueId, userId}, assignUser.schema);
147-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
148-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
181+
const gitlab = await _getClient(copilot);
149182
try {
150183
const issue = await gitlab.Issues.show(issueId, {projectId});
151184
const oldAssignees = _.without(issue.assignees.map((a) => a.id), userId);
@@ -176,8 +209,7 @@ assignUser.schema = {
176209
async function removeAssign(copilot, project, issueId, userId) {
177210
const projectId = project.id;
178211
Joi.attempt({copilot, projectId, issueId, userId}, removeAssign.schema);
179-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
180-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
212+
const gitlab = await _getClient(copilot);
181213
await _removeAssignees(gitlab, projectId, issueId, [userId]);
182214
logger.debug(`Gitlab user ${userId} is unassigned from issue number ${issueId}`);
183215
}
@@ -192,8 +224,7 @@ removeAssign.schema = assignUser.schema;
192224
*/
193225
async function getUsernameById(copilot, userId) {
194226
Joi.attempt({copilot, userId}, getUsernameById.schema);
195-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
196-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
227+
const gitlab = await _getClient(copilot);
197228
const user = await gitlab.Users.show(userId);
198229
return user ? user.username : null;
199230
}
@@ -211,8 +242,7 @@ getUsernameById.schema = {
211242
*/
212243
async function getUserIdByLogin(copilot, login) {
213244
Joi.attempt({copilot, login}, getUserIdByLogin.schema);
214-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
215-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
245+
const gitlab = await _getClient(copilot);
216246
const user = await gitlab.Users.all({username: login});
217247
return user.length ? user[0].id : null;
218248
}
@@ -235,8 +265,7 @@ getUserIdByLogin.schema = {
235265
async function markIssueAsPaid(copilot, project, issueId, challengeUUID, existLabels, winner, createCopilotPayments) { // eslint-disable-line max-params
236266
const projectId = project.id;
237267
Joi.attempt({copilot, projectId, issueId, challengeUUID, existLabels, winner, createCopilotPayments}, markIssueAsPaid.schema);
238-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
239-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
268+
const gitlab = await _getClient(copilot);
240269
const labels = _(existLabels).filter((i) => i !== config.FIX_ACCEPTED_ISSUE_LABEL)
241270
.push(config.FIX_ACCEPTED_ISSUE_LABEL, config.PAID_ISSUE_LABEL).value();
242271
try {
@@ -279,8 +308,7 @@ markIssueAsPaid.schema = {
279308
async function changeState(copilot, project, issueId, state) {
280309
const projectId = project.id;
281310
Joi.attempt({copilot, projectId, issueId, state}, changeState.schema);
282-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
283-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
311+
const gitlab = await _getClient(copilot);
284312
try {
285313
await gitlab.Issues.edit(projectId, issueId, {stateEvent: state});
286314
} catch (err) {
@@ -306,8 +334,7 @@ changeState.schema = {
306334
async function addLabels(copilot, project, issueId, labels) {
307335
const projectId = project.id;
308336
Joi.attempt({copilot, projectId, issueId, labels}, addLabels.schema);
309-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
310-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
337+
const gitlab = await _getClient(copilot);
311338
try {
312339
await gitlab.Issues.edit(projectId, issueId, {labels: _.join(labels, ',')});
313340
} catch (err) {
@@ -329,12 +356,17 @@ addLabels.schema = {
329356
* @param {Object} repoURL The repository URL
330357
*/
331358
async function getRepository(user, repoURL) {
332-
const refreshedUser = await _refreshGitlabUserAccessToken(user);
333-
const gitlab = await _authenticate(refreshedUser.accessToken);
359+
Joi.attempt({user, repoURL}, getRepository.schema);
360+
const gitlab = await _getClient(user);
334361
const _repoURL = repoURL.replace(`${config.GITLAB_API_BASE_URL}/`, '');
335362
return await gitlab.Projects.show(_repoURL);
336363
}
337364

365+
getRepository.schema = {
366+
user: Joi.object().required(),
367+
repoURL: Joi.string().required()
368+
};
369+
338370
/**
339371
* Add a user to a gitlab repository
340372
* @param {Object} copilot The copilot
@@ -343,18 +375,19 @@ async function getRepository(user, repoURL) {
343375
* @param {import('@gitbeaker/rest').AccessLevel} accessLevel The user role
344376
*/
345377
async function addUserToRepository(copilot, repository, user, accessLevel) {
346-
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
347-
const gitlab = await _authenticate(refreshedCopilot.accessToken);
348-
const member = await new Promise((resolve, reject) => {
349-
gitlab.ProjectMembers.show(repository.id, user.userProviderId)
350-
.then((result) => resolve(result))
351-
.catch((err) => {
352-
// eslint-disable-next-line no-magic-numbers
353-
if (_.get(err, 'cause.response.status') === 404) {
354-
return resolve(null);
355-
}
356-
return reject(err);
357-
});
378+
Joi.attempt({copilot, repository, user, accessLevel}, addUserToRepository.schema);
379+
const gitlab = await _getClient(copilot);
380+
const member = await new Promise(async (resolve, reject) => {
381+
try {
382+
const res = await gitlab.ProjectMembers.show(repository.id, user.userProviderId);
383+
resolve(res);
384+
} catch (err) {
385+
// eslint-disable-next-line no-magic-numbers
386+
if (_.get(err, 'cause.response.status') === 404) {
387+
resolve(null);
388+
}
389+
reject(err);
390+
}
358391
});
359392
if (!member) {
360393
await gitlab.ProjectMembers.add(repository.id, user.userProviderId, accessLevel);
@@ -365,47 +398,28 @@ async function addUserToRepository(copilot, repository, user, accessLevel) {
365398
}
366399
}
367400

401+
addUserToRepository.schema = {
402+
copilot: copilotUserSchema,
403+
repository: Joi.object().required(),
404+
user: Joi.object().required(),
405+
accessLevel: Joi.number().required()
406+
};
407+
368408
/**
369409
* Fork a gitlab repository
370410
* @param {Object} user The user
371411
* @param {ProjectSchema} repository The repository
372412
*/
373413
async function forkRepository(user, repository) {
374-
const refreshedUser = await _refreshGitlabUserAccessToken(user);
375-
const gitlab = await _authenticate(refreshedUser.accessToken);
414+
Joi.attempt({user, repository}, forkRepository.schema);
415+
const gitlab = await _getClient(user);
376416
await gitlab.Projects.fork(repository.id);
377417
}
378418

379-
/**
380-
* Refresh the copilot access token if token is needed
381-
* @param {Object} copilot the copilot
382-
* @returns {Promise} the promise result of copilot with refreshed token
383-
*/
384-
async function _refreshGitlabUserAccessToken(copilot) {
385-
if (copilot.accessTokenExpiration && new Date().getTime() > copilot.accessTokenExpiration.getTime() -
386-
(config.GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION * MS_PER_SECOND)) {
387-
const refreshTokenResult = await request
388-
.post(`${config.GITLAB_API_BASE_URL}/oauth/token`)
389-
.query({
390-
client_id: config.GITLAB_CLIENT_ID,
391-
client_secret: config.GITLAB_CLIENT_SECRET,
392-
refresh_token: copilot.refreshToken,
393-
grant_type: 'refresh_token',
394-
redirect_uri: config.GITLAB_OWNER_USER_CALLBACK_URL
395-
})
396-
.end();
397-
// save user token data
398-
const expiresIn = refreshTokenResult.body.expires_in || config.GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION;
399-
const updates = {
400-
accessToken: refreshTokenResult.body.access_token,
401-
accessTokenExpiration: new Date(new Date().getTime() + expiresIn * MS_PER_SECOND),
402-
refreshToken: refreshTokenResult.body.refresh_token
403-
};
404-
copilot = _.assign(copilot, updates);
405-
return await dbHelper.update(models.User, copilot.id, updates);
406-
}
407-
return copilot;
408-
}
419+
forkRepository.schema = {
420+
user: Joi.object().required(),
421+
repository: Joi.object().required()
422+
};
409423

410424
module.exports = {
411425
createComment,

0 commit comments

Comments
 (0)
This repository has been archived.