chef-server-slice 0.7.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. data/LICENSE +201 -0
  2. data/README.rdoc +135 -0
  3. data/app/controllers/application.rb +228 -0
  4. data/app/controllers/cookbook_attributes.rb +59 -0
  5. data/app/controllers/cookbook_definitions.rb +60 -0
  6. data/app/controllers/cookbook_files.rb +94 -0
  7. data/app/controllers/cookbook_libraries.rb +60 -0
  8. data/app/controllers/cookbook_recipes.rb +59 -0
  9. data/app/controllers/cookbook_templates.rb +80 -0
  10. data/app/controllers/cookbooks.rb +63 -0
  11. data/app/controllers/exceptions.rb +33 -0
  12. data/app/controllers/main.rb +7 -0
  13. data/app/controllers/nodes.rb +144 -0
  14. data/app/controllers/openid_consumer.rb +133 -0
  15. data/app/controllers/openid_register.rb +113 -0
  16. data/app/controllers/openid_server.rb +252 -0
  17. data/app/controllers/roles.rb +138 -0
  18. data/app/controllers/search.rb +58 -0
  19. data/app/controllers/search_entries.rb +73 -0
  20. data/app/controllers/status.rb +34 -0
  21. data/app/helpers/application_helper.rb +144 -0
  22. data/app/helpers/cookbook_attributes_helper.rb +7 -0
  23. data/app/helpers/cookbook_definitions_helper.rb +8 -0
  24. data/app/helpers/cookbook_files_helper.rb +8 -0
  25. data/app/helpers/cookbook_libraries_helper.rb +7 -0
  26. data/app/helpers/cookbook_recipes_helper.rb +8 -0
  27. data/app/helpers/cookbook_templates_helper.rb +8 -0
  28. data/app/helpers/cookbooks_helper.rb +31 -0
  29. data/app/helpers/exceptions_helper.rb +6 -0
  30. data/app/helpers/global_helpers.rb +39 -0
  31. data/app/helpers/nodes_helper.rb +33 -0
  32. data/app/helpers/openid_consumer_helper.rb +8 -0
  33. data/app/helpers/openid_register_helper.rb +8 -0
  34. data/app/helpers/openid_server_helper.rb +6 -0
  35. data/app/helpers/openid_server_helpers.rb +29 -0
  36. data/app/helpers/roles_helper.rb +5 -0
  37. data/app/helpers/search_entries_helper.rb +8 -0
  38. data/app/helpers/search_helper.rb +38 -0
  39. data/app/helpers/status_helper.rb +26 -0
  40. data/app/views/cookbook_templates/index.html.haml +7 -0
  41. data/app/views/cookbooks/index.html.haml +10 -0
  42. data/app/views/cookbooks/show.html.haml +40 -0
  43. data/app/views/exceptions/bad_request.json.erb +1 -0
  44. data/app/views/exceptions/internal_server_error.html.erb +216 -0
  45. data/app/views/exceptions/not_acceptable.html.erb +63 -0
  46. data/app/views/exceptions/not_found.html.erb +47 -0
  47. data/app/views/exceptions/standard_error.html.erb +217 -0
  48. data/app/views/layout/chef_server_slice.html.haml +53 -0
  49. data/app/views/layout/login.html.haml +37 -0
  50. data/app/views/main/index.html.erb +1 -0
  51. data/app/views/nodes/_action.html.haml +13 -0
  52. data/app/views/nodes/_form.html.haml +56 -0
  53. data/app/views/nodes/_navigation.html.haml +9 -0
  54. data/app/views/nodes/_resource.html.haml +22 -0
  55. data/app/views/nodes/edit.html.haml +7 -0
  56. data/app/views/nodes/index.html.haml +25 -0
  57. data/app/views/nodes/new.html.haml +6 -0
  58. data/app/views/nodes/show.html.haml +60 -0
  59. data/app/views/openid_consumer/index.html.haml +23 -0
  60. data/app/views/openid_consumer/start.html.haml +4 -0
  61. data/app/views/openid_login/index.html.haml +5 -0
  62. data/app/views/openid_register/index.html.haml +19 -0
  63. data/app/views/openid_register/show.html.haml +7 -0
  64. data/app/views/openid_server/decide.html.haml +27 -0
  65. data/app/views/roles/_form.html.haml +48 -0
  66. data/app/views/roles/_navigation.html.haml +9 -0
  67. data/app/views/roles/edit.html.haml +6 -0
  68. data/app/views/roles/index.html.haml +22 -0
  69. data/app/views/roles/new.html.haml +6 -0
  70. data/app/views/roles/show.html.haml +29 -0
  71. data/app/views/search/_search_form.html.haml +6 -0
  72. data/app/views/search/index.html.haml +9 -0
  73. data/app/views/search/show.html.haml +14 -0
  74. data/app/views/search_entries/index.html.haml +8 -0
  75. data/app/views/search_entries/show.html.haml +7 -0
  76. data/app/views/status/index.html.haml +88 -0
  77. data/config/init.rb +48 -0
  78. data/config/router.rb +6 -0
  79. data/lib/chef-server-slice.rb +150 -0
  80. data/lib/chef-server-slice/merbtasks.rb +103 -0
  81. data/lib/chef-server-slice/slicetasks.rb +20 -0
  82. data/lib/chef-server-slice/spectasks.rb +53 -0
  83. data/public/facebox/README.txt +4 -0
  84. data/public/facebox/b.png +0 -0
  85. data/public/facebox/bl.png +0 -0
  86. data/public/facebox/br.png +0 -0
  87. data/public/facebox/closelabel.gif +0 -0
  88. data/public/facebox/facebox.css +95 -0
  89. data/public/facebox/facebox.js +319 -0
  90. data/public/facebox/loading.gif +0 -0
  91. data/public/facebox/tl.png +0 -0
  92. data/public/facebox/tr.png +0 -0
  93. data/public/images/avatar.png +0 -0
  94. data/public/images/black_big.png +0 -0
  95. data/public/images/indicator.gif +0 -0
  96. data/public/images/merb.jpg +0 -0
  97. data/public/images/toggle-collapse-dark.png +0 -0
  98. data/public/images/toggle-collapse-light.png +0 -0
  99. data/public/images/toggle-collapse.gif +0 -0
  100. data/public/images/toggle-expand-dark.png +0 -0
  101. data/public/images/toggle-expand-light.png +0 -0
  102. data/public/images/toggle-expand.gif +0 -0
  103. data/public/images/treeBuilderImages/Thumbs.db +0 -0
  104. data/public/images/treeBuilderImages/doc.gif +0 -0
  105. data/public/images/treeBuilderImages/docNode.gif +0 -0
  106. data/public/images/treeBuilderImages/docNodeLast.gif +0 -0
  107. data/public/images/treeBuilderImages/docNodeLastFirst.gif +0 -0
  108. data/public/images/treeBuilderImages/folder.gif +0 -0
  109. data/public/images/treeBuilderImages/folderNode.gif +0 -0
  110. data/public/images/treeBuilderImages/folderNodeFirst.gif +0 -0
  111. data/public/images/treeBuilderImages/folderNodeLast.gif +0 -0
  112. data/public/images/treeBuilderImages/folderNodeLastFirst.gif +0 -0
  113. data/public/images/treeBuilderImages/folderNodeOpen.gif +0 -0
  114. data/public/images/treeBuilderImages/folderNodeOpenFirst.gif +0 -0
  115. data/public/images/treeBuilderImages/folderNodeOpenLast.gif +0 -0
  116. data/public/images/treeBuilderImages/folderNodeOpenLastFirst.gif +0 -0
  117. data/public/images/treeBuilderImages/folderOpen.gif +0 -0
  118. data/public/images/treeBuilderImages/vertLine.gif +0 -0
  119. data/public/javascripts/JSONeditor.js +1171 -0
  120. data/public/javascripts/chef.js +126 -0
  121. data/public/javascripts/jquery-1.3.2.min.js +19 -0
  122. data/public/javascripts/jquery-ui-1.7.1.custom.min.js +65 -0
  123. data/public/javascripts/jquery.editinline.js +108 -0
  124. data/public/javascripts/jquery.jeditable.mini.js +30 -0
  125. data/public/javascripts/jquery.livequery.js +250 -0
  126. data/public/javascripts/jquery.localscroll.js +104 -0
  127. data/public/javascripts/jquery.scrollTo.js +150 -0
  128. data/public/javascripts/jquery.tools.min.js +17 -0
  129. data/public/javascripts/jquery.treeTable.min.js +165 -0
  130. data/public/stylesheets/base.css +336 -0
  131. data/public/stylesheets/chef.css +157 -0
  132. data/public/stylesheets/images/ui-bg_diagonals-small_0_aaaaaa_40x40.png +0 -0
  133. data/public/stylesheets/images/ui-bg_diagonals-thick_15_444444_40x40.png +0 -0
  134. data/public/stylesheets/images/ui-bg_glass_100_f0f0f0_1x400.png +0 -0
  135. data/public/stylesheets/images/ui-bg_glass_50_99c2ff_1x400.png +0 -0
  136. data/public/stylesheets/images/ui-bg_glass_55_fbf5d0_1x400.png +0 -0
  137. data/public/stylesheets/images/ui-bg_glass_80_e6e6e6_1x400.png +0 -0
  138. data/public/stylesheets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  139. data/public/stylesheets/images/ui-bg_highlight-hard_100_f9f9f9_1x100.png +0 -0
  140. data/public/stylesheets/images/ui-bg_highlight-soft_100_e7eef3_1x100.png +0 -0
  141. data/public/stylesheets/images/ui-icons_222222_256x240.png +0 -0
  142. data/public/stylesheets/images/ui-icons_2694e8_256x240.png +0 -0
  143. data/public/stylesheets/images/ui-icons_2e83ff_256x240.png +0 -0
  144. data/public/stylesheets/images/ui-icons_72a7cf_256x240.png +0 -0
  145. data/public/stylesheets/images/ui-icons_888888_256x240.png +0 -0
  146. data/public/stylesheets/images/ui-icons_cd0a0a_256x240.png +0 -0
  147. data/public/stylesheets/images/ui-icons_ffffff_256x240.png +0 -0
  148. data/public/stylesheets/jquery-ui-1.7.1.custom.css +404 -0
  149. data/public/stylesheets/jquery.treeTable.css +43 -0
  150. data/public/stylesheets/themes/bec-green/style.css +290 -0
  151. data/public/stylesheets/themes/bec/style.css +301 -0
  152. data/public/stylesheets/themes/blue/style.css +280 -0
  153. data/public/stylesheets/themes/default/style.css +267 -0
  154. data/public/stylesheets/themes/djime-cerulean/style.css +298 -0
  155. data/public/stylesheets/themes/kathleene/style.css +272 -0
  156. data/public/stylesheets/themes/orange/style.css +263 -0
  157. data/public/stylesheets/themes/reidb-greenish/style.css +301 -0
  158. metadata +341 -0
