diff --git a/components/Roles.vue b/components/Roles.vue
index 16ce6d381..2ce58359e 100644
--- a/components/Roles.vue
+++ b/components/Roles.vue
@@ -7,7 +7,7 @@
@@ -28,6 +28,8 @@ import useConfig from '../composables/useConfig.ts';
import useDialogue from '../composables/useDialogue.ts';
import allLocalesRaw from '../locale/locales.ts';
+import { PERMISSION_AREAS } from '~/src/helpers.js';
+
const allLocales = ['*'];
for (const { code } of allLocalesRaw) {
allLocales.push(code);
@@ -53,25 +55,7 @@ export default {
return { locale, area };
}),
allLocales,
- allAreas: [
- '*',
- 'basic',
- 'panel',
- 'users',
- 'sources',
- 'nouns',
- 'terms',
- 'inclusive',
- 'census',
- 'names',
- 'translations',
- 'code',
- 'org',
- 'impersonate',
- 'community',
- 'blog',
- 'external',
- ],
+ PERMISSION_AREAS,
};
},
methods: {
diff --git a/pages/admin/index.vue b/pages/admin/index.vue
index 2ab0b6cfb..4aed5e41b 100644
--- a/pages/admin/index.vue
+++ b/pages/admin/index.vue
@@ -130,7 +130,7 @@ const impersonate = async (email: string) => {
]"
/>
{
]"
/>
@@ -171,7 +171,7 @@ const impersonate = async (email: string) => {
{
{
}
const user = () => store.user;
- const isGranted = (area = '', locale = null): boolean => {
+ const isGranted = (area: PermissionArea = '', locale = null): boolean => {
return !!store.user &&
!!store.user.authenticated &&
isGrantedForUser(store.user, locale || config.locale, area)
diff --git a/server/api/admin/users.get.ts b/server/api/admin/users.get.ts
index bbecd9440..f54cd6dfb 100644
--- a/server/api/admin/users.get.ts
+++ b/server/api/admin/users.get.ts
@@ -31,7 +31,7 @@ export default defineEventHandler(async (event) => {
conditions.push(SQL`p.locale=${locale}`);
}
if (query.adminsFilter) {
- conditions.push(SQL`u.roles != ''`);
+ conditions.push(SQL`(u.roles != '' AND u.roles != '*-external')`);
}
let conditionsSql = SQL``;
diff --git a/server/global.d.ts b/server/global.d.ts
index 245949de9..8189639de 100644
--- a/server/global.d.ts
+++ b/server/global.d.ts
@@ -2,6 +2,7 @@ import type { LocaleDescription } from '../locale/locales.ts';
import type { User } from '../src/user.ts';
import type { Database } from './db.ts';
+import type { PermissionArea } from '~/src/helpers.ts';
declare global {
type GeneratorFunc = (...args: Args) => Generator;
@@ -10,7 +11,7 @@ declare global {
export interface Request {
rawUser: User | undefined;
user: User | null;
- isGranted: (area?: string, locale?: string) => boolean;
+ isGranted: (area?: PermissionArea, locale?: string) => boolean;
locales: Record;
db: Database;
}
diff --git a/server/notify.js b/server/notify.js
index c51193374..4a700aa34 100644
--- a/server/notify.js
+++ b/server/notify.js
@@ -37,7 +37,7 @@ async function notify() {
return;
}
- const admins = await db.all('SELECT email, roles, adminNotifications FROM users WHERE roles != \'\'');
+ const admins = await db.all('SELECT email, roles, adminNotifications FROM users WHERE (roles != \'\' and roles != \'*-external\')');
const awaitingModerationGrouped = {};
let count = 0;
diff --git a/server/utils/useAuthentication.ts b/server/utils/useAuthentication.ts
index 9eb55e080..943f42ddb 100644
--- a/server/utils/useAuthentication.ts
+++ b/server/utils/useAuthentication.ts
@@ -3,9 +3,10 @@ import type { H3Event } from 'h3';
import { getLocale } from '~/server/data.ts';
import jwt from '~/server/jwt.ts';
import { isGrantedForUser } from '~/src/helpers.ts';
+import type { PermissionArea } from '~/src/helpers.ts';
import type { User } from '~/src/user.ts';
-export type IsGrantedFn = (area?: string, locale?: string) => boolean;
+export type IsGrantedFn = (area?: PermissionArea, locale?: string) => boolean;
export default async (event: H3Event) => {
let rawUser = undefined;
@@ -22,7 +23,7 @@ export default async (event: H3Event) => {
const user = rawUser?.authenticated ? rawUser : null;
const requestedLocale = getLocale(event);
- const isGranted = (area: string = '', locale = requestedLocale): boolean => {
+ const isGranted = (area: PermissionArea = '', locale = requestedLocale): boolean => {
return !!user && isGrantedForUser(user, locale, area);
};
return { rawUser, user, isGranted };
diff --git a/src/helpers.ts b/src/helpers.ts
index c663b37ff..02ac17bfe 100644
--- a/src/helpers.ts
+++ b/src/helpers.ts
@@ -251,9 +251,31 @@ export const randomItemWeighted = (array: T[]): T => {
export const randomNumber = (min: number, max: number): number => Math.floor(random() * (max - min + 1)) + min;
+export const PERMISSION_AREAS = [
+ '*',
+ 'basic',
+ 'panel',
+ 'users',
+ 'sources',
+ 'nouns',
+ 'terms',
+ 'inclusive',
+ 'census',
+ 'names',
+ 'translations',
+ 'code',
+ 'org',
+ 'impersonate',
+ 'community',
+ 'blog',
+ 'external',
+] as const;
+
const RESTRICTED_AREAS = ['code', 'org', 'impersonate', 'community'];
-export const isGrantedForUser = (user: Pick, requestedLocale: string | null, requestedArea: string = ''): boolean => {
+export type PermissionArea = typeof PERMISSION_AREAS[number] | '';
+
+export const isGrantedForUser = (user: Pick, requestedLocale: string | null, requestedArea: PermissionArea = ''): boolean => {
if (requestedArea === '*') {
// only super-admin
return user.roles.split('|').includes('*');
@@ -542,8 +564,8 @@ export const deepSet = (obj: object, path: string, value: unknown): void => {
type AdminUser = Pick;
-export const findAdmins = async (db: Database, locale: string, area: string): Promise => {
- const admins = await db.all('SELECT username, email, roles, adminNotifications FROM users WHERE roles != \'\'');
+export const findAdmins = async (db: Database, locale: string, area: PermissionArea): Promise => {
+ const admins = await db.all('SELECT username, email, roles, adminNotifications FROM users WHERE (roles != \'\' AND roles != \'*-external\')');
return admins.filter((admin) => isGrantedForUser(admin, locale, area));
};
diff --git a/test/helpers.test.ts b/test/helpers.test.ts
index 1c612d4cc..416232cea 100644
--- a/test/helpers.test.ts
+++ b/test/helpers.test.ts
@@ -9,6 +9,8 @@ import {
isGrantedForUser,
} from '../src/helpers.ts';
+import type { PermissionArea } from '~/src/helpers.ts';
+
const controlSymbols = [
{
description: 'slashes',
@@ -150,7 +152,7 @@ class GrantTest {
constructor(
public readonly givenRoles: string,
public readonly requestedLocale: string,
- public readonly requestedArea: string,
+ public readonly requestedArea: PermissionArea,
public readonly granted: boolean,
) {}
}