import Config from '../config/Config'; import { StatusCodes } from 'http-status-codes'; import GitlabVisibility from '../shared/types/Gitlab/GitlabVisibility'; import express from 'express'; import SharedConfig from '../shared/config/SharedConfig'; import { CommitSchema, ExpandedUserSchema, Gitlab, MemberSchema, ProjectBadgeSchema, ProjectSchema, ReleaseSchema, RepositoryFileExpandedSchema, RepositoryFileSchema, RepositoryTreeSchema } from '@gitbeaker/rest'; import logger from '../shared/logging/WinstonLogger'; import { AccessLevel, EditProjectOptions, ProjectVariableSchema, ProtectedBranchAccessLevel, ProtectedBranchSchema } from '@gitbeaker/core'; import DojoStatusCode from '../shared/types/Dojo/DojoStatusCode'; import SharedGitlabManager from '../shared/managers/SharedGitlabManager'; class GitlabManager extends SharedGitlabManager { constructor() { super(Config.gitlab.account.token); } getUserProfile(token: string): Promise<ExpandedUserSchema> | undefined { try { const profileApi = new Gitlab({ host : SharedConfig.gitlab.URL, oauthToken: token }); return profileApi.Users.showCurrentUser(); } catch ( e ) { return undefined; } } getRepositoryMembers(idOrNamespace: string): Promise<Array<MemberSchema>> { return this.api.ProjectMembers.all(idOrNamespace, { includeInherited: true }); } getRepositoryReleases(repoId: number): Promise<Array<ReleaseSchema>> { return this.api.ProjectReleases.all(repoId); } async getRepositoryLastCommit(repoId: number, branch: string = 'main'): Promise<CommitSchema | undefined> { try { const commits = await this.api.Commits.all(repoId, { refName : branch, maxPages: 1, perPage : 1 }); return commits.length > 0 ? commits[0] : undefined; } catch ( e ) { logger.error(JSON.stringify(e)); return undefined; } } createRepository(name: string, description: string, visibility: 'public' | 'internal' | 'private', initializeWithReadme: boolean, namespace: number, sharedRunnersEnabled: boolean, wikiEnabled: boolean, importUrl: string): Promise<ProjectSchema> { return this.api.Projects.create({ name : name, description : description, importUrl : importUrl, initializeWithReadme: initializeWithReadme, namespaceId : namespace, sharedRunnersEnabled: sharedRunnersEnabled, visibility : visibility, wikiAccessLevel : wikiEnabled ? 'enabled' : 'disabled' }); } deleteRepository(repoId: number): Promise<void> { return this.api.Projects.remove(repoId); } forkRepository(forkId: number, name: string, path: string, description: string, visibility: 'public' | 'internal' | 'private', namespace: number): Promise<ProjectSchema> { return this.api.Projects.fork(forkId, { name : name, path : path, description: description, namespaceId: namespace, visibility : visibility }); } editRepository(repoId: number, newAttributes: EditProjectOptions): Promise<ProjectSchema> { return this.api.Projects.edit(repoId, newAttributes); } changeRepositoryVisibility(repoId: number, visibility: GitlabVisibility): Promise<ProjectSchema> { return this.editRepository(repoId, { visibility: visibility }); } addRepositoryMember(repoId: number, userId: number, accessLevel: Exclude<AccessLevel, AccessLevel.ADMIN>): Promise<MemberSchema> { return this.api.ProjectMembers.add(repoId, userId, accessLevel); } addRepositoryVariable(repoId: number, key: string, value: string, isProtected: boolean, isMasked: boolean): Promise<ProjectVariableSchema> { return this.api.ProjectVariables.create(repoId, key, value, { variableType: 'env_var', protected : isProtected, masked : isMasked }); } addRepositoryBadge(repoId: number, linkUrl: string, imageUrl: string, name: string): Promise<ProjectBadgeSchema> { return this.api.ProjectBadges.add(repoId, linkUrl, imageUrl, { name: name }); } async checkTemplateAccess(projectIdOrNamespace: string, req: express.Request, res?: express.Response): Promise<boolean> { // Get the Gitlab project and check if it have public or internal visibility try { const project: ProjectSchema = await this.getRepository(projectIdOrNamespace); if ( [ 'public', 'internal' ].includes(project.visibility) ) { req.session.sendResponse(res, StatusCodes.OK); return true; } } catch ( e ) { req.session.sendResponse(res, StatusCodes.NOT_FOUND, undefined, 'Template not found', DojoStatusCode.GITLAB_TEMPLATE_NOT_FOUND); return false; } // Check if the user and dojo are members (with at least reporter access) of the project const members = await this.getRepositoryMembers(projectIdOrNamespace); const isUsersAtLeastReporter = { user: false, dojo: false }; members.forEach(member => { if ( member.access_level >= AccessLevel.REPORTER ) { if ( member.id === req.session.profile.id ) { isUsersAtLeastReporter.user = true; } else if ( member.id === Config.gitlab.account.id ) { isUsersAtLeastReporter.dojo = true; } } }); if ( isUsersAtLeastReporter.user && isUsersAtLeastReporter.dojo ) { req.session.sendResponse(res, StatusCodes.OK); return true; } else { req.session.sendResponse(res, StatusCodes.UNAUTHORIZED, undefined, 'Template access unauthorized', DojoStatusCode.GITLAB_TEMPLATE_ACCESS_UNAUTHORIZED); return false; } } protectBranch(repoId: number, branchName: string, allowForcePush: boolean, allowedToMerge: ProtectedBranchAccessLevel, allowedToPush: ProtectedBranchAccessLevel, allowedToUnprotect: ProtectedBranchAccessLevel): Promise<ProtectedBranchSchema> { return this.api.ProtectedBranches.protect(repoId, branchName, { allowForcePush : allowForcePush, mergeAccessLevel : allowedToMerge, pushAccessLevel : allowedToPush, unprotectAccessLevel: allowedToUnprotect }); } getRepositoryTree(repoId: number, recursive: boolean = true, branch: string = 'main'): Promise<Array<RepositoryTreeSchema>> { return this.api.Repositories.allRepositoryTrees(repoId, { recursive: recursive, ref : branch }); } getFile(repoId: number, filePath: string, branch: string = 'main'): Promise<RepositoryFileExpandedSchema> { return this.api.RepositoryFiles.show(repoId, filePath, branch); } private createUpdateFile(create: boolean, repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { const gitFunction = create ? this.api.RepositoryFiles.create.bind(this.api) : this.api.RepositoryFiles.edit.bind(this.api); return gitFunction(repoId, filePath, branch, fileBase64, commitMessage, { encoding : 'base64', authorName : authorName, authorEmail: authorMail }); } createFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { return this.createUpdateFile(true, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); } updateFile(repoId: number, filePath: string, fileBase64: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<RepositoryFileSchema> { return this.createUpdateFile(false, repoId, filePath, fileBase64, commitMessage, branch, authorName, authorMail); } deleteFile(repoId: number, filePath: string, commitMessage: string, branch: string = 'main', authorName: string = 'Dojo', authorMail: string | undefined = undefined): Promise<void> { return this.api.RepositoryFiles.remove(repoId, filePath, branch, commitMessage, { authorName : authorName, authorEmail: authorMail }); } } export default new GitlabManager();