summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jacob <adam@hjksolutions.com>2008-06-15 19:12:57 -0700
committerAdam Jacob <adam@hjksolutions.com>2008-06-15 19:12:57 -0700
commitebc1b392fe7e8f0fbabc305c299b4d365d2b4d9b (patch)
tree1d8882eee26e9ca7f039366c77973d79af1affb3
parent14ca7fdc284eeaac9c66abd5878d7b24bd439180 (diff)
downloadchef-ebc1b392fe7e8f0fbabc305c299b4d365d2b4d9b.tar.gz
Moving chef-server into lib, reworking specs, adding storieschef-server-package
-rw-r--r--config/chef-server.rb11
-rw-r--r--examples/config/cookbooks/fakefile/recipes/default.rb3
-rw-r--r--lib/chef.rb6
-rw-r--r--lib/chef/client.rb6
-rw-r--r--lib/chef/config.rb1
-rw-r--r--lib/chef/cookbook.rb2
-rw-r--r--lib/chef/couchdb.rb24
-rw-r--r--lib/chef_server/controllers/application.rb69
-rw-r--r--lib/chef_server/controllers/exceptions.rb20
-rw-r--r--lib/chef_server/controllers/nodes.rb74
-rw-r--r--lib/chef_server/controllers/openid_consumer.rb99
-rw-r--r--lib/chef_server/controllers/openid_register.rb74
-rw-r--r--lib/chef_server/controllers/openid_server.rb218
-rw-r--r--lib/chef_server/helpers/global_helpers.rb17
-rw-r--r--lib/chef_server/helpers/nodes_helper.rb19
-rw-r--r--lib/chef_server/helpers/openid_server_helpers.rb9
-rw-r--r--lib/chef_server/init.rb165
-rw-r--r--lib/chef_server/log/chef-server.log434
-rw-r--r--lib/chef_server/public/images/merb.jpgbin0 -> 5815 bytes
-rw-r--r--lib/chef_server/public/stylesheets/master.css153
-rw-r--r--lib/chef_server/views/exceptions/bad_request.json.erb1
-rw-r--r--lib/chef_server/views/exceptions/internal_server_error.html.erb216
-rw-r--r--lib/chef_server/views/exceptions/not_acceptable.html.erb63
-rw-r--r--lib/chef_server/views/exceptions/not_found.html.erb47
-rw-r--r--lib/chef_server/views/layout/application.html.haml22
-rw-r--r--lib/chef_server/views/nodes/_action.html.haml13
-rw-r--r--lib/chef_server/views/nodes/_node.html.haml11
-rw-r--r--lib/chef_server/views/nodes/_resource.html.haml22
-rw-r--r--lib/chef_server/views/nodes/compile.html.haml5
-rw-r--r--lib/chef_server/views/nodes/index.html.haml9
-rw-r--r--lib/chef_server/views/nodes/show.html.haml1
-rw-r--r--lib/chef_server/views/openid_consumer/index.html.haml25
-rw-r--r--lib/chef_server/views/openid_consumer/start.html.haml4
-rw-r--r--lib/chef_server/views/openid_login/index.html.haml6
-rw-r--r--lib/chef_server/views/openid_register/index.html.haml15
-rw-r--r--lib/chef_server/views/openid_register/show.html.haml5
-rw-r--r--lib/chef_server/views/openid_server/decide.html.haml27
-rw-r--r--log/chef-server.log9
-rw-r--r--log/merb_test.log7
-rw-r--r--spec/chef_server/controllers/log/merb_test.log4
-rw-r--r--spec/chef_server/controllers/nodes_spec.rb214
-rw-r--r--spec/chef_server/controllers/openid_consumer_spec.rb29
-rw-r--r--spec/chef_server/controllers/openid_register_spec.rb93
-rw-r--r--spec/chef_server/log/merb_test.log4
-rw-r--r--spec/chef_server/spec.opts4
-rw-r--r--spec/chef_server/spec_helper.rb16
-rw-r--r--spec/unit/compile_spec.rb1
-rw-r--r--stories/chef-client26
-rw-r--r--stories/chef-client.rb48
-rw-r--r--stories/story_helper.rb33
50 files changed, 2366 insertions, 18 deletions
diff --git a/config/chef-server.rb b/config/chef-server.rb
new file mode 100644
index 0000000000..c773dcf1b9
--- /dev/null
+++ b/config/chef-server.rb
@@ -0,0 +1,11 @@
+#
+# Example Chef Server Config
+
+cookbook_path File.join(File.dirname(__FILE__), "..", "examples", "config", "cookbooks")
+node_path File.join(File.dirname(__FILE__), "..", "examples", "config", "nodes")
+file_store_path File.join(File.dirname(__FILE__), "..", "examples", "store")
+log_level :debug
+
+openid_providers [ "localhost:4001", "openid.hjksolutions.com" ]
+
+Chef::Log::Formatter.show_time = false
diff --git a/examples/config/cookbooks/fakefile/recipes/default.rb b/examples/config/cookbooks/fakefile/recipes/default.rb
index 7c79033d52..927a3e1f11 100644
--- a/examples/config/cookbooks/fakefile/recipes/default.rb
+++ b/examples/config/cookbooks/fakefile/recipes/default.rb
@@ -16,4 +16,5 @@ end
mode 0644
action :create
end
-end \ No newline at end of file
+end
+
diff --git a/lib/chef.rb b/lib/chef.rb
index 18e34e0ffb..4b132e6189 100644
--- a/lib/chef.rb
+++ b/lib/chef.rb
@@ -19,7 +19,11 @@
require 'rubygems'
-Dir[File.join(File.dirname(__FILE__), 'chef/**/*.rb')].sort.each { |lib| require lib unless lib =~ /server/ }
+Dir[
+ File.join(
+ File.dirname(__FILE__),
+ 'chef/**/*.rb'
+ )].sort.each { |lib| require lib unless lib =~ /server/ }
class Chef
VERSION = '0.0.1'
diff --git a/lib/chef/client.rb b/lib/chef/client.rb
index 2cd11bb2e0..0ae7ef70e6 100644
--- a/lib/chef/client.rb
+++ b/lib/chef/client.rb
@@ -26,7 +26,7 @@ require 'facter'
class Chef
class Client
- attr_accessor :node, :registration
+ attr_accessor :node, :registration, :safe_name
def initialize()
@node = nil
@@ -43,8 +43,8 @@ class Chef
converge
end
- def build_node
- node_name = Facter["fqdn"].value ? Facter["fqdn"].value : Facter["hostname"].value
+ def build_node(node_name=nil)
+ node_name ||= Facter["fqdn"].value ? Facter["fqdn"].value : Facter["hostname"].value
@safe_name = node_name.gsub(/\./, '_')
begin
@node = @rest.get_rest("nodes/#{@safe_name}")
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 192b510d17..50524fd58b 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -49,6 +49,7 @@ class Chef
:couchdb_url => "http://localhost:5984",
:registration_url => "http://localhost:4000",
:openid_url => "http://localhost:4001",
+ :couchdb_database => "chef",
}
class << self
diff --git a/lib/chef/cookbook.rb b/lib/chef/cookbook.rb
index ed9b75a1d9..75ab8f4ad6 100644
--- a/lib/chef/cookbook.rb
+++ b/lib/chef/cookbook.rb
@@ -92,7 +92,7 @@ class Chef
def load_recipe(name, node, collection=nil, definitions=nil, cookbook_loader=nil)
cookbook_name = @name
recipe_name = nil
- nmatch = name.match(/^(.+)::(.+)$/)
+ nmatch = name.match(/^(.+?)::(.+)$/)
recipe_name = nmatch ? nmatch[2] : name
unless @recipe_names.has_key?(recipe_name)
diff --git a/lib/chef/couchdb.rb b/lib/chef/couchdb.rb
index c5605080a6..83bf3982f7 100644
--- a/lib/chef/couchdb.rb
+++ b/lib/chef/couchdb.rb
@@ -13,16 +13,16 @@ class Chef
def create_db
@database_list = @rest.get_rest("_all_dbs")
- unless @database_list.detect { |db| db == "chef" }
- response = @rest.put_rest("chef", Hash.new)
+ unless @database_list.detect { |db| db == Chef::Config[:couchdb_database] }
+ response = @rest.put_rest(Chef::Config[:couchdb_database], Hash.new)
end
- "chef"
+ Chef::Config[:couchdb_database]
end
def create_design_document(name, data)
to_update = true
begin
- old_doc = @rest.get_rest("chef/_design%2F#{name}")
+ old_doc = @rest.get_rest("#{Chef::Config[:couchdb_database]}/_design%2F#{name}")
if data["version"] != old_doc["version"]
data["_rev"] = old_doc["_rev"]
Chef::Log.debug("Updating #{name} views")
@@ -33,7 +33,7 @@ class Chef
Chef::Log.debug("Creating #{name} views for the first time")
end
if to_update
- @rest.put_rest("chef/_design%2F#{name}", data)
+ @rest.put_rest("#{Chef::Config[:couchdb_database]}/_design%2F#{name}", data)
end
true
end
@@ -49,7 +49,7 @@ class Chef
:object => { :respond_to => :to_json },
}
)
- @rest.put_rest("chef/#{obj_type}_#{safe_name(name)}", object)
+ @rest.put_rest("#{Chef::Config[:couchdb_database]}/#{obj_type}_#{safe_name(name)}", object)
end
def load(obj_type, name)
@@ -63,7 +63,7 @@ class Chef
:name => { :kind_of => String },
}
)
- @rest.get_rest("chef/#{obj_type}_#{safe_name(name)}")
+ @rest.get_rest("#{Chef::Config[:couchdb_database]}/#{obj_type}_#{safe_name(name)}")
end
def delete(obj_type, name, rev=nil)
@@ -78,14 +78,14 @@ class Chef
}
)
unless rev
- last_obj = @rest.get_rest("chef/#{obj_type}_#{safe_name(name)}")
+ last_obj = @rest.get_rest("#{Chef::Config[:couchdb_database]}/#{obj_type}_#{safe_name(name)}")
if last_obj.respond_to?(:couchdb_rev)
rev = last_obj.couchdb_rev
else
rev = last_obj['_rev']
end
end
- @rest.delete_rest("chef/#{obj_type}_#{safe_name(name)}?rev=#{rev}")
+ @rest.delete_rest("#{Chef::Config[:couchdb_database]}/#{obj_type}_#{safe_name(name)}?rev=#{rev}")
end
def list(view, inflate=false)
@@ -98,9 +98,9 @@ class Chef
}
)
if inflate
- @rest.get_rest("chef/_view/#{view}/all")
+ @rest.get_rest("#{Chef::Config[:couchdb_database]}/_view/#{view}/all")
else
- @rest.get_rest("chef/_view/#{view}/all_id")
+ @rest.get_rest("#{Chef::Config[:couchdb_database]}/_view/#{view}/all_id")
end
end
@@ -116,7 +116,7 @@ class Chef
}
)
begin
- @rest.get_rest("chef/#{obj_type}_#{safe_name(name)}")
+ @rest.get_rest("#{Chef::Config[:couchdb_database]}/#{obj_type}_#{safe_name(name)}")
true
rescue
false
diff --git a/lib/chef_server/controllers/application.rb b/lib/chef_server/controllers/application.rb
new file mode 100644
index 0000000000..4a0d247306
--- /dev/null
+++ b/lib/chef_server/controllers/application.rb
@@ -0,0 +1,69 @@
+class Application < Merb::Controller
+
+ def fix_up_node_id
+ if params.has_key?(:id)
+ params[:id].gsub!(/_/, '.')
+ end
+ end
+
+ def escape_node_id
+ if params.has_key?(:id)
+ params[:id].gsub(/_/, '.')
+ end
+ end
+
+ def login_required
+ if session[:openid]
+ return session[:openid]
+ else
+ self.store_location
+ throw(:halt, :access_denied)
+ end
+ end
+
+ def authorized_node
+ if session[:level] == :admin
+ Chef::Log.debug("Authorized as Administrator")
+ true
+ elsif session[:level] == :node
+ Chef::Log.debug("Authorized as node")
+ if session[:node_name] == params[:id].gsub(/\./, '_')
+ true
+ else
+ raise(
+ Unauthorized,
+ "You are not the correct node for this action: #{session[:node_name]} instead of #{params[:id]}"
+ )
+ end
+ else
+ Chef::Log.debug("Unauthorized")
+ raise Unauthorized, "You are not allowed to take this action."
+ end
+ end
+
+ # Store the URI of the current request in the session.
+ #
+ # We can return to this location by calling #redirect_back_or_default.
+ def store_location
+ session[:return_to] = request.uri
+ end
+
+ # Redirect to the URI stored by the most recent store_location call or
+ # to the passed default.
+ def redirect_back_or_default(default)
+ loc = session[:return_to] || default
+ session[:return_to] = nil
+ redirect loc
+ end
+
+ def access_denied
+ case content_type
+ when :html
+ store_location
+ redirect url(:openid_consumer)
+ else
+ raise Unauthorized, "You must authenticate first!"
+ end
+ end
+
+end \ No newline at end of file
diff --git a/lib/chef_server/controllers/exceptions.rb b/lib/chef_server/controllers/exceptions.rb
new file mode 100644
index 0000000000..7f72e57ad2
--- /dev/null
+++ b/lib/chef_server/controllers/exceptions.rb
@@ -0,0 +1,20 @@
+class Exceptions < Application
+
+ provides :html, :json
+
+ # handle NotFound exceptions (404)
+ def not_found
+ display params
+ end
+
+ # handle NotAcceptable exceptions (406)
+ def not_acceptable
+ display params
+ end
+
+ # handle BadRequest exceptions (400)
+ def bad_request
+ display params
+ end
+
+end \ No newline at end of file
diff --git a/lib/chef_server/controllers/nodes.rb b/lib/chef_server/controllers/nodes.rb
new file mode 100644
index 0000000000..aa7273c47d
--- /dev/null
+++ b/lib/chef_server/controllers/nodes.rb
@@ -0,0 +1,74 @@
+class Nodes < Application
+
+ provides :html, :json
+
+ before :fix_up_node_id
+ before :login_required, :only => [ :create, :update, :destroy ]
+ before :authorized_node, :only => [ :update, :destroy ]
+
+ def index
+ @node_list = Chef::Node.list
+ display @node_list
+ end
+
+ def show
+ begin
+ @node = Chef::Node.load(params[:id])
+ rescue Net::HTTPServerException => e
+ raise NotFound, "Cannot load node #{params[:id]}"
+ end
+ display @node
+ end
+
+ def create
+ @node = params.has_key?("inflated_object") ? params["inflated_object"] : nil
+ if @node
+ @status = 202
+ @node.save
+ display @node
+ else
+ raise BadRequest, "You must provide a Node to create"
+ end
+ end
+
+ def update
+ @node = params.has_key?("inflated_object") ? params["inflated_object"] : nil
+ if @node
+ @status = 202
+ @node.save
+ display @node
+ else
+ raise NotFound, "You must provide a Node to update"
+ end
+ end
+
+ def destroy
+ begin
+ @node = Chef::Node.load(params[:id])
+ rescue RuntimeError => e
+ raise BadRequest, "Node #{params[:id]} does not exist to destroy!"
+ end
+ @status = 202
+ @node.destroy
+ if content_type == :html
+ redirect url(:nodes)
+ else
+ display @node
+ end
+ end
+
+ def compile
+ # Grab a Chef::Compile object
+ compile = Chef::Compile.new()
+ compile.load_node(params[:id])
+ compile.node.save
+ compile.load_definitions
+ compile.load_recipes
+ @output = {
+ :node => compile.node,
+ :collection => compile.collection,
+ }
+ display @output
+ end
+
+end
diff --git a/lib/chef_server/controllers/openid_consumer.rb b/lib/chef_server/controllers/openid_consumer.rb
new file mode 100644
index 0000000000..1c8b76296c
--- /dev/null
+++ b/lib/chef_server/controllers/openid_consumer.rb
@@ -0,0 +1,99 @@
+require 'pathname'
+
+require "openid"
+require 'openid/store/filesystem'
+
+class OpenidConsumer < Application
+
+ provides :html, :json
+
+ def index
+ render
+ end
+
+ def start
+ check_valid_openid_provider(params[:openid_identifier])
+ begin
+ oidreq = consumer.begin(params[:openid_identifier])
+ rescue OpenID::OpenIDError => e
+ raise BadRequest, "Discovery failed for #{params[:openid_identifier]}: #{e}"
+ end
+ return_to = absolute_url(:openid_consumer_complete)
+ realm = absolute_url(:openid_consumer)
+
+ if oidreq.send_redirect?(realm, return_to, params[:immediate])
+ return redirect(oidreq.redirect_url(realm, return_to, params[:immediate]))
+ else
+ @form_text = oidreq.form_markup(realm, return_to, params[:immediate], {'id' => 'openid_form'})
+ render
+ end
+ end
+
+ def complete
+ # FIXME - url_for some action is not necessarily the current URL.
+ current_url = absolute_url(:openid_consumer_complete)
+ parameters = params.reject{|k,v| k == "controller" || k == "action"}
+ oidresp = consumer.complete(parameters, current_url)
+ case oidresp.status
+ when OpenID::Consumer::FAILURE
+ if oidresp.display_identifier
+ raise BadRequest, "Verification of #{oidresp.display_identifier} failed: #{oidresp.message}"
+ else
+ raise BadRequest, "Verification failed: #{oidresp.message}"
+ end
+ when OpenID::Consumer::SUCCESS
+ session[:openid] = oidresp.identity_url
+ if oidresp.display_identifier =~ /openid\/server\/node\/(.+)$/
+ session[:level] = :node
+ session[:node_name] = $1
+ else
+ session[:level] = :admin
+ end
+ redirect_back_or_default(absolute_url(:nodes))
+ return "Verification of #{oidresp.display_identifier} succeeded."
+ when OpenID::Consumer::SETUP_NEEDED
+ return "Immediate request failed - Setup Needed"
+ when OpenID::Consumer::CANCEL
+ return "OpenID transaction cancelled."
+ else
+ end
+ redirect absolute_url(:openid_consumer)
+ end
+
+ def logout
+ session[:openid] = nil if session.has_key?(:openid)
+ session[:level] = nil if session.has_key?(:level)
+ session[:node_name] = nil if session.has_key?(:node_name)
+ redirect url(:top)
+ end
+
+ private
+
+ # Returns true if the openid is at a valid provider, based on whether :openid_providers is
+ # defined. Raises an exception if it is not an allowed provider.
+ def check_valid_openid_provider(openid)
+ if Chef::Config[:openid_providers]
+ fp = Chef::Config[:openid_providers].detect do |p|
+ case openid
+ when /^http:\/\/#{p}/, /^#{p}/
+ true
+ else
+ false
+ end
+ end
+ unless fp
+ raise Unauthorized, "Sorry, #{openid} is not an allowed OpenID Provider."
+ end
+ end
+ true
+ end
+
+ def consumer
+ if @consumer.nil?
+ dir = Pathname.new(Merb.root).join('db').join('cstore')
+ store = OpenID::Store::Filesystem.new(dir)
+ @consumer = OpenID::Consumer.new(session, store)
+ end
+ return @consumer
+ end
+end
diff --git a/lib/chef_server/controllers/openid_register.rb b/lib/chef_server/controllers/openid_register.rb
new file mode 100644
index 0000000000..a6c30c95c5
--- /dev/null
+++ b/lib/chef_server/controllers/openid_register.rb
@@ -0,0 +1,74 @@
+# Controller for handling the login, logout process for "users" of our
+# little server. Users have no password. This is just an example.
+
+require 'openid'
+
+class OpenidRegister < Application
+
+ provides :html, :json
+
+ before :fix_up_node_id
+
+ def index
+ @headers['X-XRDS-Location'] = absolute_url(:controller => "openid_server", :action => "idp_xrds")
+ @registered_nodes = Chef::OpenIDRegistration.list(true)
+ Chef::Log.debug(@registered_nodes.inspect)
+ display @registered_nodes
+ end
+
+ def show
+ begin
+ @registered_node = Chef::OpenIDRegistration.load(params[:id])
+ rescue Net::HTTPServerException => e
+ if e.message =~ /^404/
+ raise NotFound, "Cannot load node registration for #{params[:id]}"
+ else
+ raise e
+ end
+ end
+ Merb.logger.debug(@registered_node.inspect)
+ display @registered_node
+ end
+
+ def create
+ params.has_key?(:id) or raise BadRequest, "You must provide an id to register"
+ params.has_key?(:password) or raise BadRequest, "You must provide a password to register"
+ if Chef::OpenIDRegistration.has_key?(params[:id])
+ raise BadRequest, "You cannot re-register #{params[:id]}!"
+ end
+ @registered_node = Chef::OpenIDRegistration.new
+ @registered_node.name = params[:id]
+ @registered_node.set_password(params[:password])
+ @registered_node.save
+ display @registered_node
+ end
+
+ def update
+ raise BadRequest, "You cannot update your registration -- delete #{params[:id]} and re-register"
+ end
+
+ def destroy
+ begin
+ r = Chef::OpenIDRegistration.load(params[:id])
+ rescue Exception => e
+ raise BadRequest, "Cannot find the registration for #{params[:id]}"
+ end
+ r.destroy
+ if content_type == :html
+ redirect url(:registrations)
+ else
+ display({ :message => "Deleted registration for #{params[:id]}"})
+ end
+ end
+
+ def validate
+ begin
+ r = Chef::OpenIDRegistration.load(params[:id])
+ rescue Exception => e
+ raise BadRequest, "Cannot find the registration for #{params[:id]}"
+ end
+ r.validated = r.validated ? false : true
+ r.save
+ redirect url(:registrations)
+ end
+end
diff --git a/lib/chef_server/controllers/openid_server.rb b/lib/chef_server/controllers/openid_server.rb
new file mode 100644
index 0000000000..665af1fb6d
--- /dev/null
+++ b/lib/chef_server/controllers/openid_server.rb
@@ -0,0 +1,218 @@
+require 'pathname'
+
+# load the openid library, first trying rubygems
+#begin
+# require "rubygems"
+# require_gem "ruby-openid", ">= 1.0"
+#rescue LoadError
+require "openid"
+require "openid/consumer/discovery"
+require 'openid/store/filesystem'
+require 'json'
+#end
+
+class OpenidServer < Application
+
+ provides :html, :json
+
+ include Merb::OpenidServerHelper
+ include OpenID::Server
+
+ layout nil
+
+ before :fix_up_node_id
+
+ def index
+
+ oidreq = server.decode_request(params.reject{|k,v| k == "controller" || k == "action"})
+
+ # no openid.mode was given
+ unless oidreq
+ return "This is the Chef OpenID server endpoint."
+ end
+
+ oidresp = nil
+
+ if oidreq.kind_of?(CheckIDRequest)
+ identity = oidreq.identity
+
+ if oidresp
+ nil
+ elsif self.is_authorized(identity, oidreq.trust_root)
+ oidresp = oidreq.answer(true, nil, identity)
+ elsif oidreq.immediate
+ server_url = url :openid_server
+ oidresp = oidreq.answer(false, server_url)
+ else
+ if content_type != 'application/json'
+ session[:last_oidreq] = oidreq
+ response = { :action => url(:openid_server_decision) }
+ return response.to_json
+ else
+ return show_decision_page(oidreq)
+ end
+ end
+ else
+ oidresp = server.handle_request(oidreq)
+ end
+
+ self.render_response(oidresp)
+ end
+
+ def show_decision_page(oidreq, message="Do you trust this site with your identity?")
+ session[:last_oidreq] = oidreq
+ @oidreq = oidreq
+
+ if message
+ session[:notice] = message
+ end
+
+ render :template => 'openid_server/decide'
+ end
+
+ def node_page
+ unless Chef::OpenIDRegistration.has_key?(params[:id])
+ raise NotFound, "Cannot find registration for #{params[:id]}"
+ end
+
+ # Yadis content-negotiation: we want to return the xrds if asked for.
+ accept = request.env['HTTP_ACCEPT']
+
+ # This is not technically correct, and should eventually be updated
+ # to do real Accept header parsing and logic. Though I expect it will work
+ # 99% of the time.
+ if accept and accept.include?('application/xrds+xml')
+ return node_xrds
+ end
+
+ # content negotiation failed, so just render the user page
+ xrds_url = absolute_url(:openid_node_xrds, :id => params[:id])
+ identity_page = <<EOS
+<html><head>
+<meta http-equiv="X-XRDS-Location" content="#{xrds_url}" />
+<link rel="openid.server" href="#{absolute_url(:openid_node, :id => params[:id])}" />
+</head><body><p>OpenID identity page for registration #{params[:id]}</p>
+</body></html>
+EOS
+
+ # Also add the Yadis location header, so that they don't have
+ # to parse the html unless absolutely necessary.
+ @headers['X-XRDS-Location'] = xrds_url
+ render identity_page
+ end
+
+ def node_xrds
+ types = [
+ OpenID::OPENID_2_0_TYPE,
+ OpenID::OPENID_1_0_TYPE
+ ]
+
+ render_xrds(types)
+ end
+
+ def idp_xrds
+ types = [
+ OpenID::OPENID_IDP_2_0_TYPE,
+ ]
+
+ render_xrds(types)
+ end
+
+ def decision
+ oidreq = session[:last_oidreq]
+ session[:last_oidreq] = nil
+
+ if params.has_key?(:cancel)
+ Merb.logger.info("Cancelling OpenID Authentication")
+ return(redirect(oidreq.cancel_url))
+ else
+ identity = oidreq.identity
+ identity =~ /node\/(.+)$/
+ openid_node = Chef::OpenIDRegistration.load($1)
+ unless openid_node.validated
+ raise Unauthorized, "This nodes registration has not been validated"
+ end
+ if openid_node.password == encrypt_password(openid_node.salt, params[:password])
+ if session[:approvals]
+ session[:approvals] << oidreq.trust_root
+ else
+ session[:approvals] = [oidreq.trust_root]
+ end
+ oidresp = oidreq.answer(true, nil, identity)
+ return self.render_response(oidresp)
+ else
+ raise Unauthorized, "Invalid credentials"
+ end
+ end
+ end
+
+ protected
+
+ def encrypt_password(salt, password)
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
+ end
+
+ def server
+ if @server.nil?
+ server_url = absolute_url(:openid_server)
+ dir = Pathname.new(Merb.root).join('db').join('openid-store')
+ store = OpenID::Store::Filesystem.new(dir)
+ @server = Server.new(store, server_url)
+ end
+ return @server
+ end
+
+ def approved(trust_root)
+ return false if session[:approvals].nil?
+ return session[:approvals].member?(trust_root)
+ end
+
+ def is_authorized(identity_url, trust_root)
+ return (session[:username] and (identity_url == url_for_user) and self.approved(trust_root))
+ end
+
+ def render_xrds(types)
+ type_str = ""
+
+ types.each { |uri|
+ type_str += "<Type>#{uri}</Type>\n "
+ }
+
+ yadis = <<EOS
+<?xml version="1.0" encoding="UTF-8"?>
+<xrds:XRDS
+ xmlns:xrds="xri://$xrds"
+ xmlns="xri://$xrd*($v*2.0)">
+ <XRD>
+ <Service priority="0">
+ #{type_str}
+ <URI>#{absolute_url(:openid_server)}</URI>
+ </Service>
+ </XRD>
+</xrds:XRDS>
+EOS
+
+ @headers['content-type'] = 'application/xrds+xml'
+ render yadis
+ end
+
+ def render_response(oidresp)
+ if oidresp.needs_signing
+ signed_response = server.signatory.sign(oidresp)
+ end
+ web_response = server.encode_response(oidresp)
+
+ case web_response.code
+ when HTTP_OK
+ @status = 200
+ render web_response.body
+ when HTTP_REDIRECT
+ redirect web_response.headers['location']
+ else
+ @status = 400
+ render web_response.body
+ end
+ end
+
+
+end
diff --git a/lib/chef_server/helpers/global_helpers.rb b/lib/chef_server/helpers/global_helpers.rb
new file mode 100644
index 0000000000..1ef7ac9331
--- /dev/null
+++ b/lib/chef_server/helpers/global_helpers.rb
@@ -0,0 +1,17 @@
+module Merb
+ module GlobalHelpers
+ # helpers defined here available to all views.
+ def resource_collection(collection)
+ html = "<ul>"
+ collection.each do |resource|
+ html << "<li><b>#{resource.class}</b></li>"
+ end
+ html << "</ul>"
+ html
+ end
+
+ def node_escape(node)
+ node.gsub(/\./, '_')
+ end
+ end
+end
diff --git a/lib/chef_server/helpers/nodes_helper.rb b/lib/chef_server/helpers/nodes_helper.rb
new file mode 100644
index 0000000000..5e6c25c453
--- /dev/null
+++ b/lib/chef_server/helpers/nodes_helper.rb
@@ -0,0 +1,19 @@
+module Merb
+ module NodesHelper
+ def recipe_list(node)
+ response = ""
+ node.recipes.each do |recipe|
+ response << "<li>#{recipe}</li>"
+ end
+ response
+ end
+
+ def attribute_list(node)
+ response = ""
+ node.each_attribute do |k,v|
+ response << "<li><b>#{k}</b>: #{v}</li>"
+ end
+ response
+ end
+ end
+end \ No newline at end of file
diff --git a/lib/chef_server/helpers/openid_server_helpers.rb b/lib/chef_server/helpers/openid_server_helpers.rb
new file mode 100644
index 0000000000..73be9cab27
--- /dev/null
+++ b/lib/chef_server/helpers/openid_server_helpers.rb
@@ -0,0 +1,9 @@
+module Merb
+ module OpenidServerHelper
+
+ def url_for_user
+ url(:openid_node, :username => session[:username])
+ end
+
+ end
+end
diff --git a/lib/chef_server/init.rb b/lib/chef_server/init.rb
new file mode 100644
index 0000000000..14029c1295
--- /dev/null
+++ b/lib/chef_server/init.rb
@@ -0,0 +1,165 @@
+Merb.root = File.join(File.dirname(__FILE__))
+
+#
+# ==== Structure of Merb initializer
+#
+# 1. Load paths.
+# 2. Dependencies configuration.
+# 3. Libraries (ORM, testing tool, etc) you use.
+# 4. Application-specific configuration.
+
+#
+# ==== Set up load paths
+#
+
+# Make the app's "gems" directory a place where gems are loaded from.
+# Note that gems directory must have a structure RubyGems uses for
+# directories under which gems are kept.
+#
+# To conveniently set it up use gem install -i <merb_app_root/gems>
+# when installing gems. This will set up the structure under /gems
+# automagically.
+#
+# An example:
+#
+# You want to bundle ActiveRecord and ActiveSupport with your Merb
+# application to be deployment environment independent. To do so,
+# install gems into merb_app_root/gems directory like this:
+#
+# gem install -i ~/dev/merbapp/gems activesupport-post-2.0.2gem activerecord-post-2.0.2.gem
+#
+# Since RubyGems will search merb_app_root/gems for dependencies, order
+# in statement above is important: we need to install ActiveSupport which
+# ActiveRecord depends on first.
+#
+# Remember that bundling of dependencies as gems with your application
+# makes it independent of the environment it runs in and is a very
+# good, encouraged practice to follow.
+Gem.clear_paths
+Gem.path.unshift(Merb.root / "gems")
+
+# If you want modules and classes from libraries organized like
+# merbapp/lib/magicwand/lib/magicwand.rb to autoload,
+# uncomment this.
+
+
+# Merb.push_path(:lib, Merb.root / "lib") # uses **/*.rb as path glob.
+
+# ==== Dependencies
+
+# These are some examples of how you might specify dependencies.
+# Dependencies load is delayed to one of later Merb app
+# boot stages. It may be important when
+# later part of your configuration relies on libraries specified
+# here.
+#
+# dependencies "RedCloth", "merb_helpers"
+# OR
+# dependency "RedCloth", "> 3.0"
+# OR
+# dependencies "RedCloth" => "> 3.0", "ruby-aes-cext" => "= 1.0"
+Merb::BootLoader.after_app_loads do
+ # Add dependencies here that must load after the application loads:
+ Chef::Config.from_file(File.join(File.dirname(__FILE__), "..", "..", "config", "chef-server.rb"))
+ Chef::Queue.connect
+
+ # dependency "magic_admin" # this gem uses the app's model classes
+end
+
+#
+# ==== Set up your ORM of choice
+#
+
+# Merb doesn't come with database support by default. You need
+# an ORM plugin. Install one, and uncomment one of the following lines,
+# if you need a database.
+
+# Uncomment for DataMapper ORM
+# use_orm :datamapper
+
+# Uncomment for ActiveRecord ORM
+# use_orm :activerecord
+
+# Uncomment for Sequel ORM
+# use_orm :sequel
+
+Merb.push_path(:lib, File.join(File.dirname(__FILE__), "..", "chef"))
+Merb.push_path(:controller, File.join(File.dirname(__FILE__), "controllers"))
+Merb.push_path(:model, File.join(File.dirname(__FILE__), "models"))
+Merb.push_path(:view, File.join(File.dirname(__FILE__), "views"))
+Merb.push_path(:helper, File.join(File.dirname(__FILE__), "helpers"))
+Merb.push_path(:public, Merb.root_path("public"), nil)
+
+require 'merb-haml'
+
+
+#
+# ==== Pick what you test with
+#
+
+# This defines which test framework the generators will use
+# rspec is turned on by default
+#
+# Note that you need to install the merb_rspec if you want to ue
+# rspec and merb_test_unit if you want to use test_unit.
+# merb_rspec is installed by default if you did gem install
+# merb.
+#
+# use_test :test_unit
+use_test :rspec
+
+
+#
+# ==== Set up your basic configuration
+#
+Merb::Config.use do |c|
+ # Sets up a custom session id key, if you want to piggyback sessions of other applications
+ # with the cookie session store. If not specified, defaults to '_session_id'.
+ c[:session_id_key] = '_chef_server_session_id'
+ c[:session_secret_key] = '0992ea491c30ec76c98367c1ca53b18c1e7c5b30'
+ c[:session_store] = 'cookie'
+ c[:exception_details] = true
+ c[:reload_classes] = true
+ c[:log_level] = :debug
+ c[:log_file] = Merb.log_path + "/chef-server.log"
+end
+
+Merb.logger.info("Compiling routes...")
+Merb::Router.prepare do |r|
+ # RESTful routes
+ # r.resources :posts
+
+ # This is the default route for /:controller/:action/:id
+ # This is fine for most cases. If you're heavily using resource-based
+ # routes, you may want to comment/remove this line to prevent
+ # clients from calling your create or destroy actions with a GET
+
+ r.resources :nodes
+ r.resources :nodes, :member => { :compile => :get }
+
+ #r.resources :openid do |res|
+ # res.resources :register, :controller => "openid_register"
+ # res.resources :server, :controller => "openid_server"
+ #end
+
+ r.resources :registrations, :controller => "openid_register"
+ r.resources :registrations, :controller => "openid_register", :member => { :validate => :post }
+ r.match("/openid/server").to(:controller => "openid_server", :action => "index").name(:openid_server)
+ r.match("/openid/server/server/xrds").
+ to(:controller => "openid_server", :action => 'idp_xrds').name(:openid_server_xrds)
+ r.match("/openid/server/node/:id").
+ to(:controller => "openid_server", :action => 'node_page').name(:openid_node)
+ r.match('/openid/server/node/:id/xrds').
+ to(:controller => 'openid_server', :action => 'node_xrds').name(:openid_node_xrds)
+ r.match('/openid/server/decision').to(:controller => "openid_server", :action => "decision").name(:openid_server_decision)
+ r.match('/openid/consumer').to(:controller => 'openid_consumer', :action => 'index').name(:openid_consumer)
+ r.match('/openid/consumer/start').to(:controller => 'openid_consumer', :action => 'start').name(:openid_consumer_start)
+ r.match('/openid/consumer/complete').to(:controller => 'openid_consumer', :action => 'complete').name(:openid_consumer_complete)
+ r.match('/openid/consumer/logout').to(:controller => 'openid_consumer', :action => 'logout').name(:openid_consumer_logout)
+
+ #r.default_routes
+
+ # Change this for your home page to be available at /
+ r.match('/').to(:controller => 'nodes', :action =>'index').name(:top)
+end
+
diff --git a/lib/chef_server/log/chef-server.log b/lib/chef_server/log/chef-server.log
new file mode 100644
index 0000000000..bfb7e4e771
--- /dev/null
+++ b/lib/chef_server/log/chef-server.log
@@ -0,0 +1,434 @@
+Mon, 16 Jun 2008 01:37:16 GMT ~ info ~ Logfile created
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Request:
+ ~ Routed to: {:controller=>"nodes", :action=>"index"}
+ ~ Controller 'Nodes' not found - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:50:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request:
+ ~ Routed to: {:controller=>"nodes", :action=>"index"}
+ ~ Controller 'Nodes' not found - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:50:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Request:
+ ~ Routed to: {:controller=>"nodes", :action=>"index"}
+ ~ Params: {"action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.386588, :action_time=>0.386157, :before_filters_time=>0.000113, :after_filters_time=>9.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.012587, :action_time=>0.012287, :before_filters_time=>4.0e-05, :after_filters_time=>7.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /registrations
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"openid_register"}
+ ~ {:dispatch_time=>0.064962, :action_time=>0.064639, :before_filters_time=>3.9e-05, :after_filters_time=>1.2e-05}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.011244, :action_time=>0.011029, :before_filters_time=>3.0e-05, :after_filters_time=>8.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /registrations
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"openid_register"}
+ ~ {:dispatch_time=>0.01562, :action_time=>0.015291, :before_filters_time=>4.1e-05, :after_filters_time=>8.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.01432, :action_time=>0.014052, :before_filters_time=>5.5e-05, :after_filters_time=>8.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /registrations
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"openid_register"}
+ ~ {:dispatch_time=>0.014029, :action_time=>0.013795, :before_filters_time=>2.9e-05, :after_filters_time=>1.0e-05}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.012692, :action_time=>0.012386, :before_filters_time=>4.0e-05, :after_filters_time=>9.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ No routes match the request, /stylesheets/master.css - (Merb::ControllerExceptions::NotFound)
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/dispatch/dispatcher.rb:34:in `handle'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/application.rb:53:in `call'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/handler/mongrel.rb:72:in `process'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:159:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `each'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:158:in `process_client'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:285:in `run'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `initialize'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `new'
+/Library/Ruby/Gems/1.8/gems/mongrel-1.1.4/lib/mongrel.rb:268:in `run'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/rack/adapter/mongrel.rb:22:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core/server.rb:44:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/lib/merb-core.rb:36:in `start'
+/Library/Ruby/Gems/1.8/gems/merb-core-0.9.3/bin/merb:12
+/usr/bin/merb:19:in `load'
+/usr/bin/merb:19
+ ~
+
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.018469, :action_time=>0.018213, :before_filters_time=>3.1e-05, :after_filters_time=>1.2e-05}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.013904, :action_time=>0.013629, :before_filters_time=>3.7e-05, :after_filters_time=>9.0e-06}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ Request: /registrations
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"openid_register"}
+ ~ {:dispatch_time=>0.019964, :action_time=>0.019629, :before_filters_time=>3.4e-05, :after_filters_time=>9.0e-06}
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.014237, :action_time=>0.013883, :before_filters_time=>4.6e-05, :after_filters_time=>9.0e-06}
+ ~
+
+ ~ Request: /registrations
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"openid_register"}
+ ~ {:dispatch_time=>0.020176, :action_time=>0.019838, :before_filters_time=>3.5e-05, :after_filters_time=>1.2e-05}
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.012408, :action_time=>0.012107, :before_filters_time=>4.5e-05, :after_filters_time=>7.0e-06}
+ ~
+
+ ~ Request: /nodes/adam
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"show", :id=>"adam"}
+ ~ Params: {"format"=>nil, "action"=>"show", "id"=>"adam", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.03838, :action_time=>0.038078, :before_filters_time=>4.8e-05, :after_filters_time=>1.2e-05}
+ ~
+
+ ~ Request: /stylesheets/master.css
+ ~ Request: /nodes/adam/compile
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"compile", :id=>"adam"}
+ ~ Params: {"format"=>nil, "action"=>"compile", "id"=>"adam", "controller"=>"nodes"}
+ ~ {:dispatch_time=>4.73037, :action_time=>4.730021, :before_filters_time=>5.0e-05, :after_filters_time=>1.7e-05}
+ ~
+
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Request: /nodes/latte_local
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"show", :id=>"latte_local"}
+ ~ Params: {"format"=>nil, "action"=>"show", "id"=>"latte_local", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.00587, :action_time=>0.005648, :before_filters_time=>3.6e-05, :after_filters_time=>7.0e-06}
+ ~
+
+ ~ Request: /registrations/latte_local
+ ~ Routed to: {:controller=>"openid_register", :format=>nil, :action=>"show", :id=>"latte_local"}
+ ~ Params: {"format"=>nil, "action"=>"show", "id"=>"latte_local", "controller"=>"openid_register"}
+ ~ #<Chef::OpenIDRegistration:0x12c9420 @validated=true, @couchdb_rev="744961245", @password="31c954f5c3a7681dc17835e300c092519b6c2a09", @salt="Thu Jun 12 14:15:08 -0700 20088jr1S8ApvctBoObFIdkyHrxRB6t1XY", @name="latte_local", @couchdb=#<Chef::CouchDB:0x12c93f8 @rest=#<Chef::REST:0x12c93a8 @cookies={}, @url="http://localhost:5984">>>
+ ~ {:dispatch_time=>0.017782, :action_time=>0.017557, :before_filters_time=>3.1e-05, :after_filters_time=>1.3e-05}
+ ~
+
+ ~ Request: /openid/server/node/latte_local
+ ~ Routed to: {:controller=>"openid_server", :action=>"node_page", :id=>"latte_local"}
+ ~ Params: {"action"=>"node_page", "id"=>"latte_local", "controller"=>"openid_server"}
+ ~ {:dispatch_time=>0.004956, :action_time=>0.004722, :before_filters_time=>3.3e-05, :after_filters_time=>7.0e-06}
+ ~
+
+ ~ Request: /openid/server
+ ~ Routed to: {:controller=>"openid_server", :action=>"index"}
+ ~ Params: {"openid.session_type"=>"DH-SHA1", "openid.mode"=>"associate", "openid.assoc_type"=>"HMAC-SHA1", "openid.ns"=>"http://specs.openid.net/auth/2.0", "action"=>"index", "openid.dh_consumer_public"=>"Zn3+K7sQLiNqeA4bIeX0eX4zcyaNl0/Scyve/CwPUmX5ycjl9Sb5R1sVzw3x9NsKUIqSTKSD5kfmDNj15ECLWWIuv7m6cPGdhmEf1qZyvfZSEmYV5iEFbm5JopZNQE2FtSI3iQOw0AB00tZEpmDacp39qvv1H110uTs5u2r0SDY=", "controller"=>"openid_server"}
+ ~ {:dispatch_time=>0.104243, :action_time=>0.103829, :before_filters_time=>1.9e-05, :after_filters_time=>5.0e-06}
+ ~
+
+ ~ Request: /openid/consumer/start
+ ~ Routed to: {:controller=>"openid_consumer", :action=>"start"}
+ ~ Params: {"submit"=>"Verify", "action"=>"start", "controller"=>"openid_consumer", "openid_identifier"=>"http://localhost:4001/openid/server/node/latte_local"}
+ ~ Redirecting to: http://localhost:4001/openid/server?openid.assoc_handle=%7BHMAC-SHA1%7D%7B4855c779%7D%7BdTZXcQ%3D%3D%7D&openid.claimed_id=http%3A%2F%2Flocalhost%3A4001%2Fopenid%2Fserver%2Fnode%2Flatte_local&openid.identity=http%3A%2F%2Flocalhost%3A4001%2Fopenid%2Fserver%2Fnode%2Flatte_local&openid.mode=checkid_setup&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.realm=http%3A%2F%2Flocalhost%3A4000%2Fopenid%2Fconsumer&openid.return_to=http%3A%2F%2Flocalhost%3A4000%2Fopenid%2Fconsumer%2Fcomplete
+ ~ {:dispatch_time=>0.270794, :action_time=>0.270403, :before_filters_time=>1.8e-05, :after_filters_time=>1.4e-05}
+ ~
+
+ ~ Request: /openid/server
+ ~ Routed to: {:controller=>"openid_server", :action=>"index"}
+ ~ Params: {"openid.claimed_id"=>"http://localhost:4001/openid/server/node/latte_local", "openid.mode"=>"checkid_setup", "openid.return_to"=>"http://localhost:4000/openid/consumer/complete", "openid.ns"=>"http://specs.openid.net/auth/2.0", "action"=>"index", "controller"=>"openid_server", "openid.identity"=>"http://localhost:4001/openid/server/node/latte_local", "openid.assoc_handle"=>"{HMAC-SHA1}{4855c779}{dTZXcQ==}", "openid.realm"=>"http://localhost:4000/openid/consumer"}
+ ~ {:dispatch_time=>0.002846, :action_time=>0.002304, :before_filters_time=>1.8e-05, :after_filters_time=>7.0e-06}
+ ~
+
+ ~ Request: /openid/server/decision
+ ~ Routed to: {:controller=>"openid_server", :action=>"decision"}
+ ~ Params: {"action"=>"decision", "controller"=>"openid_server", "password"=>"wCFIjJVHS7Nu2OGjW7SFn21HPSHRW1Ci8C6G4plq"}
+ ~ Redirecting to: http://localhost:4000/openid/consumer/complete?openid.assoc_handle=%7BHMAC-SHA1%7D%7B4855c779%7D%7BdTZXcQ%3D%3D%7D&openid.claimed_id=http%3A%2F%2Flocalhost%3A4001%2Fopenid%2Fserver%2Fnode%2Flatte_local&openid.identity=http%3A%2F%2Flocalhost%3A4001%2Fopenid%2Fserver%2Fnode%2Flatte_local&openid.mode=id_res&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.op_endpoint=http%3A%2F%2Flocalhost%3A4001%2Fopenid%2Fserver&openid.response_nonce=2008-06-16T01%3A52%3A57ZuyqSwR&openid.return_to=http%3A%2F%2Flocalhost%3A4000%2Fopenid%2Fconsumer%2Fcomplete&openid.sig=Zz9S7OR%2FAbC%2FzCHAgceVRSb00Iw%3D&openid.signed=assoc_handle%2Cclaimed_id%2Cidentity%2Cmode%2Cns%2Cop_endpoint%2Cresponse_nonce%2Creturn_to%2Csigned
+ ~ {:dispatch_time=>0.006955, :action_time=>0.006688, :before_filters_time=>2.1e-05, :after_filters_time=>8.0e-06}
+ ~
+
+ ~ Request: /openid/consumer/complete
+ ~ Routed to: {:controller=>"openid_consumer", :action=>"complete"}
+ ~ Params: {"openid.claimed_id"=>"http://localhost:4001/openid/server/node/latte_local", "openid.mode"=>"id_res", "openid.return_to"=>"http://localhost:4000/openid/consumer/complete", "openid.sig"=>"Zz9S7OR/AbC/zCHAgceVRSb00Iw=", "openid.ns"=>"http://specs.openid.net/auth/2.0", "openid.op_endpoint"=>"http://localhost:4001/openid/server", "action"=>"complete", "openid.response_nonce"=>"2008-06-16T01:52:57ZuyqSwR", "controller"=>"openid_consumer", "openid.identity"=>"http://localhost:4001/openid/server/node/latte_local", "openid.assoc_handle"=>"{HMAC-SHA1}{4855c779}{dTZXcQ==}", "openid.signed"=>"assoc_handle,claimed_id,identity,mode,ns,op_endpoint,response_nonce,return_to,signed"}
+ ~ Redirecting to: http://localhost:4000/nodes
+ ~ {:dispatch_time=>0.004359, :action_time=>0.003761, :before_filters_time=>9.0e-06, :after_filters_time=>6.0e-06}
+ ~
+
+ ~ Request: /nodes
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"index"}
+ ~ Params: {"format"=>nil, "action"=>"index", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.012424, :action_time=>0.012216, :before_filters_time=>2.7e-05, :after_filters_time=>1.2e-05}
+ ~
+
+ ~ Request: /nodes/latte_local
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"update", :id=>"latte_local"}
+ ~ Params: {"format"=>nil, "inflated_object"=>#<Chef::Node:0x1a007fc @couchdb_rev="3414278843", @recipe_list=["tempfile", "fakefile"], @attribute={"macosx_productname"=>"Mac OS X", "kernel"=>"Darwin", "sp_current_processor_speed"=>"2.4 GHz", "sp_smc_version"=>"1.27f1", "sp_serial_number"=>"W88153RWYJX", "hardwaremodel"=>"i386", "sp_boot_rom_version"=>"MBP41.00C1.B03", "operatingsystemrelease"=>"9.3.0", "sp_os_version"=>"Mac OS X 10.5.3 (9D34)", "ipaddress"=>"192.168.0.101", "sp_kernel_version"=>"Darwin 9.3.0", "jesse"=>"is neat", "kernelrelease"=>"9.3.0", "sp_boot_mode"=>"normal_boot", "ps"=>"ps -auxwww", "fqdn"=>"latte.local", "sp_uptime"=>"up 13:6:36:0", "macosx_productversion"=>"10.5.3", "sp_packages"=>1, "domain"=>"local", "sp_l2_cache"=>"3 MB", "macosx_buildversion"=>"9D34", "sp_boot_volume"=>"Macintosh HD", "sp_local_host_name"=>"latte", "sp_machine_name"=>"MacBookPro", "sp_bus_speed"=>"800 MHz", "sp_physical_memory"=>"4 GB", "sp_user_name"=>"Adam Jacob (adam)", "sshrsakey"=>"AAAAB3NzaC1yc2EAAAABIwAAAQEAr3ZIJusU+7/cbCyJ3hRp6MmqpmvTPl5beAuergDEcDnJ75xlt+/0V6C7kPmgNY2FMytO0wTj/myAd7Ylf2hVwMRq63Cdb/0LLAln0luiqfcLLQtZFJnhn4txV0RxtdNoaXj7rFohBHIxDJ9EAzWmNSm9tCQOY4ro65qsGLUU4YXfhcLJw4LSbsYJa8jVThb/tbcwZz6D+m4m2/lRvpXhY7cAj/dP12b3DY3v3tKl0GaELZtGuEt49S7SPPq05YfL6X1JfAd6l29FfkxGStMa/K8rg1Hmh0PJM5WQZcm+0tr9nllLLIMMf4rk2JznnFyB64NOMt97qIhtM1v7NP4pgw==", "owner"=>"Adam Jacob", "rubyversion"=>"1.8.6", "sp_cpu_type"=>"Intel Core 2 Duo", "sp_machine_model"=>"MacBookPro4,1", "sp_mmm_entry"=>{"MMM_state"=>"MMM_enabled"}, "hostname"=>"latte", "facterversion"=>"1.3.8", "operatingsystem"=>"Darwin", "macaddress"=>"00:1f:f3:4e:16:ab", "sshdsakey"=>"AAAAB3NzaC1kc3MAAACBAOzWvVwoQ0HQGtXq42x8L8Wn9rgjbVEAx3h3Uu9jMHA6ftZRiSJizgQlnoLjLkAiUARQUEho/TJWdCx/TQkWnIcrGjkPhGrovzq9212w5x8shYA3qPTppwtajenOEKJFjNSqW/i8dHkansUrGv5V49zc9KVfJ+SUkVP3CXAGndTHAAAAFQCG6h9qRnwjUhRYERqx+BJmOr3RCwAAAIByJIWvZ8qeK28S1b+ERfy0+1jVVYCqFkJfuKp3nQLi7bF21w/C0SVf9Nkpo0Ecw7hufGgyCo3XsxayedSrNQgy954aTiyqdCxakhwECIWBaNuWLTRb5oMcbzvkr/JLRNR8cOJrIb5d7KMAGrU29mZ/0smFptikGz2tc1scqP26aQAAAIEA2Gcgs3wsPa36a75qS7mkE8paDAEgiBAq64OUkkLSy6lmNNtGW0IApPYOC91S0L01aODhtJqB2txHOSP6GokxgrKTcoA5fGwYbxrGw7pddCDP+5AU8JghVIwF/Z9hGkhCk2ZQi8YUJW0lpJzEQqZmJ/XShaayeR2kbHOj4/g0BOQ=", "sp_number_processors"=>2}, @name="latte.local", @couchdb=#<Chef::CouchDB:0x1a00630 @rest=#<Chef::REST:0x1a005f4 @cookies={}, @url="http://localhost:5984">>>, "action"=>"update", "id"=>"latte_local", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.022162, :action_time=>0.021556, :before_filters_time=>0.000119, :after_filters_time=>8.0e-06}
+ ~
+
+ ~ Request: /nodes/latte_local/compile
+ ~ Routed to: {:controller=>"nodes", :format=>nil, :action=>"compile", :id=>"latte_local"}
+ ~ Params: {"format"=>nil, "action"=>"compile", "id"=>"latte_local", "controller"=>"nodes"}
+ ~ {:dispatch_time=>0.35835, :action_time=>0.358026, :before_filters_time=>3.9e-05, :after_filters_time=>1.5e-05}
+ ~
+
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ {:before_filters_time=>0.000138, :after_filters_time=>9.0e-06, :action_time=>0.001826}
+ ~ {:before_filters_time=>9.3e-05, :after_filters_time=>9.0e-06, :action_time=>0.000342}
+ ~ {:before_filters_time=>0.000177, :after_filters_time=>1.0e-05, :action_time=>0.000479}
+ ~ {:before_filters_time=>0.000149, :after_filters_time=>9.0e-06, :action_time=>0.000427}
+ ~ {:before_filters_time=>0.000123, :after_filters_time=>1.0e-05, :action_time=>0.000425}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>1.0e-05, :action_time=>0.001232}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>6.0e-06, :action_time=>0.000707}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>7.0e-06, :action_time=>0.000735}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>7.0e-06, :action_time=>0.00067}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>6.0e-06, :action_time=>0.000646}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>6.0e-06, :action_time=>0.000644}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>5.0e-06, :action_time=>0.000634}
+ ~ Redirecting to: /openid/consumer
+ ~ {:after_filters_time=>9.0e-06, :action_time=>0.000728}
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
diff --git a/lib/chef_server/public/images/merb.jpg b/lib/chef_server/public/images/merb.jpg
new file mode 100644
index 0000000000..a19dcf4048
--- /dev/null
+++ b/lib/chef_server/public/images/merb.jpg
Binary files differ
diff --git a/lib/chef_server/public/stylesheets/master.css b/lib/chef_server/public/stylesheets/master.css
new file mode 100644
index 0000000000..70580148a7
--- /dev/null
+++ b/lib/chef_server/public/stylesheets/master.css
@@ -0,0 +1,153 @@
+body {
+ font-family: Arial, Verdana, sans-serif;
+ font-size: 12px;
+ background-color: #fff;
+ margin: ;
+}
+html {
+ height: 100%;
+ margin-bottom: 1px;
+}
+#container {
+ width: 80%;
+ text-align: left;
+ background-color: #fff;
+ margin-right: auto;
+ margin-left: auto;
+}
+#header-container {
+ width: 100%;
+ padding-top: 15px;
+}
+#header-container h1, #header-container h2 {
+ margin-left: 6px;
+ margin-bottom: 6px;
+}
+.spacer {
+ width: 100%;
+ height: 15px;
+}
+hr {
+ border: 0px;
+ color: #ccc;
+ background-color: #cdcdcd;
+ height: 1px;
+ width: 100%;
+ text-align: left;
+}
+h1 {
+ font-size: 28px;
+ color: #c55;
+ background-color: #fff;
+ font-family: Arial, Verdana, sans-serif;
+ font-weight: 300;
+}
+h2 {
+ font-size: 15px;
+ color: #999;
+ font-family: Arial, Verdana, sans-serif;
+ font-weight: 300;
+ background-color: #fff;
+}
+h3 {
+ color: #4d9b12;
+ font-size: 15px;
+ text-align: left;
+ font-weight: 300;
+ padding: 5px;
+ margin-top: 5px;
+}
+
+#left-container {
+ float: left;
+ width: 250px;
+ background-color: #FFFFFF;
+ color: black;
+}
+
+#left-container h3 {
+ color: #c55;
+}
+
+#main-container {
+ margin: 5px 5px 5px 260px;
+ padding: 15px;
+ border-left: 1px solid silver;
+ min-height: 400px;
+}
+p {
+ color: #000;
+ background-color: #fff;
+ line-height: 20px;
+ padding: 5px;
+}
+a {
+ color: #4d9b12;
+ background-color: #fff;
+ text-decoration: none;
+}
+a:hover {
+ color: #4d9b12;
+ background-color: #fff;
+ text-decoration: underline;
+}
+#footer-container {
+ clear: both;
+ font-size: 12px;
+ font-family: Verdana, Arial, sans-serif;
+}
+.right {
+ float: right;
+ font-size: 100%;
+ margin-top: 5px;
+ color: #999;
+ background-color: #fff;
+}
+.left {
+ float: left;
+ font-size: 100%;
+ margin-top: 5px;
+ color: #999;
+ background-color: #fff;
+}
+#main-container ul {
+ margin-left: 3.0em;
+}
+
+div.resource_collection {
+ border: 1px solid #999;
+ float: left;
+ clear: both;
+ margin: 10px;
+ padding: 5px;
+}
+
+div.resource {
+ border-top: 1px solid #999;
+ float: left;
+ clear: both;
+}
+
+div.attr_group {
+ float: left;
+ clear: both;
+}
+
+td.attr_name {
+ font-weight: bold;
+ margin-right: 10px;
+ border-right: 1px solid #999;
+}
+
+td.attr_value {
+ padding-left: 5px;
+ border-top: 1px solid #999;
+}
+
+div.node {
+ float: left;
+ clear: both;
+ margin: 10px;
+ padding: 5px;
+ border: 1px solid #999;
+} \ No newline at end of file
diff --git a/lib/chef_server/views/exceptions/bad_request.json.erb b/lib/chef_server/views/exceptions/bad_request.json.erb
new file mode 100644
index 0000000000..f266cf99b9
--- /dev/null
+++ b/lib/chef_server/views/exceptions/bad_request.json.erb
@@ -0,0 +1 @@
+<%= { :error => params[:exception], :code => 400 }.to_json %> \ No newline at end of file
diff --git a/lib/chef_server/views/exceptions/internal_server_error.html.erb b/lib/chef_server/views/exceptions/internal_server_error.html.erb
new file mode 100644
index 0000000000..aadbfad350
--- /dev/null
+++ b/lib/chef_server/views/exceptions/internal_server_error.html.erb
@@ -0,0 +1,216 @@
+<html>
+<head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title><%= @exception_name %></title>
+ <style type="text/css" media="screen">
+ body {
+ font-family:arial;
+ font-size:11px;
+ }
+ h1 {
+ font-size:48px;
+ letter-spacing:-4px;
+ margin:0;
+ line-height:36px;
+ color:#333;
+ }
+ h1 sup {
+ font-size: 0.5em;
+ }
+ h1 sup.error_500, h1 sup.error_400 {
+ color:#990E05;
+ }
+ h1 sup.error_100, h1 sup.error_200 {
+ color:#00BF10;
+ }
+ h1 sup.error_300 {
+ /* pretty sure you cant 'see' status 300
+ errors but if you could I think they
+ would be blue */
+ color:#1B2099;
+ }
+ h2 {
+ font-size:36px;
+ letter-spacing:-3px;
+ margin:0;
+ line-height:28px;
+ color:#444;
+ }
+ a, a:visited {
+ color:#00BF10;
+ }
+ .internalError {
+ width:800px;
+ margin:50px auto;
+ }
+ .header {
+ border-bottom:10px solid #333;
+ margin-bottom:1px;
+ background-image: url("data:image/gif;base64,R0lGODlhAwADAIAAAP///8zMzCH5BAAAAAAALAAAAAADAAMAAAIEBHIJBQA7");
+ padding:20px;
+ }
+ table.trace {
+ width:100%;
+ font-family:courier, monospace;
+ letter-spacing:-1px;
+ border-collapse: collapse;
+ border-spacing:0;
+ }
+ table.trace tr td{
+ padding:0;
+ height:26px;
+ font-size:13px;
+ vertical-align:middle;
+ }
+ table.trace tr.file{
+ border-top:2px solid #fff;
+ background-color:#F3F3F3;
+ }
+ table.trace tr.source {
+ background-color:#F8F8F8;
+ display:none;
+ }
+ table.trace .open tr.source {
+ display:table-row;
+ }
+ table.trace tr.file td.expand {
+ width:23px;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAAXCAIAAABvSEP3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAdVJREFUeNqMVL+TwUAYxaRIOlEhlZHGDAUzzOQ61+AqXMV1lJSU7q/QRqm8KFUcJTNn5qJkaPyoKKVz7y4mF8na5Kt29tt9+/Z97/u81+vVQ4r9frdarS6Xi7ETDIZisRjxMGPfmk4niNPpZE+xLAugbPaZ53nzvtfMBe/3+/3dbuehBrAKhZdUKkVAWa9Xsiybv0CPZDJZLr/qa5/BwgwRjYqOKIvFYjQa/aNommZh0Ww2K5UqzwfoQOPxaLPZ3FAmk0+7lplMpt1u53J5OpBOR0eZEE9wHJfP5zud93g88QhluwWbjW+5VOmKBgKBer3eaDTDYeGBQF8+x7rqIYoiPgixWJazpA6HA+MSxRArkUgMh0M409g8Ho8+9wYxxCqVSq1W26EDHGM2m4HOHQrEc38f/Yn7cLmlIRhBENzcx8cVRZnPZ/YUep2BWkjTIfA+PKVpZAXR5QxsjiqCKvGEqqp443w+0dvy17swqD0HB3S73V5PpkNg1qBqt8kwGCjmPkinM0QJbIoEa7U6UG6ToVgs4V9G2g0ESoP5Aoi7KYX5oCgf8IKbkvn9/mr1LRQKESamzgJy0g0tSZIuB3nuGqRU9Vv9C4sKkUhEkp4soxvxI8AAhWrrtXa3X8EAAAAASUVORK5CYII=);
+ background-position:top left;
+ background-repeat:no-repeat;
+ }
+ table.trace .open tr.file td.expand {
+ width:19px;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAB1CAIAAAAqdO2mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAXZJREFUeNrslK1ywkAUhcMOBomEOiSdqLxEBJX0NaijOsjyHGGmCGyQQYaiiiw4gktkcOmZbpsuuzQ/M5XnqJ2d3S/n3nM3rTzPLUP7/Tt0+pLcGQwG3W53OLyHzPMtjYL7q9UqSRLrD4E1Gj1orCvKYuFHUWTVkOM44/HjDcp8/lL4r6NerzeZPMm1KFw0QkDn83m5fP2lHA4fNQvRtNvtjsfDd0WzmSfb2e/fdTqdOvdh/HLJZLOn0+d2HJ+KRGzbdl23EpFlmed5cp2maRzHQq1lvQ5KMi6EUZBGfup6E1pTfd+vrGW7jbQ2C9hTt9BpqNyIWaAwAy6xg2eBz5iRC/NomiZhGN5sqmnkauo0BUGgVQoBjQ80oCACgNQdZHfTYBkF2mxCtWWAqunWpahxIDUt3QYUxIFQpJHyIWpXjinabKbbwItMHT+NyjchrP8QKaSQQgoppJBCCimkkEIKKaSQQgoppJBCCimkkEIKKaSo+hRgAEFD17X08O2NAAAAAElFTkSuQmCC);
+ background-position:top left;
+ background-repeat:no-repeat;
+ }
+ table.trace tr.source td.collapse {
+ width:19px;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABcAAAB1CAIAAAAqdO2mAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVxJREFUeNrs0zFygkAUBmBlUkgJHdABlQwVkVJKKUxBYWbkALTxMJwhltyDFkss03IF8pudIcwaDaDl/6pd2P327b7d+eHwMXs4lNkzggoVKlSoUKFChQoVKlSoUKFChQoVKlSoUKFChQqVEYqm6ft9+qiSJEkYho7jTlcw2fd9NOI4nq4gEdFwXXe1Cqco63VkWVbXRTqLhTpOwQRpF7quR1E0TgGhqvLKUFCyoQqG/rks3O6kZKW/eRFpevOCoGTXVTcMQ5EyxyDEkML1c5RzuZOICIyXqn7JBVez6282MWrx731HOv2qB8Hri2lamNk0DfpVVdV1Peodappmmua8bdvzuc7zfNprzrLMth1FnGh/X8MjCAIQv/cFz/+65PcDh7rbvYv2ZUfdj+PxsyzLgVl0hKwgTqeqKApx2LeOc7t98zyv/1FWOgvx9RPii23bmL9cetJ8Ed8CDAC6aFW8bCzFhwAAAABJRU5ErkJggg==);
+ background-position:bottom left;
+ background-repeat:no-repeat;
+ background-color:#6F706F;
+ }
+ table.trace tr td.path {
+ padding-left:10px;
+ }
+ table.trace tr td.code {
+ padding-left:35px;
+ white-space: pre;
+ line-height:9px;
+ padding-bottom:10px;
+ }
+ table.trace tr td.code em {
+ font-weight:bold;
+ color:#00BF10;
+ }
+ table.trace tr td.code a {
+ width: 20px;
+ float: left;
+ }
+ table.trace tr td.code .more {
+ color:#666;
+ }
+ table.trace tr td.line {
+ width:30px;
+ text-align:right;
+ padding-right:4px;
+ }
+ .footer {
+ margin-top:5px;
+ font-size:11px;
+ color:#444;
+ text-align:right;
+ }
+ </style>
+</head>
+<body>
+ <div class="internalError">
+
+ <div class="header">
+ <h1><%= @exception_name %> <sup class="error_<%= @exception.class::STATUS %>"><%= @exception.class::STATUS %></sup></h1>
+ <% if show_details = ::Merb::Config[:exception_details] -%>
+ <h2><%= @exception.message %></h2>
+ <% else -%>
+ <h2>Sorry about that...</h2>
+ <% end -%>
+ <h3>Parameters</h3>
+ <ul>
+ <% params[:original_params].each do |param, value| %>
+ <li><strong><%= param %>:</strong> <%= value.inspect %></li>
+ <% end %>
+ <%= "<li>None</li>" if params[:original_params].empty? %>
+ </ul>
+
+ <h3>Session</h3>
+ <ul>
+ <% params[:original_session].each do |param, value| %>
+ <li><strong><%= param %>:</strong> <%= value.inspect %></li>
+ <% end %>
+ <%= "<li>None</li>" if params[:original_session].empty? %>
+ </ul>
+
+ <h3>Cookies</h3>
+ <ul>
+ <% params[:original_cookies].each do |param, value| %>
+ <li><strong><%= param %>:</strong> <%= value.inspect %></li>
+ <% end %>
+ <%= "<li>None</li>" if params[:original_cookies].empty? %>
+ </ul>
+ </div>
+
+ <% if show_details %>
+ <table class="trace">
+ <% @exception.backtrace.each_with_index do |line, index| %>
+ <tbody class="close">
+ <tr class="file">
+ <td class="expand">
+ </td>
+ <td class="path">
+ <%= (line.match(/^([^:]+)/)[1] rescue 'unknown').sub(/\/((opt|usr)\/local\/lib\/(ruby\/)?(gems\/)?(1.8\/)?(gems\/)?|.+\/app\/)/, '') %>
+ <% unless line.match(/\.erb:/) %>
+ in "<strong><%= line.match(/:in `(.+)'$/)[1] rescue '?' %></strong>"
+ <% else %>
+ (<strong>ERB Template</strong>)
+ <% end %>
+ </td>
+ <td class="line">
+ <a href="txmt://open?url=file://<%=file = (line.match(/^([^:]+)/)[1] rescue 'unknown')%>&amp;line=<%= lineno = line.match(/:([0-9]+):/)[1] rescue '?' %>"><%=lineno%></a>&nbsp;
+ </td>
+ </tr>
+ <tr class="source">
+ <td class="collapse">
+ </td>
+ <td class="code" colspan="2"><% (__caller_lines__(file, lineno, 5) rescue []).each do |llineno, lcode, lcurrent| %>
+<a href="txmt://open?url=file://<%=file%>&amp;line=<%=llineno%>"><%= llineno %></a><%='<em>' if llineno==lineno.to_i %><%= lcode.size > 90 ? CGI.escapeHTML(lcode[0..90])+'<span class="more">......</span>' : CGI.escapeHTML(lcode) %><%='</em>' if llineno==lineno.to_i %>
+<% end %>
+
+</td>
+ </tr>
+ </tbody>
+ <% end %>
+ </table>
+ <script type="text/javascript" charset="utf-8">
+ // swop the open & closed classes
+ els = document.getElementsByTagName('td');
+ for(i=0; i<els.length; i++){
+ if(els[i].className=='expand' || els[i].className=='collapse'){
+ els[i].onclick = function(e){
+ tbody = this.parentNode.parentNode;
+ if(tbody.className=='open'){
+ tbody.className='closed';
+ }else{
+ tbody.className='open';
+ }
+ }
+ }
+ }
+ </script>
+ <% end %>
+ <div class="footer">
+ lots of love, from <a href="#">merb</a>
+ </div>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/lib/chef_server/views/exceptions/not_acceptable.html.erb b/lib/chef_server/views/exceptions/not_acceptable.html.erb
new file mode 100644
index 0000000000..f632712bb2
--- /dev/null
+++ b/lib/chef_server/views/exceptions/not_acceptable.html.erb
@@ -0,0 +1,63 @@
+<div id="container">
+ <div id="header-container">
+ <img src="/images/merb.jpg" />
+ <!-- <h1>Mongrel + Erb</h1> -->
+ <h2>pocket rocket web framework</h2>
+ <hr />
+ </div>
+
+ <div id="left-container">
+ <h3>Exception:</h3>
+ <p><%= params[:exception] %></p>
+ </div>
+
+ <div id="main-container">
+ <h3>Why am I seeing this page?</h3>
+ <p>Merb couldn't find an appropriate content_type to return,
+ based on what you said was available via provides() and
+ what the client requested.</p>
+
+ <h3>How to add a mime-type</h3>
+ <pre><code>
+ Merb.add_mime_type :pdf, :to_pdf, %w[application/pdf], &quot;Content-Encoding&quot; =&gt; &quot;gzip&quot;
+ </code></pre>
+ <h3>What this means is:</h3>
+ <ul>
+ <li>Add a mime-type for :pdf</li>
+ <li>Register the method for converting objects to PDF as <code>#to_pdf</code>.</li>
+ <li>Register the incoming mime-type "Accept" header as <code>application/pdf</code>.</li>
+ <li>Specify a new header for PDF types so it will set <code>Content-Encoding</code> to gzip.</li>
+ </ul>
+
+ <h3>You can then do:</h3>
+ <pre><code>
+ class Foo &lt; Application
+ provides :pdf
+ end
+ </code></pre>
+
+ <h3>Where can I find help?</h3>
+ <p>If you have any questions or if you can't figure something out, please take a
+ look at our <a href="http://merbivore.com/"> project page</a>,
+ feel free to come chat at irc.freenode.net, channel #merb,
+ or post to <a href="http://groups.google.com/group/merb">merb mailing list</a>
+ on Google Groups.</p>
+
+ <h3>What if I've found a bug?</h3>
+ <p>If you want to file a bug or make your own contribution to Merb,
+ feel free to register and create a ticket at our
+ <a href="http://merb.lighthouseapp.com/">project development page</a>
+ on Lighthouse.</p>
+
+ <h3>How do I edit this page?</h3>
+ <p>You can change what people see when this happens by editing <tt>app/views/exceptions/not_acceptable.html.erb</tt>.</p>
+
+ </div>
+
+ <div id="footer-container">
+ <hr />
+ <div class="left"></div>
+ <div class="right">&copy; 2007 the merb dev team</div>
+ <p>&nbsp;</p>
+ </div>
+</div>
diff --git a/lib/chef_server/views/exceptions/not_found.html.erb b/lib/chef_server/views/exceptions/not_found.html.erb
new file mode 100644
index 0000000000..388c72c31d
--- /dev/null
+++ b/lib/chef_server/views/exceptions/not_found.html.erb
@@ -0,0 +1,47 @@
+<div id="container">
+ <div id="header-container">
+ <img src="/images/merb.jpg" />
+ <!-- <h1>Mongrel + Erb</h1> -->
+ <h2>pocket rocket web framework</h2>
+ <hr />
+ </div>
+
+ <div id="left-container">
+ <h3>Exception:</h3>
+ <p><%= params[:exception] %></p>
+ </div>
+
+ <div id="main-container">
+ <h3>Welcome to Merb!</h3>
+ <p>Merb is a light-weight MVC framework written in Ruby. We hope you enjoy it.</p>
+
+ <h3>Where can I find help?</h3>
+ <p>If you have any questions or if you can't figure something out, please take a
+ look at our <a href="http://merbivore.com/"> project page</a>,
+ feel free to come chat at irc.freenode.net, channel #merb,
+ or post to <a href="http://groups.google.com/group/merb">merb mailing list</a>
+ on Google Groups.</p>
+
+ <h3>What if I've found a bug?</h3>
+ <p>If you want to file a bug or make your own contribution to Merb,
+ feel free to register and create a ticket at our
+ <a href="http://merb.lighthouseapp.com/">project development page</a>
+ on Lighthouse.</p>
+
+ <h3>How do I edit this page?</h3>
+ <p>You're seeing this page because you need to edit the following files:
+ <ul>
+ <li>config/router.rb <strong><em>(recommended)</em></strong></li>
+ <li>app/views/exceptions/not_found.html.erb <strong><em>(recommended)</em></strong></li>
+ <li>app/views/layout/application.html.erb <strong><em>(change this layout)</em></strong></li>
+ </ul>
+ </p>
+ </div>
+
+ <div id="footer-container">
+ <hr />
+ <div class="left"></div>
+ <div class="right">&copy; 2007 the merb dev team</div>
+ <p>&nbsp;</p>
+ </div>
+</div>
diff --git a/lib/chef_server/views/layout/application.html.haml b/lib/chef_server/views/layout/application.html.haml
new file mode 100644
index 0000000000..f04f299d2c
--- /dev/null
+++ b/lib/chef_server/views/layout/application.html.haml
@@ -0,0 +1,22 @@
+!!! XML
+!!!
+%html
+ %head
+ %title Chef Server
+ %meta{"http-equiv" => "content-type", :content => "text/html; charset=utf-8" }
+ %link{:rel => "stylesheet", :href => "/stylesheets/master.css", :type => "text/css", :media => "screen", :charset => "utf-8" }
+ %body
+ .header
+ %a{:href => url(:nodes) } Nodes
+ |
+ %a{:href => url(:registrations)} Registrations
+ - if session[:openid]
+ |
+ %a{:href => url(:openid_consumer_logout)}= "Logout #{h session[:openid]}"
+ = "(#{session[:level].to_s})"
+ - else
+ |
+ %a{:href => url(:openid_consumer)} Login
+ = catch_content :for_layout
+
+ \ No newline at end of file
diff --git a/lib/chef_server/views/nodes/_action.html.haml b/lib/chef_server/views/nodes/_action.html.haml
new file mode 100644
index 0000000000..a809a6d70c
--- /dev/null
+++ b/lib/chef_server/views/nodes/_action.html.haml
@@ -0,0 +1,13 @@
+%table
+ - actions.each do |action, resource_hash|
+ %tr
+ %td.action_name= action.to_s
+ %td.action_resources
+ %table
+ - resource_hash.keys.sort{ |a,b| a.to_s <=> b.to_s }.each do |rk|
+ %tr
+ %td.action_when= rk.to_s
+ %td
+ - resource_hash[rk].each do |resource|
+ = partial(:resource, :resource => resource)
+ \ No newline at end of file
diff --git a/lib/chef_server/views/nodes/_node.html.haml b/lib/chef_server/views/nodes/_node.html.haml
new file mode 100644
index 0000000000..7dce36488f
--- /dev/null
+++ b/lib/chef_server/views/nodes/_node.html.haml
@@ -0,0 +1,11 @@
+.node
+ %h1
+ = "Node #{h node.name}"
+ %h2 Actions
+ %a{ :href => url(:compile_node, { :id => node_escape(node.name) }) } Compile Node
+ %h2 Recipes
+ %ol
+ = recipe_list(node)
+ %h2 Attributes
+ %ol
+ = attribute_list(node)
diff --git a/lib/chef_server/views/nodes/_resource.html.haml b/lib/chef_server/views/nodes/_resource.html.haml
new file mode 100644
index 0000000000..7b9776b816
--- /dev/null
+++ b/lib/chef_server/views/nodes/_resource.html.haml
@@ -0,0 +1,22 @@
+.resource
+ %h3= "#{h resource.to_s} (#{resource.class})"
+ %table
+ - resource.instance_variables.sort.each do |v|
+ - attr_name = v.gsub(/\@/, "")
+ - unless attr_name == "collection"
+ %tr.attr_group
+ %td.attr_name
+ = "#{h attr_name}"
+ %td.attr_value
+ - value = resource.instance_variable_get(v)
+ - if value.kind_of?(String)
+ = "#{h value}"
+ - elsif value.kind_of?(Array)
+ = "#{h value.join(", ")}"
+ - elsif value.kind_of?(Symbol)
+ = "#{h value.to_s}"
+ - elsif attr_name == "actions"
+ = partial(:action, :actions => value)
+ - else
+ = "#{h value.inspect}"
+
diff --git a/lib/chef_server/views/nodes/compile.html.haml b/lib/chef_server/views/nodes/compile.html.haml
new file mode 100644
index 0000000000..5656447ea8
--- /dev/null
+++ b/lib/chef_server/views/nodes/compile.html.haml
@@ -0,0 +1,5 @@
+= partial(:node, :node => @output[:node])
+.resource_collection
+ %h1 Resource Collection
+ - @output[:collection].each do |resource|
+ = partial(:resource, :resource => resource)
diff --git a/lib/chef_server/views/nodes/index.html.haml b/lib/chef_server/views/nodes/index.html.haml
new file mode 100644
index 0000000000..667e1c7317
--- /dev/null
+++ b/lib/chef_server/views/nodes/index.html.haml
@@ -0,0 +1,9 @@
+%h1 Node List
+- @node_list.each do |node|
+ .node
+ %a{ :href => url(:node, { :id => node.gsub(/\./, "_") }) }
+ = node
+ %form{ :method => "post", :action => url(:node, { :id => node.gsub(/\./, "_") })}
+ %input{ :type => "hidden", :name => "_method", :value => "delete" }
+ %input{ :type => "submit", :name => "Delete", :value => "Delete" }
+ \ No newline at end of file
diff --git a/lib/chef_server/views/nodes/show.html.haml b/lib/chef_server/views/nodes/show.html.haml
new file mode 100644
index 0000000000..94708af6d3
--- /dev/null
+++ b/lib/chef_server/views/nodes/show.html.haml
@@ -0,0 +1 @@
+= partial(:node, :node => @node) \ No newline at end of file
diff --git a/lib/chef_server/views/openid_consumer/index.html.haml b/lib/chef_server/views/openid_consumer/index.html.haml
new file mode 100644
index 0000000000..576ef8d787
--- /dev/null
+++ b/lib/chef_server/views/openid_consumer/index.html.haml
@@ -0,0 +1,25 @@
+%h1 OpenID Relying Party
+-if session[:alert]
+ .alert= h session[:alert]
+-if session[:error]
+ .error= h session[:error]
+-if session[:success]
+ .success= h session[:success]
+#verify-form
+ %form{ :method => "get", "accept-charset" => "UTF-8", :action => url(:openid_consumer_start) }
+ Identifier:
+ %input.openid{ :type => "text", :name => "openid_identifier" }/
+ %input{ :type => "submit", :value => "Verify"}/
+ %br
+ %input#immediate{ :name => "immediate", :type => "checkbox" }/
+ %label{:for => "immediate"} Use immediate mode
+ %br
+ %input#immediate{ :name => "use_sreg", :type => "checkbox" }/
+ %label{:for => "use_sreg"} Request registration data
+ %br
+ %input#immediate{ :name => "use_pape", :type => "checkbox" }/
+ %label{:for => "use_pape"} Request phishing-resistent auth policy
+ %br
+ %input#immediate{ :name => "force_post", :type => "checkbox" }/
+ %label{:for => "force_post"} Force the transaction to POST
+ %br
diff --git a/lib/chef_server/views/openid_consumer/start.html.haml b/lib/chef_server/views/openid_consumer/start.html.haml
new file mode 100644
index 0000000000..75ed9a9257
--- /dev/null
+++ b/lib/chef_server/views/openid_consumer/start.html.haml
@@ -0,0 +1,4 @@
+<%= @form_text %>
+<script type="text/javascript">
+document.getElementById('openid_form').submit();
+</script> \ No newline at end of file
diff --git a/lib/chef_server/views/openid_login/index.html.haml b/lib/chef_server/views/openid_login/index.html.haml
new file mode 100644
index 0000000000..17ae69017a
--- /dev/null
+++ b/lib/chef_server/views/openid_login/index.html.haml
@@ -0,0 +1,6 @@
+%h1 Login
+#login-form
+ %form{ :method => "get", :action => url(:openid_login_submit) }
+ %input{ :type => "text", :name => "username" }/
+ %input{ :type => "submit", :value => "Log In"}/
+ \ No newline at end of file
diff --git a/lib/chef_server/views/openid_register/index.html.haml b/lib/chef_server/views/openid_register/index.html.haml
new file mode 100644
index 0000000000..fe4798c59d
--- /dev/null
+++ b/lib/chef_server/views/openid_register/index.html.haml
@@ -0,0 +1,15 @@
+%h1 Registered OpenID Nodes List
+%table
+- @registered_nodes.each do |node|
+ %tr
+ %td
+ %a{ :href => url(:registration, { :id => node.name }) }
+ = h node.name
+ %td
+ - if session[:level] == :admin
+ %form{ :method => "post", :action => url(:validate_registration, { :id => node.name })}
+ - submit_name = node.validated ? "Invalidate" : "Validate"
+ %input{ :type => "submit", :name => submit_name, :value => submit_name }
+ %form{ :method => "post", :action => url(:registration, { :id => node.name })}
+ %input{ :type => "hidden", :name => "_method", :value => "delete" }
+ %input{ :type => "submit", :name => "Delete", :value => "Delete" }
diff --git a/lib/chef_server/views/openid_register/show.html.haml b/lib/chef_server/views/openid_register/show.html.haml
new file mode 100644
index 0000000000..cfd38e8963
--- /dev/null
+++ b/lib/chef_server/views/openid_register/show.html.haml
@@ -0,0 +1,5 @@
+%h1= "Registered OpenID Node #{@registered_node.name}"
+%ol
+ %li
+ %a{ :href => url(:openid_node , { :id => @registered_node.name.gsub(/\./, "_") }) } OpenID URL
+ %li= "Validated: #{@registered_node.validated}"
diff --git a/lib/chef_server/views/openid_server/decide.html.haml b/lib/chef_server/views/openid_server/decide.html.haml
new file mode 100644
index 0000000000..8082a5068d
--- /dev/null
+++ b/lib/chef_server/views/openid_server/decide.html.haml
@@ -0,0 +1,27 @@
+%form{:method => "post", :action => url(:openid_server_decision)}
+ %table
+ %tr
+ %td Site:
+ %td= @oidreq.trust_root
+ - if @oidreq.id_select
+ %tr
+ %td{:colspan => "2"}
+ You entered the server identifier at the relying party.
+ You will need to send an identifier of your choosing.
+ Enter a username and password below.
+ %tr
+ %td Identity to send:
+ %td
+ %input{:type => "text", :name => "id_to_send", :size => "25"}
+ - else
+ %tr
+ %td Identity:
+ %td= @oidreq.identity
+ %tr
+ %td
+ %label{:for => "password"} Password:
+ %td
+ %input{:type => "password", :name => "password"}
+ %input{:type => "submit", :name => "submit", :value => "Authenticate"}
+ %input{:type => "submit", :name => "cancel", :value => "Cancel"}
+
diff --git a/log/chef-server.log b/log/chef-server.log
new file mode 100644
index 0000000000..eabfde15a0
--- /dev/null
+++ b/log/chef-server.log
@@ -0,0 +1,9 @@
+Mon, 16 Jun 2008 01:13:56 GMT ~ info ~ Logfile created
+ ~ Compiling routes...
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Compiling routes...
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
+ ~ Using 'share-nothing' cookie sessions (4kb limit per client)
+ ~ Using Mongrel adapter
diff --git a/log/merb_test.log b/log/merb_test.log
new file mode 100644
index 0000000000..dae0e5d9dd
--- /dev/null
+++ b/log/merb_test.log
@@ -0,0 +1,7 @@
+Tue, 03 Jun 2008 04:33:52 GMT ~ info ~ Logfile created
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
diff --git a/spec/chef_server/controllers/log/merb_test.log b/spec/chef_server/controllers/log/merb_test.log
new file mode 100644
index 0000000000..86fbe4b9cb
--- /dev/null
+++ b/spec/chef_server/controllers/log/merb_test.log
@@ -0,0 +1,4 @@
+Tue, 27 May 2008 00:52:34 GMT ~ info ~ Logfile created
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
diff --git a/spec/chef_server/controllers/nodes_spec.rb b/spec/chef_server/controllers/nodes_spec.rb
new file mode 100644
index 0000000000..a86fa4138c
--- /dev/null
+++ b/spec/chef_server/controllers/nodes_spec.rb
@@ -0,0 +1,214 @@
+require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
+
+describe Nodes, "index action" do
+ it "should get a list of all the nodes" do
+ Chef::Node.should_receive(:list).and_return(["one"])
+ dispatch_to(Nodes, :index) do |c|
+ c.stub!(:display)
+ end
+ end
+
+ it "should send a list of nodes to display" do
+ Chef::Node.stub!(:list).and_return(["one"])
+ dispatch_to(Nodes, :index) do |c|
+ c.should_receive(:display).with(["one"])
+ end
+ end
+end
+
+describe Nodes, "show action" do
+ it "should load a node from the filestore based on the id" do
+ node = stub("Node", :null_object => true)
+ Chef::Node.should_receive(:load).with("bond").once.and_return(node)
+ dispatch_to(Nodes, :show, { :id => "bond" }) do |c|
+ c.should_receive(:display).with(node).once.and_return(true)
+ end
+ end
+
+ it "should return 200 on a well formed request" do
+ node = stub("Node", :null_object => true)
+ Chef::Node.should_receive(:load).with("bond").once.and_return(node)
+ controller = dispatch_to(Nodes, :show, { :id => "bond" }) do |c|
+ c.stub!(:display)
+ end
+ controller.status.should eql(200)
+ end
+
+ it "should raise a BadRequest if the id is not found" do
+ Chef::Node.should_receive(:load).with("bond").once.and_raise(RuntimeError)
+ lambda {
+ dispatch_to(Nodes, :show, { :id => "bond" })
+ }.should raise_error(Merb::ControllerExceptions::BadRequest)
+ end
+end
+
+describe Nodes, "create action" do
+ it "should create a node from an inflated object" do
+ mnode = mock("Node", :null_object => true)
+ mnode.stub!(:name).and_return("bond")
+ mnode.should_receive(:save).once.and_return(true)
+ controller = dispatch_to(Nodes, :create) do |c|
+ c.stub!(:params).and_return({ "inflated_object" => mnode })
+ c.stub!(:session).and_return({
+ :openid => 'http://localhost/openid/server/node/bond',
+ :level => :node,
+ :node_name => "bond",
+ })
+ c.stub!(:display)
+ end
+ controller.status.should eql(202)
+ end
+
+ it "should raise an exception if it cannot inflate an object" do
+ lambda {
+ dispatch_to(Nodes, :create) do |c|
+ c.stub!(:params).and_return({ })
+ end
+ }.should raise_error(Merb::Controller::BadRequest)
+ end
+end
+
+describe Nodes, "update action" do
+ it "should update a node from an inflated object" do
+ mnode = mock("Node", :null_object => true)
+ mnode.stub!(:name).and_return("one")
+ Chef::FileStore.should_receive(:store).with("node", "one", mnode).once.and_return(true)
+ controller = dispatch_to(Nodes, :update, { :id => "one" }) do |c|
+ c.stub!(:session).and_return({
+ :openid => 'http://localhost/openid/server/node/one',
+ :level => :node,
+ :node_name => "one",
+ })
+ c.stub!(:params).and_return({ "inflated_object" => mnode })
+ c.stub!(:display)
+ end
+ controller.status.should eql(202)
+ end
+
+ it "should raise an exception if it cannot inflate an object" do
+ lambda { dispatch_to(Nodes, :update) }.should raise_error(Merb::Controller::BadRequest)
+ end
+end
+
+describe Nodes, "destroy action" do
+ def do_destroy
+ dispatch_to(Nodes, :destroy, { :id => "one" }) do |c|
+ c.stub!(:display)
+ end
+ end
+
+ it "should load the node it's about to destroy from the filestore" do
+ mnode = stub("Node", :null_object => true)
+ Chef::FileStore.should_receive(:load).with("node", "one").once.and_return(mnode)
+ Chef::FileStore.stub!(:delete)
+ do_destroy
+ end
+
+ it "should raise an exception if it cannot find the node to destroy" do
+ Chef::FileStore.should_receive(:load).with("node", "one").once.and_raise(RuntimeError)
+ lambda { do_destroy }.should raise_error(Merb::Controller::BadRequest)
+ end
+
+ it "should remove the node from the filestore" do
+ mnode = stub("Node", :null_object => true)
+ Chef::FileStore.stub!(:load).with("node", "one").and_return(mnode)
+ Chef::FileStore.should_receive(:delete).with("node", "one")
+ do_destroy
+ end
+
+ it "should remove the node from the search index" do
+ mnode = stub("Node", :null_object => true)
+ Chef::FileStore.stub!(:load).with("node", "one").and_return(mnode)
+ Chef::FileStore.stub!(:delete)
+ do_destroy
+ end
+
+ it "should return the node it just deleted" do
+ mnode = stub("Node", :null_object => true)
+ Chef::FileStore.stub!(:load).with("node", "one").and_return(mnode)
+ Chef::FileStore.stub!(:delete)
+ dispatch_to(Nodes, :destroy, { :id => "one" }) do |c|
+ c.should_receive(:display).once.with(mnode)
+ end
+ end
+
+ it "should return a status of 202" do
+ mnode = stub("Node", :null_object => true)
+ Chef::FileStore.stub!(:load).with("node", "one").and_return(mnode)
+ Chef::FileStore.stub!(:delete)
+ controller = do_destroy
+ controller.status.should eql(202)
+ end
+end
+
+describe Nodes, "compile action" do
+ before(:each) do
+ @compile = stub("Compile", :null_object => true)
+ @node = stub("Node", :null_object => true)
+ @node.stub!(:[]).and_return(true)
+ @node.stub!(:[]=).and_return(true)
+ @node.stub!(:recipes).and_return([])
+ @compile.stub!(:load_definitions).and_return(true)
+ @compile.stub!(:load_recipes).and_return(true)
+ @compile.stub!(:collection).and_return([])
+ @compile.stub!(:node, @node)
+ @compile.stub!(:load_node).and_return(true)
+ @stored_node = stub("Stored Node", :null_object => true)
+ end
+
+ def do_compile
+ Chef::FileStore.stub!(:store).and_return(true)
+ Chef::FileStore.stub!(:load).and_return(@stored_node)
+ Chef::Compile.stub!(:new).and_return(@compile)
+ dispatch_to(Nodes, :compile, { :id => "one" }) do |c|
+ c.stub!(:display)
+ end
+ end
+
+ it "should load the node from the node resource" do
+ @compile.should_receive(:load_node).with("one").and_return(true)
+ do_compile
+ end
+
+ it "should merge the data with the currently stored node" do
+ node1 = Chef::Node.new
+ node1.name "adam"
+ node1.music "crowe"
+ node1.recipes << "monkey"
+ @compile.stub!(:node).and_return(node1)
+ @stored_node = Chef::Node.new
+ @stored_node.name "adam"
+ @stored_node.music "crown"
+ @stored_node.woot "woot"
+ @stored_node.recipes << "monkeysoup"
+ do_compile
+ node1.name.should eql("adam")
+ node1.music.should eql("crown")
+ node1.woot.should eql("woot")
+ node1.recipes.should eql([ "monkey", "monkeysoup" ])
+ end
+
+ it "should load definitions" do
+ @compile.should_receive(:load_definitions)
+ do_compile
+ end
+
+ it "should load recipes" do
+ @compile.should_receive(:load_recipes)
+ do_compile
+ end
+
+ it "should display the collection and node object" do
+ Chef::FileStore.stub!(:load).and_return(@stored_node)
+ Chef::Compile.stub!(:new).and_return(@compile)
+ dispatch_to(Nodes, :compile, { :id => "one" }) do |c|
+ c.should_receive(:display).with({ :collection => [], :node => nil })
+ end
+ end
+
+ it "should return 200" do
+ controller = do_compile
+ controller.status.should eql(200)
+ end
+
+end \ No newline at end of file
diff --git a/spec/chef_server/controllers/openid_consumer_spec.rb b/spec/chef_server/controllers/openid_consumer_spec.rb
new file mode 100644
index 0000000000..a93cad4191
--- /dev/null
+++ b/spec/chef_server/controllers/openid_consumer_spec.rb
@@ -0,0 +1,29 @@
+require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
+
+describe OpenidConsumer, "check_valid_openid_provider method" do
+ it "should confirm the openid provider if the openid_providers config is nil" do
+ Chef::Config[:openid_providers] = nil
+ c = OpenidConsumer.new(true)
+ c.send(:check_valid_openid_provider, "monkeyid").should eql(true)
+ end
+
+ it "should return true if the openid provider is in openid_providers list" do
+ Chef::Config[:openid_providers] = [ 'monkeyid' ]
+ c = OpenidConsumer.new(true)
+ c.send(:check_valid_openid_provider, "monkeyid").should eql(true)
+ end
+
+ it "should return true if the openid provider is in openid_providers list with http://" do
+ Chef::Config[:openid_providers] = [ 'monkeyid' ]
+ c = OpenidConsumer.new(true)
+ c.send(:check_valid_openid_provider, "http://monkeyid").should eql(true)
+ end
+
+ it "should raise an exception if the openid provider is not in openid_providers list" do
+ Chef::Config[:openid_providers] = [ 'monkeyid' ]
+ c = OpenidConsumer.new(true)
+ lambda {
+ c.send(:check_valid_openid_provider, "monkey")
+ }.should raise_error(Merb::Controller::Unauthorized)
+ end
+end \ No newline at end of file
diff --git a/spec/chef_server/controllers/openid_register_spec.rb b/spec/chef_server/controllers/openid_register_spec.rb
new file mode 100644
index 0000000000..6822722868
--- /dev/null
+++ b/spec/chef_server/controllers/openid_register_spec.rb
@@ -0,0 +1,93 @@
+require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')
+
+describe OpenidRegister, "index action" do
+ it "should get a list of all registered nodes" do
+ Chef::OpenIDRegistration.should_receive(:list).with(true).and_return(["one"])
+ dispatch_to(OpenidRegister, :index) do |c|
+ c.stub!(:display)
+ end
+ end
+end
+
+describe OpenidRegister, "show action" do
+ it "should raise a 404 if the nodes registration is not found" do
+ Chef::OpenIDRegistration.should_receive(:load).with("foo").and_raise(RuntimeError)
+ lambda {
+ dispatch_to(OpenidRegister, :show, { :id => "foo" })
+ }.should raise_error(Merb::ControllerExceptions::NotFound)
+ end
+
+ it "should call display on the node registration" do
+ Chef::OpenIDRegistration.stub!(:load).and_return(true)
+ dispatch_to(OpenidRegister, :show, { :id => "foo" }) do |c|
+ c.should_receive(:display).with(true)
+ end
+ end
+end
+
+describe OpenidRegister, "create action" do
+ def do_create
+ dispatch_to(OpenidRegister, :create, { :id => "foo", :password => "beck" }) do |c|
+ c.stub!(:display)
+ end
+ end
+
+ it "should require an id to register" do
+ lambda {
+ dispatch_to(OpenidRegister, :create, { :password => "beck" })
+ }.should raise_error(Merb::ControllerExceptions::BadRequest)
+ end
+
+ it "should require a password to register" do
+ lambda {
+ dispatch_to(OpenidRegister, :create, { :id => "foo" })
+ }.should raise_error(Merb::ControllerExceptions::BadRequest)
+ end
+
+ it "should return 400 if a node is already registered" do
+ Chef::OpenIDRegistration.should_receive(:has_key?).with("foo").and_return(true)
+ lambda {
+ dispatch_to(OpenidRegister, :create, { :id => "foo", :password => "beck" })
+ }.should raise_error(Merb::ControllerExceptions::BadRequest)
+ end
+
+ it "should store the registration in a new Chef::OpenIDRegistration" do
+ mock_reg = mock("Chef::OpenIDRegistration", :null_object => true)
+ mock_reg.should_receive(:name=).with("foo").and_return(true)
+ mock_reg.should_receive(:set_password).with("beck").and_return(true)
+ mock_reg.should_receive(:save).and_return(true)
+ Chef::OpenIDRegistration.stub!(:has_key?).and_return(false)
+ Chef::OpenIDRegistration.should_receive(:new).and_return(mock_reg)
+ do_create
+ end
+end
+
+describe OpenidRegister, "update action" do
+ it "should raise a 400 error" do
+ lambda {
+ dispatch_to(OpenidRegister, :update)
+ }
+ end
+end
+
+describe OpenidRegister, "destroy action" do
+ def do_destroy
+ dispatch_to(OpenidRegister, :destroy, { :id => "foo" }) do |c|
+ c.stub!(:display)
+ end
+ end
+
+ it "should return 400 if it cannot find the registration" do
+ Chef::OpenIDRegistration.should_receive(:load).and_raise(ArgumentError)
+ lambda {
+ do_destroy
+ }.should raise_error(Merb::ControllerExceptions::BadRequest)
+ end
+
+ it "should delete the registration from the store" do
+ mock_reg = mock("OpenIDRegistration")
+ mock_reg.should_receive(:destroy).and_return(true)
+ Chef::OpenIDRegistration.should_receive(:load).with("foo").and_return(mock_reg)
+ do_destroy
+ end
+end \ No newline at end of file
diff --git a/spec/chef_server/log/merb_test.log b/spec/chef_server/log/merb_test.log
new file mode 100644
index 0000000000..b2aa15df9a
--- /dev/null
+++ b/spec/chef_server/log/merb_test.log
@@ -0,0 +1,4 @@
+Mon, 16 Jun 2008 02:08:02 GMT ~ info ~ Logfile created
+ ~ Not Using Sessions
+ ~ Not Using Sessions
+ ~ Not Using Sessions
diff --git a/spec/chef_server/spec.opts b/spec/chef_server/spec.opts
new file mode 100644
index 0000000000..3a2ff550a2
--- /dev/null
+++ b/spec/chef_server/spec.opts
@@ -0,0 +1,4 @@
+--color
+--loadby
+mtime
+
diff --git a/spec/chef_server/spec_helper.rb b/spec/chef_server/spec_helper.rb
new file mode 100644
index 0000000000..90c1e0af7a
--- /dev/null
+++ b/spec/chef_server/spec_helper.rb
@@ -0,0 +1,16 @@
+require 'rubygems'
+require 'merb-core'
+require 'spec' # Satiates Autotest and anyone else not using the Rake tasks
+
+Merb.start_environment(
+ :testing => true,
+ :adapter => 'runner',
+ :environment => ENV['MERB_ENV'] || 'test',
+ :init_file => File.join(File.dirname(__FILE__), "..", "..", "lib", "chef_server", "init.rb")
+)
+
+Spec::Runner.configure do |config|
+ config.include(Merb::Test::ViewHelper)
+ config.include(Merb::Test::RouteHelper)
+ config.include(Merb::Test::ControllerHelper)
+end
diff --git a/spec/unit/compile_spec.rb b/spec/unit/compile_spec.rb
index d042837606..3ae69dad2d 100644
--- a/spec/unit/compile_spec.rb
+++ b/spec/unit/compile_spec.rb
@@ -19,6 +19,7 @@
#
require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper"))
+
describe Chef::Compile do
before(:each) do
Chef::Config.node_path(File.join(File.dirname(__FILE__), "..", "data", "compile", "nodes"))
diff --git a/stories/chef-client b/stories/chef-client
new file mode 100644
index 0000000000..93b8656268
--- /dev/null
+++ b/stories/chef-client
@@ -0,0 +1,26 @@
+Story: Applying a meal with chef-client
+
+ As a systems administrator
+ I want to configure a system
+ So that I can drink more beer
+
+ Scenario: The client should be rejected if the node is not verified
+ Given the node 'latte'
+ When it runs the chef-client
+ Then it should register with the Chef Server
+
+ Scenario: The node registers for an OpenID
+
+ Given the node 'latte'
+ When it runs the chef-client
+ Then it should register with the Chef Server
+ And CouchDB should have a 'openid_registration_latte' document
+
+ Scenario: An administrator validates the OpenID Registration
+
+ Given the user 'openid.hjksolutions.com/adam'
+ And the openid registration for 'latte'
+ When the user validates the registration
+ Then the registration validation should be true
+
+
diff --git a/stories/chef-client.rb b/stories/chef-client.rb
new file mode 100644
index 0000000000..8796559c86
--- /dev/null
+++ b/stories/chef-client.rb
@@ -0,0 +1,48 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "story_helper"))
+
+steps_for(:chef_client) do
+ # Given the node 'latte'
+ Given("the node '$node'") do |node|
+ @client = Chef::Client.new
+ @client.build_node(node)
+ end
+
+ # Given it has not registered before
+ Given("it has not registered before") do
+ Chef::FileStore.load("registration", @client.safe_name)
+ end
+
+ # When it runs the chef-client
+
+ # Then it should register with the Chef Server
+
+ # Then CouchDB should have a 'openid_registration_latte' document
+
+ # Then the registration validation should be 'false'
+
+end
+
+with_steps_for(:chef_client) do
+ create_couchdb_database
+ run File.join(File.dirname(__FILE__), "chef-client")
+end \ No newline at end of file
diff --git a/stories/story_helper.rb b/stories/story_helper.rb
new file mode 100644
index 0000000000..1c880d5a53
--- /dev/null
+++ b/stories/story_helper.rb
@@ -0,0 +1,33 @@
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: GNU General Public License version 2 or later
+#
+# This program and entire repository is free software; you can
+# redistribute it and/or modify it under the terms of the GNU
+# General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+require 'rubygems'
+require 'spec/story'
+
+require File.join(File.dirname(__FILE__), "..", "lib", "chef")
+Dir[File.join(File.dirname(__FILE__), 'lib', '**', '*.rb')].sort.each { |lib| require lib }
+
+Chef::Config.log_level(:fatal)
+Chef::Config[:couchdb_database] = "chef_test"
+
+def create_couchdb_database
+ c = Chef::CouchDB.new
+ c.create_db
+end
+