knife-opc 0.3.2 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Steven Danna (<steve@opscode.com>)
3
- # Copyright:: Copyright 2011 Opscode, Inc.
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 'chef/mixin/root_rest'
18
+ require_relative "../mixin/root_rest"
19
19
 
20
20
  module Opc
21
21
  class OpcUserDelete < Chef::Knife
22
- category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
23
- banner "knife opc user delete USERNAME [-d]"
22
+ category "CHEF ORGANIZATION MANAGEMENT"
23
+ banner "knife opc user delete USERNAME [-d] [-R]"
24
24
 
25
25
  option :no_disassociate_user,
26
- :long => "--no-disassociate-user",
27
- :short => "-d",
28
- :description => "Don't disassociate the user first"
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_relative "../org"
40
+ require_relative "../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
- orgs = root_rest.get("users/#{username}/organizations")
37
- org_names = orgs.map {|o| o['organization']['name']}
38
- org_names.each do |org|
39
- ui.output root_rest.delete("organizations/#{org}/users/#{username}")
40
- end
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
- ui.output root_rest.delete("users/#{username}")
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@opscode.com>)
3
- # Copyright:: Copyright 2011 Opscode, Inc.
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,22 +15,22 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- require 'chef/mixin/root_rest'
18
+ require_relative "../mixin/root_rest"
19
19
 
20
20
  module Opc
21
21
  class OpcUserEdit < Chef::Knife
22
- category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
22
+ category "CHEF ORGANIZATION MANAGEMENT"
23
23
  banner "knife opc user edit USERNAME"
24
24
 
25
25
  option :input,
26
- :long => '--input FILENAME',
27
- :short => '-i FILENAME',
28
- :description => 'Name of file to use for PUT or POST'
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 => '--filename FILENAME',
32
- :short => '-f FILENAME',
33
- :description => 'Write private key to FILENAME rather than STDOUT'
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,28 +43,52 @@ module Opc
43
43
  exit 1
44
44
  end
45
45
 
46
- original_user = root_rest.get("users/#{user_name}")
47
- if config[:input]
48
- edited_user = JSON.parse(IO.read(config[:input]))
49
- else
50
- edited_user = edit_data(original_user)
51
- end
46
+ original_user = root_rest.get("users/#{user_name}")
47
+ edited_user = get_updated_user(original_user)
52
48
  if original_user != edited_user
53
49
  result = root_rest.put("users/#{user_name}", edited_user)
54
50
  ui.msg("Saved #{user_name}.")
55
- if ! result['private_key'].nil?
51
+ unless result["private_key"].nil?
56
52
  if config[:filename]
57
53
  File.open(config[:filename], "w") do |f|
58
- f.print(result['private_key'])
54
+ f.print(result["private_key"])
59
55
  end
60
56
  else
61
- ui.msg result['private_key']
57
+ ui.msg result["private_key"]
62
58
  end
63
59
  end
64
60
  else
65
61
  ui.msg("User unchanged, not saving.")
66
62
  end
63
+ end
64
+
65
+ private
67
66
 
67
+ # Check the options for ex: input or filename
68
+ # Read Or Open file to update user information
69
+ # return updated user
70
+ def get_updated_user(original_user)
71
+ if config[:input]
72
+ edited_user = JSON.parse(IO.read(config[:input]))
73
+ elsif config[:filename]
74
+ file = config[:filename]
75
+ unless File.exist?(file) ? File.writable?(file) : File.writable?(File.dirname(file))
76
+ ui.fatal "File #{file} is not writable. Check permissions."
77
+ exit 1
78
+ else
79
+ output = Chef::JSONCompat.to_json_pretty(original_user)
80
+ File.open(file, "w") do |f|
81
+ f.sync = true
82
+ f.puts output
83
+ f.close
84
+ raise "Please set EDITOR environment variable. See https://docs.chef.io/knife_setup/ for details." unless system("#{config[:editor]} #{f.path}")
85
+
86
+ edited_user = JSON.parse(IO.read(f.path))
87
+ end
88
+ end
89
+ else
90
+ edited_user = JSON.parse(edit_data(original_user, false))
91
+ end
68
92
  end
