Fix upload permissions and centralize download permissions (#7109)

This commit is contained in:
Daniel Espino García
2023-02-14 21:55:02 +01:00
committed by GitHub
parent 76c8f844f9
commit f23960dea3
7 changed files with 284 additions and 69 deletions

View File

@@ -3,11 +3,11 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {combineLatest, of as of$, from as from$} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
import {of as of$, from as from$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {queryFilesForPost} from '@queries/servers/file';
import {observeConfigBooleanValue, observeLicense} from '@queries/servers/system';
import {observeCanDownloadFiles, observeConfigBooleanValue} from '@queries/servers/system';
import {fileExists} from '@utils/file';
import Files from './files';
@@ -37,23 +37,14 @@ const filesLocalPathValidation = async (files: FileModel[], authorId: string) =>
};
const enhance = withObservables(['post'], ({database, post}: EnhanceProps) => {
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
const publicLinkEnabled = observeConfigBooleanValue(database, 'EnablePublicLink');
const complianceDisabled = observeLicense(database).pipe(
switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')),
);
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
map(([download, compliance]) => compliance || download),
);
const filesInfo = queryFilesForPost(database, post.id).observeWithColumns(['local_path']).pipe(
switchMap((fs) => from$(filesLocalPathValidation(fs, post.userId))),
);
return {
canDownloadFiles,
canDownloadFiles: observeCanDownloadFiles(database),
postId: of$(post.id),
publicLinkEnabled,
filesInfo,

View File

@@ -0,0 +1,254 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {SYSTEM_IDENTIFIERS} from '@constants/database';
import DatabaseManager from '@database/manager';
import {observeCanDownloadFiles, observeCanUploadFiles} from './system';
import type ServerDataOperator from '@database/operator/server_data_operator';
import type {Database} from '@nozbe/watermelondb';
describe('observeCanUploadFiles', () => {
const serverUrl = 'baseHandler.test.com';
let database: Database;
let operator: ServerDataOperator;
beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
database = serverDatabaseAndOperator.database;
operator = serverDatabaseAndOperator.operator;
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
it('should return true if no file attachment config value', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileUpload', value: 'true'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
}, 1500);
it('should return false if file attachment config value is false', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'false'}, {id: 'EnableMobileFileUpload', value: 'true'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === false) {
done();
} else {
done.fail();
}
});
});
}, 1500);
it('should return true if file attachment config value is true and there is no license', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
}, 1500);
it('should return true if file attachment config value is true and server is not licensed', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {isLicensed: false}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if file attachment config value is true and server is licensed, but no compliance is set', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if file attachment config value is true and server is licensed, but compliance is set to false', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'false'}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if file attachment config value is true and server is licensed and compliance is set to true, but EnableMobileFileUpload is not set', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return false if file attachment config value is true and server is licensed and compliance is set to true, but EnableMobileFileUpload is set to false', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === false) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if file attachment config value is true and server is licensed and compliance is set to true, but EnableMobileFileUpload is set to true', (done) => {
operator.handleConfigs({configs: [{id: 'EnableFileAttachments', value: 'true'}, {id: 'EnableMobileFileUpload', value: 'true'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanUploadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
});
describe('observeCanDownloadFiles', () => {
const serverUrl = 'baseHandler.test.com';
let database: Database;
let operator: ServerDataOperator;
beforeEach(async () => {
await DatabaseManager.init([serverUrl]);
const serverDatabaseAndOperator = DatabaseManager.getServerDatabaseAndOperator(serverUrl);
database = serverDatabaseAndOperator.database;
operator = serverDatabaseAndOperator.operator;
});
afterEach(async () => {
await DatabaseManager.destroyServerDatabase(serverUrl);
});
it('should return true if there is no license', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
}, 1500);
it('should return true if server is not licensed', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {isLicensed: false}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if server is licensed, but no compliance is set', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if server is licensed, but compliance is set to false', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'false'}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if is licensed and compliance is set to true, but EnableMobileFileDownload is not set', (done) => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
}, 1500);
it('should return false if server is licensed and compliance is set to true, but EnableMobileFileDownload is set to false', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'false'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === false) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
it('should return true if server is licensed and compliance is set to true, but EnableMobileFileDownload is set to true', (done) => {
operator.handleConfigs({configs: [{id: 'EnableMobileFileDownload', value: 'true'}], prepareRecordsOnly: false, configsToDelete: []}).then(() => {
operator.handleSystem({systems: [{id: SYSTEM_IDENTIFIERS.LICENSE, value: {IsLicensed: 'true', Compliance: 'true'}}], prepareRecordsOnly: false}).then(() => {
observeCanDownloadFiles(database).subscribe((data) => {
if (data === true) {
done();
} else {
done.fail();
}
});
});
});
}, 1500);
});

