Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] HTTP Basic Auth #1753

Merged
merged 4 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions __tests__/__snapshots__/Storyshots.test.js.snap

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions app/containers/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { View } from 'react-native';
import FastImage from 'react-native-fast-image';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import Touch from '../utils/touch';

const formatUrl = (url, baseUrl, uriSize, avatarAuthURLFragment) => (
Expand Down Expand Up @@ -45,6 +46,7 @@ const Avatar = React.memo(({
style={avatarStyle}
source={{
uri,
headers: RocketChatSettings.customHeaders,
priority: FastImage.priority.high
}}
/>
Expand Down
1 change: 1 addition & 0 deletions app/lib/methods/actions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import random from '../../utils/random';
import EventEmitter from '../../utils/events';
import fetch from '../../utils/fetch';
import Navigation from '../Navigation';

const ACTION_TYPES = {
Expand Down
7 changes: 5 additions & 2 deletions app/lib/methods/sendFileMessage.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';

import database from '../database';
import log from '../../utils/log';
import { headers } from '../../utils/fetch';

const uploadQueue = {};

Expand Down Expand Up @@ -75,7 +75,10 @@ export function sendFileMessage(rid, fileInfo, tmid, server, user) {

xhr.setRequestHeader('X-Auth-Token', token);
xhr.setRequestHeader('X-User-Id', id);
xhr.setRequestHeader('User-Agent', headers['User-Agent']);
const { customHeaders } = RocketChatSettings;
Object.keys(customHeaders).forEach((key) => {
xhr.setRequestHeader(key, customHeaders[key]);
});

xhr.upload.onprogress = async({ total, loaded }) => {
try {
Expand Down
7 changes: 3 additions & 4 deletions app/lib/rocketchat.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AsyncStorage, InteractionManager } from 'react-native';
import semver from 'semver';
import { Rocketchat as RocketchatClient, settings as RocketChatSettings } from '@rocket.chat/sdk';
import { Rocketchat as RocketchatClient } from '@rocket.chat/sdk';
import RNUserDefaults from 'rn-user-defaults';
import { Q } from '@nozbe/watermelondb';
import * as FileSystem from 'expo-file-system';
Expand All @@ -12,7 +12,7 @@ import database from './database';
import log from '../utils/log';
import { isIOS, getBundleId } from '../utils/deviceInfo';
import { extractHostname } from '../utils/server';
import fetch, { headers } from '../utils/fetch';
import fetch, { BASIC_AUTH_KEY } from '../utils/fetch';

import { setUser, setLoginServices, loginRequest } from '../actions/login';
import { disconnect, connectSuccess, connectRequest } from '../actions/connect';
Expand Down Expand Up @@ -58,8 +58,6 @@ const MIN_ROCKETCHAT_VERSION = '0.70.0';

const STATUSES = ['offline', 'online', 'away', 'busy'];

RocketChatSettings.customHeaders = headers;

const RocketChat = {
TOKEN_KEY,
callJitsi,
Expand Down Expand Up @@ -446,6 +444,7 @@ const RocketChat = {
await RNUserDefaults.clear('currentServer');
await RNUserDefaults.clear(TOKEN_KEY);
await RNUserDefaults.clear(`${ TOKEN_KEY }-${ server }`);
await RNUserDefaults.clear(`${ BASIC_AUTH_KEY }-${ server }`);

try {
const db = database.active;
Expand Down
4 changes: 4 additions & 0 deletions app/sagas/selectServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import log from '../utils/log';
import { extractHostname } from '../utils/server';
import I18n from '../i18n';
import { SERVERS, TOKEN, SERVER_URL } from '../constants/userDefaults';
import { BASIC_AUTH_KEY, setBasicAuth } from '../utils/fetch';

const getServerInfo = function* getServerInfo({ server, raiseError = true }) {
try {
Expand Down Expand Up @@ -89,6 +90,9 @@ const handleSelectServer = function* handleSelectServer({ server, version, fetch
}
}

const basicAuth = yield RNUserDefaults.get(`${ BASIC_AUTH_KEY }-${ server }`);
setBasicAuth(basicAuth);

if (user) {
yield RocketChat.connect({ server, user, logoutOnError: true });
yield put(setUser(user));
Expand Down
22 changes: 19 additions & 3 deletions app/utils/fetch.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
import { Platform } from 'react-native';
import DeviceInfo from 'react-native-device-info';
import { settings as RocketChatSettings } from '@rocket.chat/sdk';

// this form is required by Rocket.Chat's parser in "app/statistics/server/lib/UAParserCustom.js"
export const headers = { 'User-Agent': `RC Mobile; ${ Platform.OS } ${ DeviceInfo.getSystemVersion() }; v${ DeviceInfo.getVersion() } (${ DeviceInfo.getBuildNumber() })` };
export const headers = {
'User-Agent': `RC Mobile; ${ Platform.OS } ${ DeviceInfo.getSystemVersion() }; v${ DeviceInfo.getVersion() } (${ DeviceInfo.getBuildNumber() })`
};

let _basicAuth;
export const setBasicAuth = (basicAuth) => {
_basicAuth = basicAuth;
if (basicAuth) {
RocketChatSettings.customHeaders = { ...RocketChatSettings.customHeaders, Authorization: `Basic ${ _basicAuth }` };
} else {
RocketChatSettings.customHeaders = headers;
}
};
export const BASIC_AUTH_KEY = 'BASIC_AUTH_KEY';

RocketChatSettings.customHeaders = headers;

export default (url, options = {}) => {
let customOptions = { ...options, headers };
let customOptions = { ...options, headers: RocketChatSettings.customHeaders };
if (options && options.headers) {
customOptions = { ...customOptions, headers: { ...options.headers, ...headers } };
customOptions = { ...customOptions, headers: { ...options.headers, ...customOptions.headers } };
}
return fetch(url, customOptions);
};
26 changes: 25 additions & 1 deletion app/views/NewServerView.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import * as FileSystem from 'expo-file-system';
import DocumentPicker from 'react-native-document-picker';
import ActionSheet from 'react-native-action-sheet';
import isEqual from 'deep-equal';
import RNUserDefaults from 'rn-user-defaults';
import { encode } from 'base-64';
import parse from 'url-parse';

import { serverRequest } from '../actions/server';
import sharedStyles from './Styles';
Expand All @@ -25,6 +28,7 @@ import { themes } from '../constants/colors';
import log from '../utils/log';
import { animateNextTransition } from '../utils/layoutAnimation';
import { withTheme } from '../theme';
import { setBasicAuth, BASIC_AUTH_KEY } from '../utils/fetch';

const styles = StyleSheet.create({
image: {
Expand Down Expand Up @@ -148,7 +152,22 @@ class NewServerView extends React.Component {

if (text) {
Keyboard.dismiss();
connectServer(this.completeUrl(text), cert);
const server = this.completeUrl(text);
await this.basicAuth(server, text);
connectServer(server, cert);
}
}

basicAuth = async(server, text) => {
try {
const parsedUrl = parse(text, true);
if (parsedUrl.auth.length) {
const credentials = encode(parsedUrl.auth);
await RNUserDefaults.set(`${ BASIC_AUTH_KEY }-${ server }`, credentials);
setBasicAuth(credentials);
}
} catch {
// do nothing
}
}

Expand Down Expand Up @@ -177,6 +196,11 @@ class NewServerView extends React.Component {
}

completeUrl = (url) => {
const parsedUrl = parse(url, true);
if (parsedUrl.auth.length) {
url = parsedUrl.host;
}

url = url && url.replace(/\s/g, '');

if (/^(\w|[0-9-_]){3,}$/.test(url)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@react-native-community/slider": "2.0.5",
"@rocket.chat/sdk": "1.0.0-alpha.41",
"@rocket.chat/ui-kit": "^0.2.0-alpha.25",
"base-64": "^0.1.0",
"bugsnag-react-native": "2.23.2",
"commonmark": "git+https://github.com/RocketChat/commonmark.js.git",
"commonmark-react-renderer": "git+https://github.com/RocketChat/commonmark-react-renderer.git",
Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2748,7 +2748,7 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=

base-64@0.1.0:
base-64@0.1.0, base-64@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb"
integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs=
Expand Down