concerto_client 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/concerto_client/application/app.rb +44 -55
- data/lib/concerto_client/application/views/password.haml +9 -0
- data/lib/concerto_client/application/views/setup.haml +17 -10
- data/lib/concerto_client/config_store.rb +48 -0
- data/lib/concerto_client/live_image.rb +25 -0
- data/lib/concerto_client/netconfig.rb +44 -27
- metadata +6 -3
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require 'sinatra'
|
2
|
+
require 'sinatra/base'
|
3
3
|
require 'haml'
|
4
4
|
require 'json'
|
5
5
|
require 'net/http'
|
@@ -7,10 +7,6 @@ require 'ipaddress'
|
|
7
7
|
require 'concerto_client/netconfig'
|
8
8
|
|
9
9
|
class ConcertoConfigServer < Sinatra::Base
|
10
|
-
# Some files we will use. None of these need to exist right away.
|
11
|
-
PASSWORD_FILE='/tmp/concerto_password'
|
12
|
-
URL_FILE='/tmp/concerto_url'
|
13
|
-
|
14
10
|
# push these over to netconfig.rb?
|
15
11
|
# Our list of available physical-layer connection methods...
|
16
12
|
CONNECTION_METHODS = [
|
@@ -29,14 +25,7 @@ class ConcertoConfigServer < Sinatra::Base
|
|
29
25
|
IPAddress.parse("::1")
|
30
26
|
]
|
31
27
|
|
32
|
-
|
33
|
-
begin
|
34
|
-
PASSWORD = File.open(PASSWORD_FILE) do |f|
|
35
|
-
f.readline.chomp
|
36
|
-
end
|
37
|
-
rescue Errno::ENOENT
|
38
|
-
PASSWORD = 'default'
|
39
|
-
end
|
28
|
+
set :haml, { :format => :html5, :layout => :main }
|
40
29
|
|
41
30
|
helpers do
|
42
31
|
# Get the return value of the method on obj if obj supports the method.
|
@@ -51,40 +40,39 @@ class ConcertoConfigServer < Sinatra::Base
|
|
51
40
|
end
|
52
41
|
|
53
42
|
# Enforce authentication on actions.
|
54
|
-
# Calling from within an action will check authentication and return
|
55
|
-
# if unauthorized.
|
43
|
+
# Calling from within an action will check authentication and return
|
44
|
+
# 401 if unauthorized.
|
56
45
|
def protected!
|
57
46
|
unless authorized?
|
58
|
-
response['WWW-Authenticate'] =
|
47
|
+
response['WWW-Authenticate'] = \
|
48
|
+
%(Basic realm="Concerto Configuration")
|
59
49
|
throw(:halt, [401, "Not authorized\n"])
|
60
50
|
end
|
61
51
|
end
|
62
52
|
|
63
53
|
# Check authorization credentials.
|
64
|
-
# Currently configured to check if the REMOTE_ADDR is
|
54
|
+
# Currently configured to check if the REMOTE_ADDR is local and allow
|
65
55
|
# everything if so. This permits someone at local console to configure
|
66
|
-
# without the need for a password. Others must have the correct
|
67
|
-
# to be considered authorized.
|
56
|
+
# without the need for a password. Others must have the correct
|
57
|
+
# password to be considered authorized.
|
68
58
|
def authorized?
|
69
59
|
ip = IPAddress.parse(request.env['REMOTE_ADDR'])
|
60
|
+
password = ConcertoConfig::ConfigStore.read_config(
|
61
|
+
'password', 'default'
|
62
|
+
)
|
70
63
|
if LOCALHOSTS.include? ip
|
71
64
|
# allow all requests from localhost no questions asked
|
72
65
|
true
|
73
66
|
else
|
74
67
|
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
75
|
-
@auth.provided? && @auth.basic? && @auth.credentials &&
|
68
|
+
@auth.provided? && @auth.basic? && @auth.credentials && \
|
69
|
+
@auth.credentials == ['root', password]
|
76
70
|
end
|
77
71
|
end
|
78
72
|
|
79
73
|
# Get our base URL from wherever it may be stored.
|
80
74
|
def concerto_url
|
81
|
-
|
82
|
-
File.open(URL_FILE) do |f|
|
83
|
-
f.readline.chomp
|
84
|
-
end
|
85
|
-
rescue
|
86
|
-
""
|
87
|
-
end
|
75
|
+
ConcertoConfig::ConfigStore.read_config('concerto_url', '')
|
88
76
|
end
|
89
77
|
|
90
78
|
# Try to figure out what our current IPv4 address is
|
@@ -116,7 +104,8 @@ class ConcertoConfigServer < Sinatra::Base
|
|
116
104
|
# Check if we can retrieve a URL and get a 200 status code.
|
117
105
|
def validate_url(url)
|
118
106
|
begin
|
119
|
-
# this will fail with Errno::something if server
|
107
|
+
# this will fail with Errno::something if server
|
108
|
+
# can't be reached
|
120
109
|
response = Net::HTTP.get_response(URI(url))
|
121
110
|
if response.code != "200"
|
122
111
|
# also bomb out if we don't get an OK response
|
@@ -159,7 +148,7 @@ class ConcertoConfigServer < Sinatra::Base
|
|
159
148
|
if network_ok
|
160
149
|
# Everything's up and running, we just don't know what
|
161
150
|
# our URL should be.
|
162
|
-
haml :setup
|
151
|
+
haml :setup
|
163
152
|
else
|
164
153
|
# The network settings are not sane, we don't have an IP.
|
165
154
|
# Redirect the user to the network configuration page to
|
@@ -173,10 +162,9 @@ class ConcertoConfigServer < Sinatra::Base
|
|
173
162
|
protected!
|
174
163
|
url = params[:url]
|
175
164
|
if validate_url(url)
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
165
|
+
# save to the configuration store
|
166
|
+
ConcertoConfig::ConfigStore.write_config('concerto_url', url)
|
167
|
+
|
180
168
|
# root will now redirect to the proper concerto_url
|
181
169
|
redirect '/screen'
|
182
170
|
else
|
@@ -189,7 +177,7 @@ class ConcertoConfigServer < Sinatra::Base
|
|
189
177
|
# render a page indicating that the concerto_url is no good.
|
190
178
|
# this page redirects to / every 5 seconds
|
191
179
|
get '/problem' do
|
192
|
-
haml :problem
|
180
|
+
haml :problem
|
193
181
|
end
|
194
182
|
|
195
183
|
get '/netconfig' do
|
@@ -201,7 +189,7 @@ class ConcertoConfigServer < Sinatra::Base
|
|
201
189
|
# is not implemented. This is also how we get away with just having
|
202
190
|
# one instance each of the config classes that are currently selected.
|
203
191
|
begin
|
204
|
-
cm, am = ConcertoConfig.
|
192
|
+
cm, am = ConcertoConfig.read_network_config
|
205
193
|
rescue Errno::ENOENT
|
206
194
|
cm = nil
|
207
195
|
am = nil
|
@@ -212,8 +200,7 @@ class ConcertoConfigServer < Sinatra::Base
|
|
212
200
|
haml :netsettings, :locals => {
|
213
201
|
:connection_method => cm,
|
214
202
|
:addressing_method => am
|
215
|
-
}
|
216
|
-
:format => :html5, :layout => :main
|
203
|
+
}
|
217
204
|
end
|
218
205
|
|
219
206
|
# Given the name of a class, pick a class out of a list of allowed classes.
|
@@ -276,23 +263,8 @@ class ConcertoConfigServer < Sinatra::Base
|
|
276
263
|
do_assign(cmargs, cm)
|
277
264
|
do_assign(amargs, am)
|
278
265
|
|
279
|
-
#
|
280
|
-
|
281
|
-
cm.validate
|
282
|
-
am.validate
|
283
|
-
|
284
|
-
# Serialize our instances as JSON data to be written to the config file.
|
285
|
-
json_data = {
|
286
|
-
'connection_method' => cmclass.basename,
|
287
|
-
'connection_method_args' => cm.args,
|
288
|
-
'addressing_method' => amclass.basename,
|
289
|
-
'addressing_method_args' => am.args
|
290
|
-
}
|
291
|
-
|
292
|
-
# Write the config file to disk.
|
293
|
-
File.open(ConcertoConfig::CONFIG_FILE, 'w') do |f|
|
294
|
-
f.write json_data.to_json
|
295
|
-
end
|
266
|
+
# Save the configuration file.
|
267
|
+
ConcertoConfig.write_network_config(cm, am)
|
296
268
|
|
297
269
|
# Reload network configuration.
|
298
270
|
STDERR.puts "Trying to bring down the interface"
|
@@ -300,13 +272,30 @@ class ConcertoConfigServer < Sinatra::Base
|
|
300
272
|
ConcertoConfig.configured_interface.ifdown
|
301
273
|
end
|
302
274
|
STDERR.puts "Rewriting configuration files"
|
303
|
-
ConcertoConfig::
|
275
|
+
ConcertoConfig::configure_system_network
|
304
276
|
STDERR.puts "Bringing interface back up"
|
305
277
|
ConcertoConfig.configured_interface.ifup
|
306
278
|
|
307
279
|
# Back to the network form.
|
308
280
|
redirect '/netconfig' # as a get request
|
309
281
|
end
|
282
|
+
|
283
|
+
get '/password' do
|
284
|
+
protected!
|
285
|
+
haml :password
|
286
|
+
end
|
287
|
+
|
288
|
+
post '/password' do
|
289
|
+
protected!
|
290
|
+
|
291
|
+
if params[:newpass] != params[:newpass_confirm]
|
292
|
+
# something something error handling something
|
293
|
+
redirect '/password'
|
294
|
+
end
|
295
|
+
|
296
|
+
ConcertoConfig::ConfigStore.write_config('password', params[:newpass])
|
297
|
+
redirect '/setup'
|
298
|
+
end
|
310
299
|
end
|
311
300
|
|
312
301
|
ConcertoConfigServer.run!
|
@@ -0,0 +1,9 @@
|
|
1
|
+
%form{:method=>'post'}
|
2
|
+
%p
|
3
|
+
%label{:for=>'newpass'} New Password
|
4
|
+
%input{:type=>'password', :name=>'newpass'}
|
5
|
+
%p
|
6
|
+
%label{:for=>'newpass_confirm'} Confirm Password
|
7
|
+
%input{:type=>'password', :name=>'newpass_confirm'}
|
8
|
+
%p
|
9
|
+
%input{:type=>'submit', :value=>'Change Password'}
|
@@ -1,18 +1,25 @@
|
|
1
1
|
%h1 Welcome to Concerto Player
|
2
2
|
%p
|
3
|
-
|
4
|
-
|
3
|
+
You're seeing this because your Concerto player has not yet been configured.
|
4
|
+
We need the URL of your Concerto instance before we can get up and running.
|
5
5
|
|
6
6
|
%form{:method=>'post'}
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
%p
|
8
|
+
%label{:for=>'url'} URL
|
9
|
+
%input{:type=>'text', :name=>'url'}
|
10
|
+
%input{:type=>'submit', :value=>'Here it is!'}
|
11
11
|
|
12
12
|
%p
|
13
|
-
|
14
|
-
|
13
|
+
The IPv4 address of this screen appears to be:
|
14
|
+
=my_ip
|
15
15
|
%p
|
16
|
-
|
16
|
+
==This page can be accessed remotely via http://#{my_ip}/setup
|
17
17
|
%p
|
18
|
-
|
18
|
+
Username is root, default password is 'default'.
|
19
|
+
%p
|
20
|
+
Also, you may want to
|
21
|
+
%a{:href=>'/netconfig'} change network settings
|
22
|
+
or
|
23
|
+
%a{:href=>'/password'} change the configuration password.
|
24
|
+
|
25
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'concerto_client/live_image'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# A key/value store for strings.
|
5
|
+
# Right now implemented as disk files.
|
6
|
+
module ConcertoConfig
|
7
|
+
module ConfigStore
|
8
|
+
@@path = nil
|
9
|
+
|
10
|
+
def self.read_config(name, default='')
|
11
|
+
initialize_path if not @@path
|
12
|
+
file = File.join(@@path, name)
|
13
|
+
rofile = File.join(@@ropath, name)
|
14
|
+
|
15
|
+
# Check the read/write config location first. If nothing there,
|
16
|
+
# check the read-only location. If nothing is there, return default.
|
17
|
+
# This way writes can be made at runtime on read-only media while
|
18
|
+
# still allowing some settings to be "baked into" the media.
|
19
|
+
if File.exist?(file)
|
20
|
+
IO.read(file)
|
21
|
+
elsif File.exist?(rofile)
|
22
|
+
IO.read(rofile)
|
23
|
+
else
|
24
|
+
default
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Write a config to the read/write configuration location.
|
29
|
+
def self.write_config(name, value)
|
30
|
+
initialize_path if not @@path
|
31
|
+
file = File.join(@@path, name)
|
32
|
+
|
33
|
+
File.open(file, 'w') do |f|
|
34
|
+
f.write value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.initialize_path
|
39
|
+
@@ropath = File.join(LiveImage.mountpoint, 'concerto', 'config')
|
40
|
+
if LiveImage.readonly?
|
41
|
+
@@path = '/tmp/concerto/config'
|
42
|
+
else
|
43
|
+
@@path = @@ropath
|
44
|
+
end
|
45
|
+
FileUtils.mkdir_p @@path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Functions for dealing with the live image
|
2
|
+
# (where it's mounted, if it's read-only, etc)
|
3
|
+
module ConcertoConfig
|
4
|
+
module LiveImage
|
5
|
+
def self.mountpoint
|
6
|
+
'/live/image'
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.readonly?
|
10
|
+
# on a readonly file system this will fail
|
11
|
+
if not File.exist? self.mountpoint
|
12
|
+
true
|
13
|
+
else
|
14
|
+
begin
|
15
|
+
f = Tempfile.new('test', self.mountpoint)
|
16
|
+
f.close!
|
17
|
+
false
|
18
|
+
rescue
|
19
|
+
# if the tempfile creation bombs we assume readonly
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'json'
|
5
5
|
require 'ipaddress'
|
6
|
+
require 'concerto_client/config_store'
|
6
7
|
|
7
8
|
# The big idea here is that we have connection methods (layer 2)
|
8
9
|
# and addressing methods (layer 3) and by combining that configuration
|
@@ -19,21 +20,17 @@ require 'ipaddress'
|
|
19
20
|
# files such as wpa_supplicant.conf, resolv.conf etc.
|
20
21
|
|
21
22
|
class Module
|
23
|
+
# Get the name of a class/module and strip off any leading modules.
|
24
|
+
# This is useful in determining arguments for Module#const_get.
|
22
25
|
def basename
|
23
26
|
name.gsub(/^.*::/, '')
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
30
|
module ConcertoConfig
|
28
|
-
# Where we store the name of the interface we are going to configure.
|
29
|
-
INTERFACE_FILE='/tmp/concerto_configured_interface'
|
30
|
-
|
31
31
|
# The Debian interfaces configuration file we are going to write out.
|
32
32
|
INTERFACES_FILE='/etc/network/interfaces'
|
33
33
|
|
34
|
-
# The configuration file we will read from.
|
35
|
-
CONFIG_FILE='/live/image/netconfig.json'
|
36
|
-
|
37
34
|
# Some useful interface operations.
|
38
35
|
class Interface
|
39
36
|
# Wrap an interface name (eth0, wlan0 etc) with some useful operations.
|
@@ -440,17 +437,20 @@ module ConcertoConfig
|
|
440
437
|
end
|
441
438
|
end
|
442
439
|
|
443
|
-
# Read a JSON formatted network configuration from
|
440
|
+
# Read a JSON formatted network configuration from the config store.
|
444
441
|
# This instantiates the connection and addressing method classes
|
445
|
-
# and returns the instances
|
446
|
-
|
447
|
-
|
448
|
-
|
442
|
+
# and returns the instances i.e. cm, am = read_network_config
|
443
|
+
#
|
444
|
+
# If no configuration is saved or it is corrupt this returns
|
445
|
+
# a default configuration that is somewhat likely to work.
|
446
|
+
def self.read_network_config
|
447
|
+
input = ConfigStore.read_config('network_config', '')
|
448
|
+
|
449
449
|
begin
|
450
|
-
input = IO.read(CONFIG_FILE)
|
451
450
|
args = JSON.parse(input)
|
452
|
-
rescue
|
453
|
-
# set up some sane defaults if
|
451
|
+
rescue
|
452
|
+
# set up some sane defaults if we have no configuration
|
453
|
+
# or it can't be parsed
|
454
454
|
args = {
|
455
455
|
'connection_method' => 'WiredConnection',
|
456
456
|
'addressing_method' => 'DHCPAddressing',
|
@@ -473,19 +473,38 @@ module ConcertoConfig
|
|
473
473
|
return [connection_method, addressing_method]
|
474
474
|
end
|
475
475
|
|
476
|
+
# Save the network configuration to the configuration store.
|
477
|
+
# Arguments are instances of connection method and addressing
|
478
|
+
# method classes. Throws exception if either one is not valid.
|
479
|
+
def self.write_network_config(cm, am)
|
480
|
+
# Check that everything is consistent. If not, we currently throw
|
481
|
+
# an exception, which probably is not the best long term solution.
|
482
|
+
cm.validate
|
483
|
+
am.validate
|
484
|
+
|
485
|
+
# Serialize our instances as JSON data to be written to the config file.
|
486
|
+
json_data = {
|
487
|
+
'connection_method' => cm.class.basename,
|
488
|
+
'connection_method_args' => cm.args,
|
489
|
+
'addressing_method' => am.class.basename,
|
490
|
+
'addressing_method_args' => am.args
|
491
|
+
}.to_json
|
492
|
+
|
493
|
+
# Save the serialized configuration.
|
494
|
+
ConfigStore.write_config('network_config', json_data)
|
495
|
+
end
|
496
|
+
|
476
497
|
# This reads a JSON configuration file on STDIN and writes the interfaces
|
477
498
|
# file. Also the classes instantiated will have a chance to write
|
478
499
|
# out any auxiliary files needed.
|
479
|
-
def self.
|
480
|
-
connection_method, addressing_method =
|
500
|
+
def self.configure_system_network
|
501
|
+
connection_method, addressing_method = read_network_config
|
481
502
|
|
482
503
|
ifname = connection_method.config_interface_name
|
483
504
|
|
484
505
|
# squirrel away the name of the interface we are configuring
|
485
506
|
# This will be useful later for getting network status information.
|
486
|
-
|
487
|
-
f.write ifname
|
488
|
-
end
|
507
|
+
ConfigStore.write_config('network_interface', ifname)
|
489
508
|
|
490
509
|
# Write the /etc/network/interfaces file.
|
491
510
|
File.open(INTERFACES_FILE, 'w') do |f|
|
@@ -510,13 +529,11 @@ module ConcertoConfig
|
|
510
529
|
|
511
530
|
# Get the name of the interface we configured
|
512
531
|
def self.configured_interface
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
nil
|
520
|
-
end
|
532
|
+
ifname = ConfigStore.read_config('network_interface', '')
|
533
|
+
if ifname != ''
|
534
|
+
Interface.new(ifname)
|
535
|
+
else
|
536
|
+
nil
|
537
|
+
end
|
521
538
|
end
|
522
539
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concerto_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-12 00:00:00.
|
12
|
+
date: 2012-06-12 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Client-side tools for Concerto digital signage
|
15
15
|
email: andrew@asquaredlabs.com
|
@@ -19,6 +19,8 @@ executables:
|
|
19
19
|
extensions: []
|
20
20
|
extra_rdoc_files: []
|
21
21
|
files:
|
22
|
+
- lib/concerto_client/config_store.rb
|
23
|
+
- lib/concerto_client/live_image.rb
|
22
24
|
- lib/concerto_client/netconfig.rb
|
23
25
|
- lib/concerto_client/application/app.rb
|
24
26
|
- lib/concerto_client/application/public/network.js
|
@@ -27,6 +29,7 @@ files:
|
|
27
29
|
- lib/concerto_client/application/public/trollface.png
|
28
30
|
- lib/concerto_client/application/views/main.haml
|
29
31
|
- lib/concerto_client/application/views/setup.haml
|
32
|
+
- lib/concerto_client/application/views/password.haml
|
30
33
|
- lib/concerto_client/application/views/netsettings.haml
|
31
34
|
- lib/concerto_client/application/views/problem.haml
|
32
35
|
- bin/concerto_configserver
|
@@ -51,7 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
54
|
version: '0'
|
52
55
|
requirements: []
|
53
56
|
rubyforge_project:
|
54
|
-
rubygems_version: 1.8.
|
57
|
+
rubygems_version: 1.8.24
|
55
58
|
signing_key:
|
56
59
|
specification_version: 3
|
57
60
|
summary: Concerto Client Tools
|