69
93
  end
70
94
  end
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Steven Danna (<steve@opscode.com>)
3
- # Copyright:: Copyright 2011 Opscode, Inc.
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,23 +15,33 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- require 'chef/mixin/root_rest'
18
+ require_relative "../mixin/root_rest"
19
19
 
20
20
  module Opc
21
21
  class OpcUserList < Chef::Knife
22
- category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
22
+ category "CHEF ORGANIZATION MANAGEMENT"
23
23
  banner "knife opc user list"
24
24
 
25
25
  option :with_uri,
26
- :long => "--with-uri",
27
- :short => "-w",
28
- :description => "Show corresponding URIs"
26
+ long: "--with-uri",
27
+ short: "-w",
28
+ description: "Show corresponding URIs"
29
+
30
+ option :all_info,
31
+ long: "--all-info",
32
+ short: "-a",
33
+ description: "Show corresponding details i.e. username, email, first_name, last_name, display_name"
29
34
 
30
35
  include Chef::Mixin::RootRestv0
31
36
 
32
37
  def run
33
- results = root_rest.get("users")
34
- ui.output(ui.format_list_for_display(results))
38
+ if config[:all_info]
39
+ results = root_rest.get("users?verbose=true")
40
+ ui.output results
41
+ else
42
+ results = root_rest.get("users")
43
+ ui.output(ui.format_list_for_display(results))
44
+ end
35
45
  end
36
46
  end
37
47
  end
@@ -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,17 +15,17 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- require 'chef/mixin/root_rest'
18
+ require_relative "../mixin/root_rest"
19
19
 
20
20
  module Opc
21
21
  class OpcUserPassword < Chef::Knife
22
- category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
22
+ category "CHEF ORGANIZATION MANAGEMENT"
23
23
  banner "knife opc user password USERNAME [PASSWORD | --enable-external-auth]"
24
24
 
25
25
  option :enable_external_auth,
26
- :long => "--enable-external-auth",
27
- :short => "-e",
28
- :description => "Enable external authentication for this user (such as LDAP)"
26
+ long: "--enable-external-auth",
27
+ short: "-e",
28
+ description: "Enable external authentication for this user (such as LDAP)"
29
29
 
30
30
  include Chef::Mixin::RootRestv0
31
31
 
@@ -34,7 +34,7 @@ module Opc
34
34
  # USERNAME PASSWORD or USERNAME --enable-external-auth
35
35
  #
36
36
  # note that you can't pass USERNAME PASSWORD --enable-external-auth
37
- if !((@name_args.length == 2 && !config[:enable_external_auth]) || (@name_args.length == 1 && config[:enable_external_auth]))
37
+ unless (@name_args.length == 2 && !config[:enable_external_auth]) || (@name_args.length == 1 && config[:enable_external_auth])
38
38
  show_usage
39
39
  ui.fatal("You must pass two arguments")
40
40
  ui.fatal("Note that --enable-external-auth cannot be passed with a password")
@@ -50,9 +50,9 @@ 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 = root_rest.get("users/#{user_name}")
53
+ user = root_rest.get("users/#{user_name}")
54
54
 
55
- user["password"] = password if not password.nil?
55
+ user["password"] = password unless password.nil?
56
56
 
57
57
  # if --enable-external-auth was passed, enable it, else disable it.
58
58
  # there is never a situation where we would want to enable ldap
@@ -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
- ui.msg user
68
+
70
69
  ui.msg("Authentication info updated for #{user_name}.")
71
70
  end
72
71
  end
@@ -1,6 +1,6 @@
1
1
  #
2
- # Author:: Steven Danna (<steve@opscode.com>)
3
- # Copyright:: Copyright 2011 Opscode, Inc.
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,25 +15,25 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
  #
