[IMPROVE] Webdav methods sanitization (#23924)

pull/24009/head
Douglas Fabris 3 years ago committed by GitHub
parent 9160ba13c2
commit 160619d2ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      app/api/server/lib/webdav.ts
  2. 25
      app/models/server/raw/WebdavAccounts.ts
  3. 4
      app/webdav/client/addWebdavAccount.html
  4. 4
      app/webdav/client/addWebdavAccount.js
  5. 2
      app/webdav/client/startup/messageBoxActions.js
  6. 38
      app/webdav/server/methods/addWebdavAccount.ts
  7. 12
      app/webdav/server/methods/getFileFromWebdav.ts
  8. 12
      app/webdav/server/methods/getWebdavFileList.ts
  9. 13
      app/webdav/server/methods/getWebdavFilePreview.ts
  10. 8
      app/webdav/server/methods/removeWebdavAccount.ts
  11. 2
      app/webdav/server/methods/uploadFileToWebdav.ts
  12. 6
      definition/IWebdavAccount.ts
  13. 1
      server/startup/migrations/index.ts
  14. 10
      server/startup/migrations/v251.ts

@ -7,8 +7,7 @@ export async function findWebdavAccountsByUserId({ uid }: { uid: string }): Prom
projection: {
_id: 1,
username: 1,
// eslint-disable-next-line @typescript-eslint/camelcase
server_url: 1,
serverURL: 1,
name: 1,
},
}).toArray(),

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/camelcase */
/**
* Webdav Accounts model
*/
@ -16,31 +15,31 @@ export class WebdavAccountsRaw extends BaseRaw<T> {
) {
super(col, trash);
this.col.createIndex({ user_id: 1 });
this.col.createIndex({ userId: 1 });
}
findOneByIdAndUserId(_id: string, user_id: string, options: FindOneOptions<T>): Promise<T | null> {
return this.findOne({ _id, user_id }, options);
findOneByIdAndUserId(_id: string, userId: string, options: FindOneOptions<T>): Promise<T | null> {
return this.findOne({ _id, userId }, options);
}
findOneByUserIdServerUrlAndUsername({
user_id,
server_url,
userId,
serverURL,
username,
}: {
user_id: string;
server_url: string;
userId: string;
serverURL: string;
username: string;
}, options: FindOneOptions<T>): Promise<T | null> {
return this.findOne({ user_id, server_url, username }, options);
return this.findOne({ userId, serverURL, username }, options);
}
findWithUserId(user_id: string, options: FindOneOptions<T>): Cursor<T> {
const query = { user_id };
findWithUserId(userId: string, options: FindOneOptions<T>): Cursor<T> {
const query = { userId };
return this.find(query, options);
}
removeByUserAndId(_id: string, user_id: string): Promise<DeleteWriteOpResultObject> {
return this.deleteOne({ _id, user_id });
removeByUserAndId(_id: string, userId: string): Promise<DeleteWriteOpResultObject> {
return this.deleteOne({ _id, userId });
}
}

@ -28,9 +28,9 @@
</label>
</div>
<div class="rc-input">
<label class="rc-input__label" for="pass">
<label class="rc-input__label" for="password">
<div class="rc-input__wrapper">
<input name="pass" id="pass" type="password" class="rc-input__element" autocapitalize="off" autocorrect="off" placeholder="{{_ 'Password' }}" autofocus>
<input name="password" id="password" type="password" class="rc-input__element" autocapitalize="off" autocorrect="off" placeholder="{{_ 'Password' }}" autofocus>
<div class="input-error"></div>
</div>
</label>

@ -54,8 +54,8 @@ const validate = function() {
if (!formObj.username) {
validationObj.username = t('Field_required');
}
if (!formObj.pass) {
validationObj.pass = t('Field_required');
if (!formObj.password) {
validationObj.password = t('Field_required');
}
form.find('input.error, select.error').removeClass('error');

@ -33,7 +33,7 @@ Meteor.startup(function() {
}
accounts.forEach((account) => {
const name = account.name || `${ account.username }@${ account.server_url.replace(/^https?\:\/\//i, '') }`;
const name = account.name || `${ account.username }@${ account.serverURL.replace(/^https?\:\/\//i, '') }`;
const title = t('Upload_From', {
name,
});

@ -21,12 +21,14 @@ Meteor.methods({
check(formData, Match.ObjectIncluding({
serverURL: String,
username: String,
pass: String,
password: String,
name: Match.Maybe(String),
}));
const duplicateAccount = await WebdavAccounts.findOneByUserIdServerUrlAndUsername({ user_id: userId, server_url: formData.serverURL, username: formData.username });
if (duplicateAccount !== undefined) {
throw new Meteor.Error('duplicated-account', {
const duplicateAccount = await WebdavAccounts.findOneByUserIdServerUrlAndUsername({ userId, serverURL: formData.serverURL, username: formData.username }, {});
if (duplicateAccount !== null) {
throw new Meteor.Error('duplicated-account', 'Account not found', {
method: 'addWebdavAccount',
});
}
@ -36,16 +38,16 @@ Meteor.methods({
formData.serverURL,
{
username: formData.username,
password: formData.pass,
password: formData.password,
},
);
const accountData = {
user_id: userId,
server_url: formData.serverURL,
userId,
serverURL: formData.serverURL,
username: formData.username,
password: formData.pass,
name: formData.name,
password: formData.password,
name: formData.name ?? '',
};
await client.stat('/');
@ -55,7 +57,7 @@ Meteor.methods({
account: accountData,
});
} catch (error) {
throw new Meteor.Error('could-not-access-webdav', { method: 'addWebdavAccount' });
throw new Meteor.Error('could-not-access-webdav', 'Could not access webdav', { method: 'addWebdavAccount' });
}
return true;
},
@ -73,6 +75,8 @@ Meteor.methods({
check(data, Match.ObjectIncluding({
serverURL: String,
token: String,
name: Match.Maybe(String),
}));
try {
@ -82,17 +86,17 @@ Meteor.methods({
);
const accountData = {
user_id: userId,
server_url: data.serverURL,
userId,
serverURL: data.serverURL,
token: data.token,
name: data.name,
name: data.name ?? '',
};
await client.stat('/');
await WebdavAccounts.updateOne({
user_id: userId,
server_url: data.serverURL,
name: data.name,
userId,
serverURL: data.serverURL,
name: data.name ?? '',
}, {
$set: accountData,
}, {
@ -103,7 +107,7 @@ Meteor.methods({
account: accountData,
});
} catch (error) {
throw new Meteor.Error('could-not-access-webdav', { method: 'addWebdavAccount' });
throw new Meteor.Error('could-not-access-webdav', 'Could not access webdav', { method: 'addWebdavAccount' });
}
return true;

@ -1,32 +1,34 @@
import { Meteor } from 'meteor/meteor';
import { settings } from '../../../settings';
import { settings } from '../../../settings/server';
import { getWebdavCredentials } from './getWebdavCredentials';
import { WebdavAccounts } from '../../../models/server/raw';
import { WebdavClientAdapter } from '../lib/webdavClientAdapter';
Meteor.methods({
async getFileFromWebdav(accountId, file) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid User', { method: 'getFileFromWebdav' });
}
if (!settings.get('Webdav_Integration_Enabled')) {
throw new Meteor.Error('error-not-allowed', 'WebDAV Integration Not Allowed', { method: 'getFileFromWebdav' });
}
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, Meteor.userId());
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, userId, {});
if (!account) {
throw new Meteor.Error('error-invalid-account', 'Invalid WebDAV Account', { method: 'getFileFromWebdav' });
}
try {
const cred = getWebdavCredentials(account);
const client = new WebdavClientAdapter(account.server_url, cred);
const client = new WebdavClientAdapter(account.serverURL, cred);
const fileContent = await client.getFileContents(file.filename);
const data = new Uint8Array(fileContent);
return { success: true, data };
} catch (error) {
throw new Meteor.Error('unable-to-get-file', { method: 'getFileFromWebdav' });
throw new Meteor.Error('unable-to-get-file', 'Unable to get file', { method: 'getFileFromWebdav' });
}
},
});

@ -1,13 +1,15 @@
import { Meteor } from 'meteor/meteor';
import { settings } from '../../../settings';
import { settings } from '../../../settings/server';
import { getWebdavCredentials } from './getWebdavCredentials';
import { WebdavAccounts } from '../../../models/server/raw';
import { WebdavClientAdapter } from '../lib/webdavClientAdapter';
Meteor.methods({
async getWebdavFileList(accountId, path) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid User', { method: 'getWebdavFileList' });
}
@ -15,18 +17,18 @@ Meteor.methods({
throw new Meteor.Error('error-not-allowed', 'WebDAV Integration Not Allowed', { method: 'getWebdavFileList' });
}
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, Meteor.userId());
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, userId, {});
if (!account) {
throw new Meteor.Error('error-invalid-account', 'Invalid WebDAV Account', { method: 'getWebdavFileList' });
}
try {
const cred = getWebdavCredentials(account);
const client = new WebdavClientAdapter(account.server_url, cred);
const client = new WebdavClientAdapter(account.serverURL, cred);
const data = await client.getDirectoryContents(path);
return { success: true, data };
} catch (error) {
throw new Meteor.Error('could-not-access-webdav', { method: 'getWebdavFileList' });
throw new Meteor.Error('could-not-access-webdav', 'Could not access webdav', { method: 'getWebdavFileList' });
}
},
});

@ -1,13 +1,15 @@
import { Meteor } from 'meteor/meteor';
import { createClient } from 'webdav';
import { settings } from '../../../settings';
import { settings } from '../../../settings/server';
import { getWebdavCredentials } from './getWebdavCredentials';
import { WebdavAccounts } from '../../../models/server/raw';
Meteor.methods({
async getWebdavFilePreview(accountId, path) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid User', { method: 'getWebdavFilePreview' });
}
@ -15,17 +17,16 @@ Meteor.methods({
throw new Meteor.Error('error-not-allowed', 'WebDAV Integration Not Allowed', { method: 'getWebdavFilePreview' });
}
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, Meteor.userId());
const account = await WebdavAccounts.findOneByIdAndUserId(accountId, userId, {});
if (!account) {
throw new Meteor.Error('error-invalid-account', 'Invalid WebDAV Account', { method: 'getWebdavFilePreview' });
}
try {
const cred = getWebdavCredentials(account);
const client = createClient(account.server_url, cred);
const client = createClient(account.serverURL, cred);
const serverURL = settings.get('Accounts_OAuth_Nextcloud_URL');
const res = await client.customRequest({
url: `${ serverURL }/index.php/core/preview.png?file=${ path }&x=64&y=64`,
const res = await client.customRequest(`${ serverURL }/index.php/core/preview.png?file=${ path }&x=64&y=64`, {
method: 'GET',
responseType: 'arraybuffer',
});

@ -6,15 +6,17 @@ import { Notifications } from '../../../notifications/server';
Meteor.methods({
async removeWebdavAccount(accountId) {
if (!Meteor.userId()) {
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid User', { method: 'removeWebdavAccount' });
}
check(accountId, String);
const removed = await WebdavAccounts.removeByUserAndId(accountId, Meteor.userId());
const removed = await WebdavAccounts.removeByUserAndId(accountId, userId);
if (removed) {
Notifications.notifyUser(Meteor.userId(), 'webdav', {
Notifications.notifyUser(userId, 'webdav', {
type: 'removed',
account: { _id: accountId },
});

@ -28,7 +28,7 @@ Meteor.methods({
try {
const cred = getWebdavCredentials(account);
const client = new WebdavClientAdapter(account.server_url, cred);
const client = new WebdavClientAdapter(account.serverURL, cred);
// eslint-disable-next-line @typescript-eslint/no-empty-function
await client.createDirectory(uploadFolder).catch(() => {});
await client.putFileContents(`${ uploadFolder }/${ name }`, buffer, { overwrite: false });

@ -1,9 +1,11 @@
import { IRocketChatRecord } from './IRocketChatRecord';
export interface IWebdavAccount extends IRocketChatRecord {
user_id: string;
server_url: string;
userId: string;
serverURL: string;
username: string;
password: string;
name: string;
}
export type IWebdavAccountPayload = Omit<IWebdavAccount, 'userId' | '_id' | '_updatedAt'>

@ -74,4 +74,5 @@ import './v247';
import './v248';
import './v249';
import './v250';
import './v251';
import './xrun';

@ -0,0 +1,10 @@
import { addMigration } from '../../lib/migrations';
import { WebdavAccounts } from '../../../app/models/server/raw';
addMigration({
version: 251,
async up() {
// eslint-disable-next-line quote-props
await WebdavAccounts.updateMany({}, { $rename: { 'user_id': 'userId', 'server_url': 'serverURL' } });
},
});
Loading…
Cancel
Save