Refactored permissions check. Updated redis lib.

This commit is contained in:
Mike Cao 2022-12-01 10:58:50 -08:00
parent 1a7369e1f6
commit a4e80ca3e5
5 changed files with 58 additions and 15 deletions

View File

@ -120,3 +120,23 @@ export async function checkPermission(req: NextApiRequestAuth, type: UmamiApi.Pe
return userRole.length > 0;
}
export async function canViewWebsite(userId: string, websiteId: string) {
const website = await cache.fetchWebsite(websiteId);
if (website.userId) {
return userId === website.userId;
}
return false;
}
export async function canUpdateWebsite(userId: string, websiteId: string) {
const website = await cache.fetchWebsite(websiteId);
if (website.userId) {
return userId === website.userId;
}
return false;
}

View File

@ -1,5 +1,5 @@
import { User, Website } from '@prisma/client';
import redis, { DELETED } from 'lib/redis';
import redis from 'lib/redis';
import { getSession, getUser, getWebsite } from '../queries';
async function fetchObject(key, query) {
@ -23,7 +23,7 @@ async function storeObject(key, data) {
}
async function deleteObject(key) {
return redis.set(key, DELETED);
return redis.set(key, redis.DELETED);
}
async function fetchWebsite(id): Promise<Website> {

View File

@ -34,6 +34,27 @@ export namespace UmamiApi {
TeamGuest = 'Team Guest,',
}
}
export const PERMISSIONS = {
all: 'all',
websiteCreate: 'website:create',
websiteUpdate: 'website:update',
websiteDelete: 'website:delete',
teamCreate: 'team:create',
teamUpdate: 'team:update',
teamDelete: 'team:delete',
};
export const ROLES = {
admin: { name: 'admin', permissions: [PERMISSIONS.all] },
teamOwner: { name: 'team-owner', permissions: [PERMISSIONS.teamUpdate, PERMISSIONS.teamDelete] },
teamMember: {
name: 'team-member',
permissions: [PERMISSIONS.websiteCreate, PERMISSIONS.websiteUpdate, PERMISSIONS.websiteDelete],
},
teamGuest: { name: 'team-guest' },
};
export const CURRENT_VERSION = process.env.currentVersion;
export const AUTH_TOKEN = 'umami.auth';
export const LOCALE_CONFIG = 'umami.locale';
@ -57,9 +78,6 @@ export const DEFAULT_WEBSITE_LIMIT = 10;
export const REALTIME_RANGE = 30;
export const REALTIME_INTERVAL = 3000;
export const TYPE_WEBSITE = 'website';
export const TYPE_USER = 'user';
export const THEME_COLORS = {
light: {
primary: '#2680eb',

View File

@ -3,16 +3,18 @@ import debug from 'debug';
const log = debug('umami:redis');
const REDIS = Symbol();
const DELETED = 'DELETED';
let redis;
const enabled = Boolean(process.env.REDIS_URL);
const url = process.env.REDIS_URL;
const enabled = Boolean(url);
async function getClient() {
if (!process.env.REDIS_URL) {
if (!enabled) {
return null;
}
const client = createClient({ url: process.env.REDIS_URL });
const client = createClient({ url });
client.on('error', err => log(err));
await client.connect();
@ -59,4 +61,4 @@ async function connect() {
return redis;
}
export default { enabled, client: redis, log, connect, get, set, del };
export default { enabled, client: redis, log, connect, get, set, del, DELETED };

View File

@ -1,7 +1,6 @@
import { Website } from 'interface/api/models';
import { NextApiRequestQueryBody } from 'interface/api/nextApi';
import { allowQuery } from 'lib/auth';
import { UmamiApi } from 'lib/constants';
import { canViewWebsite, canUpdateWebsite } from 'lib/auth';
import { useAuth, useCors } from 'lib/middleware';
import { NextApiResponse } from 'next';
import { methodNotAllowed, ok, serverError, unauthorized } from 'next-basics';
@ -26,17 +25,21 @@ export default async (
const { id: websiteId } = req.query;
if (!(await allowQuery(req, UmamiApi.AuthType.Website))) {
return unauthorized(res);
}
if (req.method === 'GET') {
if (!(await canViewWebsite(req.auth.user.id, websiteId))) {
return unauthorized(res);
}
const website = await getWebsite({ id: websiteId });
return ok(res, website);
}
if (req.method === 'POST') {
if (!(await canUpdateWebsite(req.auth.user.id, websiteId))) {
return unauthorized(res);
}
const { name, domain, shareId } = req.body;
try {