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.
@@ -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
- # Load our (constant) password from file.
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 401
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'] = %(Basic realm="Concerto Configuration")
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 localhost and allow
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 password
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 && @auth.credentials == ['root', PASSWORD]
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
- begin
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 can't be reached
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, :layout => :main
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
- File.open(URL_FILE, 'w') do |f|
177
- f.write url
178
- end
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, :layout => :main
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.read_config
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
- # Check that everything is consistent. If not, we currently throw
280
- # an exception, which probably is not the best long term solution.
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::configure_system
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
- 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.
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
- %p
8
- %label{:for=>'url'} URL
9
- %input{:type=>'text', :name=>'url'}
10
- %input{:type=>'submit', :value=>'Here it is!'}
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
- The IPv4 address of this screen appears to be:
14
- =my_ip
13
+ The IPv4 address of this screen appears to be:
14
+ =my_ip
15
15
  %p
16
- ==This page can be accessed remotely via http://#{my_ip}/setup
16
+ ==This page can be accessed remotely via http://#{my_ip}/setup
17
17
  %p
18
- Username is root, default password is 'default'.
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 an input stream.
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
- # i.e.
447
- # cm, am = read_config(STDIN)
448
- def self.read_config
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 Errno::ENOENT
453
- # set up some sane defaults if the config file doesn't exist
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.configure_system
480
- connection_method, addressing_method = read_config
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
- File.open(INTERFACE_FILE, 'w') do |f|
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
- begin
514
- ifname = File.open(INTERFACE_FILE) do |f|
515
- f.readline.chomp
516
- end
517
- Interface.new(ifname)
518
- rescue
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.1
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.000000000Z
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.6
57
+ rubygems_version: 1.8.24
55
58
  signing_key:
56
59
  specification_version: 3
57
60
  summary: Concerto Client Tools