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.
@@ -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