mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Server: Add support for separate admin instance
This commit is contained in:
parent
c718706f9c
commit
d78ab16021
@ -307,7 +307,7 @@ async function main() {
|
||||
}
|
||||
|
||||
appLogger().info('Starting services...');
|
||||
await startServices(ctx.joplinBase.services);
|
||||
await startServices(config(), ctx.joplinBase.services);
|
||||
|
||||
appLogger().info(`Call this for testing: \`curl ${config().apiBaseUrl}/api/ping\``);
|
||||
|
||||
|
@ -23,6 +23,11 @@ const defaultEnvValues: EnvVariables = {
|
||||
COOKIES_SECURE: false,
|
||||
RUNNING_IN_DOCKER: false,
|
||||
|
||||
// The admin panel is accessible only if this is an admin instance.
|
||||
// Additionally, processing services (those defined in setupTaskService.ts)
|
||||
// only run on the admin instance.
|
||||
IS_ADMIN_INSTANCE: true,
|
||||
|
||||
// Maxiumm allowed drift between NTP time and server time. A few
|
||||
// milliseconds is normally not an issue unless many clients are modifying
|
||||
// the same note at the exact same time. But past a certain limit, it might
|
||||
@ -152,6 +157,8 @@ export interface EnvVariables {
|
||||
|
||||
USER_DATA_AUTO_DELETE_ENABLED: boolean;
|
||||
USER_DATA_AUTO_DELETE_AFTER_DAYS: number;
|
||||
|
||||
IS_ADMIN_INSTANCE: boolean;
|
||||
}
|
||||
|
||||
const parseBoolean = (s: string): boolean => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import config from '../config';
|
||||
import { ErrorForbidden } from '../utils/errors';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, koaNext, expectNotThrow, expectHttpError, createUserAndSession } from '../utils/testing/testUtils';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, koaNext, expectNotThrow, expectHttpError, createUserAndSession, models } from '../utils/testing/testUtils';
|
||||
import checkAdminHandler from './checkAdminHandler';
|
||||
|
||||
describe('checkAdminHandler', () => {
|
||||
@ -55,4 +56,26 @@ describe('checkAdminHandler', () => {
|
||||
await expectHttpError(async () => checkAdminHandler(context, koaNext), ErrorForbidden.httpCode);
|
||||
});
|
||||
|
||||
test('should not be able to perform requests if logged in as an admin on a non-admin instance', async () => {
|
||||
const { session } = await createUserAndSession(1, true);
|
||||
|
||||
const prev = config().IS_ADMIN_INSTANCE;
|
||||
config().IS_ADMIN_INSTANCE = false;
|
||||
|
||||
const context = await koaAppContext({
|
||||
sessionId: session.id,
|
||||
request: {
|
||||
method: 'GET',
|
||||
url: '/login',
|
||||
},
|
||||
});
|
||||
|
||||
await expectHttpError(async () => checkAdminHandler(context, koaNext), ErrorForbidden.httpCode);
|
||||
|
||||
// Should have been logged out too
|
||||
expect(await models().session().exists(session.id)).toBe(false);
|
||||
|
||||
config().IS_ADMIN_INSTANCE = prev;
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,11 +1,23 @@
|
||||
import { AppContext, KoaNext } from '../utils/types';
|
||||
import { isAdminRequest } from '../utils/requestUtils';
|
||||
import { ErrorForbidden } from '../utils/errors';
|
||||
import config from '../config';
|
||||
import webLogout from '../utils/webLogout';
|
||||
|
||||
export default async function(ctx: AppContext, next: KoaNext): Promise<void> {
|
||||
const owner = ctx.joplin.owner;
|
||||
|
||||
if (isAdminRequest(ctx)) {
|
||||
if (!ctx.joplin.owner) throw new ErrorForbidden();
|
||||
if (!ctx.joplin.owner.is_admin) throw new ErrorForbidden();
|
||||
if (!config().IS_ADMIN_INSTANCE) throw new ErrorForbidden();
|
||||
if (!owner || !owner.is_admin) throw new ErrorForbidden();
|
||||
}
|
||||
|
||||
// This can happen if an instance is switched from admin to non-admin. In
|
||||
// that case, the user is still logged in as an admin, but on a non-admin
|
||||
// instance so we log him out.
|
||||
if (owner && owner.is_admin && !config().IS_ADMIN_INSTANCE) {
|
||||
await webLogout(ctx);
|
||||
throw new ErrorForbidden();
|
||||
}
|
||||
|
||||
return next();
|
||||
|
@ -3,16 +3,12 @@ import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import config from '../../config';
|
||||
import { contextSessionId } from '../../utils/requestUtils';
|
||||
import { cookieSet } from '../../utils/cookies';
|
||||
import webLogout from '../../utils/webLogout';
|
||||
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.post('logout', async (_path: SubPath, ctx: AppContext) => {
|
||||
const sessionId = contextSessionId(ctx, false);
|
||||
cookieSet(ctx, 'sessionId', '');
|
||||
cookieSet(ctx, 'adminSessionId', '');
|
||||
await ctx.joplin.models.session().logout(sessionId);
|
||||
await webLogout(ctx);
|
||||
return redirect(ctx, `${config().baseUrl}/login`);
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,6 @@ import TaskService, { RunType, Task } from './TaskService';
|
||||
|
||||
const newService = () => {
|
||||
return new TaskService(Env.Dev, models(), config(), {
|
||||
share: null,
|
||||
email: null,
|
||||
mustache: null,
|
||||
tasks: null,
|
||||
|
@ -18,11 +18,12 @@ async function setupServices(env: Env, models: Models, config: Config): Promise<
|
||||
tasks: null,
|
||||
};
|
||||
|
||||
output.tasks = await setupTaskService(env, models, config, output),
|
||||
|
||||
await output.mustache.loadPartials();
|
||||
|
||||
await output.email.checkConfiguration();
|
||||
if (config.IS_ADMIN_INSTANCE) {
|
||||
await output.email.checkConfiguration();
|
||||
output.tasks = await setupTaskService(env, models, config, output);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { Services } from '../services/types';
|
||||
import { Config } from './types';
|
||||
|
||||
export default async function startServices(services: Services) {
|
||||
void services.tasks.runInBackground();
|
||||
export default async function startServices(config: Config, services: Services) {
|
||||
if (config.IS_ADMIN_INSTANCE) {
|
||||
void services.tasks.runInBackground();
|
||||
}
|
||||
}
|
||||
|
10
packages/server/src/utils/webLogout.ts
Normal file
10
packages/server/src/utils/webLogout.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { cookieSet } from './cookies';
|
||||
import { contextSessionId } from './requestUtils';
|
||||
import { AppContext } from './types';
|
||||
|
||||
export default async (ctx: AppContext) => {
|
||||
const sessionId = contextSessionId(ctx, false);
|
||||
cookieSet(ctx, 'sessionId', '');
|
||||
cookieSet(ctx, 'adminSessionId', '');
|
||||
await ctx.joplin.models.session().logout(sessionId);
|
||||
};
|
Loading…
Reference in New Issue
Block a user