Skip to content
This repository has been archived by the owner on Mar 28, 2023. It is now read-only.

Tor #560

Merged
merged 26 commits into from
Jul 17, 2017
Merged

Tor #560

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
c4aeb7e
fixed issue on the connected peers page when the endpoint doesnt retu…
rmisio Jun 30, 2017
f7af997
WIP - handling the tor needs configuration connection state
rmisio Jul 3, 2017
68e759d
fixed bug in check to ensure only one default config can be saved
rmisio Jul 5, 2017
bc99273
WIP - managing tor in the server connect module connect function
rmisio Jul 6, 2017
d6a3a3e
more work on the configuration form configure tor state
rmisio Jul 6, 2017
6f4989e
finished up the configuration form configure-tor state
rmisio Jul 6, 2017
acfc322
fixing lint errors
rmisio Jul 6, 2017
a530be2
added in the UI to get the tor proxy configuration string
rmisio Jul 7, 2017
04bb6df
handling tor unavailable scenario
rmisio Jul 7, 2017
a1b326b
added in the tor indicator to the address bar
rmisio Jul 7, 2017
a5123ea
added in warning if tor is on and you\'re attempting to navigate to a…
rmisio Jul 7, 2017
a618d13
removing some debugging code
rmisio Jul 7, 2017
e1dd826
wired in translations for the external link tor warning modal
rmisio Jul 10, 2017
554b81b
WIP - redign to support editing active connection
rmisio Jul 10, 2017
f31263c
wired in server disconnect button
rmisio Jul 11, 2017
dcf6a36
handling deleting a live connection
rmisio Jul 11, 2017
f12c9ac
unbinding proxy set handlers before binding new ones in the server co…
rmisio Jul 11, 2017
9455761
cleaning up unnecessary clos click targets from the debug log modal
rmisio Jul 11, 2017
aa4aed9
fixed a bug where the navigable state of the page nav was not being s…
rmisio Jul 11, 2017
db6a79e
authenticated to the built-in server via an auth token
rmisio Jul 11, 2017
925fae6
removing some debugging code
rmisio Jul 11, 2017
e0be2a6
merged in master
rmisio Jul 11, 2017
cd2108e
some code cleanup
rmisio Jul 11, 2017
289aaad
fixing double path seperator issue
rmisio Jul 13, 2017
dbd2c06
some code review tweaks
rmisio Jul 17, 2017
7eb1ac4
removing transtion from tool tips
rmisio Jul 17, 2017
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
Binary file added imgs/icon-tor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export default {
return this.serverConfigs.activeServer ?
`${this.serverConfigs.activeServer.httpUrl}${urlFrag}` : '';
},

serverConfig: {},
};
37 changes: 31 additions & 6 deletions js/collections/ServerConfigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ export default class extends Collection {
this.on('sync', () => this.bindActiveServerChangeHandler());
}

/**
* The "default" config is the configuration that is associated with the built-in
* server in the bundled app.
*/
get defaultConfig() {
return this.findWhere({ default: true });
}

/**
* The "active" server is the server we are currently connected to or if we're not
* connected to any server, it's the last server we were connected to. When the app is
* re-started, a connection will automatically be attempted to this server.
*/
get activeServer() {
return this.get(this._activeId);
}
Expand Down Expand Up @@ -60,17 +73,29 @@ export default class extends Collection {
}

