summaryrefslogtreecommitdiff
path: root/chef-server
diff options
context:
space:
mode:
authorEzra Zygmuntowicz <ez@engineyard.com>2008-10-08 14:19:52 -0700
committerEzra Zygmuntowicz <ez@engineyard.com>2008-10-08 14:19:52 -0700
commitc5d33c1298834ce40b8fbf344f281045771b5371 (patch)
tree1f0d4c7eab1eb379b544282a7ce48052acf719a5 /chef-server
parent3d14601aea23dee3899d097324875274da419d84 (diff)
downloadchef-c5d33c1298834ce40b8fbf344f281045771b5371.tar.gz
big refactor of the repo layout. move to a chef gem and a chef-server gem all with proper deps
Diffstat (limited to 'chef-server')
-rw-r--r--chef-server/LICENSE201
-rw-r--r--chef-server/NOTICE10
-rw-r--r--chef-server/README.txt77
-rw-r--r--chef-server/Rakefile47
-rwxr-xr-xchef-server/bin/chef-indexer85
-rwxr-xr-xchef-server/bin/chef-server48
-rw-r--r--chef-server/lib/controllers/application.rb150
-rw-r--r--chef-server/lib/controllers/cookbook_attributes.rb55
-rw-r--r--chef-server/lib/controllers/cookbook_definitions.rb56
-rw-r--r--chef-server/lib/controllers/cookbook_files.rb117
-rw-r--r--chef-server/lib/controllers/cookbook_recipes.rb56
-rw-r--r--chef-server/lib/controllers/cookbook_templates.rb77
-rw-r--r--chef-server/lib/controllers/cookbooks.rb50
-rw-r--r--chef-server/lib/controllers/exceptions.rb38
-rw-r--r--chef-server/lib/controllers/nodes.rb78
-rw-r--r--chef-server/lib/controllers/openid_consumer.rb117
-rw-r--r--chef-server/lib/controllers/openid_register.rb89
-rw-r--r--chef-server/lib/controllers/openid_server.rb236
-rw-r--r--chef-server/lib/controllers/search.rb71
-rw-r--r--chef-server/lib/controllers/search_entries.rb68
-rw-r--r--chef-server/lib/helpers/global_helpers.rb35
-rw-r--r--chef-server/lib/helpers/nodes_helper.rb37
-rw-r--r--chef-server/lib/helpers/openid_server_helpers.rb27
-rw-r--r--chef-server/lib/init.rb206
-rw-r--r--chef-server/lib/public/images/merb.jpgbin0 -> 5815 bytes
-rw-r--r--chef-server/lib/public/stylesheets/master.css292
-rw-r--r--chef-server/lib/views/cookbook_templates/index.html.haml8
-rw-r--r--chef-server/lib/views/cookbooks/_attribute_file.html.haml2
-rw-r--r--chef-server/lib/views/cookbooks/attribute_files.html.haml4
-rw-r--r--chef-server/lib/views/cookbooks/index.html.haml7
-rw-r--r--chef-server/lib/views/cookbooks/show.html.haml24
-rw-r--r--chef-server/lib/views/exceptions/bad_request.json.erb1
-rw-r--r--chef-server/lib/views/exceptions/internal_server_error.html.erb216
-rw-r--r--chef-server/lib/views/exceptions/not_acceptable.html.erb63
-rw-r--r--chef-server/lib/views/exceptions/not_found.html.erb47
-rw-r--r--chef-server/lib/views/layout/application.html.haml26
-rw-r--r--chef-server/lib/views/nodes/_action.html.haml13
-rw-r--r--chef-server/lib/views/nodes/_node.html.haml9
-rw-r--r--chef-server/lib/views/nodes/_resource.html.haml22
-rw-r--r--chef-server/lib/views/nodes/compile.html.haml5
-rw-r--r--chef-server/lib/views/nodes/index.html.haml9
-rw-r--r--chef-server/lib/views/nodes/show.html.haml1
-rw-r--r--chef-server/lib/views/openid_consumer/index.html.haml25
-rw-r--r--chef-server/lib/views/openid_consumer/start.html.haml4
-rw-r--r--chef-server/lib/views/openid_login/index.html.haml6
-rw-r--r--chef-server/lib/views/openid_register/index.html.haml15
-rw-r--r--chef-server/lib/views/openid_register/show.html.haml5
-rw-r--r--chef-server/lib/views/openid_server/decide.html.haml27
-rw-r--r--chef-server/lib/views/search/_search_form.html.haml6
-rw-r--r--chef-server/lib/views/search/index.html.haml9
-rw-r--r--chef-server/lib/views/search/show.html.haml13
-rw-r--r--chef-server/lib/views/search_entries/index.html.haml9
-rw-r--r--chef-server/lib/views/search_entries/show.html.haml8
-rw-r--r--chef-server/pkg/chef-server-0.0.1.gembin0 -> 35328 bytes
54 files changed, 2907 insertions, 0 deletions
diff --git a/chef-server/LICENSE b/chef-server/LICENSE
new file mode 100644
index 0000000000..11069edd79
--- /dev/null
+++ b/chef-server/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/chef-server/NOTICE b/chef-server/NOTICE
new file mode 100644
index 0000000000..856fdc82b0
--- /dev/null
+++ b/chef-server/NOTICE
@@ -0,0 +1,10 @@
+Chef NOTICE
+===========
+
+Developed at HJK Solutions (http://www.hjksolutions.com).
+
+Contributors and Copyright holders:
+
+ * Copyright 2008, Adam Jacob <adam@hjksolutions.com>
+ * Copyright 2008, Arjuna Christensen <aj@hjksolutions.com>
+ * Copyright 2008, Ezra Zygmuntowicz <ezra@engineyard.com>
diff --git a/chef-server/README.txt b/chef-server/README.txt
new file mode 100644
index 0000000000..08a0d31bf2
--- /dev/null
+++ b/chef-server/README.txt
@@ -0,0 +1,77 @@
+= chef-server
+
+* http://oss.hjksolutions.com/chef
+
+== DESCRIPTION:
+
+Chef is a configuration management tool inspired by Puppet.
+
+I'm in ur netwerk, cookin up yer servers. :)
+
+== FEATURES/PROBLEMS:
+
+
+== SYNOPSIS:
+
+
+== REQUIREMENTS:
+
+RubyGems:
+
+* stomp
+* stompserver
+* ultraviolet
+* facter
+* ferret
+* merb-core
+* haml
+* ruby-openid
+* json
+
+External Servers:
+
+* stompserver (for easy stomp mq testing)
+* CouchDB
+
+== INSTALL:
+
+Install all of the above. To fire up a develpment environment, do the following:
+
+ * Start CouchDB with 'couchdb'
+ * Start stompserver with 'stompserver'
+ * Start chef-indexer with:
+
+ ./bin/chef-indexer -l debug -c ./config/chef-server.rb
+
+ * Start chef-server on port 4000 with:
+
+ ./bin/chef-server
+
+ * Start chef-server on port 4001 with:
+
+ ./bin/chef-server -p 4001
+
+ * Test run chef with:
+
+ sudo ./bin/chef-client -l debug -c ./examples/config/chef-solo.rb
+
+== LICENSE:
+
+Chef - A configuration management system
+
+Author:: Adam Jacob (<adam@hjksolutions.com>)
+Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+License:: Apache License, Version 2.0
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
diff --git a/chef-server/Rakefile b/chef-server/Rakefile
new file mode 100644
index 0000000000..4f7b21a9e3
--- /dev/null
+++ b/chef-server/Rakefile
@@ -0,0 +1,47 @@
+# -*- ruby -*-
+require 'rubygems'
+require 'rake/gempackagetask'
+
+GEM = "chef-server"
+VERSION = "0.0.1"
+AUTHOR = "Adam Jacob"
+EMAIL = "adam@hjksolutions.com"
+HOMEPAGE = "http://hjksolutions.com"
+SUMMARY = "A configuration management system server."
+
+spec = Gem::Specification.new do |s|
+ s.name = GEM
+ s.version = VERSION
+ s.platform = Gem::Platform::RUBY
+ s.has_rdoc = true
+ s.extra_rdoc_files = ["README.txt", "LICENSE", 'NOTICE']
+ s.summary = SUMMARY
+ s.description = s.summary
+ s.author = AUTHOR
+ s.email = EMAIL
+ s.homepage = HOMEPAGE
+
+ # Uncomment this to add a dependency
+ s.add_dependency "stomp"
+ s.add_dependency "stompserver"
+ s.add_dependency "ferret"
+ s.add_dependency "merb-core"
+ s.add_dependency "haml"
+ s.add_dependency "ruby-openid"
+ s.add_dependency "json"
+
+ s.bindir = "bin"
+ s.executables = %w( chef-indexer chef-server )
+
+ s.files = %w(LICENSE README.txt Rakefile) + Dir.glob("{lib}/**/*")
+end
+
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.gem_spec = spec
+end
+
+task :install => [:package] do
+ sh %{sudo gem install pkg/#{GEM}-#{VERSION}}
+end
+
+# vim: syntax=Ruby \ No newline at end of file
diff --git a/chef-server/bin/chef-indexer b/chef-server/bin/chef-indexer
new file mode 100755
index 0000000000..4788aca0f7
--- /dev/null
+++ b/chef-server/bin/chef-indexer
@@ -0,0 +1,85 @@
+#!/usr/bin/env ruby
+#
+# ./chef-indexer - Build indexes from Chef Queues!
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+require 'optparse'
+require 'chef'
+require 'rubygems'
+require 'facter'
+
+config = {
+ :config_file => "/etc/chef/config.rb",
+ :log_level => :info
+}
+opts = OptionParser.new do |opts|
+ opts.banner = "Usage: #{$0} [-d DIR|-r FILE] (options)"
+ opts.on("-c CONFIG", "--config CONFIG", "The Chef Config file to use") do |c|
+ config[:config_file] = c
+ end
+ opts.on_tail("-l LEVEL", "--loglevel LEVEL", "Set the log level (debug, info, warn, error, fatal)") do |l|
+ config[:log_level] = l.to_sym
+ end
+ opts.on_tail("-h", "--help", "Show this message") do
+ puts opts
+ exit
+ end
+end
+opts.parse!(ARGV)
+
+unless File.exists?(config[:config_file]) && File.readable?(config[:config_file])
+ puts "I cannot find or read the config file: #{config[:config_file]}"
+ puts opts
+ exit
+end
+
+# Load our config file
+Chef::Config.from_file(config[:config_file])
+if config[:log_level]
+ Chef::Log.level(config[:log_level].to_sym)
+end
+
+# Get a Chef::SearchIndex object
+indexer = Chef::SearchIndex.new
+Chef::Queue.connect
+Chef::Queue.subscribe(:queue, "index")
+Chef::Queue.subscribe(:queue, "remove")
+while 1
+ begin
+ object, headers = Chef::Queue.receive_msg
+ Chef::Log.info("Headers #{headers.inspect}")
+ if headers["destination"] == "/queue/chef/index"
+ start_timer = Time.new
+ indexer.add(object)
+ indexer.commit
+ final_timer = Time.new
+ Chef::Log.info("Indexed object from #{headers['destination']} in #{final_timer - start_timer} seconds")
+ elsif headers["destination"] == "/queue/chef/remove"
+ start_timer = Time.new
+ indexer.delete(object)
+ indexer.commit
+ final_timer = Time.new
+ Chef::Log.info("Removed object from #{headers['destination']} in #{final_timer - start_timer} seconds")
+ end
+ rescue Exception => e
+ if e.kind_of?(Interrupt)
+ raise e
+ end
+ Chef::Log.error("Received Exception: #{e}\n#{e.backtrace.join("\n")} continuing")
+ end
+end
diff --git a/chef-server/bin/chef-server b/chef-server/bin/chef-server
new file mode 100755
index 0000000000..bfd0361156
--- /dev/null
+++ b/chef-server/bin/chef-server
@@ -0,0 +1,48 @@
+#!/usr/bin/env ruby
+#
+# ./chef-server - Serving up piping hot infrastructure!
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Based on the 'merb' command, by Ezra
+
+require "rubygems"
+require "merb-core"
+require 'chef'
+
+if ARGV[0] && ARGV[0] =~ /^[^-]/
+ ARGV.push "-H"
+end
+
+unless %w[-a --adapter -i --irb-console -r --script-runner].any? { |o| ARGV.index(o) }
+ ARGV.push *%w[-a mongrel]
+end
+
+ARGV.push *[ "-I", File.join(File.dirname(__FILE__), "..", "lib", "init.rb") ]
+
+if index = ARGV.index("-C")
+ config = ARGV[index+1]
+ ARGV.delete("-C")
+ ARGV.delete(config)
+ Chef::Config.from_file(File.expand_path(config))
+else
+ Chef::Config.from_file(
+ File.join("/etc", "chef", "server.rb")
+ )
+end
+
+Merb.start
diff --git a/chef-server/lib/controllers/application.rb b/chef-server/lib/controllers/application.rb
new file mode 100644
index 0000000000..6a501ea29c
--- /dev/null
+++ b/chef-server/lib/controllers/application.rb
@@ -0,0 +1,150 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require "chef" / "mixin" / "checksum"
+
+
+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
+
+ # Load a cookbook and return a hash with a list of all the files of a
+ # given segment (attributes, recipes, definitions, libraries)
+ #
+ # === Parameters
+ # cookbook_id<String>:: The cookbook to load
+ # segment<Symbol>:: :attributes, :recipes, :definitions, :libraries
+ #
+ # === Returns
+ # <Hash>:: A hash consisting of the short name of the file in :name, and the full path
+ # to the file in :file.
+ def load_cookbook_segment(cookbook_id, segment)
+ cl = Chef::CookbookLoader.new
+ cookbook = cl[cookbook_id]
+ raise NotFound unless cookbook
+
+ files_list = segment_files(segment, cookbook)
+
+ files = Hash.new
+ files_list.each do |f|
+ full = File.expand_path(f)
+ name = File.basename(full)
+ files[name] = {
+ :name => name,
+ :file => full,
+ }
+ end
+ files
+ end
+
+ def segment_files(segment, cookbook)
+ files_list = nil
+ case segment
+ when :attributes
+ files_list = cookbook.attribute_files
+ when :recipes
+ files_list = cookbook.recipe_files
+ when :definitions
+ files_list = cookbook.definition_files
+ when :libraries
+ files_list = cookbook.library_files
+ else
+ raise ArgumentError, "segment must be one of :attributes, :recipes, :definitions or :libraries"
+ end
+ files_list
+ end
+
+ def load_all_files(segment)
+ cl = Chef::CookbookLoader.new
+ files = Array.new
+ cl.each do |cookbook|
+ segment_files(segment, cookbook).each do |sf|
+ files << {
+ :cookbook => cookbook.name,
+ :name => File.basename(sf)
+ }
+ end
+ end
+ files
+ end
+
+end \ No newline at end of file
diff --git a/chef-server/lib/controllers/cookbook_attributes.rb b/chef-server/lib/controllers/cookbook_attributes.rb
new file mode 100644
index 0000000000..803421b6f5
--- /dev/null
+++ b/chef-server/lib/controllers/cookbook_attributes.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class CookbookAttributes < Application
+
+ provides :html, :json
+
+ include Chef::Mixin::Checksum
+
+ def load_cookbook_attributes()
+ @attribute_files = load_cookbook_segment(params[:cookbook_id], :attributes)
+ end
+
+ def index
+ if params[:id]
+ show
+ else
+ load_cookbook_attributes()
+ display @attribute_files
+ end
+ end
+
+ def show
+ only_provides :json
+ load_cookbook_attributes
+ raise NotFound, "Cannot find a suitable attribute file!" unless @attribute_files.has_key?(params[:id])
+ to_send = @attribute_files[params[:id]][:file]
+ current_checksum = checksum(to_send)
+ Chef::Log.debug("old sum: #{params[:checksum]}, new sum: #{current_checksum}")
+ if current_checksum == params[:checksum]
+ display "File #{to_send} has not changed", :status => 304
+ else
+ send_file(to_send)
+ end
+ end
+
+end
+
+
diff --git a/chef-server/lib/controllers/cookbook_definitions.rb b/chef-server/lib/controllers/cookbook_definitions.rb
new file mode 100644
index 0000000000..dbfeeca4d7
--- /dev/null
+++ b/chef-server/lib/controllers/cookbook_definitions.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class CookbookDefinitions < Application
+
+ provides :html, :json
+
+ include Chef::Mixin::Checksum
+
+ def load_cookbook_definitions()
+ @definition_files = load_cookbook_segment(params[:cookbook_id], :definitions)
+ end
+
+ def index
+ if params[:id]
+ show
+ else
+ load_cookbook_definitions()
+ display @definition_files
+ end
+ end
+
+ def show
+ only_provides :json
+ load_cookbook_definitions
+ raise NotFound, "Cannot find a suitable definition file!" unless @definition_files.has_key?(params[:id])
+
+ to_send = @definition_files[params[:id]][:file]
+ current_checksum = checksum(to_send)
+ Chef::Log.debug("Old sum: #{params[:checksum]}, New sum: #{current_checksum}")
+ if current_checksum == params[:checksum]
+ display "File #{to_send} has not changed", :status => 304
+ else
+ send_file(to_send)
+ end
+ end
+
+end
+
+
diff --git a/chef-server/lib/controllers/cookbook_files.rb b/chef-server/lib/controllers/cookbook_files.rb
new file mode 100644
index 0000000000..2d8078ad7e
--- /dev/null
+++ b/chef-server/lib/controllers/cookbook_files.rb
@@ -0,0 +1,117 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class CookbookFiles < Application
+
+ provides :html, :json
+
+ include Chef::Mixin::Checksum
+
+ layout nil
+
+ def load_cookbook_files()
+ @cl = Chef::CookbookLoader.new
+ @cookbook = @cl[params[:cookbook_id]]
+ raise NotFound unless @cookbook
+
+ @remote_files = Hash.new
+ @cookbook.remote_files.each do |rf|
+ full = File.expand_path(rf)
+ name = File.basename(full)
+ rf =~ /^.+#{params[:cookbook_id]}[\\|\/]files[\\|\/](.+?)[\\|\/]#{name}/
+ singlecopy = $1
+ @remote_files[full] = {
+ :name => name,
+ :singlecopy => singlecopy,
+ :file => full,
+ }
+ end
+ @remote_files
+ end
+
+ def index
+ if params[:id]
+ if params[:recursive] == "true"
+ show_directory
+ else
+ show
+ end
+ else
+ load_cookbook_files()
+ display @remote_files
+ end
+ end
+
+ def show
+ only_provides :json
+ to_send = find_preferred_file
+ raise NotFound, "Cannot find a suitable file!" unless to_send
+ current_checksum = checksum(to_send)
+ Chef::Log.debug("old sum: #{params[:checksum]}, new sum: #{current_checksum}")
+ if current_checksum == params[:checksum]
+ display "File #{to_send} has not changed", :status => 304
+ else
+ send_file(to_send)
+ end
+ end
+
+ def show_directory
+ Chef::Log.info("totally rocking hte show_directory")
+ dir_to_send = find_preferred_file
+ unless (dir_to_send && File.directory?(dir_to_send))
+ raise NotFound, "Cannot find a suitable directory"
+ end
+
+ @directory_listing = Array.new
+ Dir[::File.join(dir_to_send, '**', '*')].sort { |a,b| b <=> a }.each do |file_to_send|
+ next if File.directory?(file_to_send)
+ file_to_send =~ /^#{dir_to_send}\/(.+)$/
+ @directory_listing << $1
+ end
+
+ display @directory_listing
+ end
+
+ protected
+
+ def find_preferred_file
+ load_cookbook_files()
+ preferences = [
+ File.join("host-#{params[:fqdn]}", "#{params[:id]}"),
+ File.join("#{params[:platform]}-#{params[:version]}", "#{params[:id]}"),
+ File.join("#{params[:platform]}", "#{params[:id]}"),
+ File.join("default", "#{params[:id]}")
+ ]
+ to_send = nil
+ @remote_files.each_key do |file|
+ Chef::Log.debug("Looking at #{file}")
+ preferences.each do |pref|
+ Chef::Log.debug("Compared to #{pref}")
+ if file =~ /#{pref}$/
+ Chef::Log.debug("Matched #{pref} for #{file}!")
+ to_send = file
+ break
+ end
+ end
+ break if to_send
+ end
+ to_send
+ end
+
+end
diff --git a/chef-server/lib/controllers/cookbook_recipes.rb b/chef-server/lib/controllers/cookbook_recipes.rb
new file mode 100644
index 0000000000..5081ddced1
--- /dev/null
+++ b/chef-server/lib/controllers/cookbook_recipes.rb
@@ -0,0 +1,56 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+class CookbookRecipes < Application
+
+ provides :html, :json
+
+ include Chef::Mixin::Checksum
+
+ def load_cookbook_recipes()
+ @recipe_files = load_cookbook_segment(params[:cookbook_id], :recipes)
+ end
+
+ def index
+ if params[:id]
+ show
+ else
+ load_cookbook_recipes()
+ display @recipe_files
+ end
+ end
+
+ def show
+ only_provides :json
+ load_cookbook_recipes
+ raise NotFound, "Cannot find a suitable recipe file!" unless @recipe_files.has_key?(params[:id])
+
+ to_send = @recipe_files[params[:id]][:file]
+ current_checksum = checksum(to_send)
+ Chef::Log.debug("old sum: #{params[:checksum]}, new sum: #{current_checksum}")
+ if current_checksum == params[:checksum]
+ display "File #{to_send} has not changed", :status => 304
+ else
+ send_file(to_send)
+ end
+ end
+
+end
+
+
diff --git a/chef-server/lib/controllers/cookbook_templates.rb b/chef-server/lib/controllers/cookbook_templates.rb
new file mode 100644
index 0000000000..4cb5e4622c
--- /dev/null
+++ b/chef-server/lib/controllers/cookbook_templates.rb
@@ -0,0 +1,77 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class CookbookTemplates < Application
+
+ provides :html, :json
+
+ def load_cookbook_templates()
+ @cl = Chef::CookbookLoader.new
+ @cookbook = @cl[params[:cookbook_id]]
+ raise NotFound unless @cookbook
+
+ @templates = Hash.new
+ @cookbook.template_files.each do |tf|
+ full = File.expand_path(tf)
+ name = File.basename(full)
+ tf =~ /^.+#{params[:cookbook_id]}[\\|\/]templates[\\|\/](.+?)[\\|\/]#{name}/
+ singlecopy = $1
+ @templates[full] = {
+ :name => name,
+ :singlecopy => singlecopy,
+ :file => full,
+ }
+ end
+ @templates
+ end
+
+ def index
+ if params[:id]
+ show
+ else
+ load_cookbook_templates()
+ display @templates
+ end
+ end
+
+ def show
+ load_cookbook_templates()
+ preferences = [
+ File.join("host-#{params[:fqdn]}", "#{params[:id]}"),
+ File.join("#{params[:platform]}-#{params[:version]}", "#{params[:id]}"),
+ File.join("#{params[:platform]}", "#{params[:id]}"),
+ File.join("default", "#{params[:id]}")
+ ]
+ to_send = nil
+ @templates.each_key do |file|
+ Chef::Log.debug("Looking at #{file}")
+ preferences.each do |pref|
+ Chef::Log.debug("Compared to #{pref}")
+ if file =~ /#{pref}/
+ Chef::Log.debug("Matched #{pref} for #{file}!")
+ to_send = file
+ break
+ end
+ end
+ break if to_send
+ end
+ raise NotFound, "Cannot find a suitable template!" unless to_send
+ send_file(to_send)
+ end
+
+end
diff --git a/chef-server/lib/controllers/cookbooks.rb b/chef-server/lib/controllers/cookbooks.rb
new file mode 100644
index 0000000000..e2a9d7f319
--- /dev/null
+++ b/chef-server/lib/controllers/cookbooks.rb
@@ -0,0 +1,50 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Cookbooks < Application
+
+ provides :html, :json
+
+ def index
+ @cl = Chef::CookbookLoader.new
+ display @cl
+ end
+
+ def show
+ @cl = Chef::CookbookLoader.new
+ @cookbook = @cl[params[:id]]
+ raise NotFound unless @cookbook
+ display @cookbook
+ end
+
+ def recipe_files
+ @recipe_files = load_all_files(:recipes)
+ display @recipe_files
+ end
+
+ def attribute_files
+ @attribute_files = load_all_files(:attributes)
+ display @attribute_files
+ end
+
+ def definition_files
+ @definition_files = load_all_files(:definitions)
+ display @definition_files
+ end
+
+end
diff --git a/chef-server/lib/controllers/exceptions.rb b/chef-server/lib/controllers/exceptions.rb
new file mode 100644
index 0000000000..d8f7307637
--- /dev/null
+++ b/chef-server/lib/controllers/exceptions.rb
@@ -0,0 +1,38 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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/chef-server/lib/controllers/nodes.rb b/chef-server/lib/controllers/nodes.rb
new file mode 100644
index 0000000000..655b264862
--- /dev/null
+++ b/chef-server/lib/controllers/nodes.rb
@@ -0,0 +1,78 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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
+
+end
diff --git a/chef-server/lib/controllers/openid_consumer.rb b/chef-server/lib/controllers/openid_consumer.rb
new file mode 100644
index 0000000000..4e3980ce3d
--- /dev/null
+++ b/chef-server/lib/controllers/openid_consumer.rb
@@ -0,0 +1,117 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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 = Chef::Config[:openid_cstore_path]
+ store = OpenID::Store::Filesystem.new(dir)
+ @consumer = OpenID::Consumer.new(session, store)
+ end
+ return @consumer
+ end
+end
diff --git a/chef-server/lib/controllers/openid_register.rb b/chef-server/lib/controllers/openid_register.rb
new file mode 100644
index 0000000000..f5113ef267
--- /dev/null
+++ b/chef-server/lib/controllers/openid_register.rb
@@ -0,0 +1,89 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+require 'openid'
+
+class OpenidRegister < Application
+
+ provides :html, :json
+
+ before :fix_up_node_id
+
+ def index
+ @headers['X-XRDS-Location'] = absolute_url(:protocol => "http", :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/chef-server/lib/controllers/openid_server.rb b/chef-server/lib/controllers/openid_server.rb
new file mode 100644
index 0000000000..4cdaf67adb
--- /dev/null
+++ b/chef-server/lib/controllers/openid_server.rb
@@ -0,0 +1,236 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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 = Chef::Config[:openid_store_path]
+ 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/chef-server/lib/controllers/search.rb b/chef-server/lib/controllers/search.rb
new file mode 100644
index 0000000000..0abbf352b8
--- /dev/null
+++ b/chef-server/lib/controllers/search.rb
@@ -0,0 +1,71 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class Search < Application
+
+ provides :html, :json
+
+ def index
+ @s = Chef::Search.new
+ @search_indexes = @s.list_indexes
+ display @search_indexes
+ end
+
+ def show
+ @s = Chef::Search.new
+ @results = nil
+ if params[:q]
+ @results = @s.search(params[:id], params[:q] == "" ? "?*" : params[:q])
+ else
+ @results = @s.search(params[:id], "?*")
+ end
+ # Boy, this should move to the search function
+ if params[:a]
+ attributes = params[:a].split(",").collect { |a| a.to_sym }
+ unless attributes.length == 0
+ @results = @results.collect do |r|
+ nr = Hash.new
+ nr[:index_name] = r[:index_name]
+ nr[:id] = r[:id]
+ attributes.each do |attrib|
+ if r.has_key?(attrib)
+ nr[attrib] = r[attrib]
+ end
+ end
+ nr
+ end
+ end
+ end
+ display @results
+ end
+
+ def destroy
+ @s = Chef::Search.new
+ @entries = @s.search(params[:id], "?*")
+ @entries.each do |entry|
+ Chef::Queue.send_msg(:queue, :remove, entry)
+ end
+ @status = 202
+ if content_type == :html
+ redirect url(:search)
+ else
+ display @entries
+ end
+ end
+
+end
diff --git a/chef-server/lib/controllers/search_entries.rb b/chef-server/lib/controllers/search_entries.rb
new file mode 100644
index 0000000000..c93add3dc5
--- /dev/null
+++ b/chef-server/lib/controllers/search_entries.rb
@@ -0,0 +1,68 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+class SearchEntries < Application
+
+ provides :html, :json
+
+ def index
+ @s = Chef::Search.new
+ @entries = @s.search(params[:search_id], "?*")
+ display @entries
+ end
+
+ def show
+ @s = Chef::Search.new
+ @entry = @s.search(params[:search_id], "id:'#{params[:search_id]}_#{params[:id]}'").first
+ display @entry
+ end
+
+ def create
+ @to_index = params
+ @to_index.delete(:controller)
+ @to_index["index_name"] = params[:search_id]
+ @to_index["id"] = "#{params[:search_id]}_#{params[:id]}"
+ @to_index.delete(:search_id)
+ Chef::Queue.send_msg(:queue, :index, @to_index)
+ if content_type == :html
+ redirect url(:search)
+ else
+ @status = 202
+ display @to_index
+ end
+ end
+
+ def update
+ create
+ end
+
+ def destroy
+ @s = Chef::Search.new
+ @entries = @s.search(params[:id], "?*")
+ @entries.each do |entry|
+ Chef::Queue.send_msg(:queue, :remove, entry)
+ end
+ @status = 202
+ if content_type == :html
+ redirect url(:search)
+ else
+ display @entries
+ end
+ end
+
+end
diff --git a/chef-server/lib/helpers/global_helpers.rb b/chef-server/lib/helpers/global_helpers.rb
new file mode 100644
index 0000000000..f3b15129a8
--- /dev/null
+++ b/chef-server/lib/helpers/global_helpers.rb
@@ -0,0 +1,35 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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/chef-server/lib/helpers/nodes_helper.rb b/chef-server/lib/helpers/nodes_helper.rb
new file mode 100644
index 0000000000..44a190a7c6
--- /dev/null
+++ b/chef-server/lib/helpers/nodes_helper.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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/chef-server/lib/helpers/openid_server_helpers.rb b/chef-server/lib/helpers/openid_server_helpers.rb
new file mode 100644
index 0000000000..dc56d2742f
--- /dev/null
+++ b/chef-server/lib/helpers/openid_server_helpers.rb
@@ -0,0 +1,27 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+module Merb
+ module OpenidServerHelper
+
+ def url_for_user
+ url(:openid_node, :username => session[:username])
+ end
+
+ end
+end
diff --git a/chef-server/lib/init.rb b/chef-server/lib/init.rb
new file mode 100644
index 0000000000..2aa5781a81
--- /dev/null
+++ b/chef-server/lib/init.rb
@@ -0,0 +1,206 @@
+#
+# Author:: Adam Jacob (<adam@hjksolutions.com>)
+# Copyright:: Copyright (c) 2008 HJK Solutions, LLC
+# License:: Apache License, Version 2.0
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+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.
+
+
+# disable the ActiveSupport json annoying stuff, as it breaks our to_json
+Merb.disable :json
+
+# ==== 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
+ Chef::Queue.connect
+
+ # create the couch design docs for nodes and openid registrations
+ Chef::Node.create_design_document
+ Chef::OpenIDRegistration.create_design_document
+
+ # 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, File.join(File.dirname(__FILE__), "public"))
+
+require 'merb-haml'
+#require 'uv'
+
+
+#
+# ==== 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] = "/var/log/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
+
+ resources :nodes
+ resources :searches, :path => "search", :controller => "search" do
+ resources :entries, :controller => "search_entries"
+ end
+
+ match("/cookbooks/_attribute_files").to(:controller => "cookbooks", :action => "attribute_files")
+ match("/cookbooks/_recipe_files").to(:controller => "cookbooks", :action => "recipe_files")
+ match("/cookbooks/_definition_files").to(:controller => "cookbooks", :action => "definition_files")
+
+# r.match("/cookbooks/:cookbook_id/templates").to(:controller => "cookbook_templates", :action => "index")
+
+ resources :cookbooks do
+ resources :templates, :controller => "cookbook_templates"
+ resources :files, :controller => "cookbook_files"
+ resources :recipes, :controller => "cookbook_recipes"
+ resources :attributes, :controller => "cookbook_attributes"
+ resources :definitions, :controller => "cookbook_definitions"
+ end
+
+ #r.resources :openid do |res|
+ # res.resources :register, :controller => "openid_register"
+ # res.resources :server, :controller => "openid_server"
+ #end
+
+ resources :registrations, :controller => "openid_register"
+ resources :registrations, :controller => "openid_register", :member => { :validate => :post }
+ match("/openid/server").to(:controller => "openid_server", :action => "index").name(:openid_server)
+ match("/openid/server/server/xrds").
+ to(:controller => "openid_server", :action => 'idp_xrds').name(:openid_server_xrds)
+ match("/openid/server/node/:id").
+ to(:controller => "openid_server", :action => 'node_page').name(:openid_node)
+ match('/openid/server/node/:id/xrds').
+ to(:controller => 'openid_server', :action => 'node_xrds').name(:openid_node_xrds)
+ match('/openid/server/decision').to(:controller => "openid_server", :action => "decision").name(:openid_server_decision)
+ match('/openid/consumer').to(:controller => 'openid_consumer', :action => 'index').name(:openid_consumer)
+ match('/openid/consumer/start').to(:controller => 'openid_consumer', :action => 'start').name(:openid_consumer_start)
+ match('/openid/consumer/complete').to(:controller => 'openid_consumer', :action => 'complete').name(:openid_consumer_complete)
+ 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 /
+ match('/').to(:controller => 'nodes', :action =>'index').name(:top)
+end
+
diff --git a/chef-server/lib/public/images/merb.jpg b/chef-server/lib/public/images/merb.jpg
new file mode 100644
index 0000000000..a19dcf4048
--- /dev/null
+++ b/chef-server/lib/public/images/merb.jpg
Binary files differ
diff --git a/chef-server/lib/public/stylesheets/master.css b/chef-server/lib/public/stylesheets/master.css
new file mode 100644
index 0000000000..13bdffbe1b
--- /dev/null
+++ b/chef-server/lib/public/stylesheets/master.css
@@ -0,0 +1,292 @@
+/* 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;
+}
+*/
+
+pre.twilight .DiffInserted {
+ background-color: #253B22;
+ color: #F8F8F8;
+}
+pre.twilight .DiffHeader {
+ background-color: #0E2231;
+ color: #F8F8F8;
+ font-style: italic;
+}
+pre.twilight .CssPropertyValue {
+ color: #F9EE98;
+}
+pre.twilight .CCCPreprocessorDirective {
+ color: #AFC4DB;
+}
+pre.twilight .Constant {
+ color: #CF6A4C;
+}
+pre.twilight .DiffChanged {
+ background-color: #4A410D;
+ color: #F8F8F8;
+}
+pre.twilight .EmbeddedSource {
+ background-color: #A3A6AD;
+}
+pre.twilight .Support {
+ color: #9B859D;
+}
+pre.twilight .MarkupList {
+ color: #F9EE98;
+}
+pre.twilight .CssConstructorArgument {
+ color: #8F9D6A;
+}
+pre.twilight .Storage {
+ color: #F9EE98;
+}
+pre.twilight .line-numbers {
+ background-color: #DDF0FF;
+ color: #000000;
+}
+pre.twilight .CssClass {
+ color: #9B703F;
+}
+pre.twilight .StringConstant {
+ color: #DDF2A4;
+}
+pre.twilight .CssAtRule {
+ color: #8693A5;
+}
+pre.twilight .MetaTagInline {
+ color: #E0C589;
+}
+pre.twilight .MarkupHeading {
+ color: #CF6A4C;
+}
+pre.twilight .CssTagName {
+ color: #CDA869;
+}
+pre.twilight .SupportConstant {
+ color: #CF6A4C;
+}
+pre.twilight .DiffDeleted {
+ background-color: #420E09;
+ color: #F8F8F8;
+}
+pre.twilight .CCCPreprocessorLine {
+ color: #8996A8;
+}
+pre.twilight .StringRegexpSpecial {
+ color: #CF7D34;
+}
+pre.twilight .EmbeddedSourceBright {
+ background-color: #9C9EA4;
+}
+pre.twilight .InvalidIllegal {
+ background-color: #241A24;
+ color: #F8F8F8;
+}
+pre.twilight .SupportFunction {
+ color: #DAD085;
+}
+pre.twilight .CssAdditionalConstants {
+ color: #CA7840;
+}
+pre.twilight .MetaTagAll {
+ color: #AC885B;
+}
+pre.twilight .StringRegexp {
+ color: #E9C062;
+}
+pre.twilight .StringEmbeddedSource {
+ color: #DAEFA3;
+}
+pre.twilight .EntityInheritedClass {
+ color: #9B5C2E;
+ font-style: italic;
+}
+pre.twilight .CssId {
+ color: #8B98AB;
+}
+pre.twilight .CssPseudoClass {
+ color: #8F9D6A;
+}
+pre.twilight .StringVariable {
+ color: #8A9A95;
+}
+pre.twilight .String {
+ color: #8F9D6A;
+}
+pre.twilight .Keyword {
+ color: #CDA869;
+}
+pre.twilight {
+ background-color: #141414;
+ color: #F8F8F8;
+}
+pre.twilight .CssPropertyName {
+ color: #C5AF75;
+}
+pre.twilight .DoctypeXmlProcessing {
+ color: #494949;
+}
+pre.twilight .InvalidDeprecated {
+ color: #D2A8A1;
+ font-style: italic;
+}
+pre.twilight .Variable {
+ color: #7587A6;
+}
+pre.twilight .Entity {
+ color: #9B703F;
+}
+pre.twilight .Comment {
+ color: #5F5A60;
+ font-style: italic;
+}
diff --git a/chef-server/lib/views/cookbook_templates/index.html.haml b/chef-server/lib/views/cookbook_templates/index.html.haml
new file mode 100644
index 0000000000..307aefd812
--- /dev/null
+++ b/chef-server/lib/views/cookbook_templates/index.html.haml
@@ -0,0 +1,8 @@
+%h1 Cookbook Templates
+- @templates.each do |template, data|
+ .index
+ %table
+ %tr
+ %td
+ %a{ :href => url(:cookbook_template, { :cookbook_id => params[:cookbook_id], :id => data[:link_name], :version => data[:version] }) }
+ = "#{data[:version]} - #{data[:name]}"
diff --git a/chef-server/lib/views/cookbooks/_attribute_file.html.haml b/chef-server/lib/views/cookbooks/_attribute_file.html.haml
new file mode 100644
index 0000000000..af859d5ff3
--- /dev/null
+++ b/chef-server/lib/views/cookbooks/_attribute_file.html.haml
@@ -0,0 +1,2 @@
+%h3= name
+= ::Uv.parse(contents, "xhtml", "ruby", true, "twilight") \ No newline at end of file
diff --git a/chef-server/lib/views/cookbooks/attribute_files.html.haml b/chef-server/lib/views/cookbooks/attribute_files.html.haml
new file mode 100644
index 0000000000..56fdcf7bee
--- /dev/null
+++ b/chef-server/lib/views/cookbooks/attribute_files.html.haml
@@ -0,0 +1,4 @@
+%h1 All Attribute Files
+- @attribute_files.each do |af_hash|
+ %h2= "#{af_hash[:cookbook]}"
+ = partial(:attribute_file, :name => af_hash[:name], :contents => af_hash[:contents]) \ No newline at end of file
diff --git a/chef-server/lib/views/cookbooks/index.html.haml b/chef-server/lib/views/cookbooks/index.html.haml
new file mode 100644
index 0000000000..008f74765c
--- /dev/null
+++ b/chef-server/lib/views/cookbooks/index.html.haml
@@ -0,0 +1,7 @@
+%h1 Cookbooks
+- @cl.each do |cookbook|
+ .index
+ %table
+ %tr
+ %td
+ %a{ :href => url(:cookbook, { :id => cookbook.name }) }= cookbook.name
diff --git a/chef-server/lib/views/cookbooks/show.html.haml b/chef-server/lib/views/cookbooks/show.html.haml
new file mode 100644
index 0000000000..1b6557e012
--- /dev/null
+++ b/chef-server/lib/views/cookbooks/show.html.haml
@@ -0,0 +1,24 @@
+%h1= "Cookbook #{h @cookbook.name}"
+.cookbook
+- if @cookbook.attribute_files.length > 0
+ %h2 Attribute Files
+ - @cookbook.attribute_files.each do |af|
+ = partial(:attribute_file, :name => File.basename(af), :contents => File.read(af))
+
+- if @cookbook.definition_files.length > 0
+ - @cookbook.definition_files.each do |df|
+ %h2 Definition Files
+ %h3= File.basename(df)
+ = ::Uv.parse(File.read(df), "xhtml", "ruby", true, "twilight")
+
+- if @cookbook.recipe_files.length > 0
+ - @cookbook.recipe_files.each do |rf|
+ %h2 Recipe Files
+ %h3= File.basename(rf)
+ = ::Uv.parse(File.read(rf), "xhtml", "ruby", true, "twilight")
+
+- if @cookbook.template_files.length > 0
+ - @cookbook.template_files.each do |tf|
+ %h2 Template Files
+ %h3= File.basename(tf)
+ = ::Uv.parse(File.read(tf), "xhtml", "html_rails", true, "twilight")
diff --git a/chef-server/lib/views/exceptions/bad_request.json.erb b/chef-server/lib/views/exceptions/bad_request.json.erb
new file mode 100644
index 0000000000..f266cf99b9
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/exceptions/internal_server_error.html.erb b/chef-server/lib/views/exceptions/internal_server_error.html.erb
new file mode 100644
index 0000000000..aadbfad350
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/exceptions/not_acceptable.html.erb b/chef-server/lib/views/exceptions/not_acceptable.html.erb
new file mode 100644
index 0000000000..f632712bb2
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/exceptions/not_found.html.erb b/chef-server/lib/views/exceptions/not_found.html.erb
new file mode 100644
index 0000000000..388c72c31d
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/layout/application.html.haml b/chef-server/lib/views/layout/application.html.haml
new file mode 100644
index 0000000000..2be4097d29
--- /dev/null
+++ b/chef-server/lib/views/layout/application.html.haml
@@ -0,0 +1,26 @@
+!!! 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(:searches) } Search
+ |
+ %a{:href => url(:nodes) } Nodes
+ |
+ %a{:href => url(:cookbooks) } Cookbooks
+ |
+ %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/chef-server/lib/views/nodes/_action.html.haml b/chef-server/lib/views/nodes/_action.html.haml
new file mode 100644
index 0000000000..8adb39338d
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/nodes/_node.html.haml b/chef-server/lib/views/nodes/_node.html.haml
new file mode 100644
index 0000000000..24b1f2816b
--- /dev/null
+++ b/chef-server/lib/views/nodes/_node.html.haml
@@ -0,0 +1,9 @@
+.node
+ %h1
+ = "Node #{h node.name}"
+ %h2 Recipes
+ %ol
+ = recipe_list(node)
+ %h2 Attributes
+ %ol
+ = attribute_list(node)
diff --git a/chef-server/lib/views/nodes/_resource.html.haml b/chef-server/lib/views/nodes/_resource.html.haml
new file mode 100644
index 0000000000..7b9776b816
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/nodes/compile.html.haml b/chef-server/lib/views/nodes/compile.html.haml
new file mode 100644
index 0000000000..5656447ea8
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/nodes/index.html.haml b/chef-server/lib/views/nodes/index.html.haml
new file mode 100644
index 0000000000..667e1c7317
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/nodes/show.html.haml b/chef-server/lib/views/nodes/show.html.haml
new file mode 100644
index 0000000000..94708af6d3
--- /dev/null
+++ b/chef-server/lib/views/nodes/show.html.haml
@@ -0,0 +1 @@
+= partial(:node, :node => @node) \ No newline at end of file
diff --git a/chef-server/lib/views/openid_consumer/index.html.haml b/chef-server/lib/views/openid_consumer/index.html.haml
new file mode 100644
index 0000000000..576ef8d787
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/openid_consumer/start.html.haml b/chef-server/lib/views/openid_consumer/start.html.haml
new file mode 100644
index 0000000000..75ed9a9257
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/openid_login/index.html.haml b/chef-server/lib/views/openid_login/index.html.haml
new file mode 100644
index 0000000000..17ae69017a
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/openid_register/index.html.haml b/chef-server/lib/views/openid_register/index.html.haml
new file mode 100644
index 0000000000..fe4798c59d
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/openid_register/show.html.haml b/chef-server/lib/views/openid_register/show.html.haml
new file mode 100644
index 0000000000..cfd38e8963
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/openid_server/decide.html.haml b/chef-server/lib/views/openid_server/decide.html.haml
new file mode 100644
index 0000000000..8082a5068d
--- /dev/null
+++ b/chef-server/lib/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/chef-server/lib/views/search/_search_form.html.haml b/chef-server/lib/views/search/_search_form.html.haml
new file mode 100644
index 0000000000..99303e1714
--- /dev/null
+++ b/chef-server/lib/views/search/_search_form.html.haml
@@ -0,0 +1,6 @@
+%form{ :method => "get", :action => url(:search, { :id => index_name }) }
+ Q:
+ %input{ :type => "text", :name => "q" }
+ A:
+ %input{ :type => "text", :name => "a" }
+ %input{ :type => "submit", :value => "Search #{index_name}" }
diff --git a/chef-server/lib/views/search/index.html.haml b/chef-server/lib/views/search/index.html.haml
new file mode 100644
index 0000000000..742c1110b0
--- /dev/null
+++ b/chef-server/lib/views/search/index.html.haml
@@ -0,0 +1,9 @@
+%h1 Search Indexes
+- @search_indexes.each do |index|
+ .index
+ %table
+ %tr
+ %td
+ %a{ :href => url(:search, { :id => index }) }= index
+ %td
+ = partial(:search_form, :index_name => index)
diff --git a/chef-server/lib/views/search/show.html.haml b/chef-server/lib/views/search/show.html.haml
new file mode 100644
index 0000000000..5380a7477a
--- /dev/null
+++ b/chef-server/lib/views/search/show.html.haml
@@ -0,0 +1,13 @@
+%h1 Search Results
+.search
+ = partial(:search_form, :index_name => params[:id])
+.query
+ %h2= "Search Query was #{params[:q] ? params[:q] : '*'}"
+- @results.each do |result|
+ .search_result
+ %h3= "#{h result[:index_name]} (#{h result[:id]})"
+ %table
+ - result.each do |k, v|
+ %tr.attr_group
+ %td.attr_name= k
+ %td.attr_value= v.kind_of?(Array) ? v.join(",") : v \ No newline at end of file
diff --git a/chef-server/lib/views/search_entries/index.html.haml b/chef-server/lib/views/search_entries/index.html.haml
new file mode 100644
index 0000000000..77d140242e
--- /dev/null
+++ b/chef-server/lib/views/search_entries/index.html.haml
@@ -0,0 +1,9 @@
+%h1= "Search Index #{h params[:search_id]}"
+- @entries.each do |result|
+ .search_result
+ %h3= "#{h result[:index_name]} (#{h result[:id]})"
+ %table
+ - result.each do |k, v|
+ %tr.attr_group
+ %td.attr_name= k
+ %td.attr_value= v.kind_of?(Array) ? v.join(",") : v \ No newline at end of file
diff --git a/chef-server/lib/views/search_entries/show.html.haml b/chef-server/lib/views/search_entries/show.html.haml
new file mode 100644
index 0000000000..7ca34b32ab
--- /dev/null
+++ b/chef-server/lib/views/search_entries/show.html.haml
@@ -0,0 +1,8 @@
+%h1= "Search Index #{h params[:search_id]} entry #{h params[:id]}"
+.search_result
+ %h3= "#{h @entry[:index_name]} (#{h @entry[:id]})"
+ %table
+ - @entry.each do |k, v|
+ %tr.attr_group
+ %td.attr_name= k
+ %td.attr_value= v.kind_of?(Array) ? v.join(",") : v \ No newline at end of file
diff --git a/chef-server/pkg/chef-server-0.0.1.gem b/chef-server/pkg/chef-server-0.0.1.gem
new file mode 100644
index 0000000000..86a59594cf
--- /dev/null
+++ b/chef-server/pkg/chef-server-0.0.1.gem
Binary files differ