[MM-14887] Allow only jpeg, png, and bmp profile image uploads (#2715)

* Allow only for jpeg, png, and bmp profile image uploads

* Add file type if missing and update error id

* Update mattermost-redux commit hash

* Point mattermost-redux to latest master commit
This commit is contained in:
Miguel Alatzar 2019-04-25 14:27:06 -07:00 committed by GitHub
parent dc554bdfa7
commit cb0e4e225a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 158 additions and 41 deletions

View file

@ -0,0 +1,28 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AttachmentButton should match snapshot 1`] = `
<TouchableOpacity
activeOpacity={0.2}
onPress={[Function]}
style={
Object {
"alignItems": "center",
"height": 34,
"justifyContent": "center",
"width": 45,
}
}
>
<Icon
allowFontScaling={false}
color="rgba(61,60,64,0.9)"
name="md-add"
size={30}
style={
Object {
"marginTop": 2,
}
}
/>
</TouchableOpacity>
`;

View file

@ -53,5 +53,6 @@ exports[`profile_picture_button should match snapshot 1`] = `
}
}
uploadFiles={[MockFunction]}
validMimeTypes={Array []}
/>
`;

View file

@ -17,6 +17,8 @@ import {DocumentPicker} from 'react-native-document-picker';
import ImagePicker from 'react-native-image-picker';
import Permissions from 'react-native-permissions';
import {lookupMimeType} from 'mattermost-redux/utils/file_utils';
import {PermissionTypes} from 'app/constants';
import {changeOpacity} from 'app/utils/theme';
import {t} from 'app/utils/i18n';
@ -27,6 +29,7 @@ export default class AttachmentButton extends PureComponent {
static propTypes = {
blurTextBox: PropTypes.func.isRequired,
browseFileTypes: PropTypes.string,
validMimeTypes: PropTypes.array,
canBrowseFiles: PropTypes.bool,
canBrowsePhotoLibrary: PropTypes.bool,
canBrowseVideoLibrary: PropTypes.bool,
@ -39,6 +42,7 @@ export default class AttachmentButton extends PureComponent {
navigator: PropTypes.object.isRequired,
onShowFileMaxWarning: PropTypes.func,
onShowFileSizeWarning: PropTypes.func,
onShowUnsupportedMimeTypeWarning: PropTypes.func,
theme: PropTypes.object.isRequired,
uploadFiles: PropTypes.func.isRequired,
wrapper: PropTypes.bool,
@ -47,6 +51,7 @@ export default class AttachmentButton extends PureComponent {
static defaultProps = {
browseFileTypes: Platform.OS === 'ios' ? 'public.item' : '*/*',
validMimeTypes: [],
canBrowseFiles: true,
canBrowsePhotoLibrary: true,
canBrowseVideoLibrary: true,
@ -321,7 +326,14 @@ export default class AttachmentButton extends PureComponent {
file.fileName = fileInfo.filename;
}
if (file.fileSize > this.props.maxFileSize) {
if (!file.type) {
file.type = lookupMimeType(file.fileName);
}
const {validMimeTypes} = this.props;
if (validMimeTypes.length && !validMimeTypes.includes(file.type)) {
this.props.onShowUnsupportedMimeTypeWarning();
} else if (file.fileSize > this.props.maxFileSize) {
this.props.onShowFileSizeWarning(file.fileName);
} else {
this.props.uploadFiles(files);

View file

@ -0,0 +1,71 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {shallow} from 'enzyme';
import Preferences from 'mattermost-redux/constants/preferences';
import AttachmentButton from './attachment_button';
jest.mock('react-intl');
describe('AttachmentButton', () => {
const baseProps = {
theme: Preferences.THEMES.default,
navigator: {},
blurTextBox: jest.fn(),
maxFileSize: 10,
uploadFiles: jest.fn(),
};
test('should match snapshot', () => {
const wrapper = shallow(
<AttachmentButton {...baseProps}/>
);
expect(wrapper.getElement()).toMatchSnapshot();
});
test('should not upload file with invalid MIME type', () => {
const props = {
...baseProps,
validMimeTypes: ['image/jpeg'],
onShowUnsupportedMimeTypeWarning: jest.fn(),
};
const wrapper = shallow(
<AttachmentButton {...props}/>
);
const file = {
type: 'image/gif',
fileSize: 10,
fileName: 'test',
};
wrapper.instance().uploadFiles([file]);
expect(props.onShowUnsupportedMimeTypeWarning).toHaveBeenCalled();
expect(props.uploadFiles).not.toHaveBeenCalled();
});
test('should upload file with valid MIME type', () => {
const props = {
...baseProps,
validMimeTypes: ['image/jpeg'],
onShowUnsupportedMimeTypeWarning: jest.fn(),
};
const wrapper = shallow(
<AttachmentButton {...props}/>
);
const file = {
type: 'image/jpeg',
fileSize: 10,
fileName: 'test',
};
wrapper.instance().uploadFiles([file]);
expect(props.onShowUnsupportedMimeTypeWarning).not.toHaveBeenCalled();
expect(props.uploadFiles).toHaveBeenCalled();
});
});

View file

@ -74,6 +74,7 @@ exports[`edit_profile should match snapshot 1`] = `
}
}
onShowFileSizeWarning={[Function]}
onShowUnsupportedMimeTypeWarning={[Function]}
removeProfileImage={[Function]}
theme={
Object {
@ -104,6 +105,13 @@ exports[`edit_profile should match snapshot 1`] = `
}
}
uploadFiles={[Function]}
validMimeTypes={
Array [
"image/jpeg",
"image/png",
"image/bmp",
]
}
wrapper={true}
>
<Connect(ProfilePicture)

View file

@ -28,6 +28,11 @@ import mattermostBucket from 'app/mattermost_bucket';
import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils';
const MAX_SIZE = 20 * 1024 * 1024;
const VALID_MIME_TYPES = [
'image/jpeg',
'image/png',
'image/bmp',
];
const holders = {
firstName: {
id: t('user.settings.general.firstName'),
@ -278,6 +283,18 @@ export default class EditProfile extends PureComponent {
Alert.alert(fileSizeWarning);
};
onShowUnsupportedMimeTypeWarning = () => {
const {formatMessage} = this.context.intl;
const fileTypeWarning = formatMessage({
id: 'mobile.file_upload.unsupportedMimeType',
defaultMessage: 'Only files of the following MIME type can be uploaded: {mimeTypes}',
}, {
mimeTypes: VALID_MIME_TYPES.join('\n'),
});
Alert.alert('', fileTypeWarning);
};
renderFirstNameSettings = () => {
const {formatMessage} = this.context.intl;
const {config, currentUser, theme} = this.props;
@ -500,6 +517,8 @@ export default class EditProfile extends PureComponent {
uploadFiles={this.handleUploadProfileImage}
removeProfileImage={this.handleRemoveProfileImage}
onShowFileSizeWarning={this.onShowFileSizeWarning}
onShowUnsupportedMimeTypeWarning={this.onShowUnsupportedMimeTypeWarning}
validMimeTypes={VALID_MIME_TYPES}
>
<ProfilePicture
userId={currentUser.id}

View file

@ -252,6 +252,7 @@
"mobile.file_upload.library": "Photo Library",
"mobile.file_upload.max_warning": "Uploads limited to 5 files maximum.",
"mobile.file_upload.video": "Video Library",
"mobile.file_upload.unsupportedMimeType": "Only files of the following MIME type can be uploaded:\n{mimeTypes}",
"mobile.flagged_posts.empty_description": "Flags are a way to mark messages for follow up. Your flags are personal, and cannot be seen by other users.",
"mobile.flagged_posts.empty_title": "No Flagged Posts",
"mobile.help.title": "Help",

55
package-lock.json generated
View file

@ -2700,7 +2700,6 @@
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"optional": true,
"requires": {
"is-extendable": "^0.1.0"
}
@ -2745,8 +2744,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
"dev": true,
"optional": true
"dev": true
},
"is-glob": {
"version": "4.0.0",
@ -4786,8 +4784,7 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"optional": true
"bundled": true
},
"aproba": {
"version": "1.2.0",
@ -4805,13 +4802,11 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"optional": true
"bundled": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -4824,18 +4819,15 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"optional": true
"bundled": true
},
"core-util-is": {
"version": "1.0.2",
@ -4938,8 +4930,7 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true,
"optional": true
"bundled": true
},
"ini": {
"version": "1.3.5",
@ -4949,7 +4940,6 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -4962,20 +4952,17 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true,
"optional": true
"bundled": true
},
"minipass": {
"version": "2.3.5",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -4992,7 +4979,6 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -5065,8 +5051,7 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"optional": true
"bundled": true
},
"object-assign": {
"version": "4.1.1",
@ -5076,7 +5061,6 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -5152,8 +5136,7 @@
},
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"optional": true
"bundled": true
},
"safer-buffer": {
"version": "2.1.2",
@ -5183,7 +5166,6 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -5201,7 +5183,6 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -5240,13 +5221,11 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true,
"optional": true
"bundled": true
},
"yallist": {
"version": "3.0.3",
"bundled": true,
"optional": true
"bundled": true
}
}
},
@ -9751,8 +9730,8 @@
"integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A=="
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#16a39c671193db23006d848c7866adbb5cae6901",
"from": "github:mattermost/mattermost-redux#16a39c671193db23006d848c7866adbb5cae6901",
"version": "github:mattermost/mattermost-redux#10399546e5cd0cb2b874ebe95436a2cdefd78eb8",
"from": "github:mattermost/mattermost-redux#10399546e5cd0cb2b874ebe95436a2cdefd78eb8",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "3.0.0",
@ -13046,8 +13025,7 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true,
"optional": true
"dev": true
},
"braces": {
"version": "2.3.2",
@ -13333,8 +13311,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
"dev": true,
"optional": true
"dev": true
},
"micromatch": {
"version": "3.1.10",

View file

@ -18,7 +18,7 @@
"intl": "1.2.5",
"jail-monkey": "2.0.0",
"jsc-android": "236355.1.1",
"mattermost-redux": "github:mattermost/mattermost-redux#16a39c671193db23006d848c7866adbb5cae6901",
"mattermost-redux": "github:mattermost/mattermost-redux#10399546e5cd0cb2b874ebe95436a2cdefd78eb8",
"mime-db": "1.38.0",
"moment-timezone": "0.5.23",
"prop-types": "15.7.2",