diff --git a/services/web/modules/gitlab-sync/app/src/GitLabApiClient.mjs b/services/web/modules/gitlab-sync/app/src/GitLabApiClient.mjs index fb6bd61035..85e7d0ea3b 100644 --- a/services/web/modules/gitlab-sync/app/src/GitLabApiClient.mjs +++ b/services/web/modules/gitlab-sync/app/src/GitLabApiClient.mjs @@ -190,7 +190,7 @@ async function fetchAllPages(url, options, operation) { } function getTokenRefreshTimestamp(token, safetyMarginInSec = 300) { - return token.created_at + token.expires_in - safetyMarginInSec; + return token.created_at + token.expires_in - safetyMarginInSec; } @@ -200,8 +200,8 @@ async function compareCommitsFull(token, repoFullName, from, to) { url.searchParams.set('to', to) return await fetchGitLabJson(url.toString(), { - headers: buildHeaders(token), - signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) + headers: buildHeaders(token), + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) }, 'compareCommitsFull') } @@ -224,8 +224,8 @@ function exchangeCodeForToken(code) { headers: buildHeaders(), json: { client_id: Settings.gitlabSync.clientID, - client_secret: Settings.gitlabSync.clientSecret, - code: code, + client_secret: Settings.gitlabSync.clientSecret, + code: code, grant_type: 'authorization_code', redirect_uri: Settings.gitlabSync.callbackURL, }, @@ -468,7 +468,7 @@ async function getBlobContent(token, repoFullName, sha) { return await fetchGitLabJson(url, { headers: buildHeaders(token), signal: AbortSignal.timeout(REQUEST_LONG_TIMEOUT_MS) - }, 'getBlobContent').then(r => Buffer.from(r.content, 'base64')) + }, 'getBlobContent').then(r => r.content || '') } async function listNewCommitsWithStatus(token, fullName, branchName, fromCommit) { diff --git a/services/web/modules/gitlab-sync/app/src/GitLabSyncController.mjs b/services/web/modules/gitlab-sync/app/src/GitLabSyncController.mjs index 3b4ddaa873..f0fa85050f 100644 --- a/services/web/modules/gitlab-sync/app/src/GitLabSyncController.mjs +++ b/services/web/modules/gitlab-sync/app/src/GitLabSyncController.mjs @@ -118,14 +118,14 @@ async function oauth2Callback(req, res) { let refresh_token let refresh_timestamp try { - [token, refresh_token, refresh_timestamp] = await api.exchangeCodeForToken(code) + [token, refresh_token, refresh_timestamp] = await api.exchangeCodeForToken(code) if (!token || !refresh_token) { HttpErrorHandler.badRequest(req, res, 'Failed to obtain access token from Git server') return } } catch (err) { const info = OError.getFullInfo(err) - const err_body = err.body + const err_body = err.body logger.error(OError.getFullStack(err)) logger.error({ info, err_body, userId }, 'Failed to obtain access token from Git server') HttpErrorHandler.badRequest(req, res, err.message || 'Bad request') diff --git a/services/web/modules/gitlab-sync/app/src/GitLabSyncHandler.mjs b/services/web/modules/gitlab-sync/app/src/GitLabSyncHandler.mjs index 64427d2443..1507313a81 100644 --- a/services/web/modules/gitlab-sync/app/src/GitLabSyncHandler.mjs +++ b/services/web/modules/gitlab-sync/app/src/GitLabSyncHandler.mjs @@ -18,17 +18,16 @@ import { InvalidTokenError, NotFoundError } from './GitSyncErrors.mjs' async function getGitConnState(userId) { try { - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) return false; - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) return false; - await api.getUser(token) + await api.getUser(token) return true } catch (err) { if (err instanceof InvalidTokenError) { logger.debug( { err, userId }, 'token invalid, treating as not connected') return false - } + } throw OError.tag(err, 'failed to validate token', { userId }) } } @@ -52,9 +51,9 @@ async function getProjectState(userId, projectId) { } let canPush try { - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); + canPush = await api.getPushPermission(token, pss.repoFullName) } catch (err) { if ((err instanceof NotFoundError) || @@ -97,16 +96,14 @@ async function unlinkRepo(userId, projectId) { async function listUserRepos(userId) { - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); return await api.listUserRepos(token) } async function getUserAndOrgs(userId) { - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); return await api.getUserAndOrgs(token) } @@ -117,9 +114,8 @@ async function getMergeOverview(userId, projectId) { if (mergeStatus === 'conflict') return null - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); const currentVersion = await HistoryManager.latestVersion(projectId.toString()) const isProjectUpdated = currentVersion !== lastSyncVersion try { @@ -148,9 +144,8 @@ async function getMergeOverview(userId, projectId) { } async function importRepo(userId, projectName, repoFullName, defaultBranchName) { - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); const defaultBranchHead = await api.getBranchHead(token, repoFullName, defaultBranchName) const fsPath = Path.join(Settings.path.dumpFolder, `gitlab_import_${crypto.randomUUID()}`) @@ -203,9 +198,8 @@ async function exportProject(userId, projectId, repoOptions) { throw new OError('Project is already linked to Git server', { projectId }) } - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); const { path_with_namespace: repoFullName, default_branch: defaultBranchName } = await api.createRepo(token, repoOptions) diff --git a/services/web/modules/gitlab-sync/app/src/GitMerge.mjs b/services/web/modules/gitlab-sync/app/src/GitMerge.mjs index 7b9d7c8dfa..63a50e9ec0 100644 --- a/services/web/modules/gitlab-sync/app/src/GitMerge.mjs +++ b/services/web/modules/gitlab-sync/app/src/GitMerge.mjs @@ -40,9 +40,8 @@ async function doGitMergeWithoutLock(userId, projectId, message, claimConflictIs claimConflictIsResolved && mergeStatus !== 'conflict' ) return { mergeStatus, repoFullName, unmergedBranchName } - const tokens = await TokenManager.getUserToken(userId) - if (!tokens) throw new InvalidTokenError('no user token', { userId, status: 400 }); - const { token } = tokens + const token = await TokenManager.getUserToken(userId) + if (!token) throw new InvalidTokenError('no user token', { userId, status: 400 }); await DocumentUpdaterHandler.promises.flushProjectToMongo(projectId) @@ -278,8 +277,7 @@ async function resolveConflictSyncState( token, repoFullName, defaultBranchName, - newOlBranchHead, - tempBranchName, + tempBranchName ) if (conflict) { diff --git a/services/web/modules/gitlab-sync/app/src/TokenManager.mjs b/services/web/modules/gitlab-sync/app/src/TokenManager.mjs index ff59e79eba..efcf3699d6 100644 --- a/services/web/modules/gitlab-sync/app/src/TokenManager.mjs +++ b/services/web/modules/gitlab-sync/app/src/TokenManager.mjs @@ -24,61 +24,61 @@ async function decryptAccessToken(tokenEncrypted) { } } -async function refreshToken(userId, tokens) { - logger.info({ userId }, "refreshing GitLab token") +async function refreshToken(userId, decData) { + logger.info({ userId }, "refreshing GitLab token") - if (!tokens.refresh_token) { - logger.error("refresh_token invalid") - return false - } + if (!decData.refresh_token) { + logger.error("refresh_token invalid") + return false + } - let token - let refresh_token - let refresh_timestamp + let token + let refresh_token + let refresh_timestamp - try { - [token, refresh_token, refresh_timestamp] = await api.refreshToken(tokens.refresh_token) - if (!token || !refresh_token) { - HttpErrorHandler.badRequest(req, res, 'Failed to refresh access token') - return false - } - } catch (err) { - const info = OError.getFullInfo(err) - logger.error(OError.getFullStack(err)) - logger.error({ info, userId }, 'Failed to refresh access token') - HttpErrorHandler.badRequest(req, res, err.message || 'Bad request') - return false - } + try { + [token, refresh_token, refresh_timestamp] = await api.refreshToken(decData.refresh_token) + if (!token || !refresh_token) { + HttpErrorHandler.badRequest(req, res, 'Failed to refresh access token') + return false + } + } catch (err) { + const info = OError.getFullInfo(err) + logger.error(OError.getFullStack(err)) + logger.error({ info, userId }, 'Failed to refresh access token') + HttpErrorHandler.badRequest(req, res, err.message || 'Bad request') + return false + } - try { - await saveUserToken(userId, { token, refresh_token, refresh_timestamp }) - } catch (err) { - const info = OError.getFullInfo(err) - const errStatus = info?.status || 500 - logger.error(OError.getFullStack(err)) - logger.error({ info, userId }, 'Error saving user token') - HttpErrorHandler.handleErrorByStatusCode(req, res, err, errStatus) - return false - } + try { + await saveUserToken(userId, { token, refresh_token, refresh_timestamp }) + } catch (err) { + const info = OError.getFullInfo(err) + const errStatus = info?.status || 500 + logger.error(OError.getFullStack(err)) + logger.error({ info, userId }, 'Error saving user token') + HttpErrorHandler.handleErrorByStatusCode(req, res, err, errStatus) + return false + } - return true + return true } // ------------------------- exports -------------------------- // async function getUserToken(userId) { const credentials = await GitLabSyncUserCredentials.findOne(normalizeQuery({ userId })) if (!credentials) throw new InvalidTokenError('no user token', { userId, status: 400 }) - let tokens = await decryptAccessToken(credentials.gitlab) + let decData = await decryptAccessToken(credentials.gitlab) const now = Math.floor(Date.now() / 1000); - if (!tokens.refresh_timestamp || tokens.refresh_timestamp < now) { - const refreshed = await refreshToken(userId, tokens) - if (refreshed) { - return await getUserToken(userId) - } + if (!decData.refresh_timestamp || decData.refresh_timestamp < now) { + const refreshed = await refreshToken(userId, decData) + if (refreshed) { + return await getUserToken(userId) + } } - return tokens + return decData.token } async function saveUserToken(userId, accessToken) {