set(models = [], options = {}) {
const hasDefaultModel = !!this.findWhere({ default: true });

// Todo: ensure this works via the myriad ways to set this collection

// Not sure why if I create a model via Collection.Create, a single model
// is being passed into this method, instead of an array. The documentation
// does not reflect this.
const modelsList = models instanceof Model ? [models] : models;

if (hasDefaultModel &&
modelsList.filter(md => md.get('default')).length) {
const defaultConfig = this.defaultConfig;
let defaultCount = this.defaultConfig ? 1 : 0;

// ensure we are not trying to set more than one default config
if (defaultConfig) {
modelsList.forEach(model => {
const jsonModel = model instanceof Model ?
model.toJSON() : model;
const defaultConfigId = this.defaultConfig ? this.defaultConfig.id : '';

if (jsonModel.default &&
(!options.merge || !jsonModel.id || jsonModel.id !== defaultConfigId)) {
defaultCount += 1;
}
});
}

if (defaultCount > 1) {
throw new Error('The collection already has a default model and you' +
' are attempting to add another one. Only one default model is allowed.');
}
Expand Down
37 changes: 33 additions & 4 deletions js/languages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
}
},
"pageNav": {
"notConnectedMenuItem": "Not Connected"
"notConnectedMenuItem": "Not Connected",
"torOnTooltip": "Tor active"
},
"chat": {
"fetchingConversations": "Retrieving your chat conversations…",
Expand Down Expand Up @@ -503,14 +504,18 @@
}
},
"connectionManagement": {
"defaultServerName": "Local Bundled Server",
"defaultServerName": "Built-in Server",
"statusBar": {
"errorPreface": "Error:",
"needHelpLink": "Need help?",
"viewLocalServerDebugLogLink": "View Server Debug Log",
"viewLocalServerDebugLogLink": "Debug Log",
"errorConnectionLost": "%{errorPreface} The connection to %{serverName} has been lost. %{links}",
"errorUnableToReachServer": "%{errorPreface} Failed to connect to %{serverName}. The server cannot be reached. %{links}",
"errorAuthFailed": "%{errorPreface} Failed to connect to %{serverName}. The username and / or password are incorrect. %{links}",
"errorAuthFailedBuiltInServer": "%{errorPreface} Failed to connect to %{serverName}. The auth cookie is invalid. %{links}",
"editLink": "edit",
"errorTorNotConfigured": "Please %{editLink} the %{serverName} configuration to confirm whether you want to use Tor or not. %{links}",
"errorTorNotAvailable": "Tor was not detected. Please %{editLink} the %{serverName} configuration to not use Tor or start Tor. %{links}",
"cancelConnAttempt": "Cancel",
"connectAttemptMsg": "Attempting to connect to %{serverName}. %{cancelConnAttempt}"
},
Expand All @@ -521,6 +526,7 @@
"btnCancel": "Cancel",
"btnRetry": "Retry",
"btnConnect": "Connect",
"btnDisconnect": "Disconnect",
"statusConnected": "Connected",
"deleteConfirm": {
"heading": "Are you sure?",
Expand All @@ -544,9 +550,25 @@
"placeholderServerIp": "The IP to your server (or localhost)",
"placeholderUsername": "The username to your server",
"placeholderPassword": "The password to your server",
"placeholderPort": "The port to your server"
"placeholderPort": "The port to your server",
"torLabel": "Tor",
"useTor": "Use Tor",
"configureTorMessage": "Tor has been detected. If you would like to use Tor, please check the \"Use Tor\" box below and specify your \"Tor SOCKS5 proxy\" setting. Click Save to confirm your decision.",
"torNotAvailableMessage": "Tor was not detected. If you would like to proceed without Tor, uncheck \"Use Tor\" below and click Save. Otherwise start up Tor and then retry connecting.",
"torProxyLabel": "Tor SOCKS5 proxy",
"torProxyPlaceholder": "127.0.0.1:9150",
"warning": "Warning:",
"torServerWarning": "%{warning} In order to be anonymous, it is critical that you also start the server in Tor mode."
}
},
"torExternalLinkWarning": {
"heading": "Warning",
"body": "You're attempting to visit an external link in your default web browser. If your default web browser is not set to Tor, you may be exposing your IP address.",
"areYouSureToProceed": "Are you sure you want to proceed?",
"doNotShowAgain": "Do not show this warning again",
"btnCancel": "Cancel",
"btnProceed": "Yes, proceed"
},
"wallet": {
"title": "Wallet",
"closeLink": "Close wallet",
Expand Down Expand Up @@ -1243,6 +1265,13 @@
"caseOffIfEmpty": "Please fill out all settings if you turn notifications on."
}
},
"serverConfigModelErrors": {
"provideValue": "Please provide a value.",
"invalidIp": "This does not appear to be a valid IP address.",
"providePortAsNumber": "Please provide a port as a number.",
"provideValidPortRange": "Please provide a number between 0 and 65535.",
"invalidTorProxy": "The value does not appear to be in the right format. It should be in the format ip-address:port, e.g. 127.0.0.1:9150. The port must be a number between 0 and 65535."
},
"errors": {
"saveError": "There was an error saving your data.",
"badResult": "The server returned an error."
Expand Down
6 changes: 6 additions & 0 deletions js/models/LocalSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class extends Model {
listingsGridViewType: 'grid',
bitcoinUnit: 'BTC',
searchProvider: 'https://search.ob1.io/search/listings',
dontShowTorExternalLinkWarning: false,
};
}

Expand Down Expand Up @@ -75,6 +76,11 @@ export default class extends Model {
addError('searchProvider', app.polyglot.t('localSettingsModelErrors.searchProvider'));
}

if (typeof attrs.dontShowTorExternalLinkWarning !== 'boolean') {
addError('dontShowTorExternalLinkWarning',
'dontShowTorExternalLinkWarning must be provided as a boolean.');
}

if (Object.keys(errObj).length && errObj) return errObj;

