conjur-cli 4.26.0 → 4.27.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/acceptance-features/audit/audit_event_send.feature +104 -0
- data/acceptance-features/audit/send.feature +70 -0
- data/acceptance-features/authentication/authenticate.feature +10 -0
- data/acceptance-features/authentication/login.feature +14 -0
- data/acceptance-features/authentication/logout.feature +16 -0
- data/acceptance-features/authentication/whoami.feature +5 -0
- data/acceptance-features/authorization/resource/annotate.feature +35 -0
- data/acceptance-features/authorization/resource/check.feature +22 -0
- data/acceptance-features/authorization/resource/create.feature +19 -0
- data/acceptance-features/authorization/resource/deny.feature +12 -0
- data/acceptance-features/authorization/resource/exists.feature +16 -0
- data/acceptance-features/authorization/resource/give.feature +22 -0
- data/acceptance-features/authorization/resource/permit.feature +20 -0
- data/acceptance-features/authorization/resource/permitted_roles.feature +16 -0
- data/acceptance-features/authorization/resource/show.feature +26 -0
- data/acceptance-features/authorization/role/create.feature +13 -0
- data/acceptance-features/authorization/role/exists.feature +19 -0
- data/acceptance-features/authorization/role/grant_to.feature +21 -0
- data/acceptance-features/authorization/role/graph.feature +58 -0
- data/acceptance-features/authorization/role/members.feature +23 -0
- data/acceptance-features/authorization/role/memberships.feature +27 -0
- data/acceptance-features/conjurenv/check.feature +28 -0
- data/acceptance-features/conjurenv/run.feature +10 -0
- data/acceptance-features/conjurenv/template.feature +11 -0
- data/acceptance-features/directory/group/create.feature +20 -0
- data/acceptance-features/directory/group/retire.feature +54 -0
- data/acceptance-features/directory/host/create.feature +23 -0
- data/acceptance-features/directory/host/retire.feature +6 -0
- data/acceptance-features/directory/layer/create.feature +10 -0
- data/acceptance-features/directory/layer/hosts-add.feature +9 -0
- data/acceptance-features/directory/layer/hosts-remove.feature +10 -0
- data/acceptance-features/directory/user/create.feature +23 -0
- data/acceptance-features/directory/user/retire.feature +6 -0
- data/acceptance-features/directory/user/update_password.feature +16 -0
- data/acceptance-features/directory/variable/create.feature +14 -0
- data/acceptance-features/directory/variable/retire.feature +17 -0
- data/acceptance-features/directory/variable/value.feature +13 -0
- data/acceptance-features/directory/variable/values-add.feature +12 -0
- data/acceptance-features/global-privilege/elevate.feature +20 -0
- data/acceptance-features/global-privilege/reveal.privilege +20 -0
- data/acceptance-features/pubkeys/add.feature +20 -0
- data/acceptance-features/pubkeys/delete.feature +9 -0
- data/acceptance-features/pubkeys/names.feature +23 -0
- data/acceptance-features/pubkeys/show.feature +25 -0
- data/acceptance-features/step_definitions/cli.rb +21 -0
- data/acceptance-features/step_definitions/graph_steps.rb +22 -0
- data/acceptance-features/step_definitions/user_steps.rb +54 -0
- data/acceptance-features/support/env.rb +5 -0
- data/acceptance-features/support/hooks.rb +179 -0
- data/acceptance-features/support/world.rb +153 -0
- data/conjur.gemspec +4 -1
- data/features/step_definitions/graph_steps.rb +2 -2
- data/features/support/hooks.rb +1 -5
- data/lib/conjur/cli.rb +1 -1
- data/lib/conjur/command/bootstrap.rb +3 -2
- data/lib/conjur/command/elevate.rb +76 -0
- data/lib/conjur/command/rspec/mock_services.rb +3 -3
- data/lib/conjur/command.rb +15 -0
- data/lib/conjur/version.rb +1 -1
- data/spec/command/elevate_spec.rb +28 -0
- metadata +85 -4
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
|
|
2
|
+
require 'conjur/api'
|
|
3
|
+
require 'conjur/cli'
|
|
4
|
+
require 'conjur/authn'
|
|
5
|
+
|
|
6
|
+
netrc = Conjur::Authn.netrc
|
|
7
|
+
username, password = Conjur::Authn.get_credentials
|
|
8
|
+
raise "Not logged in to Conjur" unless username && password
|
|
9
|
+
puts "Logging in as #{username}"
|
|
10
|
+
|
|
11
|
+
# Future Aruba
|
|
12
|
+
#Aruba.configure do |config|
|
|
13
|
+
# config.exit_timeout = 15
|
|
14
|
+
#end
|
|
15
|
+
|
|
16
|
+
Before('@conjurapi-log') do
|
|
17
|
+
set_env 'CONJURAPI_LOG', 'stderr'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
Before do
|
|
21
|
+
Conjur::Authn.save_credentials username: username, password: password
|
|
22
|
+
|
|
23
|
+
@admin_api = conjur_api = Conjur::Authn.connect
|
|
24
|
+
|
|
25
|
+
@namespace = conjur_api.create_variable("text/plain", "id").id
|
|
26
|
+
user = conjur_api.create_user "admin@#{@namespace}", ownerid: "#{Conjur.configuration.account}:user:#{username}"
|
|
27
|
+
|
|
28
|
+
netrc[Conjur::Authn.host] = [ "admin@#{@namespace}", user.api_key ]
|
|
29
|
+
netrc.save
|
|
30
|
+
|
|
31
|
+
conjur_api = Conjur::Authn.connect
|
|
32
|
+
@security_admin = conjur_api.create_group [ @namespace, "security_admin" ].join('/')
|
|
33
|
+
@security_admin.add_member user, admin_option: true
|
|
34
|
+
|
|
35
|
+
JsonSpec.memorize "MY_ROLEID", %Q("#{user.roleid}")
|
|
36
|
+
|
|
37
|
+
@admin_api.group("pubkeys-1.0/key-managers").add_member @security_admin
|
|
38
|
+
@admin_api.resource('!:!:conjur').permit 'elevate', user, grant_option: true
|
|
39
|
+
@admin_api.resource('!:!:conjur').permit 'reveal', user, grant_option: true
|
|
40
|
+
|
|
41
|
+
conjur_api.create_user "attic@#{@namespace}"
|
|
42
|
+
|
|
43
|
+
@aruba_timeout_seconds = 30
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
After do
|
|
47
|
+
if @admin_api
|
|
48
|
+
@admin_api.group("pubkeys-1.0/key-managers").remove_member @security_admin
|
|
49
|
+
end
|
|
50
|
+
tempfiles.each { |tempfile| File.unlink(tempfile) unless tempfile.nil? }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
at_exit do
|
|
54
|
+
Conjur::Authn.save_credentials username: username, password: password
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
require 'ostruct'
|
|
58
|
+
|
|
59
|
+
class MockAPI
|
|
60
|
+
attr_reader :things
|
|
61
|
+
|
|
62
|
+
def initialize
|
|
63
|
+
@things = {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def thing(kind, id)
|
|
67
|
+
(@things[kind.to_sym] || []).find{|r| r.id == id}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def thing_like(kind, id_pattern)
|
|
71
|
+
(@things[kind.to_sym] || []).find{|r| id_pattern.match(r.id)}
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def create_host(options = {})
|
|
75
|
+
id = options.delete(:id)
|
|
76
|
+
if id
|
|
77
|
+
host = thing(:host, id)
|
|
78
|
+
else
|
|
79
|
+
id = SecureRandom.uuid
|
|
80
|
+
end
|
|
81
|
+
host ||= create_thing(:host, id, options, role: true, api_key: true)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def create_user(id, options = {})
|
|
85
|
+
thing(:user, id) || create_thing(:user, id, options, role: true, api_key: true)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def create_variable(mime_type, kind)
|
|
89
|
+
create_thing(:user, SecureRandom.uuid, mime_type: mime_type, kind: kind)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def create_resource(id, options = {})
|
|
93
|
+
resource(id).tap do |resource|
|
|
94
|
+
resource.send(:"exists?=", true)
|
|
95
|
+
populate_options resource, options
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def create_role(id, options = {})
|
|
100
|
+
role(id).tap do |role|
|
|
101
|
+
role.send(:"exists?=", true)
|
|
102
|
+
populate_options role, options
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
[ :user, :host ].each do |kind|
|
|
107
|
+
define_method kind do |id|
|
|
108
|
+
thing(kind, id)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def role(id)
|
|
113
|
+
raise "Role id must be a string" unless id.is_a?(String)
|
|
114
|
+
thing(:role, id) || create_thing(:role, id, { exists?: false }, role: true)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def resource(id)
|
|
118
|
+
raise "Resource id must be a string" unless id.is_a?(String)
|
|
119
|
+
thing(:resource, id) || create_thing(:resource, id, exists?: false)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
protected
|
|
123
|
+
|
|
124
|
+
def create_thing(kind, id, options, kind_options = {})
|
|
125
|
+
thing = OpenStruct.new(kind: kind, id: id, exists?: true)
|
|
126
|
+
|
|
127
|
+
class << thing
|
|
128
|
+
def permit(privilege, role, options = {})
|
|
129
|
+
(self.permissions ||= []) << OpenStruct.new(privilege: privilege, role: role.id, grant_option: !!options[:grant_option])
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if kind_options[:api_key]
|
|
134
|
+
thing.api_key = SecureRandom.uuid
|
|
135
|
+
end
|
|
136
|
+
if kind_options[:role]
|
|
137
|
+
thing.roleid = id
|
|
138
|
+
class << thing
|
|
139
|
+
def can(privilege, resource, options = {})
|
|
140
|
+
resource.permit privilege, self, options
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
populate_options(thing, options)
|
|
146
|
+
|
|
147
|
+
store_thing kind, thing
|
|
148
|
+
|
|
149
|
+
thing
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def populate_options(thing, options)
|
|
153
|
+
options.each do |k,v|
|
|
154
|
+
thing.send("#{k}=", v)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def store_thing(kind, thing)
|
|
159
|
+
(things[kind] ||= []) << thing
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
Before("@dsl") do
|
|
164
|
+
puts "Using MockAPI"
|
|
165
|
+
puts "Using account 'cucumber'"
|
|
166
|
+
|
|
167
|
+
require 'conjur/api'
|
|
168
|
+
require 'conjur/config'
|
|
169
|
+
require 'conjur/dsl/runner'
|
|
170
|
+
|
|
171
|
+
Conjur.stub(:env).and_return "ci"
|
|
172
|
+
Conjur.stub(:stack).and_return "ci"
|
|
173
|
+
Conjur.stub(:account).and_return "cucumber"
|
|
174
|
+
|
|
175
|
+
Conjur::Core::API.stub(:conjur_account).and_return 'cucumber'
|
|
176
|
+
@mock_api ||= MockAPI.new
|
|
177
|
+
Conjur::DSL::Runner.any_instance.stub(:api).and_return @mock_api
|
|
178
|
+
end
|
|
179
|
+
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require 'aruba/api'
|
|
2
|
+
require 'conjur/api'
|
|
3
|
+
|
|
4
|
+
module ConjurCLIWorld
|
|
5
|
+
include Aruba::Api
|
|
6
|
+
|
|
7
|
+
def last_json
|
|
8
|
+
stdout_from(@last_cmd)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def find_or_create_password(username)
|
|
12
|
+
@passwords ||= {}
|
|
13
|
+
unless password = @passwords[username]
|
|
14
|
+
password = @passwords[username] = SecureRandom.hex(12)
|
|
15
|
+
end
|
|
16
|
+
password
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def namespace
|
|
20
|
+
@namespace or raise "@namespace is not initialized"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Aruba's method
|
|
24
|
+
def run(cmd, *args)
|
|
25
|
+
# it's a thunk now so it should be returned. puts can be added back as block if we want to
|
|
26
|
+
super process_cmd(cmd), *args
|
|
27
|
+
|
|
28
|
+
#puts stderr_from(cmd)
|
|
29
|
+
#puts stdout_from(cmd)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def stderr_from(cmd)
|
|
33
|
+
super process_cmd(cmd)
|
|
34
|
+
end
|
|
35
|
+
def stdout_from(cmd)
|
|
36
|
+
super process_cmd(cmd)
|
|
37
|
+
end
|
|
38
|
+
def output_from(cmd)
|
|
39
|
+
super process_cmd(cmd)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Substitute the namespace for marker $ns
|
|
43
|
+
def unescape(string)
|
|
44
|
+
string = super
|
|
45
|
+
string.gsub("$ns", namespace)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def get_process(wanted)
|
|
49
|
+
super wanted.gsub("$ns", namespace)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tempfiles
|
|
53
|
+
@tempfiles||=[]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
protected
|
|
57
|
+
|
|
58
|
+
def process_cmd(cmd)
|
|
59
|
+
cmd = cmd.dup
|
|
60
|
+
cmd.gsub!("$ns", namespace)
|
|
61
|
+
cmd.gsub!("$pubkeys_url", Conjur.configuration.pubkeys_url)
|
|
62
|
+
|
|
63
|
+
@last_cmd = cmd
|
|
64
|
+
JsonSpec.memory.each do |k,v|
|
|
65
|
+
cmd.gsub!("%{#{k}}", v)
|
|
66
|
+
end
|
|
67
|
+
cmd
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
module ConjurWorld
|
|
72
|
+
def last_json
|
|
73
|
+
last_stdout
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def last_stdout
|
|
77
|
+
raise "No commands have been run" unless last_cmd
|
|
78
|
+
stdout_from last_cmd
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attr_accessor :last_cmd
|
|
82
|
+
|
|
83
|
+
def account
|
|
84
|
+
Conjur::Core::API.conjur_account
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def role_kind
|
|
88
|
+
@role_kind ||= "cli-cukes"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def role_id_map
|
|
92
|
+
@role_id_map ||= {}
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def extract_filtered_graph json
|
|
96
|
+
graph = JSON.parse(json.to_s)
|
|
97
|
+
case graph
|
|
98
|
+
when Hash then filter_hash_graph(graph)
|
|
99
|
+
when Array then filter_array_graph(graph)
|
|
100
|
+
else raise "WTF: graph was #{graph.class}?"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def filter_hash_graph graph
|
|
105
|
+
allowed = role_id_map.values
|
|
106
|
+
edges = graph['graph']
|
|
107
|
+
filtered = edges.select do |edge|
|
|
108
|
+
allowed.member?(edge['parent']) and allowed.member?(edge['child'])
|
|
109
|
+
end
|
|
110
|
+
{'graph' => filtered}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def filter_array_graph graph
|
|
114
|
+
allowed = role_id_map.values
|
|
115
|
+
graph.select do |edge|
|
|
116
|
+
edge.all?{|v| allowed.member?(v)}
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def graph edges
|
|
121
|
+
# generate roles
|
|
122
|
+
edges.flatten.uniq.each do |role_id|
|
|
123
|
+
role_id_map[role_id] = expanded = expand_role_id(role_id)
|
|
124
|
+
run_command "conjur role create '#{expanded}'"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# generate memberships
|
|
128
|
+
edges.each do |parent, child|
|
|
129
|
+
run_command "conjur role grant_to #{expand_role_id(parent)} #{expand_role_id(child)}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def run_command cmd
|
|
134
|
+
step "I successfully run " + '`' + cmd + '`'
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def expand_role_id role_id
|
|
138
|
+
"#{account}:#{role_kind}:#{prepend_namespace role_id}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def prepend_namespace id
|
|
142
|
+
"#{namespace}-#{id}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def expand_roles string
|
|
146
|
+
role_id_map.each do |role, expanded|
|
|
147
|
+
string.gsub! role, expanded
|
|
148
|
+
end
|
|
149
|
+
string
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
World(ConjurWorld, ConjurCLIWorld)
|
data/conjur.gemspec
CHANGED
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
gem.add_dependency 'activesupport'
|
|
20
|
-
gem.add_dependency 'conjur-api', '~> 4.
|
|
20
|
+
gem.add_dependency 'conjur-api', '~> 4.19'
|
|
21
21
|
gem.add_dependency 'gli', '>=2.8.0'
|
|
22
22
|
gem.add_dependency 'highline'
|
|
23
23
|
gem.add_dependency 'netrc', '~> 0.10.2'
|
|
@@ -35,4 +35,7 @@ Gem::Specification.new do |gem|
|
|
|
35
35
|
gem.add_development_dependency 'rake', '~> 10.0'
|
|
36
36
|
gem.add_development_dependency 'io-grab', '~> 0.0.1'
|
|
37
37
|
gem.add_development_dependency 'json_spec'
|
|
38
|
+
# For cukes
|
|
39
|
+
gem.add_development_dependency 'conjur-asset-audit-send'
|
|
40
|
+
gem.add_development_dependency 'conjur-asset-host-factory'
|
|
38
41
|
end
|
|
@@ -14,8 +14,8 @@ When(/^I( successfully)? run with role expansion "(.*)"$/) do |successfully, cmd
|
|
|
14
14
|
end
|
|
15
15
|
self.last_cmd = cmd
|
|
16
16
|
if successfully
|
|
17
|
-
step "I successfully run
|
|
17
|
+
step "I successfully run `#{cmd}`"
|
|
18
18
|
else
|
|
19
|
-
step "I run
|
|
19
|
+
step "I run `#{cmd}`"
|
|
20
20
|
end
|
|
21
21
|
end
|
data/features/support/hooks.rb
CHANGED
|
@@ -122,11 +122,7 @@ Before("@dsl") do
|
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
Before('@real-api') do
|
|
125
|
-
|
|
126
|
-
cfg = File.absolute_path("#{File.dirname __FILE__}/../../.conjurrc")
|
|
127
|
-
puts "cfg_path = #{cfg}"
|
|
128
|
-
puts "contents = #{File.read(cfg)}"
|
|
129
|
-
Conjur::Config.load([cfg])
|
|
125
|
+
Conjur::Config.load
|
|
130
126
|
Conjur::Config.apply
|
|
131
127
|
@aruba_timeout_seconds = 15
|
|
132
128
|
end
|
data/lib/conjur/cli.rb
CHANGED
|
@@ -63,7 +63,7 @@ module Conjur
|
|
|
63
63
|
# do too much effort, and GLIs support for aliasing doesn't work out so well with
|
|
64
64
|
# subcommands.
|
|
65
65
|
def run args
|
|
66
|
-
|
|
66
|
+
args = args.shift.split(':') + args unless args.empty?
|
|
67
67
|
super args
|
|
68
68
|
end
|
|
69
69
|
|
|
@@ -49,6 +49,8 @@ class Conjur::Command::Bootstrap < Conjur::Command
|
|
|
49
49
|
# The admin user will always satisfy these conditions, unless they are revoked for some reason.
|
|
50
50
|
# Other users created by the bootstrap command will (typically) also have these powers.
|
|
51
51
|
def self.security_admin_manager? api
|
|
52
|
+
return true if elevated?
|
|
53
|
+
|
|
52
54
|
username = api.username
|
|
53
55
|
user = if username.index('/')
|
|
54
56
|
nil
|
|
@@ -116,7 +118,6 @@ class Conjur::Command::Bootstrap < Conjur::Command
|
|
|
116
118
|
puts "User created"
|
|
117
119
|
puts "Making '#{username}' a member and admin of group 'security_admin'"
|
|
118
120
|
security_admin.add_member user, admin_option: true
|
|
119
|
-
security_admin.resource.permit "read", user
|
|
120
121
|
puts "Adminship granted"
|
|
121
122
|
end
|
|
122
123
|
|
|
@@ -139,4 +140,4 @@ class Conjur::Command::Bootstrap < Conjur::Command
|
|
|
139
140
|
end
|
|
140
141
|
end
|
|
141
142
|
end
|
|
142
|
-
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2015 Conjur Inc
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
5
|
+
# this software and associated documentation files (the "Software"), to deal in
|
|
6
|
+
# the Software without restriction, including without limitation the rights to
|
|
7
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
8
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
9
|
+
# subject to the following conditions:
|
|
10
|
+
#
|
|
11
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
# copies or substantial portions of the Software.
|
|
13
|
+
#
|
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
16
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
17
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
18
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
19
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
20
|
+
#
|
|
21
|
+
|
|
22
|
+
# Implement privileged modes such as 'elevate' and 'reveal'
|
|
23
|
+
class Conjur::Command::Elevate < Conjur::DSLCommand
|
|
24
|
+
|
|
25
|
+
def self.subcommand args
|
|
26
|
+
code = Conjur::CLI.run args
|
|
27
|
+
raise GLI::CustomExit.new("Subcommand failed", code) unless code == 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
desc "Run a sub-command with elevated privileges"
|
|
31
|
+
long_desc <<-DESC
|
|
32
|
+
If you are allowed to do this by the Conjur server, all server-side permission checks will be bypassed and any
|
|
33
|
+
action will be allowed.
|
|
34
|
+
|
|
35
|
+
To be able to run this command, you must have the 'elevate' privilege on the resource '!:!:conjur'.
|
|
36
|
+
|
|
37
|
+
EXAMPLE
|
|
38
|
+
|
|
39
|
+
Force retirement of a user:
|
|
40
|
+
|
|
41
|
+
$ conjur elevate user retire alice
|
|
42
|
+
DESC
|
|
43
|
+
command :elevate do |c|
|
|
44
|
+
c.action do |global_options,options,args|
|
|
45
|
+
exit_now! "Subcommand is required" if args.empty?
|
|
46
|
+
|
|
47
|
+
Conjur::Command.api = api.with_privilege "elevate"
|
|
48
|
+
subcommand args
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc "Run a sub-command in 'reveal' mode"
|
|
53
|
+
long_desc <<-DESC
|
|
54
|
+
If you are allowed to do this by the Conjur server, you can inspect all data in the Conjur
|
|
55
|
+
authorization service. For example, you can list and search for all resources, regardless of
|
|
56
|
+
your ownership and privileges. You can also show details on any resource, and you can perform
|
|
57
|
+
permission checks on any resource.
|
|
58
|
+
|
|
59
|
+
To be able to run this command, you must have the 'reveal' privilege on the resource '!:!:conjur'.
|
|
60
|
+
|
|
61
|
+
EXAMPLE
|
|
62
|
+
|
|
63
|
+
List all groups:
|
|
64
|
+
|
|
65
|
+
$ conjur reveal group list -i
|
|
66
|
+
|
|
67
|
+
DESC
|
|
68
|
+
command :reveal do |c|
|
|
69
|
+
c.action do |global_options,options,args|
|
|
70
|
+
exit_now! "Subcommand is required" if args.empty?
|
|
71
|
+
|
|
72
|
+
Conjur::Command.api = api.with_privilege "reveal"
|
|
73
|
+
subcommand args
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -16,6 +16,9 @@ shared_context "with mock authn" do
|
|
|
16
16
|
let(:netrcfile) { Tempfile.new 'authtest' }
|
|
17
17
|
let(:netrc) { Netrc.read(netrcfile.path) }
|
|
18
18
|
let(:account) { 'the-account' }
|
|
19
|
+
let(:username) { 'dknuth' }
|
|
20
|
+
let(:api_key) { 'sekrit' }
|
|
21
|
+
let(:api) { Conjur::API.new_from_key(username, api_key) }
|
|
19
22
|
before do
|
|
20
23
|
allow(Conjur::Core::API).to receive(:conjur_account) { account }
|
|
21
24
|
allow(Conjur::Authn).to receive_messages(netrc: netrc, host: authn_host)
|
|
@@ -25,9 +28,6 @@ end
|
|
|
25
28
|
|
|
26
29
|
shared_context "when logged in", logged_in: true do
|
|
27
30
|
include_context "with mock authn"
|
|
28
|
-
let(:username) { 'dknuth' }
|
|
29
|
-
let(:api_key) { 'sekrit' }
|
|
30
|
-
let(:api) { Conjur::API.new_from_key(username, api_key) }
|
|
31
31
|
before do
|
|
32
32
|
allow(api).to receive(:credentials) { {} }
|
|
33
33
|
netrc[authn_host] = [username, api_key]
|
data/lib/conjur/command.rb
CHANGED
|
@@ -28,6 +28,7 @@ module Conjur
|
|
|
28
28
|
|
|
29
29
|
class << self
|
|
30
30
|
attr_accessor :prefix
|
|
31
|
+
|
|
31
32
|
def method_missing *a, &b
|
|
32
33
|
Conjur::CLI.send *a, &b
|
|
33
34
|
end
|
|
@@ -41,6 +42,14 @@ module Conjur
|
|
|
41
42
|
args.shift or raise "Missing parameter: #{name}"
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
def assert_empty(args)
|
|
46
|
+
exit_now! "Received extra command arguments" unless args.empty?
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def api= api
|
|
50
|
+
@@api = api
|
|
51
|
+
end
|
|
52
|
+
|
|
44
53
|
def api
|
|
45
54
|
@@api ||= Conjur::Authn.connect
|
|
46
55
|
end
|
|
@@ -183,7 +192,13 @@ an alternative destination role.)
|
|
|
183
192
|
end
|
|
184
193
|
end
|
|
185
194
|
|
|
195
|
+
def elevated?
|
|
196
|
+
api.privilege == 'elevate' && api.global_privilege_permitted?('elevate')
|
|
197
|
+
end
|
|
198
|
+
|
|
186
199
|
def validate_retire_privileges record, options
|
|
200
|
+
return true if elevated?
|
|
201
|
+
|
|
187
202
|
if record.respond_to?(:role)
|
|
188
203
|
memberships = current_user.role.memberships.map(&:roleid)
|
|
189
204
|
validate_privileges "You can't administer this record" do
|
data/lib/conjur/version.rb
CHANGED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Conjur::Command::Elevate do
|
|
4
|
+
describe_command "elevate user show alice" do
|
|
5
|
+
include_context "with mock authn"
|
|
6
|
+
|
|
7
|
+
let(:token) { {login: 'dknuth'} }
|
|
8
|
+
before{
|
|
9
|
+
expect(Conjur::Authn).to receive(:connect).and_return(api)
|
|
10
|
+
}
|
|
11
|
+
it "invokes the sub-command with X-Conjur-Privilege header" do
|
|
12
|
+
allow_any_instance_of(Conjur::API).to receive(:token).and_return(token)
|
|
13
|
+
expect(Conjur::Command).to receive(:api=) do |api|
|
|
14
|
+
expect(api.api_key).to eq("sekrit")
|
|
15
|
+
expect(api.privilege).to eq("elevate")
|
|
16
|
+
end.and_call_original
|
|
17
|
+
|
|
18
|
+
expect(RestClient::Request).to receive(:execute).with({
|
|
19
|
+
method: :get,
|
|
20
|
+
url: "https://core.example.com/users/alice",
|
|
21
|
+
username: "dknuth",
|
|
22
|
+
headers: {:authorization=>"Token token=\"eyJsb2dpbiI6ImRrbnV0aCJ9\"", x_conjur_privilege: "elevate"}
|
|
23
|
+
}.merge(cert_store_options)).and_return(double(:response, body: "[]"))
|
|
24
|
+
|
|
25
|
+
invoke
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|