From 069495ecb29b3604db432762209e490221df34dd Mon Sep 17 00:00:00 2001 From: enahum Date: Wed, 1 Mar 2017 13:13:17 -0300 Subject: [PATCH] PLT-5676 Validate server URL format (#315) * PLT-5676 validate server URL format * changed based on feedback --- app/scenes/select_server/select_server.js | 52 +++++++++++++++++------ app/utils/url.js | 11 +++++ assets/base/i18n/en.json | 1 + 3 files changed, 50 insertions(+), 14 deletions(-) create mode 100644 app/utils/url.js diff --git a/app/scenes/select_server/select_server.js b/app/scenes/select_server/select_server.js index ffb2451a7..3c60926bb 100644 --- a/app/scenes/select_server/select_server.js +++ b/app/scenes/select_server/select_server.js @@ -1,7 +1,7 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React, {Component} from 'react'; +import React, {PropTypes, PureComponent} from 'react'; import { Image, Keyboard, @@ -21,22 +21,46 @@ import logo from 'assets/images/logo.png'; import Client from 'service/client'; import RequestStatus from 'service/constants/request_status'; -export default class SelectServer extends Component { +import {isValidUrl, stripTrailingSlashes} from 'app/utils/url'; + +export default class SelectServer extends PureComponent { static propTypes = { - serverUrl: React.PropTypes.string.isRequired, - server: React.PropTypes.object.isRequired, - actions: React.PropTypes.object.isRequired + serverUrl: PropTypes.string.isRequired, + server: PropTypes.object.isRequired, + actions: PropTypes.object.isRequired }; - onClick = () => { - Client.setUrl(this.props.serverUrl); + constructor(props) { + super(props); - this.props.actions.getPing().then(() => { - if (this.props.server.status === RequestStatus.SUCCESS) { - Keyboard.dismiss(); - this.props.actions.goToLogin(); - } - }); + this.state = { + error: null + }; + } + + onClick = () => { + const url = this.props.serverUrl; + let error = null; + + if (isValidUrl(url)) { + Client.setUrl(stripTrailingSlashes(url)); + + this.props.actions.getPing().then(() => { + if (this.props.server.status === RequestStatus.SUCCESS) { + Keyboard.dismiss(); + this.props.actions.goToLogin(); + } + }); + } else { + error = { + intl: { + id: 'mobile.server_url.invalid_format', + defaultMessage: 'URL must start with http:// or https://' + } + }; + } + + this.setState({error}); }; inputRef = (ref) => { @@ -90,7 +114,7 @@ export default class SelectServer extends Component { defaultMessage='Proceed' /> - + diff --git a/app/utils/url.js b/app/utils/url.js new file mode 100644 index 000000000..b7403bb16 --- /dev/null +++ b/app/utils/url.js @@ -0,0 +1,11 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +export function isValidUrl(url) { + const regex = /^https?:\/\//i; + return regex.test(url); +} + +export function stripTrailingSlashes(url) { + return url.replace(/\/+$/, ''); +} diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 20de09719..e3366307c 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1527,6 +1527,7 @@ "mobile.routes.user_profile": "Profile", "mobile.routes.user_profile.send_message": "Send Message", "mobile.server_ping_failed": "Cannot connect to the server. Please check your server URL and internet connection.", + "mobile.server_url.invalid_format": "URL must start with http:// or https://", "more_channels.close": "Close", "more_channels.create": "Create New Channel", "more_channels.createClick": "Click 'Create New Channel' to make a new one",