chef_fixie 0.4.0 → 1.0.3
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 +4 -4
- data/README.md +4 -5
- data/bin/chef_fixie +1 -1
- data/doc/BulkFixup.md +1 -1
- 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 +40 -39
- data/lib/chef_fixie/config.rb +20 -19
- data/lib/chef_fixie/console.rb +9 -9
- data/lib/chef_fixie/context.rb +2 -4
- data/lib/chef_fixie/sql.rb +12 -12
- data/lib/chef_fixie/sql_objects.rb +44 -37
- data/lib/chef_fixie/utility_helpers.rb +13 -9
- data/lib/chef_fixie/version.rb +1 -1
- data/lib/chef_fixie.rb +7 -7
- 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 +19 -62
@@ -18,40 +18,44 @@
|
|
18
18
|
# Author: Mark Anderson <mark@chef.io>
|
19
19
|
#
|
20
20
|
|
21
|
-
require_relative
|
22
|
-
require_relative
|
23
|
-
require_relative
|
24
|
-
require_relative
|
21
|
+
require_relative "config"
|
22
|
+
require_relative "authz_objects"
|
23
|
+
require_relative "authz_mapper"
|
24
|
+
require_relative "utility_helpers"
|
25
25
|
|
26
26
|
module ChefFixie
|
27
27
|
module CheckOrgAssociations
|
28
28
|
def self.orgs
|
29
29
|
@orgs ||= ChefFixie::Sql::Orgs.new
|
30
30
|
end
|
31
|
+
|
31
32
|
def self.users
|
32
33
|
@users ||= ChefFixie::Sql::Users.new
|
33
34
|
end
|
35
|
+
|
34
36
|
def self.assocs
|
35
37
|
@assocs ||= ChefFixie::Sql::Associations.new
|
36
38
|
end
|
39
|
+
|
37
40
|
def self.invites
|
38
41
|
invites ||= ChefFixie::Sql::Invites.new
|
39
42
|
end
|
40
43
|
|
41
44
|
def self.make_user(user)
|
42
45
|
if user.is_a?(String)
|
43
|
-
|
46
|
+
users[user]
|
44
47
|
elsif user.is_a?(ChefFixie::Sql::User)
|
45
|
-
|
48
|
+
user
|
46
49
|
else
|
47
50
|
raise "Expected a user, got a #{user.class}"
|
48
51
|
end
|
49
52
|
end
|
53
|
+
|
50
54
|
def self.make_org(org)
|
51
55
|
if org.is_a?(String)
|
52
|
-
|
56
|
+
orgs[org]
|
53
57
|
elsif org.is_a?(ChefFixie::Sql::Org)
|
54
|
-
|
58
|
+
org
|
55
59
|
else
|
56
60
|
raise "Expected an org, got a #{org.class}"
|
57
61
|
end
|
@@ -61,7 +65,6 @@ module ChefFixie
|
|
61
65
|
user = make_user(user)
|
62
66
|
org = make_org(org)
|
63
67
|
org.groups[user.id]
|
64
|
-
|
65
68
|
end
|
66
69
|
|
67
70
|
def self.check_association(org, user, global_admins = nil)
|
@@ -85,7 +88,7 @@ module ChefFixie
|
|
85
88
|
return :user_not_in_usag
|
86
89
|
end
|
87
90
|
|
88
|
-
if !org.groups[
|
91
|
+
if !org.groups["users"].member?(usag)
|
89
92
|
return :usag_not_in_users
|
90
93
|
end
|
91
94
|
|
@@ -96,7 +99,7 @@ module ChefFixie
|
|
96
99
|
if invites.by_org_id_user_id(org.id, user.id)
|
97
100
|
return :zombie_invite
|
98
101
|
end
|
99
|
-
|
102
|
+
true
|
100
103
|
end
|
101
104
|
|
102
105
|
def self.fix_association(org, user, global_admins = nil)
|
@@ -105,7 +108,7 @@ module ChefFixie
|
|
105
108
|
user = users[user] if user.is_a?(String)
|
106
109
|
global_admins ||= org.global_admins
|
107
110
|
|
108
|
-
failure = check_association(org,user,global_admins)
|
111
|
+
failure = check_association(org, user, global_admins)
|
109
112
|
|
110
113
|
case failure
|
111
114
|
when true
|
@@ -115,14 +118,14 @@ module ChefFixie
|
|
115
118
|
usag.group_add(user)
|
116
119
|
when :usag_not_in_users
|
117
120
|
usag = org.groups[user.id]
|
118
|
-
org.groups[
|
121
|
+
org.groups["users"].group_add(usag)
|
119
122
|
when :global_admins_lacks_read
|
120
123
|
user.ace_add(:read, global_admins)
|
121
124
|
else
|
122
125
|
puts "#{org.name} #{user.name} can't fix problem #{failure} yet"
|
123
126
|
return false
|
124
127
|
end
|
125
|
-
|
128
|
+
true
|
126
129
|
end
|
127
130
|
|
128
131
|
def self.check_associations(org)
|
@@ -140,56 +143,54 @@ module ChefFixie
|
|
140
143
|
users_assoc = assocs.by_org_id(org.id).all(:all)
|
141
144
|
users_invite = invites.by_org_id(org.id).all(:all)
|
142
145
|
|
143
|
-
user_ids = users_assoc.map {|a| a.user_id }
|
144
|
-
users_in_org = user_ids.map {|i| users.by_id(i).all.first }
|
145
|
-
usernames = users_in_org.map {|u| u.name }
|
146
|
-
|
146
|
+
user_ids = users_assoc.map { |a| a.user_id }
|
147
|
+
users_in_org = user_ids.map { |i| users.by_id(i).all.first }
|
148
|
+
usernames = users_in_org.map { |u| u.name }
|
147
149
|
|
148
150
|
# check that users aren't both invited and associated
|
149
|
-
invited_ids = users_invite.map {|a| a.user_id }
|
151
|
+
invited_ids = users_invite.map { |a| a.user_id }
|
150
152
|
overlap_ids = user_ids & invited_ids
|
151
153
|
|
152
154
|
if !overlap_ids.empty?
|
153
|
-
overlap_names = overlap_ids.map {|i| users.by_id(i).all.first.name rescue "#{i}" }
|
154
|
-
puts "#{orgname} users both associated and invited: #{overlap_names.join(', ')
|
155
|
+
overlap_names = overlap_ids.map { |i| users.by_id(i).all.first.name rescue "#{i}" }
|
156
|
+
puts "#{orgname} users both associated and invited: #{overlap_names.join(', ')}"
|
155
157
|
success = false
|
156
158
|
end
|
157
159
|
|
158
160
|
# Check that we don't have zombie USAGs left around (not 100% reliable)
|
159
161
|
# because someone could create a group that looks like a USAG
|
160
162
|
possible_usags = org.groups.list(:all) - user_ids
|
161
|
-
usags = possible_usags.select {|n| n =~ /^\h+{20}$/ }
|
163
|
+
usags = possible_usags.select { |n| n =~ /^\h+{20}$/ }
|
162
164
|
if !usags.empty?
|
163
|
-
puts "#{orgname} Suspicious USAGS without associated user #{usags.join(', ')
|
165
|
+
puts "#{orgname} Suspicious USAGS without associated user #{usags.join(', ')}"
|
164
166
|
end
|
165
167
|
|
166
168
|
# Check group membership for sanity
|
167
|
-
success &= check_group(org,
|
168
|
-
success &= check_group(org,
|
169
|
+
success &= check_group(org, "billing-admins", usernames)
|
170
|
+
success &= check_group(org, "admins", usernames)
|
169
171
|
|
170
172
|
# TODO check for non-usags in users!
|
171
|
-
users_members = org.groups[
|
172
|
-
users_actors = users_members[
|
173
|
+
users_members = org.groups["users"].group
|
174
|
+
users_actors = users_members["actors"] - [[:global, "pivotal"]]
|
173
175
|
if !users_actors.empty?
|
174
176
|
puts "#{orgname} has actors in it's users group #{users_actors}"
|
175
177
|
end
|
176
|
-
non_usags = users_members[
|
178
|
+
non_usags = users_members["groups"].map { |g| g[1] } - user_ids
|
177
179
|
if !non_usags.empty?
|
178
180
|
puts "#{orgname} warning: has non usags in it's users group #{non_usags.join(', ')}"
|
179
181
|
end
|
180
182
|
|
181
|
-
|
182
183
|
# Check individual associations
|
183
184
|
users_in_org.each do |user|
|
184
|
-
result =
|
185
|
-
if
|
185
|
+
result = check_association(org, user, global_admins)
|
186
|
+
if result != true
|
186
187
|
puts "Org #{orgname} Association check failed for #{user.name} #{result}"
|
187
188
|
success = false
|
188
189
|
end
|
189
190
|
end
|
190
191
|
|
191
192
|
puts "Org #{orgname} is #{success ? 'ok' : 'bad'} (#{users_in_org.count} users)"
|
192
|
-
|
193
|
+
success
|
193
194
|
end
|
194
195
|
|
195
196
|
# expect at least one current user to be in admins and billing admins
|
@@ -199,21 +200,21 @@ module ChefFixie
|
|
199
200
|
puts "#{orgname} Missing group #{groupname}"
|
200
201
|
return :no_such_group
|
201
202
|
end
|
202
|
-
actors = g.group[
|
203
|
+
actors = g.group["actors"].map { |x| x[1] }
|
203
204
|
live = actors & users
|
204
205
|
|
205
206
|
if live.count == 0
|
206
207
|
puts "Org #{org.name} has no active users in #{groupname}"
|
207
208
|
return false
|
208
209
|
end
|
209
|
-
|
210
|
+
true
|
210
211
|
end
|
211
|
-
|
212
|
+
|
212
213
|
def self.remove_association(org, user)
|
213
214
|
# magic to make usage easier
|
214
215
|
org = make_org(org)
|
215
216
|
user = make_user(user)
|
216
|
-
|
217
|
+
|
217
218
|
# remove USAG
|
218
219
|
usag = org.groups[user.id]
|
219
220
|
usag.delete if usag
|
@@ -222,16 +223,16 @@ module ChefFixie
|
|
222
223
|
org.groups.all(:all).each do |g|
|
223
224
|
g.group_delete(user) if g.member?(user)
|
224
225
|
end
|
225
|
-
|
226
|
+
|
226
227
|
# remove read ACE
|
227
228
|
user.ace_delete(:read, org.global_admins)
|
228
229
|
|
229
230
|
# remove association record
|
230
|
-
assoc = assocs.by_org_id_user_id(org.id,user.id)
|
231
|
+
assoc = assocs.by_org_id_user_id(org.id, user.id)
|
231
232
|
assoc.delete if assoc
|
232
233
|
|
233
234
|
# remove any invites
|
234
|
-
invite = invites.by_org_id_user_id(org.id,user.id)
|
235
|
+
invite = invites.by_org_id_user_id(org.id, user.id)
|
235
236
|
invite.delete if invite
|
236
237
|
end
|
237
238
|
end
|
data/lib/chef_fixie/config.rb
CHANGED
@@ -18,10 +18,10 @@
|
|
18
18
|
#
|
19
19
|
# Much of this code was orginally derived from the orgmapper tool, which had many varied authors.
|
20
20
|
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
21
|
+
require "singleton"
|
22
|
+
require "ffi_yajl"
|
23
|
+
require "pathname"
|
24
|
+
require "veil"
|
25
25
|
|
26
26
|
module ChefFixie
|
27
27
|
def self.configure
|
@@ -66,7 +66,7 @@ module ChefFixie
|
|
66
66
|
KEYS = [:authz_uri, :sql_database, :superuser_id, :pivotal_key]
|
67
67
|
KEYS.each { |k| attr_accessor k }
|
68
68
|
|
69
|
-
def merge_opts(opts={})
|
69
|
+
def merge_opts(opts = {})
|
70
70
|
opts.each do |key, value|
|
71
71
|
send("#{key}=".to_sym, value)
|
72
72
|
end
|
@@ -84,7 +84,7 @@ module ChefFixie
|
|
84
84
|
key_len > max ? key_len : max
|
85
85
|
end
|
86
86
|
KEYS.each do |key|
|
87
|
-
value = send(key) ||
|
87
|
+
value = send(key) || "default"
|
88
88
|
txt << "# %#{max_key_len}s: %s" % [key.to_s, value]
|
89
89
|
end
|
90
90
|
txt.join("\n")
|
@@ -102,25 +102,25 @@ module ChefFixie
|
|
102
102
|
def load_from_pc(dir = "/etc/opscode")
|
103
103
|
configdir = Pathname.new(dir)
|
104
104
|
|
105
|
-
config_files = %w
|
105
|
+
config_files = %w{chef-server-running.json}
|
106
106
|
config = load_json_from_path([configdir], config_files)
|
107
107
|
|
108
|
-
secrets = load_secrets_from_path([configdir], %w
|
108
|
+
secrets = load_secrets_from_path([configdir], %w{private-chef-secrets.json} )
|
109
109
|
|
110
|
-
authz_config = config[
|
111
|
-
authz_vip = authz_config[
|
112
|
-
authz_port = authz_config[
|
110
|
+
authz_config = config["private_chef"]["oc_bifrost"]
|
111
|
+
authz_vip = authz_config["vip"]
|
112
|
+
authz_port = authz_config["port"]
|
113
113
|
@authz_uri = "http://#{authz_vip}:#{authz_port}"
|
114
114
|
|
115
|
-
@superuser_id = dig(secrets,
|
115
|
+
@superuser_id = dig(secrets, %w{oc_bifrost superuser_id}) || authz_config["superuser_id"]
|
116
116
|
|
117
|
-
sql_config = config[
|
118
|
-
erchef_config = config[
|
117
|
+
sql_config = config["private_chef"]["postgresql"]
|
118
|
+
erchef_config = config["private_chef"]["opscode-erchef"]
|
119
119
|
|
120
|
-
sql_user = sql_config[
|
121
|
-
sql_pw = dig(secrets,
|
122
|
-
sql_vip = sql_config[
|
123
|
-
sql_port = sql_config[
|
120
|
+
sql_user = sql_config["sql_user"] || erchef_config["sql_user"]
|
121
|
+
sql_pw = dig(secrets, %w{opscode_erchef sql_password}) || sql_config["sql_password"] || erchef_config["sql_password"]
|
122
|
+
sql_vip = sql_config["vip"]
|
123
|
+
sql_port = sql_config["port"]
|
124
124
|
|
125
125
|
@sql_database = "postgres://#{sql_user}:#{sql_pw}@#{sql_vip}/opscode_chef"
|
126
126
|
|
@@ -139,6 +139,7 @@ module ChefFixie
|
|
139
139
|
end
|
140
140
|
end
|
141
141
|
end
|
142
|
+
|
142
143
|
def load_secrets_from_path(pathlist, filelist)
|
143
144
|
pathlist.each do |path|
|
144
145
|
filelist.each do |file|
|
@@ -156,7 +157,7 @@ module ChefFixie
|
|
156
157
|
if hash.respond_to?(:get)
|
157
158
|
hash.get(*list)
|
158
159
|
elsif hash.nil?
|
159
|
-
nil
|
160
|
+
nil
|
160
161
|
elsif list.empty?
|
161
162
|
hash
|
162
163
|
else
|
data/lib/chef_fixie/console.rb
CHANGED
@@ -18,12 +18,12 @@
|
|
18
18
|
#
|
19
19
|
# Much of this code was orginally derived from the orgmapper tool, which had many varied authors.
|
20
20
|
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
21
|
+
require "optparse"
|
22
|
+
require "pp"
|
23
|
+
require "pry"
|
24
24
|
|
25
|
-
require_relative
|
26
|
-
require_relative
|
25
|
+
require_relative "../chef_fixie"
|
26
|
+
require_relative "context"
|
27
27
|
|
28
28
|
module ChefFixie
|
29
29
|
module Console
|
@@ -47,9 +47,9 @@ module ChefFixie
|
|
47
47
|
options = {}
|
48
48
|
OptionParser.new do |opt|
|
49
49
|
opt.banner = "Usage: fixie [config] [options]"
|
50
|
-
opt.on(
|
51
|
-
opt.on("--sql_database DATABASE",
|
52
|
-
opt.on_tail(
|
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
53
|
puts opt
|
54
54
|
puts "\nExample configuration file:\n\n"
|
55
55
|
puts ChefFixie::Config.instance.example_config
|
@@ -68,7 +68,7 @@ module ChefFixie
|
|
68
68
|
Pry.config.history.file = "~/.fixie_history"
|
69
69
|
Pry.config.prompt_name = "fixie"
|
70
70
|
Pry::Commands.block_command("fixie-help", "Show fixie's help") do
|
71
|
-
|
71
|
+
output.puts(<<-HALP)
|
72
72
|
** ORGS **
|
73
73
|
* access with ORGS or ORGS
|
74
74
|
* access a specific org: ORGS['orgname']
|
data/lib/chef_fixie/context.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2015 Chef Software Inc.
|
2
|
+
# Copyright (c) 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");
|
@@ -18,9 +18,8 @@
|
|
18
18
|
#
|
19
19
|
# Much of this code was orginally derived from the orgmapper tool, which had many varied authors.
|
20
20
|
|
21
|
-
|
22
21
|
module ChefFixie
|
23
|
-
|
22
|
+
module Context
|
24
23
|
|
25
24
|
def describe_orgs
|
26
25
|
OrgMetrics.org_stats(orgs)
|
@@ -67,6 +66,5 @@ module ChefFixie
|
|
67
66
|
|
68
67
|
ChefFixie::Dissociator.dissociate_user(org, user)
|
69
68
|
end
|
70
|
-
|
71
69
|
end
|
72
70
|
end
|
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)
|