18
- require 'chef/mixin/root_rest'
18
+ require_relative "../mixin/root_rest"
19
19
 
20
20
  module Opc
21
21
  class OpcUserShow < Chef::Knife
22
- category "OPSCODE PRIVATE CHEF ORGANIZATION MANAGEMENT"
22
+ category "CHEF ORGANIZATION MANAGEMENT"
23
23
  banner "knife opc user show USERNAME"
24
24
 
25
25
  option :with_orgs,
26
- :long => "--with-orgs",
27
- :short => "-l"
26
+ long: "--with-orgs",
27
+ short: "-l"
28
28
 
29
29
  include Chef::Mixin::RootRestv0
30
30
 
31
31
  def run
32
32
  user_name = @name_args[0]
33
- results = root_rest.get("users/#{user_name}")
33
+ results = root_rest.get("users/#{user_name}")
34
34
  if config[:with_orgs]
35
- orgs = root_rest.get("users/#{user_name}/organizations")
36
- results["organizations"] = orgs.map {|o| o['organization']['name']}
35
+ orgs = root_rest.get("users/#{user_name}/organizations")
36
+ results["organizations"] = orgs.map { |o| o["organization"]["name"] }
37
37
  end
38
38
  ui.output results
39
39
  end
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Author:: Steven Danna (<steve@chef.io>)
3
- # Copyright:: Copyright 2011 Chef Software, Inc
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 'chef/server_api'
18
+ require "chef/server_api"
19
19
  class Chef
20
20
  module Mixin
21
21
  module RootRestv0
@@ -24,7 +24,7 @@ class Chef
24
24
  # Rather than upgrade all of this code to move to v1, the goal is to remove the
25
25
  # need for this plugin. See
26
26
  # https://github.com/chef/chef/issues/3517
27
- @root_rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], {:api_version => "0"})
27
+ @root_rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root], { api_version: "0" })
28
28
  end
29
29
  end
30
30
  end
@@ -1,7 +1,7 @@
1
- require 'chef/json_compat'
2
- require 'chef/mixin/params_validate'
3
- require 'chef/rest'
4
- require 'chef/org/group_operations'
1
+ require "chef/json_compat"
2
+ require "chef/mixin/params_validate"
3
+ require "chef/server_api"
4
+ require_relative "org/group_operations"
5
5
 
6
6
  class Chef
7
7
  class Org
@@ -9,9 +9,9 @@ class Chef
9
9
  include Chef::Mixin::ParamsValidate
10
10
  include Chef::Org::GroupOperations
11
11
 
12
- def initialize(name='')
12
+ def initialize(name = "")
13
13
  @name = name
14
- @full_name = ''
14
+ @full_name = ""
15
15
  # The Chef API returns the private key of the validator
16
16
  # client on create
17
17
  @private_key = nil
@@ -19,33 +19,33 @@ class Chef
19
19
  end
20
20
 
21
21
  def chef_rest
22
- @chef_rest ||= Chef::REST.new(Chef::Config[:chef_server_root])
22
+ @chef_rest ||= Chef::ServerAPI.new(Chef::Config[:chef_server_root])
23
23
  end
24
24
 
25
- def name(arg=nil)
25
+ def name(arg = nil)
26
26
  set_or_return(:name, arg,
27
- :regex => /^[a-z0-9\-_]+$/)
27
+ regex: /^[a-z0-9\-_]+$/)
28
28
  end
29
29
 
30
- def full_name(arg=nil)
30
+ def full_name(arg = nil)
31
31
  set_or_return(:full_name,
32
- arg, :kind_of => String)
32
+ arg, kind_of: String)
33
33
  end
34
34
 
35
- def private_key(arg=nil)
35
+ def private_key(arg = nil)
36
36
  set_or_return(:private_key,
37
- arg, :kind_of => String)
37
+ arg, kind_of: String)
38
38
  end
39
39
 
