Skip to content

Commit

Permalink
feat: Add API endpoints for file storage #2082
Browse files Browse the repository at this point in the history
Signed-off-by: Andrew Burnes <[email protected]>
  • Loading branch information
apburnes committed Feb 13, 2025
1 parent a95cc3a commit 1b126fc
Show file tree
Hide file tree
Showing 55 changed files with 5,028 additions and 70 deletions.
27 changes: 27 additions & 0 deletions api/admin/controllers/file-storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const EventCreator = require('../../services/EventCreator');
const { canAdminCreateSiteFileStorage } = require('../../authorizers/file-storage');
const { serializeFileStorageService } = require('../../serializers/file-storage');
const { wrapHandlers } = require('../../utils');
const { Event } = require('../../models');
const { adminCreateSiteFileStorage } = require('../../services/file-storage');

module.exports = wrapHandlers({
async createSiteFileStorage(req, res) {
const { params, user } = req;

const siteId = parseInt(params.id, 10);
const { site } = await canAdminCreateSiteFileStorage(siteId);

const fss = await adminCreateSiteFileStorage(site);

EventCreator.audit(
Event.labels.ADMIN,
user,
'Site File Storage Service Created',
fss,
);

const data = serializeFileStorageService(fss);
return res.send(data);
},
});
2 changes: 2 additions & 0 deletions api/admin/controllers/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const Build = require('./build');
const Domain = require('./domain');
const Event = require('./event');
const FileStorage = require('./file-storage');
const Organization = require('./organization');
const OrganizationRole = require('./organization-role');
const Role = require('./role');
Expand All @@ -13,6 +14,7 @@ module.exports = {
Build,
Domain,
Event,
FileStorage,
Organization,
OrganizationRole,
Role,
Expand Down
4 changes: 4 additions & 0 deletions api/admin/routers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ apiRouter.get('/sites', AdminControllers.Site.list);
apiRouter.get('/sites/raw', AdminControllers.Site.listRaw);
apiRouter.get('/sites/:id', AdminControllers.Site.findById);
apiRouter.put('/sites/:id', AdminControllers.Site.update);
apiRouter.post(
'/sites/:id/file-storage',
AdminControllers.FileStorage.createSiteFileStorage,
);
apiRouter.get('/sites/:id/webhooks', AdminControllers.Site.listWebhooks);
apiRouter.post('/sites/:id/webhooks', AdminControllers.Site.createWebhook);
apiRouter.delete('/sites/:id', authorize(['pages.admin']), AdminControllers.Site.destroy);
Expand Down
76 changes: 76 additions & 0 deletions api/authorizers/file-storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const siteErrors = require('../responses/siteErrors');
const { FileStorageService, Site } = require('../models');
const { isSiteOrgManager, isOrgManager, isOrgUser } = require('./utils');

const canCreateSiteStorage = async (userId, siteId) => {
const { site, organization } = await isSiteOrgManager(userId, siteId);

const fss = await FileStorageService.findOne({ where: { siteId } });

if (fss) {
throw {
status: 403,
message: siteErrors.SITE_FILE_STORAGE_EXISTS,
};
}

return { site, organization };
};

const canAdminCreateSiteFileStorage = async (siteId) => {
const site = await Site.findByPk(siteId);

if (!site) {
throw {
status: 403,
message: siteErrors.SITE_DOES_NOT_EXIST,
};
}

const fss = await FileStorageService.findOne({ where: { siteId } });

if (fss) {
throw {
status: 403,
message: siteErrors.SITE_FILE_STORAGE_EXISTS,
};
}

return { site };
};

async function hasFileStorage(fssId) {
const fileStorageService = await FileStorageService.findByPk(fssId);

if (!fileStorageService) {
throw {
status: 404,
message: siteErrors.NOT_FOUND,
};
}

return { fileStorageService };
}

async function isFileStorageManager(userId, fssId) {
const { fileStorageService } = await hasFileStorage(fssId);

const { organization } = await isOrgManager(userId, fileStorageService.organizationId);

return { organization, fileStorageService };
}

async function isFileStorageUser(userId, fssId) {
const { fileStorageService } = await hasFileStorage(fssId);

const { organization } = await isOrgUser(userId, fileStorageService.organizationId);

return { fileStorageService, organization };
}

module.exports = {
canAdminCreateSiteFileStorage,
canCreateSiteStorage,
isFileStorageManager,
isFileStorageUser,
};
42 changes: 7 additions & 35 deletions api/authorizers/site.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,7 @@
const GitHub = require('../services/GitHub');
const siteErrors = require('../responses/siteErrors');
const { Organization, Site } = require('../models');
const { fetchModelById } = require('../utils/queryDatabase');

const authorize = async ({ id: userId }, { id: siteId }) => {
const site = await fetchModelById(
siteId,
Site.forUser({
id: userId,
}),
);

if (!site) {
throw 403;
}

if (!site.isActive) {
// if site is not active
throw 403;
}

if (site.organizationId) {
// if site exists in an org
const org = await site.getOrganization();
if (!org.isActive) {
throw 403;
}
}

return site;
};
const { Organization } = require('../models');
const { authorize } = require('./utils');

const authorizeRepositoryAdmin = (user, site) =>
GitHub.checkPermissions(user, site.owner, site.repository)
Expand Down Expand Up @@ -101,16 +73,16 @@ const create = async (user, siteParams) => {
}
};

