puppetfactory 0.4.0

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