@@ -0,0 +1,33 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ class ChefServerSlice::Exceptions < ChefServerSlice::Application
21
+
22
+ provides :html, :json
23
+
24
+ def standard_error
25
+ Merb.logger.warn(request.content_type)
26
+ if request.accept =~ /application\/json/
27
+ display({ "error" => request.exceptions })
28
+ else
29
+ raise request.exceptions.first
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,7 @@
1
+ class ChefServerSlice::Main < ChefServerSlice::Application
2
+
3
+ def index
4
+ render
5
+ end
6
+
7
+ end
@@ -0,0 +1,144 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'chef' / 'node'
21
+
22
+ class ChefServerSlice::Nodes < ChefServerSlice::Application
23
+
24
+ provides :html, :json
25
+
26
+ before :fix_up_node_id
27
+ before :login_required
28
+ before :authorized_node, :only => [ :update, :destroy ]
29
+
30
+ def index
31
+ @node_list = Chef::Node.list
32
+ display(@node_list.collect { |n| absolute_slice_url(:node, escape_node_id(n)) })
33
+ end
34
+
35
+ def show
36
+ begin
37
+ @node = Chef::Node.load(params[:id])
38
+ rescue Net::HTTPServerException => e
39
+ raise NotFound, "Cannot load node #{params[:id]}"
40
+ end
41
+ # TODO - might as well expand the run list here, too, rather than take multiple round trips.
42
+ recipes, defaults, overrides = @node.run_list.expand("couchdb")
43
+ @node.default = defaults
44
+ @node.override = overrides
45
+ display @node
46
+ end
47
+
48
+ def new
49
+ @node = Chef::Node.new
50
+ @available_recipes = get_available_recipes
51
+ @available_roles = Chef::Role.list.sort
52
+ @run_list = @node.run_list
53
+ render
54
+ end
55
+
56
+ def edit
57
+ begin
58
+ @node = Chef::Node.load(params[:id])
59
+ rescue Net::HTTPServerException => e
60
+ raise NotFound, "Cannot load node #{params[:id]}"
61
+ end
62
+ @available_recipes = get_available_recipes
63
+ @available_roles = Chef::Role.list.sort
64
+ @run_list = @node.run_list
65
+ render
66
+ end
67
+
68
+ def create
69
+ if params.has_key?("inflated_object")
70
+ @node = params["inflated_object"]
71
+ exists = true
72
+ begin
73
+ Chef::Node.load(@node.name)
74
+ rescue Net::HTTPServerException
75
+ exists = false
76
+ end
77
+ raise Forbidden, "Node already exists" if exists
78
+ self.status = 201
79
+ @node.save
80
+ display({ :uri => absolute_slice_url(:node, escape_node_id(@node.name)) })
81
+ else
82
+ begin
83
+ @node = Chef::Node.new
84
+ @node.name params[:name]
85
+ @node.attribute = JSON.parse(params[:attributes])
86
+ @node.run_list params[:for_node]
87
+ @node.save
88
+ redirect(slice_url(:nodes), :message => { :notice => "Created Node #{@node.name}" })
89
+ rescue
90
+ @node.attribute = JSON.parse(params[:attributes])
91
+ @available_recipes = get_available_recipes
92
+ @available_roles = Chef::Role.list.sort
93
+ @run_list = params[:for_node]
94
+ @_message = { :error => $! }
95
+ render :new
96
+ end
97
+ end
98
+ end
99
+
100
+ def update
101
+ begin
102
+ @node = Chef::Node.load(params[:id])
103
+ rescue Net::HTTPServerException => e
104
+ raise NotFound, "Cannot load node #{params[:id]}"
105
+ end
106
+
107
+ if params.has_key?("inflated_object")
108
+ updated = params['inflated_object']
109
+ @node.run_list.reset(updated.run_list)
110
+ @node.attribute = updated.attribute
111
+ @node.save
112
+ display(@node)
113
+ else
114
+ begin
115
+ @node.run_list.reset(params[:for_node] ? params[:for_node] : [])
116
+ @node.attribute = JSON.parse(params[:attributes])
117
+ @node.save
118
+ @_message = { :notice => "Updated Node" }
119
+ render :show
120
+ rescue
121
+ @available_recipes = get_available_recipes
122
+ @available_roles = Chef::Role.list.sort
123
+ @run_list = Chef::RunList.new
124
+ @run_list.reset(params[:for_node])
125
+ render :edit
126
+ end
127
+ end
128
+ end
129
+
130
+ def destroy
131
+ begin
132
+ @node = Chef::Node.load(params[:id])
133
+ rescue Net::HTTPServerException => e
134
+ raise NotFound, "Cannot load node #{params[:id]}"
135
+ end
136
+ @node.destroy
137
+ if request.accept == 'application/json'
138
+ display @node
139
+ else
140
+ redirect(absolute_slice_url(:nodes), {:message => { :notice => "Node #{params[:id]} deleted successfully" }, :permanent => true})
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,133 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'pathname'
21
+ require 'openid'
22
+ require (Chef::Config[:openid_cstore_couchdb] ? 'openid-store-couchdb' : 'openid/store/filesystem')
23
+
24
+ class ChefServerSlice::OpenidConsumer < ChefServerSlice::Application
25
+
26
+ provides :html, :json
27
+
28
+ def index
29
+ if request.xhr?
30
+ render :layout => false
31
+ else
32
+ render :layout => 'login'
33
+ end
34
+ end
35
+
36
+ def start
37
+ oid = params[:openid_identifier]
38
+ begin
39
+ oidreq = consumer.begin(oid)
40
+ rescue OpenID::OpenIDError => e
41
+ raise BadRequest, "Discovery failed for #{oid}: #{e}"
42
+ end
43
+
44
+ return_to = absolute_slice_url(:openid_consumer_complete)
45
+ realm = absolute_slice_url(:openid_consumer)
46
+
47
+ if oidreq.send_redirect?(realm, return_to, params[:immediate])
48
+ return redirect(oidreq.redirect_url(realm, return_to, params[:immediate]))
49
+ else
50
+ @form_text = oidreq.form_markup(realm, return_to, params[:immediate], {'id' => 'openid_form'})
51
+ render
52
+ end
53
+ end
54
+
55
+ def login
56
+ oid = params[:openid_identifier]
57
+ raise(Unauthorized, "Sorry, #{oid} is not an authorized OpenID.") unless is_authorized_openid_identifier?(oid, Chef::Config[:authorized_openid_identifiers])
58
+ raise(Unauthorized, "Sorry, #{oid} is not an authorized OpenID Provider.") unless is_authorized_openid_provider?(oid, Chef::Config[:authorized_openid_providers])
59
+ start
60
+ end
61
+
62
+ def complete
63
+ # FIXME - url_for some action is not necessarily the current URL.
64
+ current_url = absolute_slice_url(:openid_consumer_complete)
65
+ parameters = params.reject{|k,v| k == "controller" || k == "action"}
66
+ oidresp = consumer.complete(parameters, current_url)
67
+ case oidresp.status
68
+ when OpenID::Consumer::FAILURE
69
+ raise BadRequest, "Verification failed: #{oidresp.message}" + (oidresp.display_identifier ? " for identifier '#{oidresp.display_identifier}'" : "")
70
+ when OpenID::Consumer::SUCCESS
71
+ session[:openid] = oidresp.identity_url
72
+ if oidresp.display_identifier =~ /openid\/server\/node\/(.+)$/
73
+ reg_name = $1
74
+ reg = Chef::OpenIDRegistration.load(reg_name)
75
+ Chef::Log.error("#{reg_name} is an admin #{reg.admin}")
76
+ session[:level] = reg.admin ? :admin : :node
77
+ session[:node_name] = $1
78
+ else
79
+ session[:level] = :admin
80
+ end
81
+ redirect_back_or_default(absolute_slice_url(:nodes))
82
+ return "Verification of #{oidresp.display_identifier} succeeded."
83
+ when OpenID::Consumer::SETUP_NEEDED
84
+ return "Immediate request failed - Setup Needed"
85
+ when OpenID::Consumer::CANCEL
86
+ return "OpenID transaction cancelled."
87
+ else
88
+ end
89
+ redirect absolute_slice_url(:openid_consumer)
90
+ end
91
+
92
+ def logout
93
+ [:openid,:level,:node_name].each { |n| session.delete(n) }
94
+ redirect slice_url(:top)
95
+ end
96
+
97
+ private
98
+ def is_authorized_openid_provider?(openid, authorized_providers)
99
+ Chef::Log.debug("checking for valid openid provider: openid: #{openid}, authorized providers: #{authorized_providers}")
100
+ if authorized_providers and openid
101
+ if authorized_providers.length > 0
102
+ authorized_providers.detect { |p| Chef::Log.debug("openid: #{openid} (#{openid.class}), p: #{p} (#{p.class})"); openid.match(p) }
103
+ else
104
+ true
105
+ end
106
+ else
107
+ true
108
+ end
109
+ end
110
+
111
+ def is_authorized_openid_identifier?(openid, authorized_identifiers)
112
+ Chef::Log.debug("checking for valid openid identifier: openid: #{openid}, authorized openids: #{authorized_identifiers}")
113
+ if authorized_identifiers and openid
114
+ if authorized_identifiers.length > 0
115
+ authorized_identifiers.detect { |p| Chef::Log.debug("openid: #{openid} (#{openid.class}), p: #{p} (#{p.class})"); openid == p }
116
+ else
117
+ true
118
+ end
119
+ else
120
+ true
121
+ end
122
+ end
123
+
124
+ def consumer
125
+ @consumer ||= OpenID::Consumer.new(session,
126
+ if Chef::Config[:openid_cstore_couchdb]
127
+ OpenID::Store::CouchDB.new(Chef::Config[:couchdb_url])
128
+ else
129
+ OpenID::Store::Filesystem.new(Chef::Config[:openid_cstore_path])
130
+ end)
131
+ end
132
+
133
+ end
@@ -0,0 +1,113 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'openid'
21
+ require 'chef' / 'openid_registration'
22
+
23
+ class ChefServerSlice::OpenidRegister < ChefServerSlice::Application
24
+
25
+ provides :html, :json
26
+
27
+ before :fix_up_node_id
28
+ before :login_required, :only => [ :index, :update, :destroy, :validate, :admin ]
29
+ before :authorized_node, :only => [ :update, :destroy, :validate, :admin ]
30
+
31
+ def index
32
+ @headers['X-XRDS-Location'] = Chef::Config[:openid_url] + "/openid/server/server/xrds"
33
+ @registered_nodes = Chef::OpenIDRegistration.list(true)
34
+ Chef::Log.debug(@registered_nodes.inspect)
35
+ display @registered_nodes
36
+ end
37
+
38
+ def show
39
+ begin
40
+ @registered_node = Chef::OpenIDRegistration.load(params[:id])
41
+ rescue Net::HTTPServerException => e
42
+ if e.message =~ /^404/
43
+ raise NotFound, "Cannot load node registration for #{params[:id]}"
44
+ else
45
+ raise e
46
+ end
47
+ end
48
+ Chef::Log.debug(@registered_node.inspect)
49
+ display @registered_node
50
+ end
51
+
52
+ def create
53
+ params.has_key?(:id) or raise BadRequest, "You must provide an id to register"
54
+ params.has_key?(:password) or raise BadRequest, "You must provide a password to register"
55
+ if Chef::OpenIDRegistration.has_key?(params[:id])
56
+ raise BadRequest, "You cannot re-register #{params[:id]}!"
57
+ end
58
+ @registered_node = Chef::OpenIDRegistration.new
59
+ @registered_node.name = params[:id]
60
+ @registered_node.set_password(params[:password])
61
+ if Chef::Config[:validation_token]
62
+ if params[:validation_token] == Chef::Config[:validation_token]
63
+ @registered_node.validated = true
64
+ else
65
+ @registered_node.validated = false
66
+ end
67
+ else
68
+ @registered_node.validated = false
69
+ end
70
+ @registered_node.save
71
+ display @registered_node
72
+ end
73
+
74
+ def update
75
+ raise BadRequest, "You cannot update your registration -- delete #{params[:id]} and re-register"
76
+ end
77
+
78
+ def destroy
79
+ begin
80
+ r = Chef::OpenIDRegistration.load(params[:id])
81
+ rescue Exception => e
82
+ raise BadRequest, "Cannot find the registration for #{params[:id]}"
83
+ end
84
+ r.destroy
85
+ if content_type == :html
86
+ redirect slice_url(:registrations)
87
+ else
88
+ display({ :message => "Deleted registration for #{params[:id]}"})
89
+ end
90
+ end
91
+
92
+ def validate
93
+ begin
94
+ r = Chef::OpenIDRegistration.load(params[:id])
95
+ rescue Exception => e
96
+ raise BadRequest, "Cannot find the registration for #{params[:id]}"
97
+ end
98
+ r.validated = r.validated ? false : true
99
+ r.save
100
+ redirect slice_url(:registrations)
101
+ end
102
+
103
+ def admin
104
+ begin
105
+ r = Chef::OpenIDRegistration.load(params[:id])
106
+ rescue Exception => e
107
+ raise BadRequest, "Cannot find the registration for #{params[:id]}"
108
+ end
109
+ r.admin = r.admin ? false : true
110
+ r.save
111
+ redirect slice_url(:registrations)
112
+ end
113
+ end
@@ -0,0 +1,252 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Christopher Brown (<cb@opscode.com>)
4
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'pathname'
21
+
22
+ # load the openid library, first trying rubygems
23
+ #begin
24
+ # require "rubygems"
25
+ # require_gem "ruby-openid", ">= 1.0"
26
+ #rescue LoadError
27
+ require "openid"
28
+ require "openid/consumer/discovery"
29
+ require 'json'
30
+ require 'chef' / 'openid_registration'
31
+ #end
32
+
33
+ class ChefServerSlice::OpenidServer < ChefServerSlice::Application
34
+
35
+ provides :html, :json
36
+
37
+ include Merb::ChefServerSlice::OpenidServerHelper
38
+ include OpenID::Server
39
+
40
+ layout nil
41
+
42
+ before :fix_up_node_id
43
+ after :dump_cookies_and_session
44
+
45
+ def index
46
+
47
+ oidreq = server.decode_request(params.reject{|k,v| k == "controller" || k == "action"})
48
+
49
+ # no openid.mode was given
50
+ unless oidreq
51
+ return "This is the Chef OpenID server endpoint."
52
+ end
53
+
54
+ oidresp = nil
55
+
56
+ if oidreq.kind_of?(CheckIDRequest)
57
+ identity = oidreq.identity
58
+
59
+ if oidresp
60
+ nil
61
+ elsif self.is_authorized(identity, oidreq.trust_root)
62
+ oidresp = oidreq.answer(true, nil, identity)
63
+ elsif oidreq.immediate
64
+ server_url = slice_url :openid_server
65
+ oidresp = oidreq.answer(false, server_url)
66
+ else
67
+ if content_type == :json
68
+ session[:last_oidreq] = oidreq
69
+ response = { :action => slice_url(:openid_server_decision) }
70
+ return response.to_json
71
+ else
72
+ return show_decision_page(oidreq)
73
+ end
74
+ end
75
+ else
76
+ oidresp = server.handle_request(oidreq)
77
+ end
78
+
79
+ self.render_response(oidresp)
80
+ end
81
+
82
+ def show_decision_page(oidreq, message="Do you trust this site with your identity?")
83
+ session[:last_oidreq] = oidreq
84
+ @oidreq = oidreq
85
+
86
+ if message
87
+ session[:notice] = message
88
+ end
89
+
90
+ render :template => 'openid_server/decide'
91
+ end
92
+
93
+ def node_page
94
+ unless Chef::OpenIDRegistration.has_key?(params[:id])
95
+ raise NotFound, "Cannot find registration for #{params[:id]}"
96
+ end
97
+
98
+ # Yadis content-negotiation: we want to return the xrds if asked for.
99
+ accept = request.env['HTTP_ACCEPT']
100
+
101
+ # This is not technically correct, and should eventually be updated
102
+ # to do real Accept header parsing and logic. Though I expect it will work
103
+ # 99% of the time.
104
+ if accept and accept.include?('application/xrds+xml')
105
+ return node_xrds
106
+ end
107
+
108
+ # content negotiation failed, so just render the user page
109
+ xrds_url = absolute_slice_url(:openid_node_xrds, :id => params[:id])
110
+ identity_page = <<EOS
111
+ <html><head>
112
+ <meta http-equiv="X-XRDS-Location" content="#{xrds_url}" />
113
+ <link rel="openid.server" href="#{absolute_slice_url(:openid_node, :id => params[:id])}" />
114
+ </head><body><p>OpenID identity page for registration #{params[:id]}</p>
115
+ </body></html>
116
+ EOS
117
+
118
+ # Also add the Yadis location header, so that they don't have
119
+ # to parse the html unless absolutely necessary.
120
+ @headers['X-XRDS-Location'] = xrds_url
121
+ render identity_page
122
+ end
123
+
124
+ def node_xrds
125
+ types = [
126
+ OpenID::OPENID_2_0_TYPE,
127
+ OpenID::OPENID_1_0_TYPE
128
+ ]
129
+
130
+ render_xrds(types)
131
+ end
132
+
133
+ def idp_xrds
134
+ types = [
135
+ OpenID::OPENID_IDP_2_0_TYPE,
136
+ ]
137
+
138
+ render_xrds(types)
139
+ end
140
+
141
+ def decision
142
+ oidreq = session[:last_oidreq]
143
+ session[:last_oidreq] = nil
144
+
145
+ if params.has_key?(:cancel)
146
+ Chef::Log.info("Cancelling OpenID Authentication")
147
+ return(redirect(oidreq.cancel_url))
148
+ else
149
+ identity = oidreq.identity
150
+ identity =~ /node\/(.+)$/
151
+ openid_node = Chef::OpenIDRegistration.load($1)
152
+ unless openid_node.validated
153
+ raise Unauthorized, "This nodes registration has not been validated"
154
+ end
155
+ if openid_node.password == encrypt_password(openid_node.salt, params[:password])
156
+ if session[:approvals] and !session[:approvals].include?(oidreq.trust_root)
157
+ session[:approvals] << oidreq.trust_root
158
+ else
159
+ session[:approvals] = [oidreq.trust_root]
160
+ end
161
+ oidresp = oidreq.answer(true, nil, identity)
162
+ return self.render_response(oidresp)
163
+ else
164
+ raise Unauthorized, "Invalid credentials"
165
+ end
166
+ end
167
+ end
168
+
169
+ protected
170
+
171
+ def encrypt_password(salt, password)
172
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
173
+ end
174
+
175
+ def server
176
+ if @server.nil?
177
+ server_url = absolute_slice_url(:openid_server)
178
+ if Chef::Config[:openid_store_couchdb]
179
+ require 'openid-store-couchdb'
180
+ store = OpenID::Store::CouchDB.new(Chef::Config[:couchdb_url])
181
+ else
182
+ require 'openid/store/filesystem'
183
+ dir = Chef::Config[:openid_store_path]
184
+ store = OpenID::Store::Filesystem.new(dir)
185
+ end
186
+ @server = Server.new(store, server_url)
187
+ end
188
+ return @server
189
+ end
190
+
191
+ def approved(trust_root)
192
+ return false if session[:approvals].nil?
193
+ return session[:approvals].member?(trust_root)
194
+ end
195
+
196
+ def is_authorized(identity_url, trust_root)
197
+ return (session[:username] and (identity_url == url_for_user) and self.approved(trust_root))
198
+ end
199
+
200
+ def render_xrds(types)
201
+ type_str = ""
202
+
203
+ types.each { |uri|
204
+ type_str += "<Type>#{uri}</Type>\n "
205
+ }
206
+
207
+ yadis = <<EOS
208
+ <?xml version="1.0" encoding="UTF-8"?>
209
+ <xrds:XRDS
210
+ xmlns:xrds="xri://$xrds"
211
+ xmlns="xri://$xrd*($v*2.0)">
212
+ <XRD>
213
+ <Service priority="0">
214
+ #{type_str}
215
+ <URI>#{absolute_slice_url(:openid_server)}</URI>
216
+ </Service>
217
+ </XRD>
218
+ </xrds:XRDS>
219
+ EOS
220
+
221
+ @headers['content-type'] = 'application/xrds+xml'
222
+ render yadis
223
+ end
224
+
225
+ def render_response(oidresp)
226
+ if oidresp.needs_signing
227
+ signed_response = server.signatory.sign(oidresp)
228
+ end
229
+ web_response = server.encode_response(oidresp)
230
+
231
+ case web_response.code
232
+ when HTTP_OK
233
+ @status = 200
234
+ render web_response.body
235
+ when HTTP_REDIRECT
236
+ redirect web_response.headers['location']
237
+ else
238
+ @status = 400
239
+ render web_response.body
240
+ end
241
+ end
242
+
243
+ def dump_cookies_and_session
244
+ unless session.empty? or request.cookies.empty?
245
+ cookie_size = request.cookies.inject(0) {|sum,c| sum + c[1].length }
246
+ c, s = request.cookies.inspect, session.inspect
247
+ Chef::Log.debug("cookie dump (size: #{cookie_size}): #{c}")
248
+ Chef::Log.debug("session dump #{s}")
249
+ end
250
+ end
251
+
252
+ end