chef_fixie 0.3.0 → 1.0.2
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.
- checksums.yaml +5 -5
- data/bin/chef_fixie +1 -1
- data/doc/BulkFixup.md +1 -1
- data/doc/CommonTasks.md +14 -3
- data/lib/chef_fixie.rb +7 -7
- data/lib/chef_fixie/authz_mapper.rb +26 -28
- data/lib/chef_fixie/authz_objects.rb +44 -41
- data/lib/chef_fixie/bulk_edit_permissions.rb +24 -20
- data/lib/chef_fixie/check_org_associations.rb +56 -58
- data/lib/chef_fixie/config.rb +58 -24
- data/lib/chef_fixie/console.rb +15 -10
- data/lib/chef_fixie/context.rb +2 -4
- data/lib/chef_fixie/sql.rb +12 -12
- data/lib/chef_fixie/sql_objects.rb +49 -38
- data/lib/chef_fixie/utility_helpers.rb +13 -9
- data/lib/chef_fixie/version.rb +1 -1
- data/spec/chef_fixie/acl_spec.rb +23 -25
- data/spec/chef_fixie/assoc_invite_spec.rb +5 -8
- data/spec/chef_fixie/check_org_associations_spec.rb +14 -17
- data/spec/chef_fixie/groups_spec.rb +7 -11
- data/spec/chef_fixie/org_spec.rb +4 -5
- data/spec/chef_fixie/orgs_spec.rb +6 -9
- data/spec/spec_helper.rb +5 -6
- metadata +18 -84
- data/bin/bundler +0 -16
- data/bin/chef-apply +0 -16
- data/bin/chef-client +0 -16
- data/bin/chef-shell +0 -16
- data/bin/chef-solo +0 -16
- data/bin/chef-zero +0 -16
- data/bin/coderay +0 -16
- data/bin/edit_json.rb +0 -16
- data/bin/erubis +0 -16
- data/bin/ffi-yajl-bench +0 -16
- data/bin/fixie~ +0 -231
- data/bin/htmldiff +0 -16
- data/bin/knife +0 -16
- data/bin/ldiff +0 -16
- data/bin/net-dhcp +0 -16
- data/bin/ohai +0 -16
- data/bin/prettify_json.rb +0 -16
- data/bin/pry +0 -16
- data/bin/rackup +0 -16
- data/bin/rake +0 -16
- data/bin/rdoc +0 -16
- data/bin/restclient +0 -16
- data/bin/ri +0 -16
- data/bin/rspec +0 -16
- data/bin/s3sh +0 -16
- data/bin/sequel +0 -16
- data/bin/serverspec-init +0 -16
- data/doc/AccessingSQL.md~ +0 -32
- data/doc/BulkFixup.md~ +0 -28
- data/doc/CommonTasks.md~ +0 -0
- data/doc/GETTING_STARTED.md~ +0 -6
- data/spec/chef_fixie/assoc_invite_spec.rb~ +0 -26
- data/spec/chef_fixie/check_org_associations_spec.rb~ +0 -34
- data/spec/chef_fixie/org_spec.rb~ +0 -53
data/lib/chef_fixie/sql.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2014-2015 Chef Software Inc.
|
2
|
+
# Copyright (c) 2014-2015 Chef Software Inc.
|
3
3
|
# License :: Apache License, Version 2.0
|
4
4
|
#
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -16,20 +16,20 @@
|
|
16
16
|
#
|
17
17
|
# Author: Mark Anderson <mark@chef.io>
|
18
18
|
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
19
|
+
require "ffi_yajl"
|
20
|
+
require "uuidtools"
|
21
|
+
require "sequel"
|
22
22
|
|
23
|
-
require_relative
|
23
|
+
require_relative "config"
|
24
24
|
|
25
25
|
Sequel.default_timezone = :utc
|
26
26
|
|
27
27
|
module ChefFixie
|
28
28
|
module Sql
|
29
|
-
|
29
|
+
|
30
30
|
class InvalidConfig < StandardError
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
# A connection string passed to Sequel.connect()
|
34
34
|
#
|
35
35
|
# Examples:
|
@@ -46,25 +46,25 @@ module ChefFixie
|
|
46
46
|
|
47
47
|
# Returns the connection string or raises an error if you didn't set one.
|
48
48
|
def self.connection_string
|
49
|
-
@connection_string ||= ChefFixie.configure {|x| x.sql_database }
|
49
|
+
@connection_string ||= ChefFixie.configure { |x| x.sql_database }
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
# Returns a Sequel::Data baseobject, which wraps access to the database.
|
53
53
|
def self.default_connection
|
54
54
|
@database ||= Sequel.connect(connection_string, :max_connections => 2)
|
55
55
|
# @database.loggers << Logger.new($stdout)
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
# Generate a new UUID. Currently uses the v1 UUID scheme.
|
59
59
|
def new_uuid
|
60
60
|
UUIDTools::UUID.timestamp_create.hexdigest
|
61
61
|
end
|
62
|
-
|
62
|
+
|
63
63
|
# Parse the portion of the object that's stored as a blob o' JSON
|
64
64
|
def from_json(serialized_data)
|
65
65
|
FFI_Yajl::Parser.parse(serialized_data, :symbolize_keys => true)
|
66
66
|
end
|
67
|
-
|
67
|
+
|
68
68
|
# Encode the portion of the object that's stored as a blob o' JSON
|
69
69
|
def as_json(data)
|
70
70
|
FFI_Yajl::Encoder.encode(data)
|
@@ -17,12 +17,12 @@
|
|
17
17
|
# Author: Mark Anderson <mark@chef.io>
|
18
18
|
#
|
19
19
|
|
20
|
-
require
|
21
|
-
require
|
20
|
+
require "pp"
|
21
|
+
require "sequel"
|
22
22
|
|
23
|
-
require_relative
|
24
|
-
require_relative
|
25
|
-
require_relative
|
23
|
+
require_relative "config"
|
24
|
+
require_relative "authz_objects"
|
25
|
+
require_relative "authz_mapper"
|
26
26
|
|
27
27
|
Sequel.extension :inflector
|
28
28
|
|
@@ -51,25 +51,28 @@ module ChefFixie
|
|
51
51
|
else
|
52
52
|
class_or_name.class.to_s
|
53
53
|
end
|
54
|
-
name.split(
|
54
|
+
name.split("::")[-1]
|
55
55
|
end
|
56
56
|
|
57
57
|
# The class for the table, e.g. Orgs
|
58
58
|
def self.table_class(name)
|
59
|
-
name =
|
59
|
+
name = to_name(name)
|
60
60
|
(base + name.to_s.pluralize.camelize).constantize
|
61
61
|
end
|
62
|
+
|
62
63
|
# The class for one instance of the object, e.g. Org
|
63
64
|
def self.object_class(name)
|
64
|
-
name =
|
65
|
+
name = to_name(name)
|
65
66
|
(base + name.to_s.singularize.camelize).constantize
|
66
67
|
end
|
68
|
+
|
67
69
|
def self.singular(name)
|
68
|
-
name =
|
70
|
+
name = to_name(name)
|
69
71
|
name.to_s.singularize
|
70
72
|
end
|
73
|
+
|
71
74
|
def self.plural(name)
|
72
|
-
name =
|
75
|
+
name = to_name(name)
|
73
76
|
name.to_s.pluralize
|
74
77
|
end
|
75
78
|
end
|
@@ -79,9 +82,11 @@ module ChefFixie
|
|
79
82
|
def initialize(data)
|
80
83
|
@data = data
|
81
84
|
end
|
85
|
+
|
82
86
|
def data
|
83
87
|
@data
|
84
88
|
end
|
89
|
+
|
85
90
|
def table
|
86
91
|
Relationships.table_class(self).new
|
87
92
|
end
|
@@ -90,26 +95,27 @@ module ChefFixie
|
|
90
95
|
def self.ro_access(*args)
|
91
96
|
args.each do |field|
|
92
97
|
fundef = "def #{field}; @data.#{field}; end"
|
93
|
-
|
98
|
+
class_eval(fundef)
|
94
99
|
end
|
95
100
|
end
|
96
101
|
# TODO figure out model for write access
|
97
102
|
|
98
103
|
def self.name_field(field)
|
99
104
|
fundef = "def name; @data.#{field}; end"
|
100
|
-
|
105
|
+
class_eval(fundef)
|
101
106
|
end
|
102
107
|
|
103
108
|
def self.std_timestamp
|
104
109
|
[:created_at, :updated_at].each do |i|
|
105
|
-
|
110
|
+
ro_access(i)
|
106
111
|
end
|
107
112
|
end
|
113
|
+
|
108
114
|
# Pretty much any object with an authz id has these fields
|
109
115
|
def self.std_authz
|
110
|
-
|
116
|
+
std_timestamp
|
111
117
|
[:authz_id, :last_updated_by].each do |i|
|
112
|
-
|
118
|
+
ro_access(i)
|
113
119
|
end
|
114
120
|
end
|
115
121
|
|
@@ -117,7 +123,7 @@ module ChefFixie
|
|
117
123
|
rows = table.by_id(id)
|
118
124
|
raise "id #{id} matches more than one object" if rows.all.count != 1
|
119
125
|
rows.inner.delete
|
120
|
-
if
|
126
|
+
if respond_to?(:authz_delete)
|
121
127
|
authz_delete
|
122
128
|
end
|
123
129
|
end
|
@@ -131,13 +137,14 @@ module ChefFixie
|
|
131
137
|
funname = Relationships.plural(object)
|
132
138
|
# defer evaluation of mapper to make sure we have a chance for everyone to initialize
|
133
139
|
fundef = "def #{funname}; Relationships.table_class(:#{object}).new.by_org_id(org_id); end"
|
134
|
-
|
140
|
+
class_eval(fundef)
|
135
141
|
end
|
136
142
|
end
|
137
143
|
|
138
144
|
def initialize(data)
|
139
145
|
super(data)
|
140
146
|
end
|
147
|
+
|
141
148
|
def org_id
|
142
149
|
data[:id]
|
143
150
|
end
|
@@ -145,16 +152,20 @@ module ChefFixie
|
|
145
152
|
def global_admins
|
146
153
|
name = self.name
|
147
154
|
global_admins_name = "#{name}_global_admins"
|
148
|
-
|
155
|
+
read_access_name = "#{name}_read_access_group"
|
156
|
+
ChefFixie::Sql::Groups.new[global_admins_name] || \
|
157
|
+
ChefFixie::Sql::Groups.new[read_access_name]
|
149
158
|
end
|
150
159
|
|
160
|
+
alias read_access_group global_admins
|
161
|
+
|
151
162
|
# Iterators for objects in authz; using containers to enumerate things
|
152
163
|
# It might be better to metaprogram this up instead,
|
153
164
|
#
|
154
165
|
# TODO Write some tests to validate that this stuff
|
155
166
|
# works, since it depends on a lot of name magic...
|
156
167
|
|
157
|
-
NAME_FIXUP = {"data" => "data_bags", "sandboxes" => nil}
|
168
|
+
NAME_FIXUP = { "data" => "data_bags", "sandboxes" => nil }
|
158
169
|
def objects_by_container_type(container)
|
159
170
|
name = NAME_FIXUP.has_key?(container) ? NAME_FIXUP[container] : container
|
160
171
|
return [] if name.nil?
|
@@ -172,7 +183,7 @@ module ChefFixie
|
|
172
183
|
yield objects
|
173
184
|
end
|
174
185
|
end
|
175
|
-
|
186
|
+
nil
|
176
187
|
end
|
177
188
|
|
178
189
|
def each_authz_object
|
@@ -181,7 +192,7 @@ module ChefFixie
|
|
181
192
|
yield object
|
182
193
|
end
|
183
194
|
end
|
184
|
-
|
195
|
+
nil
|
185
196
|
end
|
186
197
|
|
187
198
|
scoped_type :container, :group, :client,
|
@@ -253,7 +264,6 @@ module ChefFixie
|
|
253
264
|
# org_migration_state_id_seq policy_revisions
|
254
265
|
# policy_revisions_policy_groups_association sandboxed_checksums
|
255
266
|
|
256
|
-
|
257
267
|
class CookbookArtifact < SqlObject
|
258
268
|
include AuthzObjectMixin
|
259
269
|
def initialize(data)
|
@@ -338,31 +348,33 @@ module ChefFixie
|
|
338
348
|
def get_table
|
339
349
|
:unknown_table
|
340
350
|
end
|
351
|
+
|
341
352
|
def mk_element(x)
|
342
353
|
x
|
343
354
|
end
|
344
355
|
|
345
356
|
def initialize(tablespec = nil)
|
346
357
|
ChefFixie::Sql.default_connection
|
347
|
-
@inner = tablespec || Sequel::Model(
|
358
|
+
@inner = tablespec || Sequel::Model(get_table)
|
348
359
|
end
|
360
|
+
|
349
361
|
def inner
|
350
362
|
# Make sure we have init
|
351
363
|
@inner
|
352
364
|
end
|
353
365
|
|
354
366
|
def filter_core(field, exp)
|
355
|
-
self.class.new(inner.filter(field=>exp))
|
367
|
+
self.class.new(inner.filter(field => exp))
|
356
368
|
end
|
357
369
|
|
358
|
-
def all(max_count
|
370
|
+
def all(max_count = :default)
|
359
371
|
if max_count == :default
|
360
372
|
max_count = ChefFixie::Sql::SqlTable.max_count_default
|
361
373
|
end
|
362
374
|
if max_count != :all
|
363
|
-
return :too_many_results if
|
375
|
+
return :too_many_results if inner.count > max_count
|
364
376
|
end
|
365
|
-
elements = inner.all.map {|org| mk_element(org) }
|
377
|
+
elements = inner.all.map { |org| mk_element(org) }
|
366
378
|
end
|
367
379
|
|
368
380
|
#
|
@@ -371,7 +383,7 @@ module ChefFixie
|
|
371
383
|
# https://stackoverflow.com/questions/9658724/ruby-metaprogramming-class-eval/9658775#9658775
|
372
384
|
def self.primary(arg)
|
373
385
|
name = :"by_#{arg}"
|
374
|
-
|
386
|
+
class_eval("def [](arg); #{name}(arg).all(1).first; end")
|
375
387
|
|
376
388
|
listfun = <<EOLF
|
377
389
|
def list(max_count=:default)
|
@@ -383,26 +395,27 @@ def list(max_count=:default)
|
|
383
395
|
end
|
384
396
|
end
|
385
397
|
EOLF
|
386
|
-
|
398
|
+
class_eval(listfun)
|
387
399
|
end
|
388
400
|
|
389
401
|
def self.filter_by(*args)
|
390
402
|
args.each do |field|
|
391
403
|
name = "by_#{field}"
|
392
404
|
fundef = "def #{name}(exp); filter_core(:#{field},exp); end"
|
393
|
-
|
405
|
+
class_eval(fundef)
|
394
406
|
end
|
395
407
|
end
|
396
408
|
|
397
409
|
def self.table(name)
|
398
410
|
fundef = "def get_table; :#{name}; end"
|
399
|
-
|
411
|
+
class_eval(fundef)
|
400
412
|
end
|
413
|
+
|
401
414
|
# doesn't work yet
|
402
415
|
# element Org in class Orgs will fail because it can't find Org (undefined)
|
403
416
|
def self.element(name)
|
404
417
|
fundef = "ElementType = name; def mk_element(x); #{name}.new(x); end"
|
405
|
-
|
418
|
+
class_eval(fundef)
|
406
419
|
end
|
407
420
|
end
|
408
421
|
|
@@ -414,7 +427,7 @@ EOLF
|
|
414
427
|
primary :name
|
415
428
|
filter_by :name, :id, :full_name, :authz_id
|
416
429
|
|
417
|
-
GlobalOrg = "0"*32
|
430
|
+
GlobalOrg = "0" * 32
|
418
431
|
|
419
432
|
def self.org_guid_to_name(guid)
|
420
433
|
"global" if guid == GlobalOrg
|
@@ -435,7 +448,7 @@ EOLF
|
|
435
448
|
|
436
449
|
def by_org_id_user_id(org_id, user_id)
|
437
450
|
# db table constraint guarantees that this is unique
|
438
|
-
inner.filter(:org_id=>org_id, :user_id=>user_id).all.first
|
451
|
+
inner.filter(:org_id => org_id, :user_id => user_id).all.first
|
439
452
|
end
|
440
453
|
|
441
454
|
end
|
@@ -445,7 +458,7 @@ EOLF
|
|
445
458
|
|
446
459
|
def by_org_id_user_id(org_id, user_id)
|
447
460
|
# db table constraint guarantees that this is unique
|
448
|
-
inner.filter(:org_id=>org_id, :user_id=>user_id).all.first
|
461
|
+
inner.filter(:org_id => org_id, :user_id => user_id).all.first
|
449
462
|
end
|
450
463
|
end
|
451
464
|
class Users < SqlTable
|
@@ -547,7 +560,7 @@ EOLF
|
|
547
560
|
filter_by :name, :id, :org_id, :authz_id
|
548
561
|
end
|
549
562
|
|
550
|
-
class Roles
|
563
|
+
class Roles < SqlTable
|
551
564
|
table :roles
|
552
565
|
element Sql::Role
|
553
566
|
register_authz :role, :object
|
@@ -556,7 +569,5 @@ EOLF
|
|
556
569
|
filter_by :name, :id, :org_id, :authz_id, :last_updated_by
|
557
570
|
end
|
558
571
|
|
559
|
-
|
560
|
-
|
561
572
|
end
|
562
573
|
end
|
@@ -18,42 +18,46 @@
|
|
18
18
|
# Author: Mark Anderson <mark@chef.io>
|
19
19
|
#
|
20
20
|
|
21
|
-
require_relative
|
22
|
-
require_relative
|
23
|
-
require_relative
|
21
|
+
require_relative "config"
|
22
|
+
require_relative "authz_objects"
|
23
|
+
require_relative "authz_mapper"
|
24
24
|
|
25
25
|
module ChefFixie
|
26
26
|
module UtilityHelpers
|
27
27
|
def self.orgs
|
28
28
|
@orgs ||= ChefFixie::Sql::Orgs.new
|
29
29
|
end
|
30
|
+
|
30
31
|
def self.users
|
31
32
|
@users ||= ChefFixie::Sql::Users.new
|
32
33
|
end
|
34
|
+
|
33
35
|
def self.assocs
|
34
36
|
@assocs ||= ChefFixie::Sql::Associations.new
|
35
37
|
end
|
38
|
+
|
36
39
|
def self.invites
|
37
40
|
invites ||= ChefFixie::Sql::Invites.new
|
38
41
|
end
|
39
42
|
|
40
43
|
def self.make_user(user)
|
41
44
|
if user.is_a?(String)
|
42
|
-
|
45
|
+
users[user]
|
43
46
|
elsif user.is_a?(ChefFixie::Sql::User)
|
44
|
-
|
47
|
+
user
|
45
48
|
else
|
46
49
|
raise Exception "Expected a user, got a #{user.class}"
|
47
50
|
end
|
48
|
-
end
|
51
|
+
end
|
52
|
+
|
49
53
|
def self.make_org(org)
|
50
54
|
if org.is_a?(String)
|
51
|
-
|
55
|
+
orgs[org]
|
52
56
|
elsif org.is_a?(ChefFixie::Sql::Org)
|
53
|
-
|
57
|
+
org
|
54
58
|
else
|
55
59
|
raise Exception "Expected an org, got a #{org.class}"
|
56
60
|
end
|
57
|
-
end
|
61
|
+
end
|
58
62
|
end
|
59
63
|
end
|
data/lib/chef_fixie/version.rb
CHANGED
data/spec/chef_fixie/acl_spec.rb
CHANGED
@@ -1,46 +1,45 @@
|
|
1
1
|
|
2
|
-
require
|
2
|
+
require "rspec"
|
3
3
|
require "spec_helper"
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "chef_fixie"
|
5
|
+
require "chef_fixie/config"
|
6
6
|
|
7
7
|
RSpec.describe ChefFixie::Sql::Orgs, "ACL access" do
|
8
|
-
let (:test_org_name) { "ponyville"}
|
8
|
+
let (:test_org_name) { "ponyville" }
|
9
9
|
let (:orgs) { ChefFixie::Sql::Orgs.new }
|
10
10
|
let (:users) { ChefFixie::Sql::Users.new }
|
11
11
|
let (:test_org) { orgs[test_org_name] }
|
12
12
|
|
13
13
|
# TODO this should use a freshly created object and purge it afterwords.
|
14
14
|
# But we need to write the create object feature still
|
15
|
-
|
15
|
+
|
16
16
|
context "Fetch acl for actor (client)" do
|
17
17
|
let (:testclient) { test_org.clients.all.first }
|
18
|
-
let (:testuser) { users[
|
19
|
-
let (:pivotal) { users[
|
18
|
+
let (:testuser) { users["spitfire"] }
|
19
|
+
let (:pivotal) { users["pivotal"] }
|
20
20
|
let (:client_container) { test_org.containers["clients"] }
|
21
|
-
|
21
|
+
|
22
22
|
it "We can fetch the acl" do
|
23
23
|
acl = testclient.acl
|
24
|
-
expect(acl.keys).to include(* %w
|
24
|
+
expect(acl.keys).to include(* %w{create read update delete grant})
|
25
25
|
end
|
26
26
|
|
27
27
|
it "we can add a user to an ace" do
|
28
|
-
# This requires either a temp object or good cleanup
|
28
|
+
# This requires either a temp object or good cleanup
|
29
29
|
# acl = testclient.acl
|
30
30
|
# expect(acl["read"]["actors"].not_to include("wonderbolts")
|
31
|
-
|
31
|
+
|
32
32
|
testclient.ace_add(:read, testuser)
|
33
33
|
|
34
34
|
acl = testclient.acl
|
35
35
|
expect(acl["read"]["actors"]).to include([:global, testuser.name])
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
it "we can add then delete a user from an ace" do
|
39
39
|
testclient.ace_add(:read, testuser)
|
40
40
|
acl = testclient.acl
|
41
41
|
expect(acl["read"]["actors"]).to include([:global, testuser.name])
|
42
42
|
|
43
|
-
|
44
43
|
testclient.ace_delete(:read, testuser)
|
45
44
|
|
46
45
|
acl = testclient.acl
|
@@ -49,26 +48,26 @@ RSpec.describe ChefFixie::Sql::Orgs, "ACL access" do
|
|
49
48
|
|
50
49
|
it "we can copy users from another acl" do
|
51
50
|
testclient.ace_delete(:all, pivotal)
|
52
|
-
|
51
|
+
|
53
52
|
testclient.acl_add_from_object(client_container)
|
54
53
|
|
55
54
|
acl = testclient.acl
|
56
|
-
%w
|
55
|
+
%w{create read update delete grant}.each do |action|
|
57
56
|
expect(acl[action]["actors"]).to include([:global, pivotal.name])
|
58
57
|
end
|
59
58
|
end
|
60
|
-
|
59
|
+
|
61
60
|
end
|
62
61
|
|
63
62
|
context "ACE Membership" do
|
64
|
-
|
65
|
-
let (:admingroup) { test_org.groups[
|
66
|
-
let (:testobject) { test_org.groups[
|
67
|
-
let (:notadmingroup) { test_org.groups[
|
68
|
-
let (:adminuser) { users[
|
69
|
-
let (:notadminuser) { users[
|
70
|
-
let (:pivotal) { users[
|
71
|
-
|
63
|
+
|
64
|
+
let (:admingroup) { test_org.groups["admins"] }
|
65
|
+
let (:testobject) { test_org.groups["admins"] }
|
66
|
+
let (:notadmingroup) { test_org.groups["clients"] }
|
67
|
+
let (:adminuser) { users["rainbowdash"] }
|
68
|
+
let (:notadminuser) { users["mary"] }
|
69
|
+
let (:pivotal) { users["pivotal"] }
|
70
|
+
|
72
71
|
it "Privileged users and groups are part of the read ACE" do
|
73
72
|
expect(testobject.ace_member?(:read, admingroup)).to be true
|
74
73
|
expect(testobject.ace_member?(:read, pivotal)).to be true
|
@@ -79,5 +78,4 @@ RSpec.describe ChefFixie::Sql::Orgs, "ACL access" do
|
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
82
|
-
|
83
81
|
end
|