mirror of https://github.com/laurent22/joplin.git
Server: Put admin pages under /admin
parent
fd322edaab
commit
09cbe3cb7c
|
@ -24,6 +24,7 @@ import { RouteResponseFormat, routeResponseFormat } from './utils/routeUtils';
|
|||
import { parseEnv } from './env';
|
||||
import storageConnectionCheck from './utils/storageConnectionCheck';
|
||||
import { setLocale } from '@joplin/lib/locale';
|
||||
import checkAdminHandler from './middleware/checkAdminHandler';
|
||||
|
||||
interface Argv {
|
||||
env?: Env;
|
||||
|
@ -196,6 +197,7 @@ async function main() {
|
|||
|
||||
app.use(apiVersionHandler);
|
||||
app.use(ownerHandler);
|
||||
app.use(checkAdminHandler);
|
||||
app.use(notificationHandler);
|
||||
app.use(clickJackingHandler);
|
||||
app.use(routeHandler);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// The possible env variables and their defaults are listed below.
|
||||
//
|
||||
// The env variables can be of type string, integer or boolean. When the type is
|
||||
// boolean, set the variable to "0" or "1" in your env file.
|
||||
// boolean, set the variable to "true", "false", "0" or "1" in your env file.
|
||||
|
||||
export enum MailerSecurity {
|
||||
None = 'none',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import { ErrorForbidden } from '../utils/errors';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, koaNext, expectNotThrow, expectHttpError, createUserAndSession } from '../utils/testing/testUtils';
|
||||
import checkAdminHandler from './checkAdminHandler';
|
||||
|
||||
describe('checkAdminHandler', function() {
|
||||
|
||||
beforeAll(async () => {
|
||||
await beforeAllDb('checkAdminHandler');
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await afterAllTests();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await beforeEachDb();
|
||||
});
|
||||
|
||||
test('should access /admin if the user is admin', async function() {
|
||||
const { session } = await createUserAndSession(1, true);
|
||||
|
||||
const context = await koaAppContext({
|
||||
sessionId: session.id,
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/admin/organizations',
|
||||
},
|
||||
});
|
||||
|
||||
await expectNotThrow(async () => checkAdminHandler(context, koaNext));
|
||||
});
|
||||
|
||||
test('should not access /admin if the user is not admin', async function() {
|
||||
const { session } = await createUserAndSession(1);
|
||||
|
||||
const context = await koaAppContext({
|
||||
sessionId: session.id,
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/admin/organizations',
|
||||
},
|
||||
});
|
||||
|
||||
await expectHttpError(async () => checkAdminHandler(context, koaNext), ErrorForbidden.httpCode);
|
||||
});
|
||||
|
||||
test('should not access /admin if the user is not logged in', async function() {
|
||||
const context = await koaAppContext({
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/admin/organizations',
|
||||
},
|
||||
});
|
||||
|
||||
await expectHttpError(async () => checkAdminHandler(context, koaNext), ErrorForbidden.httpCode);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import { AppContext, KoaNext } from '../utils/types';
|
||||
import { isAdminRequest } from '../utils/requestUtils';
|
||||
import { ErrorForbidden } from '../utils/errors';
|
||||
|
||||
export default async function(ctx: AppContext, next: KoaNext): Promise<void> {
|
||||
if (isAdminRequest(ctx)) {
|
||||
if (!ctx.joplin.owner) throw new ErrorForbidden();
|
||||
if (!ctx.joplin.owner.is_admin) throw new ErrorForbidden();
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
|
@ -92,7 +92,8 @@ export default class NotificationModel extends BaseModel<Notification> {
|
|||
return this.add(userId, NotificationKey.Any, NotificationLevel.Normal, message);
|
||||
}
|
||||
|
||||
public async addError(userId: Uuid, message: string) {
|
||||
public async addError(userId: Uuid, error: string | Error) {
|
||||
const message = typeof error === 'string' ? error : error.message;
|
||||
return this.add(userId, NotificationKey.Any, NotificationLevel.Error, message);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,10 @@ export function isApiRequest(ctx: AppContext): boolean {
|
|||
return ctx.path.indexOf('/api/') === 0;
|
||||
}
|
||||
|
||||
export function isAdminRequest(ctx: AppContext): boolean {
|
||||
return ctx.path.indexOf('/admin/') === 0;
|
||||
}
|
||||
|
||||
export function userIp(ctx: AppContext): string {
|
||||
if (ctx.headers['x-real-ip']) return ctx.headers['x-real-ip'];
|
||||
return ctx.ip;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isValidOrigin, parseSubPath, splitItemPath } from './routeUtils';
|
||||
import { findMatchingRoute, isValidOrigin, parseSubPath, splitItemPath } from './routeUtils';
|
||||
import { ItemAddressingType } from '../services/database/types';
|
||||
import { RouteType } from './types';
|
||||
|
||||
|
@ -26,6 +26,58 @@ describe('routeUtils', function() {
|
|||
}
|
||||
});
|
||||
|
||||
it('should find a matching route', async function() {
|
||||
const testCases: any[] = [
|
||||
['/admin/organizations', {
|
||||
route: 1,
|
||||
basePath: 'admin/organizations',
|
||||
subPath: {
|
||||
id: '',
|
||||
link: '',
|
||||
addressingType: 1,
|
||||
raw: '',
|
||||
schema: 'admin/organizations',
|
||||
},
|
||||
}],
|
||||
|
||||
['/api/users/123', {
|
||||
route: 2,
|
||||
basePath: 'api/users',
|
||||
subPath: {
|
||||
id: '123',
|
||||
link: '',
|
||||
addressingType: 1,
|
||||
raw: '123',
|
||||
schema: 'api/users/:id',
|
||||
},
|
||||
}],
|
||||
|
||||
['/help', {
|
||||
route: 3,
|
||||
basePath: 'help',
|
||||
subPath: {
|
||||
id: '',
|
||||
link: '',
|
||||
addressingType: 1,
|
||||
raw: '',
|
||||
schema: 'help',
|
||||
},
|
||||
}],
|
||||
];
|
||||
|
||||
const routes: Record<string, any> = {
|
||||
'admin/organizations': 1,
|
||||
'api/users': 2,
|
||||
'help': 3,
|
||||
};
|
||||
|
||||
for (const testCase of testCases) {
|
||||
const [path, expected] = testCase;
|
||||
const actual = findMatchingRoute(path, routes);
|
||||
expect(actual).toEqual(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should split an item path', async function() {
|
||||
const testCases: any[] = [
|
||||
['root:/Documents/MyFile.md:', ['root', 'Documents', 'MyFile.md']],
|
||||
|
|
Loading…
Reference in New Issue