40
- def guid(arg=nil)
40
+ def guid(arg = nil)
41
41
  set_or_return(:guid,
42
- arg, :kind_of => String)
42
+ arg, kind_of: String)
43
43
  end
44
44
 
45
45
  def to_hash
46
46
  result = {
47
47
  "name" => @name,
48
- "full_name" => @full_name
48
+ "full_name" => @full_name,
49
49
  }
50
50
  result["private_key"] = @private_key if @private_key
51
51
  result["guid"] = @guid if @guid
@@ -57,15 +57,15 @@ class Chef
57
57
  end
58
58
 
59
59
  def create
60
- payload = {:name => self.name, :full_name => self.full_name}
60
+ payload = { name: name, full_name: full_name }
61
61
  new_org = chef_rest.post_rest("organizations", payload)
62
- Chef::Org.from_hash(self.to_hash.merge(new_org))
62
+ Chef::Org.from_hash(to_hash.merge(new_org))
63
63
  end
64
64
 
65
65
  def update
66
- payload = {:name => self.name, :full_name => self.full_name}
66
+ payload = { name: name, full_name: full_name }
67
67
  new_org = chef_rest.put_rest("organizations/#{name}", payload)
68
- Chef::Org.from_hash(self.to_hash.merge(new_org))
68
+ Chef::Org.from_hash(to_hash.merge(new_org))
69
69
  end
70
70
 
71
71
  def destroy
@@ -73,22 +73,20 @@ class Chef
73
73
  end
74
74
 
75
75
  def save
76
- begin
77
- create
78
- rescue Net::HTTPServerException => e
79
- if e.response.code == "409"
80
- update
81
- else
82
- raise e
83
- end
76
+ create
77
+ rescue Net::HTTPServerException => e
78
+ if e.response.code == "409"
79
+ update
80
+ else
81
+ raise e
84
82
  end
85
83
  end
86
84
 
87
85
  def associate_user(username)
88
- request_body = {:user => username}
86
+ request_body = { user: username }
89
87
  response = chef_rest.post_rest "organizations/#{@name}/association_requests", request_body
90
88
  association_id = response["uri"].split("/").last
91
- chef_rest.put_rest "users/#{username}/association_requests/#{association_id}", { :response => 'accept' }
89
+ chef_rest.put_rest "users/#{username}/association_requests/#{association_id}", { response: "accept" }
92
90
  end
93
91
 
94
92
  def dissociate_user(username)
@@ -98,10 +96,10 @@ class Chef
98
96
  # Class methods
99
97
  def self.from_hash(org_hash)
100
98
  org = Chef::Org.new
101
- org.name org_hash['name']
102
- org.full_name org_hash['full_name']
103
- org.private_key org_hash['private_key'] if org_hash.key?('private_key')
104
- org.guid org_hash['guid'] if org_hash.key?('guid')
99
+ org.name org_hash["name"]
100
+ org.full_name org_hash["full_name"]
101
+ org.private_key org_hash["private_key"] if org_hash.key?("private_key")
102
+ org.guid org_hash["guid"] if org_hash.key?("guid")
105
103
  org
106
104
  end
107
105
 
@@ -114,12 +112,12 @@ class Chef
114
112
  end
115
113
 
116
114
  def self.load(org_name)
117
- response = Chef::REST.new(Chef::Config[:chef_server_root]).get_rest("organizations/#{org_name}")
115
+ response = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get_rest("organizations/#{org_name}")
118
116
  Chef::Org.from_hash(response)
119
117
  end
120
118
 
121
- def self.list(inflate=false)
122
- orgs = Chef::REST.new(Chef::Config[:chef_server_root]).get_rest('organizations')
119
+ def self.list(inflate = false)
120
+ orgs = Chef::ServerAPI.new(Chef::Config[:chef_server_root]).get_rest("organizations")
123
121
  if inflate
124
122
  orgs.inject({}) do |org_map, (name, _url)|
125
123
  org_map[name] = Chef::Org.load(name)