chef_fixie_shahid 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +69 -0
- data/bin/chef_fixie +5 -0
- data/doc/AccessingSQL.md +68 -0
- data/doc/BulkFixup.md +33 -0
- data/doc/CommonTasks.md +31 -0
- data/doc/GETTING_STARTED.md +228 -0
- data/fixie.conf.example +8 -0
- data/lib/chef_fixie_shahid.rb +28 -0
- data/lib/chef_fixie_shahid/authz_mapper.rb +141 -0
- data/lib/chef_fixie_shahid/authz_objects.rb +295 -0
- data/lib/chef_fixie_shahid/bulk_edit_permissions.rb +161 -0
- data/lib/chef_fixie_shahid/check_org_associations.rb +239 -0
- data/lib/chef_fixie_shahid/config.rb +174 -0
- data/lib/chef_fixie_shahid/console.rb +96 -0
- data/lib/chef_fixie_shahid/context.rb +70 -0
- data/lib/chef_fixie_shahid/sql.rb +74 -0
- data/lib/chef_fixie_shahid/sql_objects.rb +573 -0
- data/lib/chef_fixie_shahid/utility_helpers.rb +63 -0
- data/lib/chef_fixie_shahid/version.rb +3 -0
- data/spec/chef_fixie/acl_spec.rb +81 -0
- data/spec/chef_fixie/assoc_invite_spec.rb +44 -0
- data/spec/chef_fixie/check_org_associations_spec.rb +137 -0
- data/spec/chef_fixie/groups_spec.rb +30 -0
- data/spec/chef_fixie/org_spec.rb +25 -0
- data/spec/chef_fixie/orgs_spec.rb +50 -0
- data/spec/spec_helper.rb +40 -0
- metadata +216 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 Chef Software Inc.
|
3
|
+
# License :: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
# Author: Mark Anderson <mark@chef.io>
|
18
|
+
#
|
19
|
+
# Much of this code was orginally derived from the orgmapper tool, which had many varied authors.
|
20
|
+
|
21
|
+
require "optparse"
|
22
|
+
require "pp"
|
23
|
+
require "pry"
|
24
|
+
|
25
|
+
require_relative "../chef_fixie"
|
26
|
+
require_relative "context"
|
27
|
+
|
28
|
+
module ChefFixie
|
29
|
+
module Console
|
30
|
+
extend self
|
31
|
+
|
32
|
+
def start
|
33
|
+
@@started_from_command_line = true
|
34
|
+
configure
|
35
|
+
ChefFixie.setup
|
36
|
+
configure_pry
|
37
|
+
Pry.start
|
38
|
+
end
|
39
|
+
|
40
|
+
def configure
|
41
|
+
config_file = nil
|
42
|
+
if ARGV.first && ARGV[0].chars.first != "-" && config_file = ARGV.shift
|
43
|
+
config_file = File.expand_path(config_file)
|
44
|
+
end
|
45
|
+
ChefFixie.load_config(config_file)
|
46
|
+
|
47
|
+
options = {}
|
48
|
+
OptionParser.new do |opt|
|
49
|
+
opt.banner = "Usage: fixie [config] [options]"
|
50
|
+
opt.on("--authz_uri AUTH_URI", "The URI of the opscode authz service") { |v| options[:authz_uri] = v }
|
51
|
+
opt.on("--sql_database DATABASE", "The URI of the opscode_chef database") { |v| options[:sql_database] = v }
|
52
|
+
opt.on_tail("-h", "--help", "Show this message") do
|
53
|
+
puts opt
|
54
|
+
puts "\nExample configuration file:\n\n"
|
55
|
+
puts ChefFixie::Config.instance.example_config
|
56
|
+
puts "\n"
|
57
|
+
exit(1)
|
58
|
+
end
|
59
|
+
opt.parse!(ARGV)
|
60
|
+
end
|
61
|
+
pp :cli_opts => options if ENV["DEBUG"]
|
62
|
+
|
63
|
+
ChefFixie::Config.instance.merge_opts(options)
|
64
|
+
puts ChefFixie::Config.instance.to_text
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure_pry
|
68
|
+
Pry.config.history.file = "~/.fixie_history"
|
69
|
+
Pry.config.prompt_name = "fixie"
|
70
|
+
Pry::Commands.block_command("fixie-help", "Show fixie's help") do
|
71
|
+
output.puts(<<-HALP)
|
72
|
+
** ORGS **
|
73
|
+
* access with ORGS or ORGS
|
74
|
+
* access a specific org: ORGS['orgname']
|
75
|
+
|
76
|
+
** USERS **
|
77
|
+
* users.find('clownco-org-admin')
|
78
|
+
* users.grep :clownco
|
79
|
+
* users.usernames
|
80
|
+
|
81
|
+
** RAW SQL ACCESS**
|
82
|
+
* sql[:users].select(:column, :column).where(:column => "condition").all
|
83
|
+
|
84
|
+
** irb Help **
|
85
|
+
irb_help
|
86
|
+
|
87
|
+
HALP
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def started_from_command_line?
|
92
|
+
@@started_from_command_line == true
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2015 Chef Software Inc.
|
3
|
+
# License :: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
# Author: Mark Anderson <mark@chef.io>
|
18
|
+
#
|
19
|
+
# Much of this code was orginally derived from the orgmapper tool, which had many varied authors.
|
20
|
+
|
21
|
+
module ChefFixie
|
22
|
+
module Context
|
23
|
+
|
24
|
+
def describe_orgs
|
25
|
+
OrgMetrics.org_stats(orgs)
|
26
|
+
end
|
27
|
+
|
28
|
+
def orgs
|
29
|
+
ChefFixie::Organizations.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def jobs
|
33
|
+
ChefFixie::Jobs.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def users
|
37
|
+
ChefFixie::Users.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def global_groups
|
41
|
+
ChefFixie::GlobalGroups.new
|
42
|
+
end
|
43
|
+
|
44
|
+
def sql
|
45
|
+
ChefFixie::Sql.default_connection
|
46
|
+
end
|
47
|
+
|
48
|
+
def associate_user(username, orgname)
|
49
|
+
unless user = users.find(username)
|
50
|
+
raise ArgumentError, "No users matched '#{username}'"
|
51
|
+
end
|
52
|
+
unless org = ORGS[orgname]
|
53
|
+
raise ArgumentError, "No orgs matched '#{orgname}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
ChefFixie::Associator.associate_user(org, user)
|
57
|
+
end
|
58
|
+
|
59
|
+
def dissociate_user(username, orgname)
|
60
|
+
unless user = users.find(username)
|
61
|
+
raise ArgumentError, "No users matched '#{username}'"
|
62
|
+
end
|
63
|
+
unless org = ORGS[orgname]
|
64
|
+
raise ArgumentError, "No orgs matched '#{orgname}'"
|
65
|
+
end
|
66
|
+
|
67
|
+
ChefFixie::Dissociator.dissociate_user(org, user)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014-2015 Chef Software Inc.
|
3
|
+
# License :: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
# Author: Mark Anderson <mark@chef.io>
|
18
|
+
|
19
|
+
require "ffi_yajl"
|
20
|
+
require "uuidtools"
|
21
|
+
require "sequel"
|
22
|
+
|
23
|
+
require_relative "config"
|
24
|
+
|
25
|
+
Sequel.default_timezone = :utc
|
26
|
+
|
27
|
+
module ChefFixie
|
28
|
+
module Sql
|
29
|
+
|
30
|
+
class InvalidConfig < StandardError
|
31
|
+
end
|
32
|
+
|
33
|
+
# A connection string passed to Sequel.connect()
|
34
|
+
#
|
35
|
+
# Examples:
|
36
|
+
# * "mysql2://root@localhost/opscode_chef"
|
37
|
+
# * "mysql2://user:password@host/opscode_chef"
|
38
|
+
# * "jdbc:mysql://localhost/test?user=root&password=root"
|
39
|
+
#
|
40
|
+
# See also: http://sequel.rubyforge.org/rdoc/files/doc/opening_databases_rdoc.html
|
41
|
+
def self.connection_string=(sequel_connection_string)
|
42
|
+
@database.disconnect if @database.respond_to?(:disconnect)
|
43
|
+
@database = nil
|
44
|
+
@connection_string = sequel_connection_string
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the connection string or raises an error if you didn't set one.
|
48
|
+
def self.connection_string
|
49
|
+
@connection_string ||= ChefFixie.configure { |x| x.sql_database }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a Sequel::Data baseobject, which wraps access to the database.
|
53
|
+
def self.default_connection
|
54
|
+
@database ||= Sequel.connect(connection_string, :max_connections => 2)
|
55
|
+
# @database.loggers << Logger.new($stdout)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Generate a new UUID. Currently uses the v1 UUID scheme.
|
59
|
+
def new_uuid
|
60
|
+
UUIDTools::UUID.timestamp_create.hexdigest
|
61
|
+
end
|
62
|
+
|
63
|
+
# Parse the portion of the object that's stored as a blob o' JSON
|
64
|
+
def from_json(serialized_data)
|
65
|
+
FFI_Yajl::Parser.parse(serialized_data, :symbolize_keys => true)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Encode the portion of the object that's stored as a blob o' JSON
|
69
|
+
def as_json(data)
|
70
|
+
FFI_Yajl::Encoder.encode(data)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,573 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2014-2015 Chef Software Inc.
|
3
|
+
# License :: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
# Author: Mark Anderson <mark@chef.io>
|
18
|
+
#
|
19
|
+
|
20
|
+
require "pp"
|
21
|
+
require "sequel"
|
22
|
+
|
23
|
+
require_relative "config"
|
24
|
+
require_relative "authz_objects"
|
25
|
+
require_relative "authz_mapper"
|
26
|
+
|
27
|
+
Sequel.extension :inflector
|
28
|
+
|
29
|
+
module ChefFixie
|
30
|
+
module Sql
|
31
|
+
|
32
|
+
# Maps entity names like 'org' to the table class (Orgs) and the entity class (Org), as well as the cannonical
|
33
|
+
# each table has a name, a class to wrap the table, an row, and a class to map the row.
|
34
|
+
# Wrapping this in a class to handle things if we have to not be consisitent with our naming.
|
35
|
+
# table :orgs, class wrapper Orgs, row :org, class for row Org
|
36
|
+
module Relationships
|
37
|
+
|
38
|
+
def self.base
|
39
|
+
"ChefFixie::Sql" + "::" # this should be autogenerated not hardcoded
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.to_name(class_or_name)
|
43
|
+
name =
|
44
|
+
case
|
45
|
+
when class_or_name.is_a?(Symbol)
|
46
|
+
class_or_name.to_s
|
47
|
+
when class_or_name.is_a?(Class)
|
48
|
+
class_or_name.name
|
49
|
+
when class_or_name.is_a?(String)
|
50
|
+
class_or_name
|
51
|
+
else
|
52
|
+
class_or_name.class.to_s
|
53
|
+
end
|
54
|
+
name.split("::")[-1]
|
55
|
+
end
|
56
|
+
|
57
|
+
# The class for the table, e.g. Orgs
|
58
|
+
def self.table_class(name)
|
59
|
+
name = to_name(name)
|
60
|
+
(base + name.to_s.pluralize.camelize).constantize
|
61
|
+
end
|
62
|
+
|
63
|
+
# The class for one instance of the object, e.g. Org
|
64
|
+
def self.object_class(name)
|
65
|
+
name = to_name(name)
|
66
|
+
(base + name.to_s.singularize.camelize).constantize
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.singular(name)
|
70
|
+
name = to_name(name)
|
71
|
+
name.to_s.singularize
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.plural(name)
|
75
|
+
name = to_name(name)
|
76
|
+
name.to_s.pluralize
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# we declare these first so that the 'element' metaprogramming in SqlTable works
|
81
|
+
class SqlObject
|
82
|
+
def initialize(data)
|
83
|
+
@data = data
|
84
|
+
end
|
85
|
+
|
86
|
+
def data
|
87
|
+
@data
|
88
|
+
end
|
89
|
+
|
90
|
+
def table
|
91
|
+
Relationships.table_class(self).new
|
92
|
+
end
|
93
|
+
|
94
|
+
# TODO rework this to use better style
|
95
|
+
def self.ro_access(*args)
|
96
|
+
args.each do |field|
|
97
|
+
fundef = "def #{field}; @data.#{field}; end"
|
98
|
+
class_eval(fundef)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
# TODO figure out model for write access
|
102
|
+
|
103
|
+
def self.name_field(field)
|
104
|
+
fundef = "def name; @data.#{field}; end"
|
105
|
+
class_eval(fundef)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.std_timestamp
|
109
|
+
[:created_at, :updated_at].each do |i|
|
110
|
+
ro_access(i)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Pretty much any object with an authz id has these fields
|
115
|
+
def self.std_authz
|
116
|
+
std_timestamp
|
117
|
+
[:authz_id, :last_updated_by].each do |i|
|
118
|
+
ro_access(i)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete
|
123
|
+
rows = table.by_id(id)
|
124
|
+
raise "id #{id} matches more than one object" if rows.all.count != 1
|
125
|
+
rows.inner.delete
|
126
|
+
if respond_to?(:authz_delete)
|
127
|
+
authz_delete
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class Org < SqlObject
|
133
|
+
include AuthzObjectMixin
|
134
|
+
|
135
|
+
def self.scoped_type(*args)
|
136
|
+
args.each do |object|
|
137
|
+
funname = Relationships.plural(object)
|
138
|
+
# defer evaluation of mapper to make sure we have a chance for everyone to initialize
|
139
|
+
fundef = "def #{funname}; Relationships.table_class(:#{object}).new.by_org_id(org_id); end"
|
140
|
+
class_eval(fundef)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def initialize(data)
|
145
|
+
super(data)
|
146
|
+
end
|
147
|
+
|
148
|
+
def org_id
|
149
|
+
data[:id]
|
150
|
+
end
|
151
|
+
|
152
|
+
def global_admins
|
153
|
+
name = self.name
|
154
|
+
global_admins_name = "#{name}_global_admins"
|
155
|
+
read_access_name = "#{name}_read_access_group"
|
156
|
+
ChefFixie::Sql::Groups.new[global_admins_name] || \
|
157
|
+
ChefFixie::Sql::Groups.new[read_access_name]
|
158
|
+
end
|
159
|
+
|
160
|
+
alias read_access_group global_admins
|
161
|
+
|
162
|
+
# Iterators for objects in authz; using containers to enumerate things
|
163
|
+
# It might be better to metaprogram this up instead,
|
164
|
+
#
|
165
|
+
# TODO Write some tests to validate that this stuff
|
166
|
+
# works, since it depends on a lot of name magic...
|
167
|
+
|
168
|
+
NAME_FIXUP = { "data" => "data_bags", "sandboxes" => nil }
|
169
|
+
def objects_by_container_type(container)
|
170
|
+
name = NAME_FIXUP.has_key?(container) ? NAME_FIXUP[container] : container
|
171
|
+
return [] if name.nil?
|
172
|
+
|
173
|
+
object_type = name.to_sym
|
174
|
+
# raise Exception "No such object_type #{object_type}" unless respond_to?(object_type)
|
175
|
+
send(object_type).all(:all)
|
176
|
+
end
|
177
|
+
|
178
|
+
def each_authz_object_by_class
|
179
|
+
containers = self.containers.all(:all)
|
180
|
+
containers.each do |container|
|
181
|
+
objects = objects_by_container_type(container.name)
|
182
|
+
if block_given?
|
183
|
+
yield objects
|
184
|
+
end
|
185
|
+
end
|
186
|
+
nil
|
187
|
+
end
|
188
|
+
|
189
|
+
def each_authz_object
|
190
|
+
each_authz_object_by_class do |objectlist|
|
191
|
+
objectlist.each do |object|
|
192
|
+
yield object
|
193
|
+
end
|
194
|
+
end
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
|
198
|
+
scoped_type :container, :group, :client,
|
199
|
+
:cookbook_artifact, :cookbook, :data_bag, :environment, :node, :policy, :policy_group , :role
|
200
|
+
|
201
|
+
# Maybe autogenerate this from data.columns?
|
202
|
+
ro_access :id, :authz_id, :assigned_at, :last_updated_by, :created_at, :updated_at, :name, :full_name
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# Some types have an org_id field and may be scoped to an org (some, like groups are able to be global as well)
|
207
|
+
# This sets up a filtered accessor that limits
|
208
|
+
#
|
209
|
+
# module ScopedType
|
210
|
+
# def self.included(base)
|
211
|
+
# pp :base=>base
|
212
|
+
# Org.scoped_type(base)
|
213
|
+
# end
|
214
|
+
# end
|
215
|
+
|
216
|
+
class Container < SqlObject
|
217
|
+
include AuthzContainerMixin
|
218
|
+
|
219
|
+
def initialize(data)
|
220
|
+
super(data)
|
221
|
+
end
|
222
|
+
|
223
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name
|
224
|
+
end
|
225
|
+
class Group < SqlObject
|
226
|
+
include AuthzGroupMixin
|
227
|
+
|
228
|
+
def initialize(data)
|
229
|
+
super(data)
|
230
|
+
end
|
231
|
+
|
232
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name
|
233
|
+
end
|
234
|
+
|
235
|
+
class User < SqlObject
|
236
|
+
include AuthzActorMixin
|
237
|
+
def initialize(data)
|
238
|
+
super(data)
|
239
|
+
end
|
240
|
+
name_field :username
|
241
|
+
ro_access :id, :authz_id, :last_updated_by, :created_at, :updated_at, :username, :email, :public_key, :pubkey_version, :serialized_object, :external_authentication_uid, :recovery_authentication_enabled, :admin, :hashed_password, :salt, :hash_type
|
242
|
+
end
|
243
|
+
class Client < SqlObject
|
244
|
+
include AuthzActorMixin
|
245
|
+
def initialize(data)
|
246
|
+
super(data)
|
247
|
+
end
|
248
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name
|
249
|
+
end
|
250
|
+
|
251
|
+
# Objects
|
252
|
+
|
253
|
+
# At the time of writing there are more objects in sql than we
|
254
|
+
# support here; we should add them. We have only covered the
|
255
|
+
# objects that have their own authz info
|
256
|
+
# Missing objects include:
|
257
|
+
# checksums cookbook_artifact_version_checksums
|
258
|
+
# cookbook_artifact_versions cookbook_artifact_versions_id_seq
|
259
|
+
# cookbook_artifacts_id_seq cookbook_version_checksums
|
260
|
+
# cookbook_version_dependencies cookbook_versions
|
261
|
+
# cookbook_versions_by_rank cookbooks_id_seq data_bag_items
|
262
|
+
# joined_cookbook_version keys keys_by_name node_policy opc_customers
|
263
|
+
# opc_customers_id_seq opc_users org_migration_state
|
264
|
+
# org_migration_state_id_seq policy_revisions
|
265
|
+
# policy_revisions_policy_groups_association sandboxed_checksums
|
266
|
+
|
267
|
+
class CookbookArtifact < SqlObject
|
268
|
+
include AuthzObjectMixin
|
269
|
+
def initialize(data)
|
270
|
+
super(data)
|
271
|
+
end
|
272
|
+
ro_access :id, :org_id, :authz_id, :name
|
273
|
+
end
|
274
|
+
|
275
|
+
class Cookbook < SqlObject
|
276
|
+
include AuthzObjectMixin
|
277
|
+
def initialize(data)
|
278
|
+
super(data)
|
279
|
+
end
|
280
|
+
ro_access :id, :org_id, :authz_id, :name
|
281
|
+
end
|
282
|
+
|
283
|
+
class DataBag < SqlObject
|
284
|
+
include AuthzObjectMixin
|
285
|
+
def initialize(data)
|
286
|
+
super(data)
|
287
|
+
end
|
288
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name
|
289
|
+
end
|
290
|
+
|
291
|
+
# data bag item needs some prep work to do since it doesn't have authz stuff.
|
292
|
+
|
293
|
+
class Environment < SqlObject
|
294
|
+
include AuthzObjectMixin
|
295
|
+
def initialize(data)
|
296
|
+
super(data)
|
297
|
+
end
|
298
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name, :serialized_object
|
299
|
+
# serialized_object requires work since most of the time it isn't wanted
|
300
|
+
end
|
301
|
+
|
302
|
+
class Node < SqlObject
|
303
|
+
include AuthzObjectMixin
|
304
|
+
def initialize(data)
|
305
|
+
super(data)
|
306
|
+
end
|
307
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name, :serialized_object
|
308
|
+
# serialized_object requires work since most of the time it isn't wanted
|
309
|
+
end
|
310
|
+
|
311
|
+
class Policy < SqlObject
|
312
|
+
include AuthzObjectMixin
|
313
|
+
def initialize(data)
|
314
|
+
super(data)
|
315
|
+
end
|
316
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :name
|
317
|
+
# serialized_object requires work since most of the time it isn't wanted
|
318
|
+
end
|
319
|
+
|
320
|
+
class PolicyGroup < SqlObject
|
321
|
+
include AuthzObjectMixin
|
322
|
+
def initialize(data)
|
323
|
+
super(data)
|
324
|
+
end
|
325
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :name, :serialized_object
|
326
|
+
# serialized_object requires work since most of the time it isn't wanted
|
327
|
+
end
|
328
|
+
|
329
|
+
class Role < SqlObject
|
330
|
+
include AuthzObjectMixin
|
331
|
+
def initialize(data)
|
332
|
+
super(data)
|
333
|
+
end
|
334
|
+
ro_access :id, :org_id, :authz_id, :last_updated_by, :created_at, :updated_at, :name, :serialized_object
|
335
|
+
# serialized_object requires work since most of the time it isn't wanted
|
336
|
+
end
|
337
|
+
|
338
|
+
#
|
339
|
+
#
|
340
|
+
#
|
341
|
+
class SqlTable
|
342
|
+
include AuthzMapper
|
343
|
+
|
344
|
+
def self.max_count_default
|
345
|
+
50
|
346
|
+
end
|
347
|
+
|
348
|
+
def get_table
|
349
|
+
:unknown_table
|
350
|
+
end
|
351
|
+
|
352
|
+
def mk_element(x)
|
353
|
+
x
|
354
|
+
end
|
355
|
+
|
356
|
+
def initialize(tablespec = nil)
|
357
|
+
ChefFixie::Sql.default_connection
|
358
|
+
@inner = tablespec || Sequel::Model(get_table)
|
359
|
+
end
|
360
|
+
|
361
|
+
def inner
|
362
|
+
# Make sure we have init
|
363
|
+
@inner
|
364
|
+
end
|
365
|
+
|
366
|
+
def filter_core(field, exp)
|
367
|
+
self.class.new(inner.filter(field => exp))
|
368
|
+
end
|
369
|
+
|
370
|
+
def all(max_count = :default)
|
371
|
+
if max_count == :default
|
372
|
+
max_count = ChefFixie::Sql::SqlTable.max_count_default
|
373
|
+
end
|
374
|
+
if max_count != :all
|
375
|
+
return :too_many_results if inner.count > max_count
|
376
|
+
end
|
377
|
+
elements = inner.all.map { |org| mk_element(org) }
|
378
|
+
end
|
379
|
+
|
380
|
+
#
|
381
|
+
# TODO Improve these via define_method
|
382
|
+
# See http://blog.jayfields.com/2007/10/ruby-defining-class-methods.html
|
383
|
+
# https://stackoverflow.com/questions/9658724/ruby-metaprogramming-class-eval/9658775#9658775
|
384
|
+
def self.primary(arg)
|
385
|
+
name = :"by_#{arg}"
|
386
|
+
class_eval("def [](arg); #{name}(arg).all(1).first; end")
|
387
|
+
|
388
|
+
listfun = <<EOLF
|
389
|
+
def list(max_count=:default)
|
390
|
+
elements = all(max_count)
|
391
|
+
if elements == :too_many_results
|
392
|
+
elements
|
393
|
+
else
|
394
|
+
elements.map {|e| e.#{arg} }.sort
|
395
|
+
end
|
396
|
+
end
|
397
|
+
EOLF
|
398
|
+
class_eval(listfun)
|
399
|
+
end
|
400
|
+
|
401
|
+
def self.filter_by(*args)
|
402
|
+
args.each do |field|
|
403
|
+
name = "by_#{field}"
|
404
|
+
fundef = "def #{name}(exp); filter_core(:#{field},exp); end"
|
405
|
+
class_eval(fundef)
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def self.table(name)
|
410
|
+
fundef = "def get_table; :#{name}; end"
|
411
|
+
class_eval(fundef)
|
412
|
+
end
|
413
|
+
|
414
|
+
# doesn't work yet
|
415
|
+
# element Org in class Orgs will fail because it can't find Org (undefined)
|
416
|
+
def self.element(name)
|
417
|
+
fundef = "ElementType = name; def mk_element(x); #{name}.new(x); end"
|
418
|
+
class_eval(fundef)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
class Orgs < SqlTable
|
423
|
+
table :orgs
|
424
|
+
element Sql::Org
|
425
|
+
register_authz :org, :object
|
426
|
+
|
427
|
+
primary :name
|
428
|
+
filter_by :name, :id, :full_name, :authz_id
|
429
|
+
|
430
|
+
GlobalOrg = "0" * 32
|
431
|
+
|
432
|
+
def self.org_guid_to_name(guid)
|
433
|
+
"global" if guid == GlobalOrg
|
434
|
+
# Cache the class
|
435
|
+
@orgs ||= Orgs.new
|
436
|
+
names = @orgs.by_id(guid).all(1)
|
437
|
+
if names.count == 1
|
438
|
+
names.first.name
|
439
|
+
else
|
440
|
+
"unknown-#{guid}"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
class Associations < SqlTable
|
446
|
+
table :org_user_associations
|
447
|
+
filter_by :org_id, :user_id, :last_updated_by
|
448
|
+
|
449
|
+
def by_org_id_user_id(org_id, user_id)
|
450
|
+
# db table constraint guarantees that this is unique
|
451
|
+
inner.filter(:org_id => org_id, :user_id => user_id).all.first
|
452
|
+
end
|
453
|
+
|
454
|
+
end
|
455
|
+
class Invites < SqlTable
|
456
|
+
table :org_user_invites
|
457
|
+
filter_by :org_id, :user_id, :last_updated_by
|
458
|
+
|
459
|
+
def by_org_id_user_id(org_id, user_id)
|
460
|
+
# db table constraint guarantees that this is unique
|
461
|
+
inner.filter(:org_id => org_id, :user_id => user_id).all.first
|
462
|
+
end
|
463
|
+
end
|
464
|
+
class Users < SqlTable
|
465
|
+
table :users
|
466
|
+
element Sql::User
|
467
|
+
register_authz :user, :actor
|
468
|
+
|
469
|
+
primary :username
|
470
|
+
filter_by :id, :authz_id, :username, :email
|
471
|
+
end
|
472
|
+
class Clients < SqlTable
|
473
|
+
table :clients
|
474
|
+
element Sql::Client
|
475
|
+
register_authz :client, :actor
|
476
|
+
|
477
|
+
primary :name
|
478
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
479
|
+
end
|
480
|
+
|
481
|
+
class Containers < SqlTable
|
482
|
+
table :containers
|
483
|
+
element Sql::Container
|
484
|
+
register_authz :container, :container
|
485
|
+
|
486
|
+
primary :name
|
487
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
488
|
+
end
|
489
|
+
class Groups < SqlTable
|
490
|
+
table :groups
|
491
|
+
element Sql::Group
|
492
|
+
register_authz :group, :group
|
493
|
+
|
494
|
+
primary :name
|
495
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
496
|
+
end
|
497
|
+
|
498
|
+
# Objects
|
499
|
+
# todo check
|
500
|
+
class CookbookArtifacts < SqlTable
|
501
|
+
table :cookbook_artifacts
|
502
|
+
element Sql::CookbookArtifact
|
503
|
+
register_authz :cookbook_artifact, :object
|
504
|
+
|
505
|
+
primary :name
|
506
|
+
filter_by :name, :id, :org_id, :authz_id
|
507
|
+
end
|
508
|
+
|
509
|
+
class Cookbooks < SqlTable
|
510
|
+
table :cookbooks
|
511
|
+
element Sql::Cookbook
|
512
|
+
register_authz :cookbook, :object
|
513
|
+
|
514
|
+
primary :name
|
515
|
+
filter_by :name, :id, :org_id, :authz_id
|
516
|
+
end
|
517
|
+
|
518
|
+
class DataBags < SqlTable
|
519
|
+
table :data_bags
|
520
|
+
element Sql::DataBag
|
521
|
+
register_authz :data_bag, :object
|
522
|
+
|
523
|
+
primary :name
|
524
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
525
|
+
end
|
526
|
+
|
527
|
+
class Environments < SqlTable
|
528
|
+
table :environments
|
529
|
+
element Sql::Environment
|
530
|
+
register_authz :environment, :object
|
531
|
+
|
532
|
+
primary :name
|
533
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
534
|
+
end
|
535
|
+
|
536
|
+
class Nodes < SqlTable
|
537
|
+
table :nodes
|
538
|
+
element Sql::Node
|
539
|
+
register_authz :node, :object
|
540
|
+
|
541
|
+
primary :name
|
542
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
543
|
+
end
|
544
|
+
|
545
|
+
class Policies < SqlTable
|
546
|
+
table :policies
|
547
|
+
element Sql::Policy
|
548
|
+
register_authz :policy, :object
|
549
|
+
|
550
|
+
primary :name
|
551
|
+
filter_by :name, :id, :org_id, :authz_id
|
552
|
+
end
|
553
|
+
|
554
|
+
class PolicyGroups < SqlTable
|
555
|
+
table :policy_groups
|
556
|
+
element Sql::PolicyGroup
|
557
|
+
register_authz :policygroup, :object
|
558
|
+
|
559
|
+
primary :name
|
560
|
+
filter_by :name, :id, :org_id, :authz_id
|
561
|
+
end
|
562
|
+
|
563
|
+
class Roles < SqlTable
|
564
|
+
table :roles
|
565
|
+
element Sql::Role
|
566
|
+
register_authz :role, :object
|
567
|
+
|
568
|
+
primary :name
|
569
|
+
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
573
|
+
end
|