refactor(api): asEditor instead of copy-pasted admin/manager/root check

This commit is contained in:
41666 2021-07-17 19:07:10 -04:00
parent 0ed5d696df
commit 31ea2e2183
5 changed files with 105 additions and 223 deletions

View file

@ -1,51 +1,18 @@
import { UserGuildPermissions } from '@roleypoly/types';
import { isRoot, withSession } from '../utils/api-tools';
import { getGuild, GuildRateLimiterKey, useGuildRateLimiter } from '../utils/guild';
import {
lowPermissions,
missingParameters,
notFound,
ok,
rateLimited,
} from '../utils/responses';
import { asEditor, getGuild, GuildRateLimiterKey } from '../utils/guild';
import { notFound, ok } from '../utils/responses';
export const ClearGuildCache = withSession(
(session) =>
export const ClearGuildCache = asEditor(
{
rateLimitKey: GuildRateLimiterKey.cacheClear,
rateLimitTimeoutSeconds: 60 * 5,
},
(session, { guildID }) =>
async (request: Request): Promise<Response> => {
const url = new URL(request.url);
const [, , guildID] = url.pathname.split('/');
if (!guildID) {
return missingParameters();
}
const rateLimit = useGuildRateLimiter(
guildID,
GuildRateLimiterKey.cacheClear,
60 * 5
); // 5 minute RL TTL, 288 times per day.
if (!isRoot(session.user.id)) {
const guild = session.guilds.find((guild) => guild.id === guildID);
if (!guild) {
return notFound();
}
if (
guild?.permissionLevel !== UserGuildPermissions.Manager &&
guild?.permissionLevel !== UserGuildPermissions.Admin
) {
return lowPermissions();
}
if (await rateLimit()) {
return rateLimited();
}
}
const result = await getGuild(guildID, { skipCachePull: true });
if (!result) {
return notFound();
}
return ok();
}
);

View file

@ -1,83 +0,0 @@
import { CategoryType, Features, GuildData as GuildDataT } from '@roleypoly/types';
import KSUID from 'ksuid';
import { onlyRootUsers, respond } from '../utils/api-tools';
import { GuildData } from '../utils/kv';
// Temporary use.
export const CreateRoleypolyData = onlyRootUsers(
async (request: Request): Promise<Response> => {
const data: GuildDataT = {
id: '386659935687147521',
message:
'Hey, this is kind of a demo setup so features/use cases can be shown off.\n\nThanks for using Roleypoly <3',
features: Features.Preview,
auditLogWebhook: null,
categories: [
{
id: KSUID.randomSync().string,
name: 'Demo Roles',
type: CategoryType.Multi,
hidden: false,
position: 0,
roles: [
'557825026406088717',
'557824994269200384',
'557824893241131029',
'557812915386843170',
'557812901717737472',
'557812805546541066',
],
},
{
id: KSUID.randomSync().string,
name: 'Colors',
type: CategoryType.Single,
hidden: false,
position: 1,
roles: ['394060232893923349', '394060145799331851', '394060192846839809'],
},
{
id: KSUID.randomSync().string,
name: 'Test Roles',
type: CategoryType.Multi,
hidden: false,
position: 5,
roles: ['558104828216213505', '558103534453653514', '558297233582194728'],
},
{
id: KSUID.randomSync().string,
name: 'Region',
type: CategoryType.Multi,
hidden: false,
position: 3,
roles: [
'397296181803483136',
'397296137066774529',
'397296218809827329',
'397296267283267605',
],
},
{
id: KSUID.randomSync().string,
name: 'Opt-in Channels',
type: CategoryType.Multi,
hidden: false,
position: 4,
roles: ['414514823959674890', '764230661904007219'],
},
{
id: KSUID.randomSync().string,
name: 'Pronouns',
type: CategoryType.Multi,
hidden: false,
position: 2,
roles: ['485916566790340608', '485916566941335583', '485916566311927808'],
},
],
};
await GuildData.put(data.id, data);
return respond({ ok: true });
}
);

View file

@ -1,69 +1,15 @@
import {
Features,
GuildData as GuildDataT,
UserGuildPermissions,
} from '@roleypoly/types';
import { isRoot, withSession } from '../utils/api-tools';
import { GuildRateLimiterKey, useGuildRateLimiter } from '../utils/guild';
import { asEditor, GuildRateLimiterKey } from '../utils/guild';
import { fetchLegacyServer, transformLegacyGuild } from '../utils/import-from-legacy';
import { GuildData } from '../utils/kv';
import {
conflict,
lowPermissions,
missingParameters,
notFound,
ok,
rateLimited,
} from '../utils/responses';
import { notFound, ok } from '../utils/responses';
export const SyncFromLegacy = withSession(
(session) =>
export const SyncFromLegacy = asEditor(
{
rateLimitKey: GuildRateLimiterKey.legacyImport,
rateLimitTimeoutSeconds: 60 * 20,
},
(session, { guildID }) =>
async (request: Request): Promise<Response> => {
const url = new URL(request.url);
const [, , guildID] = url.pathname.split('/');
if (!guildID) {
return missingParameters();
}
const rateLimit = useGuildRateLimiter(
guildID,
GuildRateLimiterKey.legacyImport,
60 * 20
); // 20 minute RL TTL, 72 times per day.
// Allow root users to trigger this too, just in case.
if (!isRoot(session.user.id)) {
const guild = session.guilds.find((guild) => guild.id === guildID);
if (!guild) {
return notFound();
}
if (
guild?.permissionLevel !== UserGuildPermissions.Manager &&
guild?.permissionLevel !== UserGuildPermissions.Admin
) {
return lowPermissions();
}
if (await rateLimit()) {
return rateLimited();
}
}
const shouldForce = url.searchParams.get('force') === 'yes';
// Not using getGuildData as we want null feedback, not a zeroed out object.
const checkGuild = await GuildData.get<GuildDataT>(guildID);
// Don't force, and guild exists in our side, but LegacyGuild flag is set,
// fail this request.
if (
!shouldForce &&
checkGuild &&
(checkGuild.features & Features.LegacyGuild) === Features.LegacyGuild
) {
return conflict();
}
const legacyGuild = await fetchLegacyServer(guildID);
if (!legacyGuild) {
return notFound();

View file

@ -1,44 +1,15 @@
import { sendAuditLog, validateAuditLogWebhook } from '@roleypoly/api/utils/audit-log';
import {
GuildDataUpdate,
SessionData,
UserGuildPermissions,
WebhookValidationStatus,
} from '@roleypoly/types';
import { withSession } from '../utils/api-tools';
import { getGuildData } from '../utils/guild';
import { GuildDataUpdate, WebhookValidationStatus } from '@roleypoly/types';
import { asEditor, getGuildData } from '../utils/guild';
import { GuildData } from '../utils/kv';
import {
invalid,
lowPermissions,
missingParameters,
notFound,
ok,
} from '../utils/responses';
import { invalid, ok } from '../utils/responses';
export const UpdateGuild = withSession(
(session: SessionData) =>
export const UpdateGuild = asEditor(
{},
(session, { guildID, guild }) =>
async (request: Request): Promise<Response> => {
const url = new URL(request.url);
const [, , guildID] = url.pathname.split('/');
if (!guildID) {
return missingParameters();
}
const guildUpdate = (await request.json()) as GuildDataUpdate;
const guild = session.guilds.find((guild) => guild.id === guildID);
if (!guild) {
return notFound();
}
if (
guild?.permissionLevel !== UserGuildPermissions.Manager &&
guild?.permissionLevel !== UserGuildPermissions.Admin
) {
return lowPermissions();
}
const oldGuildData = await getGuildData(guildID);
const newGuildData = {
...oldGuildData,