Skip to content

Commit

Permalink
Backwards compatability
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaldwinroberts committed Dec 7, 2016
1 parent 06d015f commit bfcf889
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 134 deletions.
111 changes: 49 additions & 62 deletions ManualTests.md
@@ -1,65 +1,52 @@
# Manual tests
After making changes all the tests should be run on both connman and Network Manager.

## Test 1
1. Retry flag = false
2. No credentials stored
3. Make sure the hotspot starts
4. Set correct credentials
5. Make sure it connects and then exits

## Test 2
1. Retry flag = false
2. Correct credentials stored
3. Make sure it connects and then exits

## Test 3
1. Retry flag = false
2. No credentials stored
3. Make sure the hotspot starts
4. Set incorrect credentials (password less than 8 chars)
5. Make sure it does not connect and then exits

## Test 4
1. Retry flag = false
2. No credentials stored
3. Make sure the hotspot starts
4. Set incorrect credentials (password greater than 8 chars)
5. Make sure it does not connect and then exits

## Test 5
1. Retry flag = false
2. Incorrect credentials stored
3. Make sure it does not connect and then exits

## Test 6
1. Retry flag = true
2. No credentials stored
3. Make sure the hotspot starts
4. Set correct credentials
5. Make sure it connects and then exits

## Test 7
1. Retry flag = true
2. Correct credentials stored
3. Make sure it connects and then exits

## Test 8
1. Retry flag = true
2. No credentials stored
3. Make sure the hotspot starts
4. Set incorrect credentials (password less than 8 chars)
5. Make sure it does not connect and go to Test 6, Step 3

## Test 9
1. Retry flag = true
2. No credentials stored
3. Make sure the hotspot starts
4. Set incorrect credentials (password greater than 8 chars)
5. Make sure it does not connect and go to Test 6, Step 3

## Test 10
1. Retry flag = true
2. Incorrect credentials stored
3. Make sure it does not connect and go to Test 6, Step 3

### Test 1
1. No credentials stored
2. Run start script
3. Enter incorrect credentials (less than 8 chars)
4. Make sure it retries
5. Enter incorrect credentials (more than 8 chars)
6. Make sure it retries
7. Enter correct credentials
8. Make sure it connects and exits

### Test 2
1. Correct credentials stored
2. Run start script
3. Make sure it connects and exits

### Test 3
1. Incorrect credentials stored
2. Run start script
3. Make sure it fails to connect and exits

### Test 4
1. No credentials stored
2. Run node app.js
3. Enter incorrect credentials (less than 8 chars)
4. Make sure it retries
5. Enter incorrect credentials (more than 8 chars)
6. Make sure it retries
7. Enter correct credentials
8. Make sure it connects and exits

### Test 5
1. Correct credentials stored
2. Run node app.js
3. Enter incorrect credentials (less than 8 chars)
4. Make sure it retries
5. Enter incorrect credentials (more than 8 chars)
6. Make sure it retries
7. Enter correct credentials
8. Make sure it connects and exits

### Test 6
1. Incorrect credentials stored
2. Run node app.js
3. Enter incorrect credentials (less than 8 chars)
4. Make sure it retries
5. Enter incorrect credentials (more than 8 chars)
6. Make sure it retries
7. Enter correct credentials
8. Make sure it connects and exits
20 changes: 6 additions & 14 deletions README.md
@@ -1,20 +1,12 @@
# resin-wifi-connect
An app to allow WiFi configuration to be set via a captive portal. It checks whether WiFi is connected, tries to join the saved network, and if this fails, it opens an access point to which you can connect using a laptop or mobile phone and input new WiFi credentials.

## Warning
The latest version of resin-wifi-connect adds support for Network Manager which is replacing connman in ResinOS 2.0 onwards. Although this app still supports connman, there have been some breaking changes made to the control flow and subsequently the commands used to start the app. Please ensure you test resin-wifi-connect with your device and code base to ensure there are no issues before deploying to your fleet.