View File

@@ -226,9 +226,9 @@ export const observeIsCustomStatusExpirySupported = (database: Database) => {
);
};
export const observeConfigBooleanValue = (database: Database, key: keyof ClientConfig) => {
export const observeConfigBooleanValue = (database: Database, key: keyof ClientConfig, defaultValue = false) => {
return observeConfigValue(database, key).pipe(
switchMap((v) => of$(v === 'true')),
switchMap((v) => of$(v ? v === 'true' : defaultValue)),
distinctUntilChanged(),
);
};
@@ -511,16 +511,24 @@ export const observeLastDismissedAnnouncement = (database: Database) => {
};
export const observeCanUploadFiles = (database: Database) => {
const enableFileAttachments = observeConfigBooleanValue(database, 'EnableFileAttachments');
const enableMobileFileUpload = observeConfigBooleanValue(database, 'EnableMobileFileUpload');
const enableFileAttachments = observeConfigBooleanValue(database, 'EnableFileAttachments', true);
const enableMobileFileUpload = observeConfigBooleanValue(database, 'EnableMobileFileUpload', true);
const license = observeLicense(database);
return combineLatest([enableFileAttachments, enableMobileFileUpload, license]).pipe(
switchMap(([efa, emfu, l]) => of$(
efa ||
(l?.IsLicensed !== 'true' && l?.Compliance !== 'true' && emfu),
),
),
efa &&
(l?.IsLicensed !== 'true' || l?.Compliance !== 'true' || emfu),
)),
);
};
export const observeCanDownloadFiles = (database: Database) => {
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload', true);
const license = observeLicense(database);
return combineLatest([enableMobileFileDownload, license]).pipe(
switchMap(([emfd, l]) => of$((l?.IsLicensed !== 'true' || l?.Compliance !== 'true' || emfd))),
);
};

View File

@@ -3,26 +3,16 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import {combineLatest, of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {observeConfigBooleanValue, observeLicense} from '@queries/servers/system';
import {observeCanDownloadFiles} from '@queries/servers/system';
import DocumentRenderer from './document_renderer';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhanced = withObservables([], ({database}: WithDatabaseArgs) => {
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
const complianceDisabled = observeLicense(database).pipe(
switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')),
);
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
switchMap(([download, compliance]) => of$(compliance || download)),
);
return {
canDownloadFiles,
canDownloadFiles: observeCanDownloadFiles(database),
};
});

View File

