summaryrefslogtreecommitdiff
path: root/chef-server-webui
diff options
context:
space:
mode:
authorLogan Lowell <fractaloop@thefrontside.net>2012-04-05 07:43:09 -0500
committerBryan McLellan <btm@opscode.com>2012-08-03 11:43:30 -0700
commit32a5f96ef9fe9e0a7531a94b57a8d0fae4fab69b (patch)
treef873183df6ecd94d11d661ae0eb1c324d5da01cd /chef-server-webui
parent65f8bd6c38d951d59a80aee65ae0bbaf42ea39f0 (diff)
downloadchef-32a5f96ef9fe9e0a7531a94b57a8d0fae4fab69b.tar.gz
Bring over the routes and controllers
Diffstat (limited to 'chef-server-webui')
-rw-r--r--chef-server-webui/app/controllers/clients_controller.rb121
-rw-r--r--chef-server-webui/app/controllers/cookbooks_controller.rb186
-rw-r--r--chef-server-webui/app/controllers/databag_items_controller.rb99
-rw-r--r--chef-server-webui/app/controllers/databags_controller.rb82
-rw-r--r--chef-server-webui/app/controllers/environments_controller.rb216
-rw-r--r--chef-server-webui/app/controllers/nodes_controller.rb143
-rw-r--r--chef-server-webui/app/controllers/openid_consumer_controller.rb157
-rw-r--r--chef-server-webui/app/controllers/roles_controller.rb137
-rw-r--r--chef-server-webui/app/controllers/search_controller.rb60
-rw-r--r--chef-server-webui/app/controllers/search_entries_controller.rb62
-rw-r--r--chef-server-webui/app/controllers/status_controller.rb42
-rw-r--r--chef-server-webui/app/controllers/users_controller.rb177
-rw-r--r--chef-server-webui/config/routes.rb142
13 files changed, 1568 insertions, 56 deletions
diff --git a/chef-server-webui/app/controllers/clients_controller.rb b/chef-server-webui/app/controllers/clients_controller.rb
new file mode 100644
index 0000000000..39653c613c
--- /dev/null
+++ b/chef-server-webui/app/controllers/clients_controller.rb
@@ -0,0 +1,121 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/api_client'
+
+class ClientsController < ApplicationController
+ respond_to :html, :json
+ before_filter :login_required
+ before_filter :require_admin, :exclude => [:index, :show]
+
+ # GET /clients
+ def index
+ begin
+ @clients_list = Chef::ApiClient.list().keys.sort
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not list clients"}
+ @clients_list = []
+ end
+ respond_with @clients_list
+ end
+
+ # GET /clients/:id
+ def show
+ @client = begin
+ @client = Chef::ApiClient.load(params[:id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not load client #{params[:id]}"}
+ Chef::ApiClient.new
+ end
+ respond_with @client
+ end
+
+ # GET /clients/:id/edit
+ def edit
+ @client = begin
+ Chef::ApiClient.load(params[:id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not load client #{params[:id]}"}
+ Chef::ApiClient.new
+ end
+ respond_with @client
+ end
+
+ # GET /clients/new
+ def new
+ raise AdminAccessRequired unless params[:user_id] == session[:user] unless session[:level] == :admin
+ @client = Chef::ApiClient.new
+ respond_with @client
+ end
+
+ # POST /clients
+ def create
+ begin
+ @client = Chef::ApiClient.new
+ @client.name(params[:name])
+ @client.admin(str_to_bool(params[:admin])) if params[:admin]
+ response = @client.create
+ @private_key = OpenSSL::PKey::RSA.new(response["private_key"])
+ @_message = { :notice => "Created Client #{@client.name}. Please copy the following private key as the client's validation key." }
+ @client = Chef::ApiClient.load(params[:name])
+ render :show
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not create client" }
+ render :new
+ end
+ end
+
+ # PUT /clients/:id
+ def update
+ begin
+ @client = Chef::ApiClient.load(params[:id])
+ if params[:regen_private_key]
+ @client.create_keys
+ @private_key = @client.private_key
+ end
+ params[:admin] ? @client.admin(true) : @client.admin(false)
+ @client.save
+ @_message = @private_key.nil? ? { :notice => "Updated Client" } : { :notice => "Created Client #{@client.name}. Please copy the following private key as the client's validation key." }
+ render :show
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not update client" }
+ render :edit
+ end
+ end
+
+ # DELETE /clients/:id
+ def destroy
+ begin
+ @client = Chef::ApiClient.load(params[:id])
+ @client.destroy
+ redirect_to clients_url, :notice => "Client #{params[:id]} deleted successfully"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not delete client #{params[:id]}" }
+ @clients_list = Chef::ApiClient.list()
+ render :index
+ end
+ end
+
+end
+
diff --git a/chef-server-webui/app/controllers/cookbooks_controller.rb b/chef-server-webui/app/controllers/cookbooks_controller.rb
new file mode 100644
index 0000000000..3a51aeb9e6
--- /dev/null
+++ b/chef-server-webui/app/controllers/cookbooks_controller.rb
@@ -0,0 +1,186 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Author:: Seth Falcon (<seth@opscode.com>)
+# Copyright:: Copyright (c) 2008-2011 Opscode, Inc.
+# 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/cookbook_loader'
+require 'chef/cookbook_version'
+
+class CookbooksController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required
+ before_filter :params_helper
+
+ attr_reader :cookbook_id
+ def params_helper
+ @cookbook_id = params[:id] || params[:cookbook_id]
+ end
+
+ def index
+ @cl = fetch_cookbook_versions(6)
+ respond_with @cl
+ end
+
+ def show
+ begin
+ all_books = fetch_cookbook_versions("all", :cookbook => cookbook_id)
+ @versions = all_books[cookbook_id].map { |v| v["version"] }
+ if params[:cb_version] == "_latest"
+ redirect_to show_specific_version_cookbook_url(cookbook_id, @versions.first)
+ return
+ end
+ @version = params[:cb_version]
+ if !@versions.include?(@version)
+ msg = { :warning => ["Cookbook #{cookbook_id} (#{params[:cb_version]})",
+ "is not available in the #{session[:environment]}",
+ "environment."
+ ].join(" ") }
+ redirect_to cookbooks_url, :flash => msg
+ return
+ end
+ cookbook_url = "cookbooks/#{cookbook_id}/#{@version}"
+ rest = Chef::REST.new(Chef::Config[:chef_server_url])
+ @cookbook = rest.get_rest(cookbook_url)
+ raise NotFound unless @cookbook
+ @manifest = @cookbook.manifest
+ display @cookbook
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => $!}
+ @cl = {}
+ render :index
+ end
+ end
+
+ # GET /cookbooks/cookbook_id
+ # provides :json, for the javascript on the environments web form.
+ def cb_versions
+ respond_to :json
+ use_envs = session[:environment] && !params[:ignore_environments]
+ num_versions = params[:num_versions] || "all"
+ all_books = fetch_cookbook_versions(num_versions, :cookbook => cookbook_id,
+ :use_envs => use_envs)
+ respond_with ({ cookbook_id => all_books[cookbook_id] })
+ end
+
+ ## ------
+ ## Helpers
+ ##
+ ## TODO: move these to a cookbooks helper module
+ ## ------
+
+ def recipe_files
+ # node = params.has_key?('node') ? params[:node] : nil
+ # @recipe_files = load_all_files(:recipes, node)
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @recipe_files = r.get_rest("cookbooks/#{params[:id]}/recipes")
+ display @recipe_files
+ end
+
+ def attribute_files
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @recipe_files = r.get_rest("cookbooks/#{params[:id]}/attributes")
+ display @attribute_files
+ end
+
+ def definition_files
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @recipe_files = r.get_rest("cookbooks/#{params[:id]}/definitions")
+ display @definition_files
+ end
+
+ def library_files
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @recipe_files = r.get_rest("cookbooks/#{params[:id]}/libraries")
+ display @lib_files
+ end
+
+ def more_versions_link(cookbook)
+ link_to("+", "JavaScript:void(0);",
+ :title => "show other versions of #{cookbook}",
+ :data => cookbook,
+ :class => "cookbook_version_toggle")
+ end
+
+ def all_versions_link(cookbook)
+ link_to("show all versions...", "JavaScript:void(0);",
+ :class => "show_all",
+ :id => "#{cookbook}_show_all",
+ :data => cookbook,
+ :title => "show all versions of #{cookbook}")
+ end
+
+ def cookbook_link(version)
+ show_specific_version_cookbook_path(version, @cookbook_id)
+ end
+
+ def cookbook_parts
+ Chef::CookbookVersion::COOKBOOK_SEGMENTS.map do |p|
+ part = p.to_s
+ case part
+ when "files"
+ [part, "plain"]
+ else
+ [part, "ruby"]
+ end
+ end.sort { |a, b| a[0] <=> b[0] }
+ end
+
+ def highlight_content(url, type)
+ case type
+ when "plain"
+ show_plain_file(url)
+ else
+ syntax_highlight(url)
+ end
+ end
+
+ private
+
+ def fetch_cookbook_versions(num_versions, options={})
+ opts = { :use_envs => true, :cookbook => nil }.merge(options)
+ url = if opts[:use_envs]
+ env = session[:environment] || "_default"
+ "environments/#{env}/cookbooks"
+ else
+ "cookbooks"
+ end
+ # we want to display at most 5 versions, but we ask for 6. This
+ # tells us if we should display a 'show all' button or not.
+ url += "/#{opts[:cookbook]}" if opts[:cookbook]
+ url += "?num_versions=#{num_versions}"
+ begin
+ result = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest(url)
+ result.inject({}) do |ans, (name, cb)|
+ cb["versions"].each do |v|
+ v["url"] = url(:show_specific_version_cookbook, :cookbook_id => name,
+ :cb_version => v["version"])
+ end
+ ans[name] = cb["versions"]
+ ans
+ end
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => $!}
+ {}
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/databag_items_controller.rb b/chef-server-webui/app/controllers/databag_items_controller.rb
new file mode 100644
index 0000000000..165ea09c4e
--- /dev/null
+++ b/chef-server-webui/app/controllers/databag_items_controller.rb
@@ -0,0 +1,99 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/data_bag_item'
+
+class DatabagItemsController < ApplicationController
+
+ respond_to :html, :json
+ before_filter :login_required
+
+ def edit
+ begin
+ @databag_item = Chef::DataBagItem.load(params[:databag_id], params[:id])
+ @default_data = @databag_item.raw_data
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not load the databag item" }
+ end
+ end
+
+ def update
+ begin
+ @databag_item = Chef::DataBagItem.new
+ @databag_item.data_bag params[:databag_id]
+ @databag_item.raw_data = Chef::JSONCompat.from_json(params[:json_data])
+ raise ArgumentError, "Updating id is not allowed" unless @databag_item.raw_data['id'] == params[:id] #to be consistent with other objects, changing id is not allowed.
+ @databag_item.save
+ redirect_to databag_databag_items_url(params[:databag_id], @databag_item.name), :notice => "Updated Databag Item #{@databag_item.name}"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not update the databag item" }
+ @databag_item = Chef::DataBagItem.load(params[:databag_id], params[:id])
+ @default_data = @databag_item
+ render :edit
+ end
+ end
+
+ def new
+ @default_data = {'id'=>''}
+ end
+
+ def create
+ begin
+ @databag_name = params[:databag_id]
+ @databag_item = Chef::DataBagItem.new
+ @databag_item.data_bag @databag_name
+ @databag_item.raw_data = Chef::JSONCompat.from_json(params[:json_data])
+ @databag_item.create
+ redirect_to(databag_databag_items_url(@databag_name), :notice => "Databag item created successfully" )
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not create databag item" }
+ render :new
+ end
+ end
+
+ def index
+ end
+
+ def show
+ begin
+ @databag_name = params[:databag_id]
+ @databag_item_name = params[:id]
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @databag_item = r.get_rest("data/#{params[:databag_id]}/#{params[:id]}")
+ display @databag_item
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to databag_databag_items_url, :error => "Could not show the databag item"
+ end
+ end
+
+ def destroy(databag_id=params[:databag_id], item_id=params[:id])
+ begin
+ @databag_item = Chef::DataBagItem.new
+ @databag_item.destroy(databag_id, item_id)
+ redirect_to databag_databag_items_url, :notice => "Databag item deleted successfully"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to databag_databag_items_url, :error => "Could not delete databag item"
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/databags_controller.rb b/chef-server-webui/app/controllers/databags_controller.rb
new file mode 100644
index 0000000000..2c55326937
--- /dev/null
+++ b/chef-server-webui/app/controllers/databags_controller.rb
@@ -0,0 +1,82 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/data_bag'
+
+class DatabagsController < ApplicationController
+
+ respond_to :html, :json
+ before_filter :login_required
+ before_filter :require_admin
+
+ def new
+ @databag = Chef::DataBag.new
+ end
+
+ def create
+ begin
+ @databag = Chef::DataBag.new
+ @databag.name params[:name]
+ @databag.create
+ redirect_to databags_url, :notice => "Created Databag #{@databag.name}"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not create databag" }
+ render :new
+ end
+ end
+
+ def index
+ @databags = begin
+ Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data")
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Could not list databags" }
+ {}
+ end
+ end
+
+ def show
+ begin
+ @databag_name = params[:id]
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ @databag = r.get_rest("data/#{params[:id]}")
+ raise NotFound unless @databag
+ display @databag
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @databags = Chef::DataBag.list
+ @_message = { :error => "Could not load databag"}
+ render :index
+ end
+ end
+
+ def destroy
+ begin
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ r.delete_rest("data/#{params[:id]}")
+ redirect_to databags_url, :notice => "Data bag #{params[:id]} deleted successfully"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @databags = Chef::DataBag.list
+ @_message = { :error => "Could not delete databag"}
+ render :index
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/environments_controller.rb b/chef-server-webui/app/controllers/environments_controller.rb
new file mode 100644
index 0000000000..738ca0fe99
--- /dev/null
+++ b/chef-server-webui/app/controllers/environments_controller.rb
@@ -0,0 +1,216 @@
+#
+# Author:: Stephen Delano (<stephen@opscode.com>)
+# Copyright:: Copyright (c) 2010 Opscode, Inc.
+# 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/environment'
+
+class EnvironmentsController < ApplicationController
+
+ respond_to :html, :json
+ before_filter :login_required
+ before_filter :require_admin, :only => [:create, :update, :destroy]
+
+ # GET /environments
+ def index
+ @environment_list = begin
+ Chef::Environment.list
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = "Could not list environments"
+ {}
+ end
+ end
+
+ # GET /environments/:id
+ def show
+ load_environment
+ end
+
+ # GET /environemnts/new
+ def new
+ @environment = Chef::Environment.new
+ load_cookbooks
+ render :new
+ end
+
+ # POST /environments
+ def create
+ @environment = Chef::Environment.new
+ if @environment.update_from_params(processed_params=process_params)
+ begin
+ @environment.create
+ redirect_to environments_url :notice => "Created Environment #{@environment.name}"
+ rescue Net::HTTPServerException => e
+ if conflict?(e)
+ Chef::Log.debug("Got 409 conflict creating environment #{params[:name]}\n#{format_exception(e)}")
+ redirect_to new_environment_url, :error => "An environment with that name already exists"
+ elsif forbidden?(e)
+ # Currently it's not possible to get 403 here. I leave the code here for completeness and may be useful in the future.[nuo]
+ Chef::Log.debug("Got 403 forbidden creating environment #{params[:name]}\n#{format_exception(e)}")
+ redirect_to new_environment_url, :error => "Permission Denied. You do not have permission to create an environment."
+ else
+ Chef::Log.error("Error communicating with the API server\n#{format_exception(e)}")
+ raise
+ end
+ end
+ else
+ load_cookbooks
+ # By rendering :new, the view shows errors from @environment.invalid_fields
+ render :new
+ end
+ end
+
+ # GET /environments/:id/edit
+ def edit
+ load_environment
+ if @environment.name == "_default"
+ msg = { :warning => "The '_default' environment cannot be edited." }
+ redirect_to environments_url, :flash => msg
+ return
+ end
+ load_cookbooks
+ end
+
+ # PUT /environments/:id
+ def update
+ load_environment
+ if @environment.update_from_params(process_params(params[:id]))
+ begin
+ @environment.save
+ redirect_to environment_url(@environment.name), :notice => "Updated Environment #{@environment.name}"
+ rescue Net::HTTPServerException => e
+ if forbidden?(e)
+ # Currently it's not possible to get 403 here. I leave the code here for completeness and may be useful in the future.[nuo]
+ Chef::Log.debug("Got 403 forbidden updating environment #{params[:name]}\n#{format_exception(e)}")
+ redirect_to edit_environment_url, :error => "Permission Denied. You do not have permission to update an environment."
+ else
+ Chef::Log.error("Error communicating with the API server\n#{format_exception(e)}")
+ raise
+ end
+ end
+ else
+ load_cookbooks
+ # By rendering :new, the view shows errors from @environment.invalid_fields
+ render :edit
+ end
+ end
+
+ # DELETE /environments/:id
+ def destroy
+ begin
+ @environment = Chef::Environment.load(params[:id])
+ @environment.destroy
+ redirect_to environments_url, :notice => "Environment #{@environment.name} deleted successfully."
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @environment_list = Chef::Environment.list()
+ @_message = {:error => "Could not delete environment #{params[:id]}: #{e.message}"}
+ render :index
+ end
+ end
+
+ # GET /environments/:environment_id/cookbooks
+ def list_cookbooks
+ # TODO: rescue loading the environment
+ @environment = Chef::Environment.load(params[:environment_id])
+ @cookbooks = begin
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ r.get_rest("/environments/#{params[:environment_id]}/cookbooks").inject({}) do |res, (cookbook, url)|
+ # we just want the cookbook name and the version
+ res[cookbook] = url.split('/').last
+ res
+ end
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = "Could not load cookbooks for environment #{params[:environment_id]}"
+ {}
+ end
+ end
+
+ # GET /environments/:environment_id/nodes
+ def list_nodes
+ # TODO: rescue loading the environment
+ @environment = Chef::Environment.load(params[:environment_id])
+ @nodes = begin
+ r = Chef::REST.new(Chef::Config[:chef_server_url])
+ r.get_rest("/environments/#{params[:environment_id]}/nodes").keys.sort
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = "Could not load nodes for environment #{params[:environment_id]}"
+ []
+ end
+ end
+
+ # GET /environments/:environment/recipes
+ def list_recipes
+ respond_with :recipes => list_available_recipes_for(params[:environment_id])
+ end
+
+ # GET /environments/:environment_id/set
+ def select_environment
+ name = params[:environment_id]
+ referer = request.referer || nodes_url
+ if name == '_none'
+ session[:environment] = nil
+ else
+ # TODO: check if environment exists
+ session[:environment] = name
+ end
+ redirect_to referer
+ end
+
+ private
+
+ def load_environment
+ @environment = begin
+ Chef::Environment.load(params[:id])
+ rescue Net::HTTPServerException => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = "Could not load environment #{params[:id]}"
+ @environment = Chef::Environment.new
+ false
+ end
+ end
+
+ def load_cookbooks
+ begin
+ # @cookbooks is a hash, keys are cookbook names, values are their URIs.
+ @cookbooks = Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("cookbooks").keys.sort
+ rescue Net::HTTPServerException => e
+ Chef::Log.error(format_exception(e))
+ redirect_to new_environment_url, :error => "Could not load the list of available cookbooks."
+ end
+ end
+
+ def process_params(name=params[:name])
+ {:name => name, :description => params[:description], :default_attributes => params[:default_attributes], :override_attributes => params[:override_attributes], :cookbook_version => search_params_for_cookbook_version_constraints}
+ end
+
+ def search_params_for_cookbook_version_constraints
+ cookbook_version_constraints = {}
+ index = 0
+ params.each do |k,v|
+ cookbook_name_box_id = k[/cookbook_name_(\d+)/, 1]
+ unless cookbook_name_box_id.nil? || v.nil? || v.empty?
+ cookbook_version_constraints[index] = v + " " + params["operator_#{cookbook_name_box_id}"] + " " + params["cookbook_version_#{cookbook_name_box_id}"].strip # e.g. {"0" => "foo > 0.3.0"}
+ index = index + 1
+ end
+ end
+ Chef::Log.debug("cookbook version constraints are: #{cookbook_version_constraints.inspect}")
+ cookbook_version_constraints
+ end
+end
diff --git a/chef-server-webui/app/controllers/nodes_controller.rb b/chef-server-webui/app/controllers/nodes_controller.rb
new file mode 100644
index 0000000000..6a646a78d0
--- /dev/null
+++ b/chef-server-webui/app/controllers/nodes_controller.rb
@@ -0,0 +1,143 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/node'
+
+class NodesController < ApplicationController
+
+ respond_to :html
+
+ before_filter :login_required
+ before_filter :require_admin, :only => [:destroy]
+
+ def index
+ begin
+ if session[:environment]
+ node_hash = Chef::Node.list_by_environment(session[:environment])
+ else
+ node_hash = Chef::Node.list
+ end
+ @node_list = node_hash.keys.sort
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not list nodes"}
+ @node_hash = {}
+ end
+ end
+
+ def show
+ begin
+ @node =Chef::Node.load(params[:id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not load node #{params[:id]}"}
+ @node = Chef::Node.new
+ end
+ end
+
+ def new
+ begin
+ @node = Chef::Node.new
+ @node.chef_environment(session[:environment] || "_default")
+ @available_recipes = list_available_recipes_for(@node.chef_environment)
+ @available_roles = Chef::Role.list.keys.sort
+ @run_list = @node.run_list
+ @env = session[:environment]
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @node_list = Chef::Node.list()
+ @_message = {:error => "Could not load available recipes, roles, or the run list"}
+ render :index
+ end
+ end
+
+ def edit
+ begin
+ @node = Chef::Node.load(params[:id])
+ @env = @node.chef_environment
+ @available_recipes = list_available_recipes_for(@node.chef_environment)
+ @available_roles = Chef::Role.list.keys.sort
+ @run_list = @node.run_list
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @node = Chef::Node.new
+ @available_recipes = []
+ @available_roles = []
+ @run_list = []
+ @_message = {:error => "Could not load node #{params[:id]}"}
+ end
+ end
+
+ def create
+ begin
+ @node = Chef::Node.new
+ @node.name params[:name]
+ @node.chef_environment params[:chef_environment]
+ @node.normal_attrs = Chef::JSONCompat.from_json(params[:attributes])
+ @node.run_list.reset!(params[:for_node] ? params[:for_node] : [])
+ raise ArgumentError, "Node name cannot be blank" if (params[:name].nil? || params[:name].length==0)
+ @node.create
+ redirect_to nodes_url, :notice => "Created Node #{@node.name}"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @node.normal_attrs = Chef::JSONCompat.from_json(params[:attributes])
+ @available_recipes = list_available_recipes_for(@node.chef_environment)
+ @available_roles = Chef::Role.list.keys.sort
+ @node.run_list params[:for_node]
+ @run_list = @node.run_list
+ flash[:error] = "Exception raised creating node, #{e.message.length <= 150 ? e.message : "please check logs for details"}"
+ render :new
+ end
+ end
+
+ def update
+ begin
+ @node = Chef::Node.load(params[:id])
+ @node.chef_environment(params[:chef_environment])
+ @node.run_list.reset!(params[:for_node] ? params[:for_node] : [])
+ @node.normal_attrs = Chef::JSONCompat.from_json(params[:attributes])
+ @node.save
+ @_message = { :notice => "Updated Node" }
+ render :show
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @available_recipes = list_available_recipes_for(@node.chef_environment)
+ @available_roles = Chef::Role.list.keys.sort
+ @run_list = Chef::RunList.new
+ @run_list.reset!(params[:for_node])
+ flash[:error] = "Exception raised updating node, #{e.message.length <= 150 ? e.message : "please check logs for details"}"
+ render :edit
+ end
+ end
+
+ def destroy
+ begin
+ @node = Chef::Node.load(params[:id])
+ @node.destroy
+ redirect_to nodes_url, :notice => "Node #{params[:id]} deleted successfully"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @node_list = Chef::Node.list()
+ flash[:error] = "Could not delete the node"
+ render :index
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/openid_consumer_controller.rb b/chef-server-webui/app/controllers/openid_consumer_controller.rb
new file mode 100644
index 0000000000..d062d8f3a7
--- /dev/null
+++ b/chef-server-webui/app/controllers/openid_consumer_controller.rb
@@ -0,0 +1,157 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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 (Chef::Config[:openid_cstore_couchdb] ? 'openid-store-couchdb' : 'openid/store/filesystem')
+
+class OpenidConsumerController < ApplicationController
+
+ respond_to :html
+
+ def index
+ if request.xhr?
+ render :layout => false
+ else
+ render :layout => 'login'
+ end
+ end
+
+ def start
+ oid = params[:openid_identifier]
+ begin
+ oidreq = consumer.begin(oid)
+ rescue OpenID::OpenIDError => e
+ raise BadRequest, "Discovery failed for #{oid}: #{e}"
+ end
+
+ return_to = openid_consumer_complete_url
+ realm = openid_consumer_url
+
+ if oidreq.send_redirect?(realm, return_to, params[:immediate])
+ return redirect_to(oidreq.redirect_url(realm, return_to, params[:immediate]))
+ else
+ @form_text = oidreq.form_markup(realm, return_to, params[:immediate], {'id' => 'openid_form'})
+ end
+ end
+
+ def login
+ if session[:user]
+ redirect_to nodes_url, :flash => { :warning => "You've already logged in with user #{session[:user]}" }
+ else
+ oid = params[:openid_identifier]
+ raise(Unauthorized, "Sorry, #{oid} is not an authorized OpenID.") unless is_authorized_openid_identifier?(oid, Chef::Config[:authorized_openid_identifiers])
+ raise(Unauthorized, "Sorry, #{oid} is not an authorized OpenID Provider.") unless is_authorized_openid_provider?(oid, Chef::Config[:authorized_openid_providers])
+ start
+ end
+ end
+
+ def complete
+ # FIXME - url_for some action is not necessarily the current URL.
+ current_url = openid_consumer_complete_url
+ parameters = params.reject{|k,v| k == "controller" || k == "action"}
+ oidresp = consumer.complete(parameters, current_url)
+ case oidresp.status
+ when OpenID::Consumer::FAILURE
+ raise BadRequest, "Verification failed: #{oidresp.message}" + (oidresp.display_identifier ? " for identifier '#{oidresp.display_identifier}'" : "")
+ when OpenID::Consumer::SUCCESS
+ #session[:openid] = oidresp.identity_url
+ # The "if" condition no longer seems need to/can be reached, so I took it out. [nuo]
+ #
+ # if oidresp.display_identifier =~ /openid\/server\/node\/(.+)$/
+ # reg_name = $1
+ # reg = Chef::OpenIDRegistration.load(reg_name)
+ # Chef::Log.error("#{reg_name} is an admin #{reg.admin}")
+ # session[:level] = reg.admin ? :admin : :node
+ # session[:node_name] = $1
+ #else
+ users = Chef::WebUIUser.list
+ #TODO: This is expensive. Should think of a better way [nuo]
+ # Go through each user object and check if the current OpenID associates with the user
+ users.each do |u, url|
+ user = Chef::WebUIUser.load(u)
+ if user.openid == oidresp.identity_url
+ session[:user] = user.name
+ if user.admin
+ session[:level] = :admin
+ else
+ session[:level] = :user
+ end
+ break
+ end
+ end
+ if session[:user].nil?
+ redirect_to openid_consumer_url, :error => "No user is associated with this OpenID."
+ return "No user is associated with this OpenID."
+ end
+ #end
+ redirect_back_or_default(nodes_url)
+ 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 openid_consumer_url
+ end
+
+ def logout
+ cleanup_session
+ redirect_to top_url
+ end
+
+ private
+ def is_authorized_openid_provider?(openid, authorized_providers)
+ Chef::Log.debug("Checking for valid openid provider: openid: #{openid}, authorized providers: #{authorized_providers}")
+ if authorized_providers and openid
+ if authorized_providers.length > 0
+ authorized_providers.detect { |p| Chef::Log.debug("Openid: #{openid} (#{openid.class}), p: #{p} (#{p.class})"); openid.match(p) }
+ else
+ true
+ end
+ else
+ true
+ end
+ end
+
+ def is_authorized_openid_identifier?(openid, authorized_identifiers)
+ Chef::Log.debug("Checking for valid openid identifier: openid: #{openid}, authorized openids: #{authorized_identifiers}")
+ if authorized_identifiers and openid
+ if authorized_identifiers.length > 0
+ authorized_identifiers.detect { |p| Chef::Log.debug("Openid: #{openid} (#{openid.class}), p: #{p} (#{p.class})"); openid == p }
+ else
+ true
+ end
+ else
+ true
+ end
+ end
+
+ def consumer
+ @consumer ||= OpenID::Consumer.new(session,
+ if Chef::Config[:openid_cstore_couchdb]
+ OpenID::Store::CouchDB.new(Chef::Config[:couchdb_url])
+ else
+ OpenID::Store::Filesystem.new(Chef::Config[:openid_cstore_path])
+ end)
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/roles_controller.rb b/chef-server-webui/app/controllers/roles_controller.rb
new file mode 100644
index 0000000000..16b93de8ca
--- /dev/null
+++ b/chef-server-webui/app/controllers/roles_controller.rb
@@ -0,0 +1,137 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/role'
+
+class RolesController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required
+ before_filter :require_admin, :only => [:destroy]
+
+ # GET /roles
+ def index
+ @role_list = begin
+ Chef::Role.list()
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not list roles"}
+ {}
+ end
+ end
+
+ # GET /roles/:id
+ def show
+ @role = begin
+ Chef::Role.load(params[:id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not load role #{params[:id]}."}
+ Chef::Role.new
+ end
+
+ @current_env = session[:environment] || "_default"
+ @env_run_list_exists = @role.env_run_lists.has_key?(@current_env)
+ @run_list = @role.run_list_for(@current_env)
+ @recipes = @run_list.expand(@current_env, 'server').recipes
+ end
+
+ # GET /roles/new
+ def new
+ begin
+ @role = Chef::Role.new
+ @available_roles = Chef::Role.list.keys.sort
+ @environments = Chef::Environment.list.keys.sort
+ @run_lists = @environments.inject({}) { |run_lists, env| run_lists[env] = @role.env_run_lists[env]; run_lists}
+ @current_env = "_default"
+ @available_recipes = list_available_recipes_for(@current_env)
+ @existing_run_list_environments = @role.env_run_lists.keys
+ # merb select helper has no :include_blank => true, so fix the view in the controller.
+ @existing_run_list_environments.unshift('')
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to roles_url, :error => "Could not load available recipes, roles, or the run list."
+ end
+ end
+
+ # GET /roles/:id/edit
+ def edit
+ begin
+ @role = Chef::Role.load(params[:id])
+ @available_roles = Chef::Role.list.keys.sort
+ @environments = Chef::Environment.list.keys.sort
+ @current_env = session[:environment] || "_default"
+ @run_list = @role.run_list
+ @run_lists = @environments.inject({}) { |run_lists, env| run_lists[env] = @role.env_run_lists[env]; run_lists}
+ @existing_run_list_environments = @role.env_run_lists.keys
+ # merb select helper has no :include_blank => true, so fix the view in the controller.
+ @existing_run_list_environments.unshift('')
+ @available_recipes = list_available_recipes_for(@current_env)
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to roles_url, :error => "Could not load role #{params[:id]}. #{e.message}"
+ end
+ end
+
+ # POST /roles
+ def create
+ begin
+ @role = Chef::Role.new
+ @role.name(params[:name])
+ @role.env_run_lists(params[:env_run_lists])
+ @role.description(params[:description]) if params[:description] != ''
+ @role.default_attributes(Chef::JSONCompat.from_json(params[:default_attributes])) if params[:default_attributes] != ''
+ @role.override_attributes(Chef::JSONCompat.from_json(params[:override_attributes])) if params[:override_attributes] != ''
+ @role.create
+ redirect_to roles_url, :notice => "Created Role #{@role.name}"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to new_role_url, :error => "Could not create role. #{e.message}"
+ end
+ end
+
+ # PUT /roles/:id
+ def update
+ begin
+ @role = Chef::Role.load(params[:id])
+ @role.env_run_lists(params[:env_run_lists])
+ @role.description(params[:description]) if params[:description] != ''
+ @role.default_attributes(Chef::JSONCompat.from_json(params[:default_attributes])) if params[:default_attributes] != ''
+ @role.override_attributes(Chef::JSONCompat.from_json(params[:override_attributes])) if params[:override_attributes] != ''
+ @role.save
+ redirect_to role_url(params[:id]), :notice => "Updated Role"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to edit_role_url(params[:id]), :error => "Could not update role #{params[:id]}. #{e.message}"
+ end
+ end
+
+ # DELETE /roles/:id
+ def destroy
+ begin
+ @role = Chef::Role.load(params[:id])
+ @role.destroy
+ redirect_to roles_url, :notice => "Role #{@role.name} deleted successfully."
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ redirect_to roles_url, :error => "Could not delete role #{params[:id]}"
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/search_controller.rb b/chef-server-webui/app/controllers/search_controller.rb
new file mode 100644
index 0000000000..7c118f6670
--- /dev/null
+++ b/chef-server-webui/app/controllers/search_controller.rb
@@ -0,0 +1,60 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# 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/search/query'
+
+class SearchController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required
+
+ def index
+ @s = Chef::Search::Query.new
+ @search_indexes = begin
+ @s.list_indexes
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = {:error => "Could not list search indexes"}
+ {}
+ end
+ end
+
+ def show
+ begin
+ @s = Chef::Search::Query.new
+ query = (params[:q].nil? || params[:q].empty?) ? "*:*" : URI.escape(params[:q], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
+ @results = @s.search(params[:id], query)
+ @type = if params[:id].to_s == "node" || params[:id].to_s == "role" || params[:id].to_s == "client" || params[:id].to_s == "environment"
+ params[:id]
+ else
+ "databag"
+ end
+ @results = @results - @results.last(2)
+ @results.each do |result|
+ result.delete(nil)
+ end
+ @results
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @_message = { :error => "Unable to find the #{params[:id]}. (#{$!})" }
+ @search_indexes = @s.list_indexes
+ render :index
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/search_entries_controller.rb b/chef-server-webui/app/controllers/search_entries_controller.rb
new file mode 100644
index 0000000000..9b7304f594
--- /dev/null
+++ b/chef-server-webui/app/controllers/search_entries_controller.rb
@@ -0,0 +1,62 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Christopher Brown (<cb@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/search'
+#require 'chef/queue'
+
+class SearchEntriesController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required
+
+ def index
+ @s = Chef::Search.new
+ @entries = @s.search(params[:search_id])
+ end
+
+ def show
+ @s = Chef::Search.new
+ @entry = @s.search(params[:search_id], "id:'#{params[:search_id]}_#{params[:id]}'").first
+ 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)
+ redirect_to search_url
+ 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
+ redirect_to search_url
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/status_controller.rb b/chef-server-webui/app/controllers/status_controller.rb
new file mode 100644
index 0000000000..f2dc56c089
--- /dev/null
+++ b/chef-server-webui/app/controllers/status_controller.rb
@@ -0,0 +1,42 @@
+#
+# Author:: Joe Williams (joe@joetify.com)
+# Author:: Nuo Yan (nuo@opscode.com)
+# Copyright:: Copyright (c) 2009 Opscode, Inc.
+# 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/node'
+
+class StatusController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required
+
+ def index
+ begin
+ @status = Chef::Node.list(true)
+ if session[:environment]
+ @status = Chef::Node.list_by_environment(session[:environment],true)
+ else
+ @status = Chef::Node.list(true)
+ end
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @status = {}
+ @_message = {:error => "Could not list status"}
+ end
+ end
+
+end
diff --git a/chef-server-webui/app/controllers/users_controller.rb b/chef-server-webui/app/controllers/users_controller.rb
new file mode 100644
index 0000000000..1936f4bf84
--- /dev/null
+++ b/chef-server-webui/app/controllers/users_controller.rb
@@ -0,0 +1,177 @@
+#
+# Author:: Nuo Yan (<nuo@opscode.com>)
+# Copyright:: Copyright (c) 2008 Opscode, Inc.
+# 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/webui_user'
+require 'uri'
+
+class UsersController < ApplicationController
+
+ respond_to :html
+ before_filter :login_required, :except => [:login, :login_exec, :complete]
+ before_filter :require_admin, :except => [:login, :login_exec, :complete, :show, :edit, :logout, :destroy]
+
+ # List users, only if the user is admin.
+ def index
+ begin
+ @users = Chef::WebUIUser.list
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ set_user_and_redirect
+ end
+ end
+
+ # Edit user. Admin can edit everyone, non-admin user can only edit itself.
+ def edit
+ begin
+ @user = Chef::WebUIUser.load(params[:user_id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ set_user_and_redirect
+ end
+ end
+
+ # Show the details of a user. If the user is not admin, only able to show itself; otherwise able to show everyone
+ def show
+ begin
+ @user = Chef::WebUIUser.load(params[:user_id])
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ set_user_and_redirect
+ end
+ end
+
+ # PUT to /users/:user_id/update
+ def update
+ begin
+ @user = Chef::WebUIUser.load(params[:user_id])
+
+ if session[:level] == :admin and !is_last_admin?
+ @user.admin = params[:admin] =~ /1/ ? true : false
+ end
+
+ if params[:user_id] == session[:user] && params[:admin] == 'false'
+ session[:level] = :user
+ end
+
+ if not params[:new_password].nil? and not params[:new_password].length == 0
+ @user.set_password(params[:new_password], params[:confirm_new_password])
+ end
+
+ if params[:openid].length == 0 or params[:openid].nil?
+ @user.set_openid(nil)
+ else
+ @user.set_openid(URI.parse(params[:openid]).normalize.to_s)
+ end
+ @user.save
+ flash[:notice] = "Updated user #{@user.name}."
+ render :show
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @u = Chef::WebUIUser.load(params[:user_id])
+ flash[:error] = "Could not update user #{@user.name}."
+ render :edit
+ end
+ end
+
+ def new
+ begin
+ @user = Chef::WebUIUser.new
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ set_user_and_redirect
+ end
+ end
+
+ def create
+ begin
+ @user = Chef::WebUIUser.new
+ @user.name = params[:name]
+ @user.set_password(params[:password], params[:password2])
+ @user.admin = true if params[:admin]
+ (params[:openid].length == 0 || params[:openid].nil?) ? @user.set_openid(nil) : @user.set_openid(URI.parse(params[:openid]).normalize.to_s)
+ @user.create
+ redirect_to users_url, :notice => "Created User #{params[:name]}"
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ flash[:error] = "Could not create user"
+ session[:level] != :admin ? set_user_and_redirect : (render :new)
+ end
+ end
+
+ def login
+ @user = Chef::WebUIUser.new
+ session[:user] ? (redirect_to nodes_url, :flash => { :warning => "You've already logged in with user #{session[:user]}" } ) : (render :layout => 'login')
+ end
+
+ def login_exec
+ begin
+ @user = Chef::WebUIUser.load(params[:name])
+ raise(Unauthorized, "Wrong username or password.") unless @user.verify_password(params[:password])
+ complete
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ @user = Chef::WebUIUser.new
+ flash[:error] = "Could not complete logging in."
+ render :login
+ end
+ end
+
+ def complete
+ session[:user] = params[:name]
+ session[:level] = (@user.admin == true ? :admin : :user)
+ (@user.name == Chef::Config[:web_ui_admin_user_name] && @user.verify_password(Chef::Config[:web_ui_admin_default_password])) ? redirect_to(users_edit_url(@user.name), :flash => { :warning => "Please change the default password" }) : redirect_back_or_default(nodes_url)
+ end
+
+ def logout
+ cleanup_session
+ redirect_to top_url
+ end
+
+ def destroy
+ begin
+ raise Forbidden, "A non-admin user can only delete itself" if (params[:user_id] != session[:user] && session[:level] != :admin)
+ raise Forbidden, "The last admin user cannot be deleted" if (is_admin? && is_last_admin? && session[:user] == params[:user_id])
+ @user = Chef::WebUIUser.load(params[:user_id])
+ @user.destroy
+ logout if params[:user_id] == session[:user]
+ redirect_to users_url, :notice => "User #{params[:user_id]} deleted successfully."
+ rescue => e
+ Chef::Log.error("#{e}\n#{e.backtrace.join("\n")}")
+ session[:level] != :admin ? set_user_and_redirect : redirect_to_list_users({ :error => $! })
+ end
+ end
+
+ private
+
+ def set_user_and_redirect
+ begin
+ @user = Chef::WebUIUser.load(session[:user]) rescue (raise NotFound, "Cannot find User #{session[:user]}, maybe it got deleted by an Administrator.")
+ rescue
+ logout_and_redirect_to_login
+ else
+ redirect_to users_show_url(session[:user]), :error => $!
+ end
+ end
+
+ def redirect_to_list_users(message)
+ flash = message
+ @users = Chef::WebUIUser.list
+ render :index
+ end
+
+end
diff --git a/chef-server-webui/config/routes.rb b/chef-server-webui/config/routes.rb
index 6bcc587859..8bc44f5b46 100644
--- a/chef-server-webui/config/routes.rb
+++ b/chef-server-webui/config/routes.rb
@@ -1,58 +1,88 @@
+#
+# Author:: Adam Jacob (<adam@opscode.com>)
+# Author:: Daniel DeLeo (<dan@opscode.com>)
+# Copyright:: Copyright (c) 2008-2010 Opscode, Inc.
+# 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.
+#
+
ChefServerWebui::Application.routes.draw do
- # The priority is based upon order of creation:
- # first created -> highest priority.
-
- # Sample of regular route:
- # match 'products/:id' => 'catalog#view'
- # Keep in mind you can assign values other than :controller and :action
-
- # Sample of named route:
- # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
- # This route can be invoked with purchase_url(:id => product.id)
-
- # Sample resource route (maps HTTP verbs to controller actions automatically):
- # resources :products
-
- # Sample resource route with options:
- # resources :products do
- # member do
- # get 'short'
- # post 'toggle'
- # end
- #
- # collection do
- # get 'sold'
- # end
- # end
-
- # Sample resource route with sub-resources:
- # resources :products do
- # resources :comments, :sales
- # resource :seller
- # end
-
- # Sample resource route with more complex sub-resources
- # resources :products do
- # resources :comments
- # resources :sales do
- # get 'recent', :on => :collection
- # end
- # end
-
- # Sample resource route within a namespace:
- # namespace :admin do
- # # Directs /admin/products/* to Admin::ProductsController
- # # (app/controllers/admin/products_controller.rb)
- # resources :products
- # end
-
- # You can have the root of your site routed with "root"
- # just remember to delete public/index.html.
- # root :to => 'welcome#index'
-
- # See how all your routes lay out with "rake routes"
-
- # This is a legacy wild controller route that's not recommended for RESTful applications.
- # Note: This route will make all actions in every controller accessible via GET requests.
- # match ':controller(/:action(/:id))(.:format)'
+ resources :nodes, :id => /[^\/]+/
+ match "/nodes/_environments/:environment_id", :to => "nodes#index", :as => :nodes_by_environment
+
+ resources :clients, :id => /[^\/]+/
+ resources :roles
+
+ resources :environments do
+ match "/recipes", :only => :get, :to => "environments#list_recipes"
+ match "/cookbooks", :to => "environments#list_cookbooks", :as => :cookbooks, :only => :get
+ match "/nodes", :to => "environments#list_nodes", :as => :nodes, :only => :get
+ match "/select", :to => "environments#select_environment", :as => :select, :only => :get
+ end
+
+ # match '/environments/create' :to => "environments#create", :as => :environments_create
+
+ match "/status", :to => "status#index", :as => :status, :only => :get
+
+ resources :searches, :path => "search", :controller => "search"
+ match "/search/:search_id/entries", :only => 'get', :to => "search_entries", :action => "index"
+ match "/search/:search_id/entries", :only => 'post', :to => "search_entries", :action => "create"
+ match "/search/:search_id/entries/:id", :only => 'get', :to => "search_entries", :action => "show"
+ match "/search/:search_id/entries/:id", :only => 'put', :to => "search_entries", :action => "create"
+ match "/search/:search_id/entries/:id", :only => 'post', :to => "search_entries", :action => "update"
+ match "/search/:search_id/entries/:id", :only => 'delete', :to => "search_entries", :action => "destroy"
+
+ match "/cookbooks/_attribute_files", :to => "cookbooks#attribute_files"
+ match "/cookbooks/_recipe_files", :to => "cookbooks#recipe_files"
+ match "/cookbooks/_definition_files", :to => "cookbooks#definition_files"
+ match "/cookbooks/_library_files", :to => "cookbooks#library_files"
+ match "/cookbooks/_environments/:environment_id", :to => "cookbooks#index", :as => :cookbooks_by_environment
+
+ match "/cookbooks/:cookbook_id", :cookbook_id => /[\w\.]+/, :only => :get, :to => "cookbooks#cb_versions"
+ match "/cookbooks/:cookbook_id/:cb_version", :cb_version => /[\w\.]+/, :only => :get, :to => "cookbooks#show", :as => :show_specific_version_cookbook
+ resources :cookbooks
+
+ resources :clients
+
+ match "/databags/:databag_id/databag_items", :only => :get, :to => "databags#show", :id=>":databag_id"
+
+ resources :databags do
+ resources :databag_items
+ end
+
+ match '/openid/consumer', :to => 'openid_consumer#index', :as => :openid_consumer
+ match '/openid/consumer/start', :to => 'openid_consumer#start', :as => :openid_consumer_start
+ match '/openid/consumer/login', :to => 'openid_consumer#login', :as => :openid_consumer_login
+ match '/openid/consumer/complete', :to => 'openid_consumer#complete', :as => :openid_consumer_complete
+ match '/openid/consumer/logout', :to => 'openid_consumer#logout', :as => :openid_consumer_logout
+
+ match '/login', :to => 'users#login', :as => :users_login
+ match '/logout', :to => 'users#logout', :as => :users_logout
+
+ match '/users', :to => 'users#index', :as => :users
+ match '/users/create', :to => 'users#create', :as => :users_create
+ match '/users/start', :to => 'users#start', :as => :users_start
+
+ match '/users/login', :to => 'users#login', :as => :users_login
+ match '/users/login_exec', :to => 'users#login_exec', :as => :users_login_exec
+ match '/users/complete', :to => 'users#complete', :as => :users_complete
+ match '/users/logout', :to => 'users#logout', :as => :users_logout
+ match '/users/new', :to => 'users#new', :as => :users_new
+ match '/users/:user_id/edit', :to => 'users#edit', :as => :users_edit
+ match '/users/:user_id', :to => 'users#show', :as => :users_show
+ match '/users/:user_id/delete', :only => :delete, :to => 'users#destroy', :as => :users_delete
+ match '/users/:user_id/update', :only => :put, :to => 'users#update', :as => :users_update
+
+ match '/', :to => 'nodes#index', :as => :top
end