const createBuild = (user, site) => authorize(user, site);
const createBuild = (user, site) => authorize(user.id, site.id);

const showActions = (user, site) => authorize(user, site);
const showActions = (user, site) => authorize(user.id, site.id);

const findOne = (user, site) => authorize(user, site);
const findOne = (user, site) => authorize(user.id, site.id);

const update = (user, site) => authorize(user, site);
const update = (user, site) => authorize(user.id, site.id);

const destroy = (user, site) =>
authorize(user, site).then(() => authorizeRepositoryAdmin(user, site));
authorize(user.id, site.id).then(() => authorizeRepositoryAdmin(user, site));

module.exports = {
create,
Expand Down
90 changes: 90 additions & 0 deletions api/authorizers/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const siteErrors = require('../responses/siteErrors');
const { Organization, Site } = require('../models');
const { fetchModelById } = require('../utils/queryDatabase');

const authorize = async (userId, siteId) => {
const site = await fetchModelById(
siteId,
Site.forUser({
id: userId,
}),
);

if (!site) {
throw {
status: 404,
message: siteErrors.NOT_FOUND,
};
}

if (!site.isActive) {
// if site is not active
throw {
status: 403,
message: siteErrors.ORGANIZATION_INACTIVE,
};
}

if (site.organizationId) {
// if site exists in an org
const org = await site.getOrganization();
if (!org.isActive) {
throw {
status: 403,
message: siteErrors.ORGANIZATION_INACTIVE,
};
}
}

return site;
};

const isSiteOrgManager = async (userId, siteId) => {
const site = await authorize(userId, siteId);
const organization = await fetchModelById(
site.organizationId,
Organization.forManagerRole({ id: userId }),
);

if (!organization) {
throw {
status: 403,
message: siteErrors.ORGANIZATION_MANAGER_ACCESS,
};
}

return { site, organization };
};

const isOrgManager = async (userId, orgId) => {
const organization = await fetchModelById(
orgId,
Organization.forManagerRole({ id: userId }),
);

if (!organization) {
throw {
status: 403,
message: siteErrors.ORGANIZATION_MANAGER_ACCESS,
};
}

return { organization };
};

const isOrgUser = async (userId, orgId) => {
const organization = await fetchModelById(orgId, Organization.forUser({ id: userId }));

if (!organization) {
throw {
status: 403,
message: siteErrors.ORGANIZATION_USER_ACCESS,
};
}

return { organization };
};

const isSiteUser = authorize;

module.exports = { authorize, isOrgManager, isOrgUser, isSiteOrgManager, isSiteUser };
Loading

0 comments on commit 1b126fc

Please sign in to comment.