mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-14 18:27:44 +02:00
Server: Disallow changing email address until a secure solution to change it is implemented
This commit is contained in:
parent
67a000add9
commit
f8d2c26c8e
@ -40,6 +40,9 @@ export interface EnvVariables {
|
||||
ACCOUNT_TYPES_ENABLED?: string;
|
||||
|
||||
ERROR_STACK_TRACES?: string;
|
||||
|
||||
SUPPORT_EMAIL?: string;
|
||||
BUSINESS_EMAIL?: string;
|
||||
}
|
||||
|
||||
let runningInDocker_: boolean = false;
|
||||
@ -135,6 +138,7 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any
|
||||
const viewDir = `${rootDir}/src/views`;
|
||||
const appPort = env.APP_PORT ? Number(env.APP_PORT) : 22300;
|
||||
const baseUrl = baseUrlFromEnv(env, appPort);
|
||||
const supportEmail = env.SUPPORT_EMAIL || 'admin@localhost';
|
||||
|
||||
config_ = {
|
||||
appVersion: packageJson.version,
|
||||
@ -156,6 +160,8 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any
|
||||
signupEnabled: env.SIGNUP_ENABLED === '1',
|
||||
termsEnabled: env.TERMS_ENABLED === '1',
|
||||
accountTypesEnabled: env.ACCOUNT_TYPES_ENABLED === '1',
|
||||
supportEmail,
|
||||
businessEmail: env.BUSINESS_EMAIL || supportEmail,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ export default async function(ctx: AppContext) {
|
||||
notifications: ctx.joplin.notifications || [],
|
||||
hasNotifications: !!ctx.joplin.notifications && !!ctx.joplin.notifications.length,
|
||||
owner: ctx.joplin.owner,
|
||||
supportEmail: config().supportEmail,
|
||||
});
|
||||
} else {
|
||||
ctx.response.status = 200;
|
||||
|
@ -132,15 +132,18 @@ export default class UserModel extends BaseModel<User> {
|
||||
const previousResource = await this.load(resource.id);
|
||||
|
||||
if (!user.is_admin && resource.id !== user.id) throw new ErrorForbidden('non-admin user cannot modify another user');
|
||||
if (!user.is_admin && 'is_admin' in resource) throw new ErrorForbidden('non-admin user cannot make themselves an admin');
|
||||
if (user.is_admin && user.id === resource.id && 'is_admin' in resource && !resource.is_admin) throw new ErrorForbidden('admin user cannot make themselves a non-admin');
|
||||
|
||||
// TODO: Maybe define a whitelist of properties that can be changed
|
||||
if ('max_item_size' in resource && !user.is_admin && resource.max_item_size !== previousResource.max_item_size) throw new ErrorForbidden('non-admin user cannot change max_item_size');
|
||||
if ('max_total_item_size' in resource && !user.is_admin && resource.max_total_item_size !== previousResource.max_total_item_size) throw new ErrorForbidden('non-admin user cannot change max_total_item_size');
|
||||
if ('can_share_folder' in resource && !user.is_admin && resource.can_share_folder !== previousResource.can_share_folder) throw new ErrorForbidden('non-admin user cannot change can_share_folder');
|
||||
if ('account_type' in resource && !user.is_admin && resource.account_type !== previousResource.account_type) throw new ErrorForbidden('non-admin user cannot change account_type');
|
||||
if ('must_set_password' in resource && !user.is_admin && resource.must_set_password !== previousResource.must_set_password) throw new ErrorForbidden('non-admin user cannot change must_set_password');
|
||||
const canBeChangedByNonAdmin = [
|
||||
'full_name',
|
||||
'password',
|
||||
];
|
||||
|
||||
for (const key of Object.keys(resource)) {
|
||||
if (!user.is_admin && !canBeChangedByNonAdmin.includes(key) && (resource as any)[key] !== (previousResource as any)[key]) {
|
||||
throw new ErrorForbidden(`non-admin user cannot change "${key}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action === AclAction.Delete) {
|
||||
|
@ -3,6 +3,8 @@ import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import MarkdownIt = require('markdown-it');
|
||||
import config from '../../config';
|
||||
import markdownUtils from '@joplin/lib/markdownUtils';
|
||||
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
router.public = true;
|
||||
@ -13,7 +15,7 @@ router.get('privacy', async (_path: SubPath, _ctx: AppContext) => {
|
||||
|
||||
## Who are we?
|
||||
|
||||
The Joplin Cloud web service is owned by Cozid Ltd, registered in England and Wales.
|
||||
The Joplin Cloud web service is owned by Cozic Ltd, registered in England and Wales.
|
||||
|
||||
## What information do we collect?
|
||||
|
||||
@ -55,7 +57,7 @@ We keep your data for as long as you use the service. If you would like to stop
|
||||
|
||||
## How to contact us?
|
||||
|
||||
Please contact us at [team@joplincloud.com](mailto:team@joplincloud.com) for any question.`);
|
||||
Please contact us at [${markdownUtils.escapeTitleText(config().supportEmail)}](mailto:${markdownUtils.escapeLinkUrl(config().supportEmail)}) for any question.`);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -3,6 +3,8 @@ import Router from '../../utils/Router';
|
||||
import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import MarkdownIt = require('markdown-it');
|
||||
import markdownUtils from '@joplin/lib/markdownUtils';
|
||||
import config from '../../config';
|
||||
|
||||
const router: Router = new Router(RouteType.Web);
|
||||
router.public = true;
|
||||
@ -35,7 +37,7 @@ The use of this website is subject to the following terms of use:
|
||||
|
||||
- Your use of this website and any dispute arising out of such use of the website is subject to the laws of England, Northern Ireland, Scotland and Wales.
|
||||
|
||||
- Please contact us at [team@joplincloud.com](mailto:team@joplincloud.com) for any question.`);
|
||||
- Please contact us at [${markdownUtils.escapeTitleText(config().supportEmail)}](mailto:${markdownUtils.escapeLinkUrl(config().supportEmail)}) for any question.`);
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
@ -163,9 +163,9 @@ describe('index/users', function() {
|
||||
|
||||
const userModel = models().user();
|
||||
|
||||
await patchUser(session.id, { id: user.id, email: 'test2@example.com' });
|
||||
await patchUser(session.id, { id: user.id, full_name: 'new name' });
|
||||
const modUser: User = await userModel.load(user.id);
|
||||
expect(modUser.email).toBe('test2@example.com');
|
||||
expect(modUser.full_name).toBe('new name');
|
||||
});
|
||||
|
||||
test('should change the password', async function() {
|
||||
@ -340,6 +340,9 @@ describe('index/users', function() {
|
||||
// non-admin cannot change can_share_folder
|
||||
await models().user().save({ id: user1.id, can_share_folder: 0 });
|
||||
await expectHttpError(async () => patchUser(session1.id, { id: user1.id, can_share_folder: 1 }), ErrorForbidden.httpCode);
|
||||
|
||||
// non-admin cannot change non-whitelisted properties
|
||||
await expectHttpError(async () => patchUser(session1.id, { id: user1.id, email: 'candothat@example.com' }), ErrorForbidden.httpCode);
|
||||
});
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@ interface GlobalParams {
|
||||
privacyUrl?: string;
|
||||
showErrorStackTraces?: boolean;
|
||||
userDisplayName?: string;
|
||||
supportEmail?: string;
|
||||
}
|
||||
|
||||
export function isView(o: any): boolean {
|
||||
|
@ -109,6 +109,8 @@ export interface Config {
|
||||
database: DatabaseConfig;
|
||||
mailer: MailerConfig;
|
||||
stripe: StripeConfig;
|
||||
supportEmail: string;
|
||||
businessEmail: string;
|
||||
}
|
||||
|
||||
export enum HttpMethod {
|
||||
|
@ -11,8 +11,9 @@
|
||||
<div class="field">
|
||||
<label class="label">Email</label>
|
||||
<div class="control">
|
||||
<input class="input" type="email" name="email" value="{{user.email}}"/>
|
||||
<input class="input" type="email" name="email" value="{{user.email}}" disabled/>
|
||||
</div>
|
||||
<p class="help">For security reasons the email cannot currently be changed. To request a change please contact {{global.supportEmail}}</p>
|
||||
</div>
|
||||
|
||||
{{#global.owner.is_admin}}
|
||||
|
Loading…
Reference in New Issue
Block a user