From a9d9782fdb3861b91b87afa8bbfcbc404960a3e0 Mon Sep 17 00:00:00 2001 From: Drew Bollinger Date: Fri, 22 Nov 2024 12:23:41 -0500 Subject: [PATCH] chore: remove jobs.js (#4677) --- api/jobs.js | 33 --- api/services/SiteUserAuditor.js | 144 ------------- index.js | 2 - package.json | 1 - .../api/unit/services/SiteUserAuditor.test.js | 190 ------------------ yarn.lock | 21 +- 6 files changed, 1 insertion(+), 390 deletions(-) delete mode 100644 api/jobs.js delete mode 100644 api/services/SiteUserAuditor.js delete mode 100644 test/api/unit/services/SiteUserAuditor.test.js diff --git a/api/jobs.js b/api/jobs.js deleted file mode 100644 index ae459a223..000000000 --- a/api/jobs.js +++ /dev/null @@ -1,33 +0,0 @@ -const schedule = require('node-schedule'); - -const { logger } = require('../winston'); - -const SiteUserAuditor = require('./services/SiteUserAuditor'); -const EventCreator = require('./services/EventCreator'); -const { Event } = require('./models'); - -const { CF_INSTANCE_INDEX } = process.env; - -function handleResults(results) { - const errors = results.filter((r) => r.status === 'rejected').map((r) => r.reason); - if (errors.length === 0) { - return; - } - - errors.forEach((error) => { - EventCreator.error(Event.labels.SITE_USER, error); - logger.error(error); - }); -} - -function scheduleJobs() { - if (CF_INSTANCE_INDEX === '0') { - // audit users and remove sites w/o repo push permissions - schedule.scheduleJob('15 0 * * *', () => { - logger.info('Auditing All Sites'); - SiteUserAuditor.auditAllSites().then(handleResults); - }); - } -} - -module.exports = scheduleJobs; diff --git a/api/services/SiteUserAuditor.js b/api/services/SiteUserAuditor.js deleted file mode 100644 index 1b9d5392d..000000000 --- a/api/services/SiteUserAuditor.js +++ /dev/null @@ -1,144 +0,0 @@ -const Sequelize = require('sequelize'); -const { User, Site, Event } = require('../models'); -const GitHub = require('./GitHub'); -const { logger } = require('../../winston'); -const UserActionCreator = require('./UserActionCreator'); -const EventCreator = require('./EventCreator'); - -const auditUser = (user, auditor) => { - let repos; - return GitHub.getRepositories(user.githubAccessToken) - .then((_repos) => { - repos = _repos; - return user.getSites(); - }) - .then((sites) => { - const removed = []; - sites.forEach((site) => { - const fullName = [site.owner, site.repository].join('/').toUpperCase(); - const repoFound = repos.find((repo) => repo.full_name.toUpperCase() === fullName); - if (!repoFound || !repoFound.permissions.push) { - // site does not have push permissions - const r = site - .removeUser(user) - .then(() => - UserActionCreator.addRemoveAction({ - userId: auditor.id, - targetId: user.id, - targetType: 'user', - siteId: site.id, - }), - ) - .then(() => { - const message = - 'Removed user from site. User does not have write permisisons'; - EventCreator.audit(Event.labels.SITE_USER, user, message, { - siteId: site.id, - }); - }); - removed.push(r); - } - }); - return Promise.all(removed); - }); -}; - -const auditAllUsers = () => { - let auditor; - return User.findOne({ - where: { - username: process.env.USER_AUDITOR, - }, - }) - .then((model) => { - auditor = model; - return User.findAll({ - attributes: ['id', 'username', 'githubAccessToken', 'signedInAt'], - where: { - githubAccessToken: { - [Sequelize.Op.ne]: null, - }, - signedInAt: { - [Sequelize.Op.ne]: null, - }, - }, - order: [['signedInAt', 'DESC']], - }); - }) - .then((users) => Promise.all(users.map((user) => auditUser(user, auditor)))); -}; - -const auditSite = (auditor, site, userIndex = 0) => { - let collaborators; - const user = site.Users[userIndex]; - if (!user) { - return Promise.resolve(); - } - - return GitHub.getCollaborators(user.githubAccessToken, site.owner, site.repository) - .then((collabs) => { - collaborators = collabs; - }) - .catch(logger.warn) - .then(() => { - if (collaborators && collaborators.length > 0) { - const pushCollabs = collaborators - .filter((c) => c.permissions.push) - .map((c) => c.login.toLowerCase()); - const usersToRemove = site.Users.filter((u) => !pushCollabs.includes(u.username)); - const removed = []; - usersToRemove.forEach((u) => { - const r = site - .removeUser(u) - .then(() => - UserActionCreator.addRemoveAction({ - userId: auditor.id, - targetId: u.id, - targetType: 'user', - siteId: site.id, - }), - ) - .then(() => { - EventCreator.audit( - Event.labels.SITE_USER, - user, - 'Removed user from site. User does not have write permissions', - { - siteId: site.id, - }, - ); - }); - - removed.push(r); - }); - return Promise.all(removed); - } - return auditSite(auditor, site, userIndex + 1); - }); -}; - -const auditAllSites = async () => { - const auditor = await User.findOne({ - where: { - username: process.env.USER_AUDITOR, - }, - }); - const sites = await Site.findAll({ - attributes: ['id', 'owner', 'repository'], - include: [ - { - model: User.scope('withGithub'), - attributes: ['id', 'username', 'githubAccessToken', 'signedInAt'], - }, - ], - order: [[User, 'signedInAt', 'DESC']], - }); - const auditedSites = sites.map((site) => auditSite(auditor, site)); - return Promise.allSettled(auditedSites); -}; - -module.exports = { - auditAllUsers, - auditAllSites, - auditUser, -}; diff --git a/index.js b/index.js index 803247399..65197d895 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,11 @@ const { logger } = require('./winston'); const server = require('./api/server'); -const scheduleJobs = require('./api/jobs'); const socketIO = require('./api/socketIO'); const { PORT = 1337 } = process.env; require('./app'); -scheduleJobs(); socketIO.init(server); server.listen(PORT, () => { diff --git a/package.json b/package.json index 49be2897a..141041bee 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "jsonwebtoken": "^9.0.2", "lodash.merge": "^4.6.2", "moment": "^2.29.2", - "node-schedule": "^2.1.1", "nunjucks": "^3.2.4", "passport": "^0.7.0", "passport-github": "^1.1.0", diff --git a/test/api/unit/services/SiteUserAuditor.test.js b/test/api/unit/services/SiteUserAuditor.test.js deleted file mode 100644 index d8ee861ea..000000000 --- a/test/api/unit/services/SiteUserAuditor.test.js +++ /dev/null @@ -1,190 +0,0 @@ -const { expect } = require('chai'); -const proxyquire = require('proxyquire').noCallThru(); -const factory = require('../../support/factory'); -const MockGitHub = require('../../support/mockGitHub'); -const { SiteUser, User, UserAction } = require('../../../../api/models'); - -const SiteUserAuditor = proxyquire('../../../../api/services/SiteUserAuditor', { - './GitHub': MockGitHub, -}); - -describe('SiteUserAuditor', () => { - before(() => - factory.user({ - username: process.env.USER_AUDITOR, - }), - ); - after(() => - User.truncate({ - force: true, - cascade: true, - }), - ); - - context('auditAllUsers', () => { - it('it should remove sites without push from user and site w/o repo', (done) => { - let user; - - factory - .user() - .then((model) => { - user = model; - return Promise.resolve(); - }) - .then(() => MockGitHub.getRepositories(user.githubAccessToken)) - .then((repositories) => { - const sites = []; - repositories.forEach((r) => { - const fullName = r.full_name.split('/'); - const owner = fullName[0]; - const repository = fullName[1]; - sites.push( - factory.site({ - owner, - repository, - users: [user], - }), - ); - }); - sites.push( - factory.site({ - owner: 'owner', - repository: 'remove-repo', - users: [user], - }), - ); - return Promise.all(sites); - }) - .then(() => - UserAction.findAll({ - where: { - targetId: user.id, - }, - }), - ) - .then((userActions) => { - expect(userActions.length).to.eql(0); - return SiteUser.findAll({ - where: { - user_sites: user.id, - }, - }); - }) - .then((siteUsers) => { - expect(siteUsers.length).to.eql(11); - return SiteUserAuditor.auditAllUsers(); - }) - .then(() => - SiteUser.findAll({ - where: { - user_sites: user.id, - }, - }), - ) - .then((siteUsers) => { - expect(siteUsers.length).to.eql(9); - return UserAction.findAll({ - where: { - targetId: user.id, - }, - }); - }) - .then((userActions) => { - expect(userActions.length).to.eql(2); - done(); - }) - .catch(done); - }); - }); - - context('auditAllSites', () => { - it('should remove user from site if not push collaborator', (done) => { - let site; - const owner = 'owner'; - const repository = 'repo'; - - MockGitHub.getCollaborators('githubAccessToken', owner, repository) - .then((collabos) => { - const users = []; - const signedInAt = new Date('2011-01-30'); - collabos.forEach((c) => - users.push( - factory.user({ - username: c.login, - signedInAt, - }), - ), - ); - users.push( - factory.user({ - username: 'non-collab1', - githubAccessToken: 'reject', - }), - ); - users.push( - factory.user({ - username: 'non-collab2', - signedInAt, - }), - ); - return Promise.all(users); - }) - .then((users) => - factory.site({ - owner, - repository, - users, - }), - ) - .then((model) => { - site = model; - return SiteUser.findAll({ - where: { - site_users: site.id, - }, - }); - }) - .then((siteUsers) => { - expect(siteUsers.length).to.eql(12); - return UserAction.findAll({ - where: { - siteId: site.id, - }, - }); - }) - .then((userActions) => { - expect(userActions.length).to.eql(0); - return SiteUserAuditor.auditAllSites(); - }) - .then(() => - SiteUser.findAll({ - where: { - site_users: site.id, - }, - }), - ) - .then((siteUsers) => expect(siteUsers.length).to.eql(9)) - .then(() => SiteUserAuditor.auditAllSites()) - .then(() => - SiteUser.findAll({ - where: { - site_users: site.id, - }, - }), - ) - .then((siteUsers) => { - expect(siteUsers.length).to.eql(9); - return UserAction.findAll({ - where: { - siteId: site.id, - }, - }); - }) - .then((userActions) => { - expect(userActions.length).to.eql(3); - done(); - }) - .catch(done); - }); - }); -}); diff --git a/yarn.lock b/yarn.lock index cf769974d..73d02d864 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5097,7 +5097,7 @@ create-react-class@^15.5.1: loose-envify "^1.3.1" object-assign "^4.1.1" -cron-parser@^4.2.0, cron-parser@^4.6.0: +cron-parser@^4.6.0: version "4.9.0" resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== @@ -8712,11 +8712,6 @@ logform@^2.7.0: safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" -long-timeout@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/long-timeout/-/long-timeout-0.1.1.tgz#9721d788b47e0bcb5a24c2e2bee1a0da55dab514" - integrity sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -9207,15 +9202,6 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -node-schedule@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/node-schedule/-/node-schedule-2.1.1.tgz#6958b2c5af8834954f69bb0a7a97c62b97185de3" - integrity sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ== - dependencies: - cron-parser "^4.2.0" - long-timeout "0.1.1" - sorted-array-functions "^1.3.0" - nodemon@^3.0.1: version "3.1.7" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.7.tgz#07cb1f455f8bece6a499e0d72b5e029485521a54" @@ -11049,11 +11035,6 @@ socket.io@^4.7.2: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" -sorted-array-functions@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz#8605695563294dffb2c9796d602bd8459f7a0dd5" - integrity sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA== - source-list-map@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"