@@ -9,7 +9,7 @@ import {switchMap} from 'rxjs/operators';
import {General} from '@constants';
import {observeChannel} from '@queries/servers/channel';
import {observePost} from '@queries/servers/post';
import {observeConfigBooleanValue, observeCurrentChannelId, observeCurrentUserId, observeLicense} from '@queries/servers/system';
import {observeCanDownloadFiles, observeConfigBooleanValue, observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
import {observeTeammateNameDisplay, observeUser} from '@queries/servers/user';
import Footer from './footer';
@@ -27,7 +27,6 @@ const enhanced = withObservables(['item'], ({database, item}: FooterProps) => {
const currentChannelId = observeCurrentChannelId(database);
const currentUserId = observeCurrentUserId(database);
const license = observeLicense(database);
const teammateNameDisplay = observeTeammateNameDisplay(database);
const author = post.pipe(
@@ -50,17 +49,12 @@ const enhanced = withObservables(['item'], ({database, item}: FooterProps) => {
const enablePostUsernameOverride = observeConfigBooleanValue(database, 'EnablePostUsernameOverride');
const enablePostIconOverride = observeConfigBooleanValue(database, 'EnablePostIconOverride');
const enablePublicLink = observeConfigBooleanValue(database, 'EnablePublicLink');
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
const complianceDisabled = license.pipe(switchMap((l) => of$(l?.IsLicensed === 'false' || l?.Compliance === 'false')));
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
switchMap(([download, compliance]) => of$(compliance || download)),
);
const channelName = channel.pipe(switchMap((c: ChannelModel) => of$(c.displayName)));
const isDirectChannel = channel.pipe(switchMap((c: ChannelModel) => of$(c.type === General.DM_CHANNEL)));
return {
author,
canDownloadFiles,
canDownloadFiles: observeCanDownloadFiles(database),
channelName,
currentUserId,
enablePostIconOverride,

View File

@@ -4,28 +4,16 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import compose from 'lodash/fp/compose';
import {combineLatest, of as of$} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
import {observeLicense, observeConfigBooleanValue} from '@queries/servers/system';
import {observeConfigBooleanValue, observeCanDownloadFiles} from '@queries/servers/system';
import OptionMenus from './option_menus';
import type {WithDatabaseArgs} from '@typings/database/database';
const enhance = withObservables([], ({database}: WithDatabaseArgs) => {
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
const complianceDisabled = observeLicense(database).pipe(
switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')),
);
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
map(([download, compliance]) => compliance || download),
);
return {
canDownloadFiles,
canDownloadFiles: observeCanDownloadFiles(database),
enablePublicLink: observeConfigBooleanValue(database, 'EnablePublicLink'),
};
});

View File

@@ -4,12 +4,12 @@
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
import withObservables from '@nozbe/with-observables';
import compose from 'lodash/fp/compose';
import {combineLatest, of as of$} from 'rxjs';
import {map, switchMap} from 'rxjs/operators';
import {of as of$} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {queryChannelsById} from '@queries/servers/channel';
import {queryAllCustomEmojis} from '@queries/servers/custom_emoji';
import {observeLicense, observeConfigBooleanValue} from '@queries/servers/system';
import {observeConfigBooleanValue, observeCanDownloadFiles} from '@queries/servers/system';
import {observeCurrentUser} from '@queries/servers/user';
import {mapCustomEmojiNames} from '@utils/emoji/helpers';
import {getTimezone} from '@utils/user';
@@ -26,16 +26,6 @@ const enhance = withObservables(['fileChannelIds'], ({database, fileChannelIds}:
const fileChannels = queryChannelsById(database, fileChannelIds).observeWithColumns(['displayName']);
const currentUser = observeCurrentUser(database);
const enableMobileFileDownload = observeConfigBooleanValue(database, 'EnableMobileFileDownload');
const complianceDisabled = observeLicense(database).pipe(
switchMap((lcs) => of$(lcs?.IsLicensed === 'false' || lcs?.Compliance === 'false')),
);
const canDownloadFiles = combineLatest([enableMobileFileDownload, complianceDisabled]).pipe(
map(([download, compliance]) => compliance || download),
);
return {
appsEnabled: observeConfigBooleanValue(database, 'FeatureFlagAppsEnabled'),
currentTimezone: currentUser.pipe((switchMap((user) => of$(getTimezone(user?.timezone))))),
@@ -44,7 +34,7 @@ const enhance = withObservables(['fileChannelIds'], ({database, fileChannelIds}:
),
isTimezoneEnabled: observeConfigBooleanValue(database, 'ExperimentalTimezone'),
fileChannels,
canDownloadFiles,
canDownloadFiles: observeCanDownloadFiles(database),
publicLinkEnabled: observeConfigBooleanValue(database, 'EnablePublicLink'),
};
});