## How to use this
This is a [resin.io](http://resin.io) application. Check out our [Getting Started](http://docs.resin.io/#/pages/installing/gettingStarted.md) guide if it's your first time using Resin.

This project is meant to be integrated as part of a larger application (that is, _your_ application). An example on how to use this on a Python project can be found [here](https://github.com/resin-io-projects/resin-wifi-connect-python-example).

If you need to add dependencies, add the corresponding statements in the [Dockerfile](./Dockerfile.template) template. You can add the commands that run your app in the [start](./start) script.

The app can be run in 2 modes; `--retry=true` and `--retry=false`. In the first mode the app will always remove the saved credentials and open an access point if it cannot connect to the saved network or there is no saved network available (we recomend this mode for initial setup and when moving your device to a new location). In the second mode the app will never remove the saved credentials or open an access point if there is a saved network, however it will open an Access point if there is no saved network (we recomend this mode during normal use).

This app only exits after a successful connection (unless `--retry=false` mode is used), so if you add your app after [line 3](./start#L3) you ensure that everything happens after wifi is correctly configured.

If you need to add dependencies, add the corresponding statements in the [Dockerfile](./Dockerfile.template) template. You can add the commands that run your app in the [start](./start) script. This app only exits after a successful connection, so if you add your app after [line 3](./start#L3) you ensure that everything happens after wifi is correctly configured.
This is a node.js application, but your app can be any language/framework you want as long as you install it properly - if you need help, check out our [Dockerfile guide](http://docs.resin.io/#/pages/using/dockerfile.md). This project uses a Resin feature called "Dockerfile template": the base image is chosen depending on the architecture, specified by the `%%RESIN_ARCH%%` variable (see [line 1](./Dockerfile.template#L1) in the template).

## Supported boards / dongles
Expand All @@ -32,7 +24,7 @@ The software has also been successfully tested on RaspberryPi 3 with its onboard

Given these results, it is probable that most dongles with *Atheros* or *Ralink* chipsets will work.

The following dongles are known **not** to work (as the driver is not friendly with access point mode, connman or Network Manager):
The following dongles are known **not** to work (as the driver is not friendly with access point mode, Connman or Network Manager):
* Official Raspberry Pi dongle (BCM43143 chip)
* Addon NWU276 (Mediatek MT7601 chip)
* Edimax (Realtek RTL8188CUS chip)
Expand All @@ -42,10 +34,10 @@ The software is expected to work with other Resin supported boards as long as yo
Please [contact us](https://resin.io/contact/) or raise [an issue](https://github.com/resin-io/resin-wifi-connect/issues) if you hit any trouble.

## How it works
This app interacts with either connman or Network Manager in ResinOS. It checks whether the wifi has been previously provisioned, and if it hasn't, it opens an access point to which you can connect using a laptop or mobile phone.
This app interacts with either Connman or Network Manager in Resin's base OS. It checks whether the wifi has been previously provisioned, and if it hasn't, it opens an access point to which you can connect using a laptop or mobile phone.

The access point's name (SSID) is, by default, "ResinAP". You can change this by setting the `PORTAL_SSID` environment variable. By default, the network is unprotected, but you can add a WPA2 passphrase by setting the `PORTAL_PASSPHRASE` environment variable.

When you connect to the access point, any web page you open will be redirected to our captive portal page, where you can select the SSID and passphrase of the WiFi network to connect to. After this, the app will disable the access point and try to connect. If it succeeds, the network will be saved. If it fails and `--retry=true` mode is used it will enable the access point for you to try again.

An important detail is that in `--retry=false` mode, the project will not attempt to enter access point mode if a successful configuration has happened in the past. This means that if you go through the process and then move the device to a different network, it will be trying to connect forever. It is left to the user application to decide which is the appropriate condition to re-enter access point mode. This can be "been offline for more than 1 day" or "user pushed the reset button" or something else. To re-enter access point mode, simply run `node app.js --retry=true` as done in the provided [start](https://github.com/resin-io/resin-wifi-connect/blob/master/start) script.
When you connect to the access point, any web page you open will be redirected to our captive portal page, where you can select the SSID and passphrase of the WiFi network to connect to. After this, the app will disable the access point and try to connect. If it fails, it will enable the access point for you to try again. If it succeeds, the network will be remembered by Connman or Network Manager as a favorite.
An important detail is that by default, the project will not attempt to enter access point mode if a successful configuration has happened in the past. This means that if you go through the process and then move the device to a different network, it will be trying to connect forever. It is left to the user application to decide which is the appropriate condition to re-enter access point mode. This can be "been offline for more than 1 day" or "user pushed the reset button" or something else. To re-enter access point mode, simply re-run `node src/app.js` (without the clear flag as it defaults to true) as done in the provided [start](https://github.com/resin-io/resin-wifi-connect/blob/master/start) script.
1 change: 0 additions & 1 deletion package.json
Expand Up @@ -4,7 +4,6 @@
"repository": "https://github.com/pcarranzav/resin-wifi-connect",
"description": "A tool to allow wifi configuration to be set via a captive portal",
"scripts": {
"start": "cd src && node app.js",
"lint": "resin-lint src",
"test": "npm run lint"
},
Expand Down
40 changes: 27 additions & 13 deletions src/app.coffee
Expand Up @@ -55,10 +55,10 @@ run = ->
.then (setup) ->
if setup
console.log('Credentials found')
hotspot.stop()
hotspot.stop(manager)
.then ->
console.log('Connecting')
manager.connect(15000)
manager.connect(15000) # Delay needed to allow manager to connect
.then ->
console.log('Connected')
console.log('Exiting')
Expand All @@ -67,7 +67,9 @@ run = ->
error(e)
else
console.log('Credentials not found')
wifiScan.scanAsync()
hotspot.stop(manager)
.then ->
wifiScan.scanAsync()
.then (results) ->
ssids = results
hotspot.start(manager)
Expand All @@ -76,21 +78,24 @@ run = ->

app.listen(80)

retry = false
if process.argv[2] == '--retry=true'
console.log('Retry enabled')
retry = true
else if process.argv[2] == '--retry=false'
console.log('Retry disabled')
retry = true
clear = true
manager = null

if process.argv[2] == '--clear=true'
console.log('Clear enabled')
clear = true
else if process.argv[2] == '--clear=false'
console.log('Clear disabled')
clear = false
else if not process.argv[2]?
console.log('No retry flag passed')
console.log('Retry disabled')
console.log('No clear flag passed')
console.log('Clear enabled')
else
console.log('Invalid retry flag passed')
console.log('Invalid clear flag passed')
console.log('Exiting')
process.exit()

manager = null
systemd.exists('NetworkManager.service')
.then (result) ->
if result
Expand All @@ -99,6 +104,15 @@ systemd.exists('NetworkManager.service')
else
console.log('Using connman.service')
manager = connman
.then ->
if clear
console.log('Clearing credentials')
manager.clearCredentials()
.then ->
manager.isSetup()
.then (setup) ->
if setup
retry = false
.then ->
run()
.catch (e) ->
Expand Down
2 changes: 2 additions & 0 deletions src/connman.coffee
Expand Up @@ -49,6 +49,8 @@ exports.setCredentials = (ssid, passphrase) ->

exports.clearCredentials = ->
fs.unlinkAsync(config.persistentConfig)
.catch code: 'ENOENT', (e) ->
return

exports.connect = (timeout) ->
bus.getInterfaceAsync(SERVICE, WIFI_OBJECT, TECHNOLOGY_INTERFACE)
Expand Down
2 changes: 2 additions & 0 deletions src/dnsmasq.coffee
Expand Up @@ -30,6 +30,8 @@ exports.stop = ->
return Promise.resolve()

new Promise (resolve, reject) ->
console.log('Stopping dnsmasq..')

ps.kill('SIGTERM')

timeout = setTimeout ->
Expand Down
2 changes: 2 additions & 0 deletions src/hostapd.coffee
Expand Up @@ -37,6 +37,8 @@ exports.stop = ->
return Promise.resolve()

new Promise (resolve, reject) ->
console.log('Stopping hostapd..')

ps.kill('SIGTERM')

timeout = setTimeout ->
Expand Down
73 changes: 39 additions & 34 deletions src/networkManager.coffee
Expand Up @@ -10,8 +10,8 @@ systemd = require './systemd'

SERVICE = 'org.freedesktop.NetworkManager'

# This allows us to say, if there IS a connection TYPE other than '802-3-ethernet' then network manager has been set up previously.
WHITE_LIST = ['802-3-ethernet']
# This allows us to say, if there IS an existing connection id that is not in the whitelist then network manager has been set up previously.
WHITE_LIST = ['resin-sample', 'Wired connection 1']

NM_STATE_CONNECTED_GLOBAL = 70
NM_DEVICE_TYPE_WIFI = 2
Expand Down Expand Up @@ -63,32 +63,37 @@ exports.connect = (timeout) ->
.then (validDevices) ->
if validDevices.length is 0
throw ('No valid devices found.')
bus.getInterfaceAsync(SERVICE, '/org/freedesktop/NetworkManager', 'org.freedesktop.NetworkManager')
.delay(1000) # Delay needed to avoid "Error: org.freedesktop.NetworkManager.UnknownConnection at Error (native)"
.then (manager) ->
manager.ActivateConnectionAsync('/', validDevices[0], '/')
.then ->
new Promise (resolve, reject) ->
handler = (value) ->
if value == NM_STATE_CONNECTED_GLOBAL
getConnections()
.filter(validateConnection)
.then (validConnections) ->
if validConnections.length is 0
throw ('No valid connections found.')
bus.getInterfaceAsync(SERVICE, '/org/freedesktop/NetworkManager', 'org.freedesktop.NetworkManager')
.delay(1000) # Delay needed to avoid "Error: org.freedesktop.NetworkManager.UnknownConnection at Error (native)" when activating the connection
.then (manager) ->
manager.ActivateConnectionAsync(validConnections[0], validDevices[0], '/')
.then ->
new Promise (resolve, reject) ->
handler = (value) ->
if value == NM_STATE_CONNECTED_GLOBAL
manager.removeListener('StateChanged', handler)
resolve()

# Listen for 'Connected' signals
manager.on('StateChanged', handler)

# But try to read in case we registered the event handler
# after is was already connected
manager.CheckConnectivityAsync()
.then (state) ->
if state == NM_CONNECTIVITY_FULL or state == NM_CONNECTIVITY_LIMITED
manager.removeListener('StateChanged', handler)
resolve()

setTimeout ->
manager.removeListener('StateChanged', handler)
resolve()

# Listen for 'Connected' signals
manager.on('StateChanged', handler)

# But try to read in case we registered the event handler
# after is was already connected
manager.CheckConnectivityAsync()
.then (state) ->
if state == NM_CONNECTIVITY_FULL or state == NM_CONNECTIVITY_LIMITED
manager.removeListener('StateChanged', handler)
resolve()

setTimeout ->
manager.removeListener('StateChanged', handler)
reject('Timed out')
, timeout
reject('Timed out')
, timeout

getConnections = ->
bus.getInterfaceAsync(SERVICE, '/org/freedesktop/NetworkManager/Settings', 'org.freedesktop.NetworkManager.Settings')
Expand All @@ -97,20 +102,20 @@ getConnections = ->
getConnection = (connection) ->
bus.getInterfaceAsync(SERVICE, connection, 'org.freedesktop.NetworkManager.Settings.Connection')

validateConnection = (connection) ->
getConnection(connection)
.call('GetSettingsAsync')
.then (settings) ->
return settings.connection.type not in WHITE_LIST

deleteConnection = (connection) ->
getConnection(connection)
.then (connection) ->
connection.GetSettingsAsync()
.then (settings) ->
if settings.connection.type not in WHITE_LIST
if settings.connection.id not in WHITE_LIST
connection.DeleteAsync()

validateConnection = (connection) ->
getConnection(connection)
.call('GetSettingsAsync')
.then (settings) ->
return settings.connection.id not in WHITE_LIST

getDevices = ->
bus.getInterfaceAsync(SERVICE, '/org/freedesktop/NetworkManager', 'org.freedesktop.NetworkManager')
.call('GetDevicesAsync')
Expand Down
10 changes: 7 additions & 3 deletions src/wifi-scan.coffee
@@ -1,11 +1,12 @@
Promise = require 'bluebird'
{ exec } = require 'child_process'
execAsync = Promise.promisify(exec)
child_process = Promise.promisifyAll require 'child_process'
# { exec } = require 'child_process'
# execAsync = Promise.promisify(exec)

config = require './config'

exports.scanAsync = ->
execAsync("iw #{config.iface} scan ap-force")
child_process.execAsync("iw #{config.iface} scan ap-force")
.then (output) ->
bsss = {}
bss = null
Expand Down Expand Up @@ -35,3 +36,6 @@ exports.scanAsync = ->

# sort by signal strength
return networks.sort((a, b) -> b.signal - a.signal)
.catch code: 240, (e) ->
Promise.delay(1000) # Delay needed to avoid `Command failed: iw wlan0 scan ap-force command failed: Device or resource busy (-16)`
.then(exports.scanAsync)

0 comments on commit bfcf889

Please sign in to comment.