knife-opc 0.3.2 → 0.4.0
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/README.md +71 -107
- data/lib/chef/knife/opc_org_create.rb +14 -14
- data/lib/chef/knife/opc_org_delete.rb +3 -3
- data/lib/chef/knife/opc_org_edit.rb +4 -4
- data/lib/chef/knife/opc_org_list.rb +5 -5
- data/lib/chef/knife/opc_org_show.rb +3 -3
- data/lib/chef/knife/opc_org_user_add.rb +10 -9
- data/lib/chef/knife/opc_org_user_remove.rb +54 -2
- data/lib/chef/knife/opc_user_create.rb +37 -21
- data/lib/chef/knife/opc_user_delete.rb +113 -14
- data/lib/chef/knife/opc_user_edit.rb +13 -14
- data/lib/chef/knife/opc_user_list.rb +3 -3
- data/lib/chef/knife/opc_user_password.rb +4 -5
- data/lib/chef/knife/opc_user_show.rb +6 -6
- data/lib/chef/mixin/root_rest.rb +3 -3
- data/lib/chef/org.rb +31 -33
- data/lib/chef/org/group_operations.rb +44 -4
- data/lib/knife-opc/version.rb +1 -1
- metadata +10 -66
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Marc Paradise (<marc@getchef.com>)
|
3
|
-
# Copyright:: Copyright 2014 Chef Software, Inc
|
3
|
+
# Copyright:: Copyright 2014-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -22,8 +22,15 @@ module Opc
|
|
22
22
|
banner "knife opc org user remove ORG_NAME USER_NAME"
|
23
23
|
attr_accessor :org_name, :username
|
24
24
|
|
25
|
+
option :force_remove_from_admins,
|
26
|
+
:long => "--force",
|
27
|
+
:short => "-f",
|
28
|
+
:description => "Force removal of user from the organization's admins and billing-admins group."
|
29
|
+
|
25
30
|
deps do
|
26
|
-
require
|
31
|
+
require "chef/org"
|
32
|
+
require "chef/org/group_operations"
|
33
|
+
require "chef/json_compat"
|
27
34
|
end
|
28
35
|
|
29
36
|
def run
|
@@ -36,16 +43,61 @@ module Opc
|
|
36
43
|
end
|
37
44
|
|
38
45
|
org = Chef::Org.new(@org_name)
|
46
|
+
|
47
|
+
if config[:force_remove_from_admins]
|
48
|
+
if org.actor_delete_would_leave_admins_empty?
|
49
|
+
failure_error_message(org_name, username)
|
50
|
+
ui.msg <<-EOF
|
51
|
+
You ran with --force which force removes the user from the admins and billing-admins groups.
|
52
|
+
However, removing #{username} from the admins group would leave it empty, which breaks the org.
|
53
|
+
Please add another user to org #{org_name} admins group and try again.
|
54
|
+
EOF
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
remove_user_from_admin_group(org, org_name, username, "admins")
|
58
|
+
remove_user_from_admin_group(org, org_name, username, "billing-admins")
|
59
|
+
end
|
60
|
+
|
39
61
|
begin
|
40
62
|
org.dissociate_user(@username)
|
41
63
|
rescue Net::HTTPServerException => e
|
42
64
|
if e.response.code == "404"
|
43
65
|
ui.msg "User #{username} is not associated with organization #{org_name}"
|
44
66
|
exit 1
|
67
|
+
elsif e.response.code == "403"
|
68
|
+
body = Chef::JSONCompat.from_json(e.response.body)
|
69
|
+
if body.has_key?("error") && body["error"] == "Please remove #{username} from this organization's admins group before removing him or her from the organization."
|
70
|
+
failure_error_message(org_name, username)
|
71
|
+
ui.msg <<-EOF
|
72
|
+
User #{username} is in the organization's admin group. Removing users from an organization without removing them from the admins group is not allowed.
|
73
|
+
Re-run this command with --force to remove this user from the admins prior to removing it from the organization.
|
74
|
+
EOF
|
75
|
+
exit 1
|
76
|
+
else
|
77
|
+
raise e
|
78
|
+
end
|
45
79
|
else
|
46
80
|
raise e
|
47
81
|
end
|
48
82
|
end
|
49
83
|
end
|
84
|
+
|
85
|
+
def failure_error_message(org_name, username)
|
86
|
+
ui.error "Error removing user #{username} from organization #{org_name}."
|
87
|
+
end
|
88
|
+
|
89
|
+
def remove_user_from_admin_group(org, org_name, username, admin_group_string)
|
90
|
+
org.remove_user_from_group(admin_group_string, username)
|
91
|
+
rescue Net::HTTPServerException => e
|
92
|
+
if e.response.code == "404"
|
93
|
+
ui.warn <<-EOF
|
94
|
+
User #{username} is not in the #{admin_group_string} group for organization #{org_name}.
|
95
|
+
You probably don't need to pass --force.
|
96
|
+
EOF
|
97
|
+
else
|
98
|
+
raise e
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
50
102
|
end
|
51
103
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
|
-
# Author:: Steven Danna (<steve@
|
3
|
-
# Copyright:: Copyright 2011
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright 2011-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,7 +15,7 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
require
|
18
|
+
require "chef/mixin/root_rest"
|
19
19
|
|
20
20
|
module Opc
|
21
21
|
class OpcUserCreate < Chef::Knife
|
@@ -23,14 +23,19 @@ module Opc
|
|
23
23
|
banner "knife opc user create USERNAME FIRST_NAME [MIDDLE_NAME] LAST_NAME EMAIL PASSWORD"
|
24
24
|
|
25
25
|
option :filename,
|
26
|
-
:long =>
|
27
|
-
:short =>
|
28
|
-
:description =>
|
26
|
+
:long => "--filename FILENAME",
|
27
|
+
:short => "-f FILENAME",
|
28
|
+
:description => "Write private key to FILENAME rather than STDOUT"
|
29
29
|
|
30
30
|
option :orgname,
|
31
|
-
:long =>
|
32
|
-
:short =>
|
33
|
-
:description =>
|
31
|
+
:long => "--orgname ORGNAME",
|
32
|
+
:short => "-o ORGNAME",
|
33
|
+
:description => "Associate new user to an organization matching ORGNAME"
|
34
|
+
|
35
|
+
option :passwordprompt,
|
36
|
+
:long => "--prompt-for-password",
|
37
|
+
:short => "-p",
|
38
|
+
:description => "Prompt for user password"
|
34
39
|
|
35
40
|
include Chef::Mixin::RootRestv0
|
36
41
|
|
@@ -40,11 +45,18 @@ module Opc
|
|
40
45
|
username, first_name, middle_name, last_name, email, password = @name_args
|
41
46
|
when 5
|
42
47
|
username, first_name, last_name, email, password = @name_args
|
48
|
+
when 4
|
49
|
+
username, first_name, last_name, email = @name_args
|
43
50
|
else
|
44
51
|
ui.fatal "Wrong number of arguments"
|
45
52
|
show_usage
|
46
53
|
exit 1
|
47
54
|
end
|
55
|
+
password = prompt_for_password if config[:passwordprompt]
|
56
|
+
unless password
|
57
|
+
ui.fatal "You must either provide a password or use the --prompt-for-password (-p) option"
|
58
|
+
exit 1
|
59
|
+
end
|
48
60
|
middle_name ||= ""
|
49
61
|
|
50
62
|
user_hash = {
|
@@ -54,32 +66,36 @@ module Opc
|
|
54
66
|
:last_name => last_name,
|
55
67
|
:display_name => "#{first_name} #{last_name}",
|
56
68
|
:email => email,
|
57
|
-
:password => password
|
69
|
+
:password => password,
|
58
70
|
}
|
59
71
|
|
60
72
|
# Check the file before creating the user so the api is more transactional.
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
73
|
+
if config[:filename]
|
74
|
+
file = config[:filename]
|
75
|
+
unless File.exists?(file) ? File.writable?(file) : File.writable?(File.dirname(file))
|
76
|
+
ui.fatal "File #{config[:filename]} is not writable. Check permissions."
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
68
80
|
|
69
81
|
result = root_rest.post("users/", user_hash)
|
70
82
|
if config[:filename]
|
71
83
|
File.open(config[:filename], "w") do |f|
|
72
|
-
f.print(result[
|
84
|
+
f.print(result["private_key"])
|
73
85
|
end
|
74
86
|
else
|
75
|
-
ui.msg result[
|
87
|
+
ui.msg result["private_key"]
|
76
88
|
end
|
77
89
|
if config[:orgname]
|
78
|
-
request_body = {:user => username}
|
90
|
+
request_body = { :user => username }
|
79
91
|
response = root_rest.post("organizations/#{config[:orgname]}/association_requests", request_body)
|
80
92
|
association_id = response["uri"].split("/").last
|
81
|
-
root_rest.put("users/#{username}/association_requests/#{association_id}", {:response =>
|
93
|
+
root_rest.put("users/#{username}/association_requests/#{association_id}", { :response => "accept" })
|
82
94
|
end
|
83
95
|
end
|
96
|
+
|
97
|
+
def prompt_for_password
|
98
|
+
ui.ask("Please enter the user's password: ") { |q| q.echo = false }
|
99
|
+
end
|
84
100
|
end
|
85
101
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
|
-
# Author:: Steven Danna (<steve@
|
3
|
-
# Copyright:: Copyright 2011
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright 2011-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,31 +15,130 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
require
|
18
|
+
require "chef/mixin/root_rest"
|
19
19
|
|
20
20
|
module Opc
|
21
21
|
class OpcUserDelete < Chef::Knife
|
22
22
|
category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
|
23
|
-
banner "knife opc user delete USERNAME [-d]"
|
23
|
+
banner "knife opc user delete USERNAME [-d] [-R]"
|
24
24
|
|
25
25
|
option :no_disassociate_user,
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
:long => "--no-disassociate-user",
|
27
|
+
:short => "-d",
|
28
|
+
:description => "Don't disassociate the user first"
|
29
29
|
|
30
|
+
option :remove_from_admin_groups,
|
31
|
+
long: "--remove-from-admin-groups",
|
32
|
+
short: "-R",
|
33
|
+
description: "If the user is a member of any org admin groups, attempt to remove from those groups. Ignored if --no-disassociate-user is set."
|
34
|
+
|
35
|
+
attr_reader :username
|
30
36
|
include Chef::Mixin::RootRestv0
|
31
37
|
|
38
|
+
deps do
|
39
|
+
require "chef/org"
|
40
|
+
require "chef/org/group_operations"
|
41
|
+
end
|
42
|
+
|
32
43
|
def run
|
33
|
-
username = @name_args[0]
|
44
|
+
@username = @name_args[0]
|
45
|
+
admin_memberships = []
|
46
|
+
unremovable_memberships = []
|
47
|
+
|
34
48
|
ui.confirm "Do you want to delete the user #{username}"
|
49
|
+
|
35
50
|
unless config[:no_disassociate_user]
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
51
|
+
ui.stderr.puts("Checking organization memberships...")
|
52
|
+
orgs = org_memberships(username)
|
53
|
+
if orgs.length > 0
|
54
|
+
ui.stderr.puts("Checking admin group memberships for #{orgs.length} org(s).")
|
55
|
+
admin_memberships, unremovable_memberships = admin_group_memberships(orgs, username)
|
56
|
+
end
|
57
|
+
|
58
|
+
unless admin_memberships.empty?
|
59
|
+
unless config[:remove_from_admin_groups]
|
60
|
+
error_exit_admin_group_member!(username, admin_memberships)
|
61
|
+
end
|
62
|
+
|
63
|
+
unless unremovable_memberships.empty?
|
64
|
+
error_exit_cant_remove_admin_membership!(username, unremovable_memberships)
|
65
|
+
end
|
66
|
+
remove_from_admin_groups(admin_memberships, username)
|
67
|
+
end
|
68
|
+
disassociate_user(orgs, username)
|
69
|
+
end
|
70
|
+
|
71
|
+
delete_user(username)
|
72
|
+
end
|
73
|
+
|
74
|
+
def disassociate_user(orgs, username)
|
75
|
+
orgs.each { |org| org.dissociate_user(username) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def org_memberships(username)
|
79
|
+
org_data = root_rest.get("users/#{username}/organizations")
|
80
|
+
org_data.map { |org| Chef::Org.new(org["organization"]["name"]) }
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove_from_admin_groups(admin_of, username)
|
84
|
+
admin_of.each do |org|
|
85
|
+
ui.stderr.puts "Removing #{username} from admins group of '#{org.name}'"
|
86
|
+
org.remove_user_from_group("admins", username)
|
41
87
|
end
|
42
|
-
|
88
|
+
end
|
89
|
+
|
90
|
+
def admin_group_memberships(orgs, username)
|
91
|
+
admin_of = []
|
92
|
+
unremovable = []
|
93
|
+
orgs.each do |org|
|
94
|
+
if org.user_member_of_group?(username, "admins")
|
95
|
+
admin_of << org
|
96
|
+
if org.actor_delete_would_leave_admins_empty?
|
97
|
+
unremovable << org
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
[admin_of, unremovable]
|
102
|
+
end
|
103
|
+
|
104
|
+
def delete_user(username)
|
105
|
+
ui.stderr.puts "Deleting user #{username}."
|
106
|
+
root_rest.delete("users/#{username}")
|
107
|
+
end
|
108
|
+
|
109
|
+
# Error message that says how to removed from org
|
110
|
+
# admin groups before deleting
|
111
|
+
# Further
|
112
|
+
def error_exit_admin_group_member!(username, admin_of)
|
113
|
+
message = "#{username} is in the 'admins' group of the following organization(s):\n\n"
|
114
|
+
admin_of.each { |org| message << "- #{org.name}\n" }
|
115
|
+
message << <<EOM
|
116
|
+
|
117
|
+
Run this command again with the --remove-from-admin-groups option to
|
118
|
+
remove the user from these admin group(s) automatically.
|
119
|
+
|
120
|
+
EOM
|
121
|
+
ui.fatal message
|
122
|
+
exit 1
|
123
|
+
end
|
124
|
+
|
125
|
+
def error_exit_cant_remove_admin_membership!(username, only_admin_of)
|
126
|
+
message = <<EOM
|
127
|
+
|
128
|
+
#{username} is the only member of the 'admins' group of the
|
129
|
+
following organization(s):
|
130
|
+
|
131
|
+
EOM
|
132
|
+
only_admin_of.each { |org| message << "- #{org.name}\n" }
|
133
|
+
message << <<EOM
|
134
|
+
|
135
|
+
Removing the only administrator of an organization can break it.
|
136
|
+
Assign additional users or groups to the admin group(s) before
|
137
|
+
deleting this user.
|
138
|
+
|
139
|
+
EOM
|
140
|
+
ui.fatal message
|
141
|
+
exit 1
|
43
142
|
end
|
44
143
|
end
|
45
144
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
|
-
# Author:: Steven Danna (<steve@
|
3
|
-
# Copyright:: Copyright 2011
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright 2011-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,7 +15,7 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
require
|
18
|
+
require "chef/mixin/root_rest"
|
19
19
|
|
20
20
|
module Opc
|
21
21
|
class OpcUserEdit < Chef::Knife
|
@@ -23,14 +23,14 @@ module Opc
|
|
23
23
|
banner "knife opc user edit USERNAME"
|
24
24
|
|
25
25
|
option :input,
|
26
|
-
:long =>
|
27
|
-
:short =>
|
28
|
-
:description =>
|
26
|
+
:long => "--input FILENAME",
|
27
|
+
:short => "-i FILENAME",
|
28
|
+
:description => "Name of file to use for PUT or POST"
|
29
29
|
|
30
30
|
option :filename,
|
31
|
-
:long =>
|
32
|
-
:short =>
|
33
|
-
:description =>
|
31
|
+
:long => "--filename FILENAME",
|
32
|
+
:short => "-f FILENAME",
|
33
|
+
:description => "Write private key to FILENAME rather than STDOUT"
|
34
34
|
|
35
35
|
include Chef::Mixin::RootRestv0
|
36
36
|
|
@@ -43,7 +43,7 @@ module Opc
|
|
43
43
|
exit 1
|
44
44
|
end
|
45
45
|
|
46
|
-
original_user =
|
46
|
+
original_user = root_rest.get("users/#{user_name}")
|
47
47
|
if config[:input]
|
48
48
|
edited_user = JSON.parse(IO.read(config[:input]))
|
49
49
|
else
|
@@ -52,19 +52,18 @@ module Opc
|
|
52
52
|
if original_user != edited_user
|
53
53
|
result = root_rest.put("users/#{user_name}", edited_user)
|
54
54
|
ui.msg("Saved #{user_name}.")
|
55
|
-
if ! result[
|
55
|
+
if ! result["private_key"].nil?
|
56
56
|
if config[:filename]
|
57
57
|
File.open(config[:filename], "w") do |f|
|
58
|
-
f.print(result[
|
58
|
+
f.print(result["private_key"])
|
59
59
|
end
|
60
60
|
else
|
61
|
-
ui.msg result[
|
61
|
+
ui.msg result["private_key"]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
else
|
65
65
|
ui.msg("User unchanged, not saving.")
|
66
66
|
end
|
67
|
-
|
68
67
|
end
|
69
68
|
end
|
70
69
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
|
-
# Author:: Steven Danna (<steve@
|
3
|
-
# Copyright:: Copyright 2011
|
2
|
+
# Author:: Steven Danna (<steve@chef.io>)
|
3
|
+
# Copyright:: Copyright 2011-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,7 +15,7 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
require
|
18
|
+
require "chef/mixin/root_rest"
|
19
19
|
|
20
20
|
module Opc
|
21
21
|
class OpcUserList < Chef::Knife
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Tyler Cloke (<tyler@getchef.com>)
|
3
|
-
# Copyright:: Copyright 2014 Chef, Inc.
|
3
|
+
# Copyright:: Copyright 2014-2016 Chef Software, Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -15,7 +15,7 @@
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
|
-
require
|
18
|
+
require "chef/mixin/root_rest"
|
19
19
|
|
20
20
|
module Opc
|
21
21
|
class OpcUserPassword < Chef::Knife
|
@@ -50,7 +50,7 @@ module Opc
|
|
50
50
|
# true or false, there is no way of knowing if the user is using ldap or not,
|
51
51
|
# so we will update the user every time, instead of checking if we are actually
|
52
52
|
# changing anything before we PUT.
|
53
|
-
user =
|
53
|
+
user = root_rest.get("users/#{user_name}")
|
54
54
|
|
55
55
|
user["password"] = password if not password.nil?
|
56
56
|
|
@@ -64,9 +64,8 @@ module Opc
|
|
64
64
|
root_rest.put("users/#{user_name}", user)
|
65
65
|
rescue => e
|
66
66
|
raise e
|
67
|
-
exit 1
|
68
67
|
end
|
69
|
-
|
68
|
+
|
70
69
|
ui.msg("Authentication info updated for #{user_name}.")
|
71
70
|
end
|
72
71
|
end
|