return undefined;
Expand Down
52 changes: 41 additions & 11 deletions js/models/ServerConfig.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import app from '../app';
import BaseModel from './BaseModel';
import LocalStorageSync from '../utils/backboneLocalStorage';
import is from 'is_js';
Expand All @@ -17,6 +18,10 @@ export default class extends BaseModel {
port: 4002,
SSL: false,
default: false,
useTor: false,
confirmedTor: false,
torProxy: '127.0.0.1:9150',
dontShowTorExternalLinkWarning: false,
};
}

Expand All @@ -28,7 +33,7 @@ export default class extends BaseModel {
};

if (!is.existy(attrs.name) || is.empty(attrs.name)) {
addError('name', 'Please provide a value.');
addError('name', app.polyglot.t('serverConfigModelErrors.provideValue'));
} else {
// Slight hack since backbone doesn't document Model.collection and
// it will only refer to the first collection that a Model belongs.
Expand All @@ -42,35 +47,60 @@ export default class extends BaseModel {
}

if (!is.existy(attrs.serverIp) || is.empty(attrs.serverIp)) {
addError('serverIp', 'Please provide a value.');
addError('serverIp', app.polyglot.t('serverConfigModelErrors.provideValue'));
} else {
if (!is.ip(attrs.serverIp)) {
addError('serverIp', 'This does not appear to be a valid IP address.');
addError('serverIp', app.polyglot.t('serverConfigModelErrors.invalidIp'));
}
}

if (!this.isLocalServer()) {
if (!attrs.username) {
addError('username', 'Please provide a value.');
addError('username', app.polyglot.t('serverConfigModelErrors.provideValue'));
}

if (!attrs.password) {
addError('password', 'Please provide a value.');
addError('password', app.polyglot.t('serverConfigModelErrors.provideValue'));
}

if (!attrs.SSL) {
addError('SSL', 'SSL must be turned on for remote servers.');
}
}

if (!attrs.default) {
if (!is.number(attrs.port)) {
addError('port', 'Please provide a number.');
if (attrs.useTor) {
if (!attrs.torProxy) {
addError('torProxy', app.polyglot.t('serverConfigModelErrors.provideValue'));
} else if (typeof attrs.torProxy !== 'string') {
addError('torProxy', 'Please provide the tor proxy configuration as a string.');
} else {
if (!is.within(attrs.port, -1, 65536)) {
addError('port', 'Please provide a number between 0 and 65535.');
let valid = true;
const split = attrs.torProxy.split(':');

if (split.length !== 2) {
valid = false;
} else {
if (!is.ip(split[0])) {
valid = false;
} else if (!is.within(parseInt(split[1], 10), -1, 65536)) {
valid = false;
}
}

if (!valid) {
addError('torProxy', app.polyglot.t('serverConfigModelErrors.invalidTorProxy'));
}
}
}

if (!attrs.default) {
if (attrs.port === undefined || attrs.port === '') {
addError('port', app.polyglot.t('serverConfigModelErrors.provideValue'));
} else if (!is.number(attrs.port)) {
addError('port', app.polyglot.t('serverConfigModelErrors.providePortAsNumber'));
} else if (!is.within(attrs.port, -1, 65536)) {
addError('port', app.polyglot.t('serverConfigModelErrors.provideValidPortRange'));
}
} else {
if (is.existy(attrs.port) && attrs.port !== this.defaults().port) {
// For now, not allowing the port to be changed on the default server,
Expand Down Expand Up @@ -103,7 +133,7 @@ export default class extends BaseModel {
needsAuthentication() {
let needsAuth = false;

if (!this.isLocalServer()) {
if (!this.isLocalServer() || this.get('default')) {
needsAuth = true;
} else {
if (this.get('username') || this.get('password')) {
Expand Down
3 changes: 2 additions & 1 deletion js/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ export default class ObRouter extends Router {
}

connectedPeers() {
const peerFetch = $.get(app.getServerUrl('ob/peers')).done((peersData) => {
const peerFetch = $.get(app.getServerUrl('ob/peers')).done((data) => {
const peersData = data || [];
const peers = peersData.map(peer => (peer.slice(peer.lastIndexOf('/') + 1)));

this.loadPage(
Expand Down
14 changes: 12 additions & 2 deletions js/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ServerConfig from './models/ServerConfig';
import serverConnect, {
events as serverConnectEvents,
getSocket,
getCurrentConnection,
} from './utils/serverConnect';
import LocalSettings from './models/LocalSettings';
import ObRouter from './router';
Expand Down Expand Up @@ -411,8 +412,17 @@ function start() {
app.profile = new Profile({ peerID: data.peerID });

app.settings = new Settings();
// If the server is running testnet, set that here
app.testnet = data.testnet; // placeholder for later when we need this data for purchases

// This is the server config as returned by ob/config. It has nothing to do with
// app.serverConfigs which is a collection of server configuration data related
// to connecting with a server. The latter is stored in local storage.
app.serverConfig = data || {};

const curConn = getCurrentConnection();

if (curConn && curConn.status !== 'disconnected') {
app.pageNav.torIndicatorOn = app.serverConfig.tor && curConn.server.get('useTor');
}

// We'll default our server language to whatever is stored locally.
app.settings.set('language', app.localSettings.get('language'));
Expand Down
23 changes: 21 additions & 2 deletions js/startup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

import { screen, shell } from 'electron';
import $ from 'jquery';
import Backbone from 'backbone';
import { getBody } from '../utils/selectors';
import app from '../app';
import Backbone from 'backbone';
import TorExternalLinkWarning from '../views/modals/TorExternalLinkWarning';

export function fixLinuxZoomIssue() {
// fix zoom issue on Linux hiDPI
Expand Down Expand Up @@ -42,7 +44,24 @@ export function handleLinks() {
Backbone.history.navigate(href.slice(5), true);
} else {
// external link
shell.openExternal(link.protocol === 'file:' ? `http://${href}` : href);
const activeServer = app.serverConfigs.activeServer;
const localSettings = app.localSettings;
const warningOptedOut = app.localSettings &&
localSettings.get('dontShowTorExternalLinkWarning');

if (activeServer && activeServer.get('useTor') && !warningOptedOut) {
const warningModal = new TorExternalLinkWarning({ url: href })
.render()
.open();

warningModal.on('cancelClick', () => warningModal.close());
warningModal.on('confirmClick', () => {
shell.openExternal(link.protocol === 'file:' ? `http://${href}` : href);
warningModal.close();
});
} else {
shell.openExternal(link.protocol === 'file:' ? `http://${href}` : href);
}
}
} else {
if (!href.startsWith('#')) {
Expand Down
7 changes: 1 addition & 6 deletions js/templates/connectedPeersPage.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,4 @@ <h1 class="flexExpand"><%= ob.polyT('connectedPeersPage.heading') %></h1>
<hr class="clrBr">
<a class="btn clrBr clrP js-morePeersBtn">Load More</a>
</div>
</div>

<% if (!ob.peers.length) { %>
<p><%= ob.polyT('connectedPeersPage.noResults') %></p>
<% } %>

</div>
15 changes: 9 additions & 6 deletions js/templates/modals/connectionManagement/configuration.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
<div class="flexVCent clrT">
<div class="col4 <% if (ob.status === 'connected') print('txB') %>"><%= ob.name %></div>
<div class="col4 flexVCent gutterHTn <% if (ob.status === 'connected') print('txB') %>">
<% if (ob.status === 'connected') { %>
<span class="ion-ios-checkmark-empty clrTEmph1 tx1"></span>
<% }%>
<div><%= ob.name %></div>
</div>
<div class="col4 <% if (ob.status === 'connected') print('txB') %>"><%= ob.serverIp %></div>
<div class="col4">
<div class="flexHRight">
<% if (ob.status === 'connected') { %>
<div class="txB flexVCent flexHRight gutterHTn"><span class="ion-ios-checkmark-empty clrTEmph1 tx1"></span><span><%= ob.polyT('connectionManagement.configurations.statusConnected') %></span></div>
<% } else { %>
<div class="gutterHTn">
<a class="iconBtn clrP clrBr ion-trash-b js-btnDelete <% if (ob.deleteConfirmOn) print('confirmDisabled') %> <% if (ob.default) print('hide') %>"></a>
<a class="iconBtn clrP clrBr ion-ios-gear js-btnEdit <% if (ob.default) print('hide') %>"></a>
<a class="iconBtn clrP clrBr ion-ios-gear js-btnEdit"></a>
<% if (ob.status === 'connecting') { %>
<a class="btn clrP clrBr js-btnCancel btnConnectCancel">
<%= ob.spinner({ className: 'spinnerSm' }) %>
<%= ob.polyT('connectionManagement.configurations.btnCancel') %>
</a>
<% } else if (ob.status === 'connected') { %>
<a class="btn clrP clrBr js-btnDisconnect btnDisconnect"><%= ob.polyT('connectionManagement.configurations.btnDisconnect') %></a>
<% } else { %>
<% const btnText = ob.status === 'connect-attempt-failed' ? ob.polyT('connectionManagement.configurations.btnRetry') : ob.polyT('connectionManagement.configurations.btnConnect') %>
<a class="btn clrP clrBr js-btnConnect btnConnectCancel"><%= btnText %></a>
<% } %>
</div>
<% } %>
</div>
</div>
</div>
Expand Down
Loading