puppetfactory 0.4.0

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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +13 -0
  3. data/README.md +0 -0
  4. data/bin/pfsh +31 -0
  5. data/bin/puppetfactory +153 -0
  6. data/lib/puppetfactory.rb +300 -0
  7. data/lib/puppetfactory/cli.rb +114 -0
  8. data/lib/puppetfactory/dashboard/rake_tasks.rb +69 -0
  9. data/lib/puppetfactory/dashboard/serverspec_helper.rb +84 -0
  10. data/lib/puppetfactory/dashboard/spec_helper.rb +26 -0
  11. data/lib/puppetfactory/helpers.rb +37 -0
  12. data/lib/puppetfactory/monkeypatches.rb +30 -0
  13. data/lib/puppetfactory/plugins.rb +11 -0
  14. data/lib/puppetfactory/plugins/certificates.rb +28 -0
  15. data/lib/puppetfactory/plugins/classification.rb +75 -0
  16. data/lib/puppetfactory/plugins/code_manager.rb +156 -0
  17. data/lib/puppetfactory/plugins/console_user.rb +62 -0
  18. data/lib/puppetfactory/plugins/dashboard.rb +128 -0
  19. data/lib/puppetfactory/plugins/docker.rb +193 -0
  20. data/lib/puppetfactory/plugins/example.rb +88 -0
  21. data/lib/puppetfactory/plugins/github.rb +102 -0
  22. data/lib/puppetfactory/plugins/gitlab.rb +62 -0
  23. data/lib/puppetfactory/plugins/hooks.rb +46 -0
  24. data/lib/puppetfactory/plugins/login_shell.rb +10 -0
  25. data/lib/puppetfactory/plugins/logs.rb +34 -0
  26. data/lib/puppetfactory/plugins/r10k.rb +112 -0
  27. data/lib/puppetfactory/plugins/shell_user.rb +69 -0
  28. data/lib/puppetfactory/plugins/user_environment.rb +77 -0
  29. data/public/dashboard.js +100 -0
  30. data/public/font-awesome/css/font-awesome.css +2199 -0
  31. data/public/font-awesome/css/font-awesome.min.css +4 -0
  32. data/public/font-awesome/fonts/FontAwesome.otf +0 -0
  33. data/public/font-awesome/fonts/fontawesome-webfont.eot +0 -0
  34. data/public/font-awesome/fonts/fontawesome-webfont.svg +685 -0
  35. data/public/font-awesome/fonts/fontawesome-webfont.ttf +0 -0
  36. data/public/font-awesome/fonts/fontawesome-webfont.woff +0 -0
  37. data/public/font-awesome/fonts/fontawesome-webfont.woff2 +0 -0
  38. data/public/gitviz/LICENSE.md +20 -0
  39. data/public/gitviz/README.md +13 -0
  40. data/public/gitviz/css/explaingit.css +227 -0
  41. data/public/gitviz/css/vendor/1140.css +130 -0
  42. data/public/gitviz/images/forkme_right_red_aa0000.png +0 -0
  43. data/public/gitviz/images/grippy-close.png +0 -0
  44. data/public/gitviz/images/grippy.png +0 -0
  45. data/public/gitviz/images/prompt.gif +0 -0
  46. data/public/gitviz/index.html +734 -0
  47. data/public/gitviz/js/controlbox.js +459 -0
  48. data/public/gitviz/js/explaingit.js +74 -0
  49. data/public/gitviz/js/historyview.js +979 -0
  50. data/public/gitviz/js/main.js +56 -0
  51. data/public/gitviz/js/vendor/d3.min.js +4 -0
  52. data/public/gitviz/js/vendor/jquery-latest.min.js +6 -0
  53. data/public/gitviz/js/vendor/normalize.css +396 -0
  54. data/public/gitviz/js/vendor/require.min.js +35 -0
  55. data/public/gitviz/memtest.html +44 -0
  56. data/public/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  57. data/public/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  58. data/public/images/ui-bg_flat_10_000000_40x100.png +0 -0
  59. data/public/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  60. data/public/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  61. data/public/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  62. data/public/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  63. data/public/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  64. data/public/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  65. data/public/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  66. data/public/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  67. data/public/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  68. data/public/images/ui-icons_222222_256x240.png +0 -0
  69. data/public/images/ui-icons_228ef1_256x240.png +0 -0
  70. data/public/images/ui-icons_454545_256x240.png +0 -0
  71. data/public/images/ui-icons_ef8c08_256x240.png +0 -0
  72. data/public/images/ui-icons_ffd27a_256x240.png +0 -0
  73. data/public/images/ui-icons_ffffff_256x240.png +0 -0
  74. data/public/jquery-1.11.1.min.js +4 -0
  75. data/public/jquery-ui.css +464 -0
  76. data/public/jquery-ui.min.css +7 -0
  77. data/public/jquery-ui.min.js +13 -0
  78. data/public/jquery-ui.structure.min.css +5 -0
  79. data/public/jquery-ui.theme.min.css +5 -0
  80. data/public/jquery.activity-indicator-1.0.0.min.js +10 -0
  81. data/public/jquery.js +9789 -0
  82. data/public/loginscripts.js +18 -0
  83. data/public/scripts.js +36 -0
  84. data/public/style.css +193 -0
  85. data/public/usermanagement.js +133 -0
  86. data/templates/init_scripts.erb +10 -0
  87. data/templates/puppet.conf.erb +10 -0
  88. data/templates/site.pp.erb +50 -0
  89. data/views/dashboard.erb +62 -0
  90. data/views/home.erb +43 -0
  91. data/views/index.erb +29 -0
  92. data/views/logs.erb +26 -0
  93. data/views/shell.erb +35 -0
  94. data/views/users.erb +69 -0
  95. metadata +256 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ed93b40457051de5d2851c3ec2dcffd491d292ee
