knife-opc 0.3.2 → 0.4.7

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.
@@ -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)