summaryrefslogtreecommitdiff
path: root/chef-server/lib/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'chef-server/lib/controllers')
-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
14 files changed, 1258 insertions, 0 deletions
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