4
+ data.tar.gz: 19f16c05357c6c9d2a8dc6d0d4109ce72acbaf81
5
+ SHA512:
6
+ metadata.gz: ed0b47fdac3c0b0133809422900eaf137e92ea9b1d55c77bc35a01b26f6f3e9b3abc537d98f96f1696afe2530b99ca660358e7d2c3303cc64892e34fbae10e43
7
+ data.tar.gz: c27f57e1691131f6be87d27a1011676d9ef7b9d3266cce444dbe7341c8cd2fbbc756d23b5d4786814a61695456e7cd8bf72c1fe9b4b12b1bb1078ee1ac05be55
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2014-2016 Ben Ford, Josh Samuelson, Puppet, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
File without changes
data/bin/pfsh ADDED
@@ -0,0 +1,31 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'yaml'
5
+ require 'puppetfactory'
6
+
7
+ if File.exist? '/etc/puppetfactory/config.yaml'
8
+ options = YAML.load_file('/etc/puppetfactory/config.yaml') rescue {}
9
+ else
10
+ options = YAML.load_file('/etc/puppetfactory.yaml') rescue {}
11
+ end
12
+
13
+ options[:plugins] ||= [:LoginShell]
14
+ options[:logfile] ||= STDOUT
15
+ options[:session] ||= '12345'
16
+
17
+ $logger = Logger.new(options[:logfile])
18
+
19
+ plugins = options[:plugins].map do |plugin|
20
+ require "puppetfactory/plugins/#{plugin.snake_case}"
21
+ Puppetfactory::Plugins::const_get(plugin).new(options)
22
+ end
23
+
24
+ resp = plugins.select { |plugin| plugin.respond_to? :login }
25
+ raise "The login action is not exposed by any plugins" if resp.size == 0
26
+ raise "The login action is exposed by multiple loaded plugins! (#{resp.map {|p| p.class }})" unless resp.size == 1
27
+
28
+ print 'Please enter the session ID: '
29
+ raise 'Incorrect session ID' unless STDIN.gets.strip == options[:session]
30
+
31
+ resp.first.send(:login)
data/bin/puppetfactory ADDED
@@ -0,0 +1,153 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'puppetfactory'
5
+ require 'optparse'
6
+ require 'yaml'
7
+
8
+ NAME = File.basename($PROGRAM_NAME)
9
+
10
+ if File.exist? '/etc/puppetfactory/config.yaml'
11
+ options = YAML.load_file('/etc/puppetfactory/config.yaml') rescue {}
12
+ else
13
+ options = YAML.load_file('/etc/puppetfactory.yaml') rescue {}
14
+ end
15
+
16
+ optparse = OptionParser.new { |opts|
17
+ opts.banner = "Usage : #{NAME} [command] [target]
18
+
19
+ When called with no arguments, this will start the PuppetFactory server.
20
+ You can also pass arguments to invoke CLI commands.
21
+ * ls | list
22
+ * List all PuppetFactory users
23
+ * new |create <username> [password]
24
+ * Create a new PuppetFactory user with an optional password
25
+ * remove | delete <username>
26
+ * Delete a PuppetFactory user
27
+ * repair | reprovision <username>
28
+ * Reprovision a PuppetFactory user container
29
+ * redeploy <username>
30
+ * Delete a user's environment from disk and redeploy it with r10k
31
+ * configprint
32
+ * Display the value of all Puppetfactory options.
33
+
34
+ This will send commands to the PuppetFactory server specified in
35
+ /etc/puppetfactory/config.yaml, defaulting to localhost if unspecified.
36
+ "
37
+
38
+ opts.on("-s SERVER", "--server SERVER", "Remote PuppetFactory server.") do |arg|
39
+ options[:server] = arg
40
+ end
41
+
42
+ opts.on("-d", "--debug", "Display extra debugging information.") do
43
+ options[:debug] = true
44
+ end
45
+
46
+ opts.separator('')
47
+
48
+ opts.on("-h", "--help", "Displays this help") do
49
+ puts opts
50
+ exit
51
+ end
52
+ }
53
+ optparse.parse!
54
+
55
+ options[:puppet] ||= '/opt/puppetlabs/bin/puppet'
56
+ options[:confdir] ||= '/etc/puppetlabs/puppet'
57
+ options[:codedir] ||= '/etc/puppetlabs/code'
58
+ options[:environments] ||= "#{options[:codedir]}/environments"
59
+
60
+ options[:root] ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
61
+ options[:logfile] ||= '/var/log/puppetfactory'
62
+ options[:templatedir] ||= "#{File.dirname(__FILE__)}/../templates"
63
+
64
+ options[:port] ||= 8000
65
+ options[:bind] ||= '0.0.0.0'
66
+
67
+ options[:user] ||= 'admin'
68
+ options[:password] ||= 'admin'
69
+ options[:session] ||= '12345'
70
+
71
+ options[:master] ||= `hostname`.strip
72
+ options[:usersuffix] ||= 'puppetfactory.vm'
73
+ options[:usergroups] ||= []
74
+
75
+ options[:puppetcode] ||= '/var/opt/puppetcode'
76
+ options[:modulepath] ||= :none
77
+
78
+ options[:gitserver] ||= 'https://github.com'
79
+ options[:gituser] ||= 'puppetlabs'
80
+ options[:controlrepo] ||= 'control-repo.git'
81
+ options[:repomodel] ||= :single
82
+
83
+ options[:plugins] ||= [:ShellUser, :Logs]
84
+
85
+ def validate(options, option, values)
86
+ unless values.include? options[option]
87
+ raise "#{option} must be one of #{values.inspect}"
88
+ end
89
+ end
90
+
91
+ validate(options, :modulepath, [:readwrite, :readonly, :none])
92
+ validate(options, :repomodel, [:single, :peruser])
93
+
94
+ Puppetfactory::Helpers.configure(options)
95
+
96
+ # why do I have to do this? This page implies I shouldn't.
97
+ # https://github.com/sinatra/sinatra#logging
98
+ $logger = Logger::new(options[:logfile])
99
+ $logger.level = options[:debug] ? Logger::DEBUG : Logger::INFO
100
+ $logger.formatter = proc { |severity,datetime,progname,msg| "[#{datetime}] #{severity}: #{msg}\n" }
101
+
102
+ # if no arguments, start up the server
103
+ if ARGV.size == 0
104
+ Puppetfactory.run! options
105
+
106
+ # opts = {
107
+ # :Port => 8000,
108
+ # :Host => '0.0.0.0',
109
+ # :Logger => WEBrick::Log::new(options[:logfile], WEBrick::Log::INFO),
110
+ # :ServerType => options[:debug] ? WEBrick::SimpleServer : WEBrick::Daemon,
111
+ # :DocumentRoot => options[:docroot],
112
+ # :SSLEnable => false,
113
+ # # :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
114
+ # # :SSLCertificate => OpenSSL::X509::Certificate.new( File.open(File.join(CERT_PATH, 'server.crt')).read),
115
+ # # :SSLPrivateKey => OpenSSL::PKey::RSA.new( File.open(File.join(CERT_PATH, 'server.key')).read),
116
+ # # :SSLCertName => [ [ "CN",WEBrick::Utils::getservername ] ]
117
+ # }.merge(options)
118
+ #
119
+ # Rack::Handler::WEBrick.run(Puppetfactory, opts) do |server|
120
+ # require 'pry'
121
+ # binding.pry
122
+ #
123
+ # [:INT, :TERM].each { |sig| trap(sig) { server.stop } }
124
+ # end
125
+ else
126
+ require 'puppetfactory/cli'
127
+
128
+ puppetfactory = Puppetfactory::Cli.new(options)
129
+
130
+ command, user, password = ARGV
131
+ case command
132
+ when 'ls', 'list'
133
+ puppetfactory.list
134
+
135
+ when 'new', 'create'
136
+ puppetfactory.create(user, password)
137
+
138
+ when 'remove', 'delete'
139
+ puppetfactory.delete(user)
140
+
141
+ when 'repair', 'reprovision'
142
+ puppetfactory.repair(user)
143
+ when 'redeploy'
144
+ puppetfactory.redeploy(user)
145
+ when 'configprint'
146
+ puts options.to_yaml
147
+
148
+ else
149
+ puts "Unknown command: #{command}"
150
+ puts "Run #{NAME} -h for usage."
151
+ exit 1
152
+ end
153
+ end
@@ -0,0 +1,300 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'sinatra/base'
5
+ require 'webrick'
6
+ # require 'webrick/https'
7
+ # require 'openssl'
8
+ require 'resolv'
9
+ require 'json'
10
+ require 'fileutils'
11
+ require 'erb'
12
+ require 'yaml'
13
+ require 'time'
14
+ require 'docker'
15
+ require 'rest-client'
16
+ require 'open3'
17
+
18
+ class Puppetfactory < Sinatra::Base
19
+ require 'puppetfactory/helpers'
20
+ require 'puppetfactory/monkeypatches'
21
+ require 'puppetfactory/plugins'
22
+
23
+ set :views, File.dirname(__FILE__) + '/../views'
24
+ set :public_folder, File.dirname(__FILE__) + '/../public'
25
+ set :erb, :trim => '-'
26
+
27
+ configure :production, :development do
28
+ enable :logging
29
+ enable :sessions
30
+ end
31
+
32
+ def initialize(app=nil)
33
+ super(app)
34
+
35
+ # lets us pretend that the settings object is a hash in our plugins
36
+ def settings.[](opt)
37
+ settings.send(opt) if settings.respond_to? opt
38
+ end
39
+
40
+ # Add a link back to the server so that plugins can add routes
41
+ def settings.puppetfactory
42
+ self
43
+ end
44
+
45
+ @loaded_plugins = settings.plugins.map do |plugin|
46
+ require "puppetfactory/plugins/#{plugin.snake_case}"
47
+ Puppetfactory::Plugins::const_get(plugin).new(settings)
48
+ end
49
+ @loaded_plugins = @loaded_plugins.sort_by { |plugin| plugin.weight }
50
+
51
+ ensure_single_action(:users)
52
+ ensure_single_action(:login)
53
+ # ensure_single_action(:deploy) # TODO: maybe this shouldn't be limited like this.
54
+ # But if not, we need a plugin.suitability() method.
55
+ end
56
+
57
+ # UI tab endpoints
58
+ get '/' do
59
+ @tabs = merge(plugins(:tabs, privileged?))
60
+
61
+ erb :index
62
+ end
63
+
64
+ get '/home' do
65
+ erb :home
66
+ end
67
+
68
+ get '/users' do
69
+ @users = load_users()
70
+ @current = merge(plugins(:userinfo, session[:username], true)) if session.include? :username
71
+
72
+ erb :users
73
+ end
74
+
75
+ get '/shell' do
76
+ erb :shell
77
+ end
78
+ # End UI tabs
79
+
80
+ # set the currently active user. This should probably be a PUT action.
81
+ get '/users/active/:username' do |username|
82
+ session[:username] = username
83
+ {"status" => "ok"}.to_json
84
+ end
85
+
86
+ # admin login
87
+ get '/login' do
88
+ protected!
89
+ redirect '/'
90
+ end
91
+
92
+ # create a new username with the default password.
93
+ get '/new/:username' do |username|
94
+ protected!
95
+ create(username)
96
+ end
97
+
98
+ post '/new' do
99
+ confined!
100
+ session[:username] = params[:username]
101
+ create(params[:username], params[:password])
102
+ end
103
+
104
+ get '/shell/login' do
105
+ redirect "http://#{request.host}:4200"
106
+ end
107
+
108
+ get '/port/:port/' do |port|
109
+ redirect "http://#{request.host}:#{port}"
110
+ end
111
+
112
+ # RESTful API endpoints
113
+ # Return details for all users as JSON
114
+ get '/api/users' do
115
+ load_users(true).to_json
116
+ end
117
+
118
+ # create a user
119
+ post '/api/users' do
120
+ # no need for all the returned status. That was a workaround for not having any real logging
121
+ create(params[:username], params[:password])
122
+ end
123
+
124
+ # Return details for single user
125
+ get '/api/users/:username' do
126
+ username = params[:username]
127
+ load_user(username).to_json
128
+ end
129
+
130
+ # perform an action on a given user
131
+ put '/api/users/:username' do |username|
132
+ case params[:action]
133
+ when 'deploy'
134
+ resp = plugins(:deploy, username)
135
+
136
+ when 'redeploy'
137
+ resp = plugins(:redeploy, username)
138
+
139
+ when 'repair'
140
+ resp = plugins(:repair, username)
141
+
142
+ when 'select'
143
+ session[:username] = username
144
+ resp = [true]
145
+
146
+ else
147
+ raise "Unknown user action: #{params[:action]}."
148
+ end
149
+ if resp.select { |response| response == false }.size == 0
150
+ {"status" => "success"}.to_json
151
+ else
152
+ {"status" => "failure"}.to_json
153
+ end
154
+ end
155
+
156
+ # delete a user
157
+ delete '/api/users/:username' do
158
+ delete(params[:username])
159
+ end
160
+
161
+
162
+ # These endpoints don't seemed to be used for anything
163
+ # get '/api/users/:username/port' do
164
+ # user_port(params[:username])
165
+ # end
166
+ #
167
+ # get '/api/users/:username/node_group_status' do
168
+ # node_group_status(params[:username]).to_json
169
+ # end
170
+ #
171
+ #
172
+ # get '/api/users/:username/console_user_status' do
173
+ # console_user_status(params[:username]).to_json
174
+ # end
175
+
176
+
177
+ not_found do
178
+ halt 404, 'page not found'
179
+ end
180
+
181
+ helpers do
182
+ # call a method of a single named plugin.
183
+ def plugin(name, action, *args)
184
+ plugin = @loaded_plugins.find {|plugin| plugin.class.name == "Puppetfactory::Plugins::#{name.to_s}" }
185
+ plugin.send(action, *args)
186
+ end
187
+
188
+ # call a method on all plugins that implement it
189
+ def plugins(action, *args)
190
+ @loaded_plugins.map do |plugin|
191
+ next unless plugin.respond_to? action
192
+
193
+ plugin.send(action, *args)
194
+ end.compact
195
+ end
196
+
197
+ # call a method on all plugins that implement it
198
+ def reversedplugins(action, *args)
199
+ @loaded_plugins.reverse.map do |plugin|
200
+ next unless plugin.respond_to? action
201
+
202
+ plugin.send(action, *args)
203
+ end.compact
204
+ end
205
+
206
+ def ensure_single_action(action)
207
+ resp = @loaded_plugins.select { |plugin| plugin.respond_to? action }
208
+ raise "The #{action} action is not exposed by any plugins" if resp.size == 0
209
+ raise "The #{action} action is exposed by multiple loaded plugins! (#{resp.map {|p| p.class }})" unless resp.size == 1
210
+ end
211
+
212
+ def action_enabled?(action)
213
+ resp = @loaded_plugins.select { |plugin| plugin.respond_to? action }
214
+ resp.size != 0
215
+ end
216
+
217
+ # Take an array of hashes and squash them into a single hash.
218
+ # Keys later in the list override those which come earlier
219
+ def merge(elements)
220
+ elements.inject({}) do |memo, element|
221
+ memo.merge! element
222
+ end
223
+ end
224
+
225
+
226
+
227
+ def create(username, password = 'puppetlabs')
228
+ begin
229
+ responses = plugins(:create, username, password)
230
+ errors = responses.select { |response| response == false }.size
231
+
232
+ if errors == 0
233
+ { :status => :success, :message => "Created user #{username}."}.to_json
234
+ else
235
+ # TODO: should we call delete to cleanup?
236
+ #plugins(:delete, username) # Don't leave artifacts.
237
+ { :status => :failure, :message => "There were #{errors} errors creating #{username}. See logs for details."}.to_json
238
+ end
239
+ rescue => e
240
+ $logger.warn e.backtrace
241
+ {:status => :failure, :message => "Fatal error creating #{username}: #{e.message}"}.to_json
242
+ end
243
+ end
244
+
245
+ def delete(username)
246
+ begin
247
+ responses = reversedplugins(:delete, username)
248
+ errors = responses.select { |response| response == false }.size
249
+
250
+ if errors == 0
251
+ { :status => :success, :message => "Deleted user #{username}."}.to_json
252
+ else
253
+ { :status => :failure, :message => "There were #{errors} errors deleting #{username}. See logs for details."}.to_json
254
+ end
255
+ rescue => e
256
+ {:status => :failure, :message => "Fatal error deleting #{username}: #{e.message}"}.to_json
257
+ end
258
+ end
259
+
260
+ def load_users(extended = false)
261
+ users = {}
262
+ plugins(:users).flatten.each do |username|
263
+ users[username] = merge(plugins(:userinfo, username, extended))
264
+ end
265
+ users
266
+ end
267
+
268
+
269
+ # Basic auth boilerplate
270
+ def protected!
271
+ unless authorized?
272
+ response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
273
+ throw(:halt, [401, "Not authorized\n"])
274
+ end
275
+ end
276
+
277
+ def confined!
278
+ unless params[:session] == settings.session
279
+ throw(:halt, [403, "Only classroom members are allowed to create accounts!\n"])
280
+ end
281
+ end
282
+
283
+ def privileged?
284
+ session[:privileges] == 'admin'
285
+ end
286
+
287
+ def authorized?
288
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
289
+
290
+ if @auth.provided? && @auth.basic? && @auth.credentials == [settings.user, settings.password]
291
+ session[:privileges] = 'admin'
292
+ true
293
+ else
294
+ session.delete :privileges
295
+ false
296
+ end
297
+ end
298
+
299
+ end
300
+ end