concerto_client 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|