conjur-cli 5.6.6 → 6.0.0.rc1
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/.dockerignore +1 -1
- data/.gitignore +2 -0
- data/.rubocop.yml +1 -1
- data/APPLIANCE_VERSION +1 -1
- data/CHANGELOG.md +3 -42
- data/Gemfile +4 -7
- data/Humanfile.md +31 -0
- data/Jenkinsfile +34 -63
- data/README.md +41 -55
- data/Rakefile +5 -1
- data/bin/conjur +0 -2
- data/build-deb.sh +1 -3
- data/ci/cli-test.sh +6 -0
- data/ci/package.sh +3 -1
- data/ci/publish.sh +2 -2
- data/ci/secrets/publish.yml +2 -2
- data/ci/wait_for_server.sh +10 -0
- data/conjur-cli.gemspec +7 -7
- data/dev/docker-compose.yml +24 -0
- data/dev/start.sh +15 -0
- data/dev/stop.sh +5 -0
- data/docker-compose.yml +30 -0
- data/features/authentication/authenticate.feature +34 -0
- data/features/authentication/login.feature +13 -0
- data/features/authentication/logout.feature +15 -0
- data/{acceptance-features → features}/authentication/whoami.feature +0 -0
- data/features/authorization/resource/annotate.feature +22 -0
- data/features/authorization/resource/check.feature +47 -0
- data/{acceptance-features → features}/authorization/resource/exists.feature +18 -6
- data/features/authorization/resource/permitted_roles.feature +35 -0
- data/features/authorization/resource/show.feature +34 -0
- data/features/authorization/role/exists.feature +28 -0
- data/features/authorization/role/members.feature +45 -0
- data/features/authorization/role/memberships.feature +43 -0
- data/features/conjurenv/check.feature +34 -0
- data/features/conjurenv/run.feature +15 -0
- data/{acceptance-features → features}/conjurenv/template.feature +8 -3
- data/{acceptance-features → features}/directory/user/update_password.feature +8 -2
- data/{acceptance-features → features}/directory/variable/value.feature +9 -5
- data/{acceptance-features → features}/directory/variable/values-add.feature +8 -3
- data/features/hostfactory/tokens.feature +22 -0
- data/features/pubkeys/show.feature +18 -0
- data/features/step_definitions/authn_steps.rb +22 -0
- data/features/step_definitions/cli_steps.rb +28 -0
- data/features/step_definitions/file_steps.rb +12 -0
- data/features/step_definitions/flow_control_steps.rb +7 -0
- data/features/step_definitions/graph_steps.rb +4 -3
- data/{acceptance-features → features}/step_definitions/http_steps.rb +0 -0
- data/features/step_definitions/overrides.rb +9 -0
- data/features/step_definitions/policy_steps.rb +11 -0
- data/{acceptance-features → features}/step_definitions/trusted_proxy_steps.rb +0 -0
- data/features/support/blank.yml +1 -0
- data/features/support/env.rb +21 -7
- data/features/support/hooks.rb +31 -116
- data/features/support/world.rb +16 -76
- data/jenkins.sh +33 -0
- data/lib/conjur/authenticator.rb +83 -0
- data/lib/conjur/authn.rb +5 -20
- data/lib/conjur/cli.rb +13 -6
- data/lib/conjur/command.rb +30 -350
- data/lib/conjur/command/authn.rb +23 -15
- data/lib/conjur/command/host_factories.rb +2 -74
- data/lib/conjur/command/hosts.rb +6 -113
- data/lib/conjur/command/init.rb +20 -35
- data/lib/conjur/command/{secrets.rb → policies.rb} +33 -22
- data/lib/conjur/command/pubkeys.rb +3 -63
- data/lib/conjur/command/resources.rb +45 -162
- data/lib/conjur/command/roles.rb +11 -181
- data/lib/conjur/command/rspec/helpers.rb +0 -1
- data/lib/conjur/command/rspec/mock_services.rb +4 -4
- data/lib/conjur/command/users.rb +2 -159
- data/lib/conjur/command/variables.rb +5 -218
- data/lib/conjur/complete.rb +2 -2
- data/lib/conjur/config.rb +1 -11
- data/lib/conjur/conjurenv.rb +12 -9
- data/lib/conjur/identifier_manipulation.rb +3 -5
- data/lib/conjur/version.rb +2 -2
- data/{publish-rubygem.sh → publish.sh} +0 -4
- data/spec/authn_spec.rb +4 -0
- data/spec/command/hosts_spec.rb +2 -69
- data/spec/command/init_spec.rb +16 -11
- data/spec/command/pubkeys_spec.rb +1 -46
- data/spec/command/resources_spec.rb +21 -170
- data/spec/command/roles_spec.rb +5 -181
- data/spec/command/users_spec.rb +3 -79
- data/spec/command_spec.rb +1 -20
- data/spec/complete_spec.rb +1 -23
- data/spec/config_spec.rb +1 -1
- data/spec/spec_helper.rb +4 -5
- data/test.sh +29 -25
- metadata +92 -212
- data/.githooks/pre_commit/run_specs.rb +0 -23
- data/Dockerfile +0 -15
- data/Dockerfile.fpm +0 -18
- data/Dockerfile.publish +0 -12
- data/Dockerfile.standalone +0 -33
- data/Dockerfile.validate-packaging +0 -9
- data/VERSION +0 -1
- data/acceptance-features/audit/audit_event_send.feature +0 -107
- data/acceptance-features/audit/fetch.feature +0 -16
- data/acceptance-features/audit/send.feature +0 -51
- data/acceptance-features/authentication/authenticate.feature +0 -10
- data/acceptance-features/authentication/login.feature +0 -12
- data/acceptance-features/authentication/logout.feature +0 -13
- data/acceptance-features/authorization/resource/annotate.feature +0 -35
- data/acceptance-features/authorization/resource/check.feature +0 -24
- data/acceptance-features/authorization/resource/create.feature +0 -21
- data/acceptance-features/authorization/resource/deny.feature +0 -12
- data/acceptance-features/authorization/resource/give.feature +0 -24
- data/acceptance-features/authorization/resource/permit.feature +0 -20
- data/acceptance-features/authorization/resource/permitted_roles.feature +0 -16
- data/acceptance-features/authorization/resource/show.feature +0 -28
- data/acceptance-features/authorization/role/create.feature +0 -13
- data/acceptance-features/authorization/role/exists.feature +0 -19
- data/acceptance-features/authorization/role/grant_to.feature +0 -21
- data/acceptance-features/authorization/role/graph.feature +0 -57
- data/acceptance-features/authorization/role/members.feature +0 -23
- data/acceptance-features/authorization/role/memberships.feature +0 -27
- data/acceptance-features/bootstrap.feature +0 -13
- data/acceptance-features/conjurenv/check.feature +0 -21
- data/acceptance-features/conjurenv/run.feature +0 -10
- data/acceptance-features/directory/group/create.feature +0 -20
- data/acceptance-features/directory/group/retire.feature +0 -54
- data/acceptance-features/directory/host/create.feature +0 -23
- data/acceptance-features/directory/host/retire.feature +0 -6
- data/acceptance-features/directory/hostfactory/create.feature +0 -28
- data/acceptance-features/directory/hostfactory/tokens.feature +0 -16
- data/acceptance-features/directory/layer/create.feature +0 -10
- data/acceptance-features/directory/layer/hosts-add.feature +0 -9
- data/acceptance-features/directory/layer/hosts-remove.feature +0 -10
- data/acceptance-features/directory/layer/retire.feature +0 -43
- data/acceptance-features/directory/user/create.feature +0 -23
- data/acceptance-features/directory/user/retire.feature +0 -6
- data/acceptance-features/directory/variable/create.feature +0 -14
- data/acceptance-features/directory/variable/retire.feature +0 -17
- data/acceptance-features/dsl/policy_owner.feature +0 -45
- data/acceptance-features/dsl/resource_owner.feature +0 -17
- data/acceptance-features/dsl/retire.feature +0 -15
- data/acceptance-features/global-privilege/elevate.feature +0 -20
- data/acceptance-features/global-privilege/reveal.privilege +0 -20
- data/acceptance-features/pubkeys/add.feature +0 -22
- data/acceptance-features/pubkeys/delete.feature +0 -9
- data/acceptance-features/pubkeys/names.feature +0 -26
- data/acceptance-features/pubkeys/show.feature +0 -27
- data/acceptance-features/step_definitions/cli_steps.rb +0 -57
- data/acceptance-features/step_definitions/graph_steps.rb +0 -22
- data/acceptance-features/step_definitions/user_steps.rb +0 -51
- data/acceptance-features/support/env.rb +0 -23
- data/acceptance-features/support/hooks.rb +0 -178
- data/acceptance-features/support/world.rb +0 -176
- data/acceptance-features/trusted_proxies.feature +0 -82
- data/bin/conjurize +0 -26
- data/bin/jsonfield +0 -70
- data/build-standalone +0 -6
- data/deprecations.sh +0 -38
- data/features/conjurize.feature +0 -134
- data/features/dsl_context.feature +0 -36
- data/features/dsl_host_create.feature +0 -11
- data/features/dsl_ownership.feature +0 -30
- data/features/dsl_permission.feature +0 -45
- data/features/dsl_resource_create.feature +0 -23
- data/features/dsl_role_create.feature +0 -11
- data/features/dsl_user_create.feature +0 -23
- data/features/jsonfield.feature +0 -49
- data/features/role_graph.feature +0 -58
- data/features/step_definitions/conjurize_steps.rb +0 -5
- data/features/step_definitions/dsl_steps.rb +0 -52
- data/features/support/conjur.conf +0 -6
- data/lib/conjur/command/assets.rb +0 -121
- data/lib/conjur/command/audit.rb +0 -155
- data/lib/conjur/command/bootstrap.rb +0 -129
- data/lib/conjur/command/dsl_command.rb +0 -75
- data/lib/conjur/command/elevate.rb +0 -76
- data/lib/conjur/command/field.rb +0 -45
- data/lib/conjur/command/groups.rb +0 -208
- data/lib/conjur/command/ids.rb +0 -34
- data/lib/conjur/command/layers.rb +0 -211
- data/lib/conjur/command/ldapsync.rb +0 -118
- data/lib/conjur/command/rspec/audit_helpers.rb +0 -68
- data/lib/conjur/command/rubydsl.rb +0 -93
- data/lib/conjur/command/script.rb +0 -48
- data/lib/conjur/command/server.rb +0 -67
- data/lib/conjur/conjurize.rb +0 -71
- data/lib/conjur/conjurize/script.rb +0 -150
- data/lib/conjur/dsl/runner.rb +0 -273
- data/publish-deb.sh +0 -6
- data/push-image +0 -29
- data/spec/command/assets_spec.rb +0 -115
- data/spec/command/audit_spec.rb +0 -376
- data/spec/command/elevate_spec.rb +0 -28
- data/spec/command/env_spec.rb +0 -168
- data/spec/command/groups_spec.rb +0 -77
- data/spec/command/host_factories_spec.rb +0 -38
- data/spec/command/layers_spec.rb +0 -35
- data/spec/command/ldapsync_spec.rb +0 -28
- data/spec/command/rubydsl_spec.rb +0 -63
- data/spec/command/variable_expiration_spec.rb +0 -164
- data/spec/command/variables_spec.rb +0 -192
- data/spec/conjurize/script_spec.rb +0 -62
- data/spec/conjurize_spec.rb +0 -70
- data/spec/dsl/runner_spec.rb +0 -93
- data/spec/env_spec.rb +0 -214
data/features/support/world.rb
CHANGED
|
@@ -1,88 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
last_stdout
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
def last_stdout
|
|
8
|
-
raise "No commands have been run" unless last_cmd
|
|
9
|
-
stdout_from last_cmd
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
attr_accessor :last_cmd
|
|
13
|
-
|
|
14
|
-
def account
|
|
15
|
-
Conjur::Core::API.conjur_account
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def role_kind
|
|
19
|
-
@role_kind ||= "cli-cukes"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def role_id_map
|
|
23
|
-
@role_id_map ||= {}
|
|
1
|
+
module CLIWorld
|
|
2
|
+
def tempfiles
|
|
3
|
+
@tempfiles ||= []
|
|
24
4
|
end
|
|
25
5
|
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def filter_hash_graph graph
|
|
36
|
-
allowed = role_id_map.values
|
|
37
|
-
edges = graph['graph']
|
|
38
|
-
filtered = edges.select do |edge|
|
|
39
|
-
allowed.member?(edge['parent']) and allowed.member?(edge['child'])
|
|
6
|
+
def api_key_of username
|
|
7
|
+
role = @policy_response.created_roles["cucumber:user:#{username}"]
|
|
8
|
+
if role
|
|
9
|
+
role['api_key']
|
|
10
|
+
else
|
|
11
|
+
$conjur.resource("cucumber:user:#{username}").rotate_api_key
|
|
40
12
|
end
|
|
41
|
-
{'graph' => filtered}
|
|
42
13
|
end
|
|
43
|
-
|
|
44
|
-
def filter_array_graph graph
|
|
45
|
-
allowed = role_id_map.values
|
|
46
|
-
graph.select do |edge|
|
|
47
|
-
edge.all?{|v| allowed.member?(v)}
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def graph edges
|
|
52
|
-
# generate roles
|
|
53
|
-
edges.flatten.uniq.each do |role_id|
|
|
54
|
-
role_id_map[role_id] = expanded = expand_role_id(role_id)
|
|
55
|
-
run_command "conjur role create '#{expanded}'"
|
|
56
|
-
end
|
|
57
14
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
run_command "conjur role grant_to #{expand_role_id(parent)} #{expand_role_id(child)}"
|
|
61
|
-
end
|
|
15
|
+
def clear_last_json
|
|
16
|
+
@last_json = nil
|
|
62
17
|
end
|
|
63
18
|
|
|
64
|
-
def
|
|
65
|
-
|
|
19
|
+
def last_json
|
|
20
|
+
@last_json || last_command_started.stdout
|
|
66
21
|
end
|
|
67
22
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def prepend_namespace id
|
|
73
|
-
"#{namespace}-#{id}"
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def namespace
|
|
77
|
-
@namespace ||= "ns-#{Time.now.to_i}-#{rand(1 << 32)}"
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def expand_roles string
|
|
81
|
-
role_id_map.each do |role, expanded|
|
|
82
|
-
string.gsub! role, expanded
|
|
83
|
-
end
|
|
84
|
-
string
|
|
23
|
+
def load_policy id, policy, method
|
|
24
|
+
@policy_response = $conjur.load_policy id, policy, method: method
|
|
85
25
|
end
|
|
86
26
|
end
|
|
87
27
|
|
|
88
|
-
World(
|
|
28
|
+
World(CLIWorld)
|
data/jenkins.sh
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash -ex
|
|
2
|
+
|
|
3
|
+
# Constants
|
|
4
|
+
RUBY_VERSION_DEFAULT="2.2.4"
|
|
5
|
+
|
|
6
|
+
# Arguments
|
|
7
|
+
RUBY_VERSION=${1-${RUBY_VERSION_DEFAULT}}
|
|
8
|
+
|
|
9
|
+
# Script
|
|
10
|
+
|
|
11
|
+
# Clones 'Dockerfile' and updates the Ruby version in FROM, returning the cloned file's path
|
|
12
|
+
function dockerfile_path {
|
|
13
|
+
echo "Setting Ruby version as ${RUBY_VERSION}" >&2
|
|
14
|
+
cp "Dockerfile" "Dockerfile.${RUBY_VERSION}"
|
|
15
|
+
sed -i -e "s/${RUBY_VERSION_DEFAULT}/${RUBY_VERSION}/g" Dockerfile.${RUBY_VERSION}
|
|
16
|
+
|
|
17
|
+
echo "Dockerfile.${RUBY_VERSION}"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
rm -f Gemfile.lock # Needed for bundle to work right
|
|
21
|
+
|
|
22
|
+
IMAGE_NAME="cli-ruby:${RUBY_VERSION}" # The tag is the version of Ruby tested against
|
|
23
|
+
|
|
24
|
+
docker build -t ${IMAGE_NAME} -f $(dockerfile_path) .
|
|
25
|
+
|
|
26
|
+
docker run --rm \
|
|
27
|
+
-v $PWD:/src \
|
|
28
|
+
${IMAGE_NAME} \
|
|
29
|
+
bash -c '''
|
|
30
|
+
bundle update
|
|
31
|
+
bundle exec rake jenkins
|
|
32
|
+
bundle exec rake build
|
|
33
|
+
'''
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Conjur
|
|
2
|
+
# Keeps a fresh Conjur access token in a named file by re-authenticating as needed.
|
|
3
|
+
class Authenticator
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
TOKEN_LIFESPAN = ( ENV['CONJUR_TOKEN_LIFESPAN'] || 5 * 60 ).to_i.seconds
|
|
8
|
+
DELAY = ( ENV['CONJUR_TOKEN_REFRESH_DELAY'] || 10 ).to_i.seconds
|
|
9
|
+
|
|
10
|
+
attr_reader :authenticate, :filename
|
|
11
|
+
|
|
12
|
+
# +authenticate+ should be a proc that authenticates with Conjur and returns an
|
|
13
|
+
# access token as a Hash.
|
|
14
|
+
def initialize authenticate, filename
|
|
15
|
+
@authenticate = authenticate
|
|
16
|
+
@filename = filename
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def default_filename
|
|
21
|
+
"/run/conjur-access-token"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Check the token every +DELAY+ seconds and refresh it if it's out of date.
|
|
25
|
+
def run authenticate:, filename: default_filename
|
|
26
|
+
while true
|
|
27
|
+
authenticator = Authenticator.new(authenticate, filename)
|
|
28
|
+
authenticator.refresh unless authenticator.fresh?
|
|
29
|
+
sleep DELAY
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def fresh?
|
|
35
|
+
token && (token_age <= TOKEN_LIFESPAN)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Perform atomic replacement of the token
|
|
39
|
+
def refresh
|
|
40
|
+
token = authenticate.call
|
|
41
|
+
file = Tempfile.new('conjur-access-token.')
|
|
42
|
+
begin
|
|
43
|
+
file.write JSON.pretty_generate(token)
|
|
44
|
+
file.close
|
|
45
|
+
FileUtils.mv file.path, filename
|
|
46
|
+
Conjur.log << "Refreshed Conjur auth token to #{filename.inspect}\n" if Conjur.log
|
|
47
|
+
ensure
|
|
48
|
+
file.unlink
|
|
49
|
+
end
|
|
50
|
+
rescue
|
|
51
|
+
$stderr.puts $!
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def token
|
|
55
|
+
return false if @token == false
|
|
56
|
+
@token ||= load_token
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
protected
|
|
60
|
+
|
|
61
|
+
def random nbytes = 12
|
|
62
|
+
@random ||= Random.new
|
|
63
|
+
@random.bytes(nbytes).unpack('h*').first
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def directory
|
|
67
|
+
File.dirname(filename)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def load_token
|
|
71
|
+
return false unless File.file?(filename)
|
|
72
|
+
JSON.parse(File.read(filename)) rescue false
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def token_born
|
|
76
|
+
File.mtime(filename)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def token_age
|
|
80
|
+
Time.now - token_born
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
data/lib/conjur/authn.rb
CHANGED
|
@@ -34,7 +34,6 @@ module Conjur::Authn
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
autoload :API, 'conjur/authn-api'
|
|
38
37
|
class << self
|
|
39
38
|
def login(options = {})
|
|
40
39
|
delete_credentials
|
|
@@ -43,18 +42,14 @@ module Conjur::Authn
|
|
|
43
42
|
|
|
44
43
|
def authenticate(options = {})
|
|
45
44
|
require 'conjur/api'
|
|
46
|
-
Conjur::API.authenticate
|
|
45
|
+
Conjur::API.authenticate *get_credentials(options)
|
|
47
46
|
end
|
|
48
47
|
|
|
49
48
|
def delete_credentials
|
|
50
|
-
netrc.delete
|
|
49
|
+
netrc.delete Conjur.configuration.authn_url
|
|
51
50
|
netrc.save
|
|
52
51
|
end
|
|
53
52
|
|
|
54
|
-
def host
|
|
55
|
-
Conjur::Authn::API.host
|
|
56
|
-
end
|
|
57
|
-
|
|
58
53
|
def netrc
|
|
59
54
|
@netrc ||= read_netrc
|
|
60
55
|
end
|
|
@@ -83,7 +78,7 @@ module Conjur::Authn
|
|
|
83
78
|
end
|
|
84
79
|
|
|
85
80
|
def read_credentials
|
|
86
|
-
netrc[
|
|
81
|
+
netrc[Conjur.configuration.authn_url]
|
|
87
82
|
end
|
|
88
83
|
|
|
89
84
|
def fetch_credentials(options = {})
|
|
@@ -94,7 +89,7 @@ module Conjur::Authn
|
|
|
94
89
|
alias save_credentials fetch_credentials
|
|
95
90
|
|
|
96
91
|
def write_credentials
|
|
97
|
-
netrc[
|
|
92
|
+
netrc[Conjur.configuration.authn_url] = @credentials
|
|
98
93
|
netrc.save
|
|
99
94
|
@credentials
|
|
100
95
|
end
|
|
@@ -128,10 +123,8 @@ module Conjur::Authn
|
|
|
128
123
|
end
|
|
129
124
|
if token = token_from_environment
|
|
130
125
|
cls.new_from_token token
|
|
131
|
-
elsif token_file = token_from_file
|
|
132
|
-
cls.new_from_token_file token_file
|
|
133
126
|
else
|
|
134
|
-
cls.new_from_key
|
|
127
|
+
cls.new_from_key *get_credentials(options)
|
|
135
128
|
end
|
|
136
129
|
end
|
|
137
130
|
|
|
@@ -155,13 +148,5 @@ module Conjur::Authn
|
|
|
155
148
|
require 'base64'
|
|
156
149
|
JSON.parse(Base64.decode64(token))
|
|
157
150
|
end
|
|
158
|
-
|
|
159
|
-
def token_from_file
|
|
160
|
-
token_file = ENV['CONJUR_AUTHN_TOKEN_FILE']
|
|
161
|
-
if token_file && !File.exists?(token_file)
|
|
162
|
-
$stderr.puts "Warning: CONJUR_AUTHN_TOKEN_FILE #{token_file.inspect} does not exist"
|
|
163
|
-
end
|
|
164
|
-
token_file
|
|
165
|
-
end
|
|
166
151
|
end
|
|
167
152
|
end
|
data/lib/conjur/cli.rb
CHANGED
|
@@ -37,6 +37,7 @@ module Conjur
|
|
|
37
37
|
autoload :Log, 'conjur/log'
|
|
38
38
|
autoload :IdentifierManipulation, 'conjur/identifier_manipulation'
|
|
39
39
|
autoload :Authn, 'conjur/authn'
|
|
40
|
+
autoload :Authenticator, 'conjur/authenticator'
|
|
40
41
|
autoload :Command, 'conjur/command'
|
|
41
42
|
autoload :DSL, 'conjur/dsl/runner'
|
|
42
43
|
autoload :DSLCommand, 'conjur/command/dsl_command'
|
|
@@ -117,7 +118,7 @@ module Conjur
|
|
|
117
118
|
require 'conjur/api'
|
|
118
119
|
|
|
119
120
|
if command.name_for_help.first == "init" and options.has_key?("account")
|
|
120
|
-
ENV["CONJUR_ACCOUNT"]=options["account"]
|
|
121
|
+
ENV["CONJUR_ACCOUNT"] = options["account"]
|
|
121
122
|
end
|
|
122
123
|
apply_config
|
|
123
124
|
require 'active_support/core_ext'
|
|
@@ -154,12 +155,18 @@ module Conjur
|
|
|
154
155
|
$stderr.puts "error: this command is not supported by the current Conjur server version"
|
|
155
156
|
run_default_handler = false
|
|
156
157
|
elsif exception.is_a?(RestClient::Exception) && exception.response
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
run_default_handler = false # suppress default error message
|
|
158
|
+
if exception.http_code == 401
|
|
159
|
+
$stderr.puts "Unable to authenticate with Conjur. Please check your credentials."
|
|
160
|
+
run_default_handler = false
|
|
161
161
|
else
|
|
162
|
-
|
|
162
|
+
err = Conjur::Error.create exception.response.body
|
|
163
|
+
|
|
164
|
+
if err
|
|
165
|
+
$stderr.puts "error: " + err.message
|
|
166
|
+
run_default_handler = false # suppress default error message
|
|
167
|
+
else
|
|
168
|
+
$stderr.puts exception.response.body
|
|
169
|
+
end
|
|
163
170
|
end
|
|
164
171
|
end
|
|
165
172
|
|
data/lib/conjur/command.rb
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
20
20
|
#
|
|
21
21
|
require 'base64'
|
|
22
|
-
require 'semantic'
|
|
23
22
|
|
|
24
23
|
module Conjur
|
|
25
24
|
class Command
|
|
@@ -29,7 +28,7 @@ module Conjur
|
|
|
29
28
|
|
|
30
29
|
class << self
|
|
31
30
|
attr_accessor :prefix
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
def method_missing *a, &b
|
|
34
33
|
Conjur::CLI.send *a, &b
|
|
35
34
|
end
|
|
@@ -70,61 +69,12 @@ module Conjur
|
|
|
70
69
|
def command.nodoc; true end
|
|
71
70
|
end
|
|
72
71
|
|
|
73
|
-
def acting_as_option command
|
|
74
|
-
return if command.flags.member?(:"as-group") # avoid duplicate flags
|
|
75
|
-
command.desc 'Perform all actions as the specified Group'
|
|
76
|
-
command.arg_name 'GROUP'
|
|
77
|
-
command.flag [:'as-group']
|
|
78
|
-
|
|
79
|
-
command.desc 'Perform all actions as the specified Role'
|
|
80
|
-
command.arg_name 'ROLE'
|
|
81
|
-
command.flag [:'as-role']
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def collection_option command
|
|
85
|
-
command.desc 'An optional prefix for created roles and resources'
|
|
86
|
-
command.arg_name 'collection'
|
|
87
|
-
command.flag [:collection]
|
|
88
|
-
end
|
|
89
|
-
|
|
90
72
|
def context_option command
|
|
91
73
|
command.desc "Load context from this config file, and save it when finished. The file permissions will be 0600 by default."
|
|
92
74
|
command.arg_name "FILE"
|
|
93
75
|
command.flag [:c, :context]
|
|
94
76
|
end
|
|
95
77
|
|
|
96
|
-
def interactive_option command
|
|
97
|
-
command.arg_name 'interactive'
|
|
98
|
-
command.desc 'Create variable interactively'
|
|
99
|
-
command.switch [:i, :'interactive']
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def annotate_option command
|
|
103
|
-
command.arg_name 'annotate'
|
|
104
|
-
command.desc 'Add variable annotations interactively'
|
|
105
|
-
command.switch [:a, :annotate]
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def min_version command, version
|
|
109
|
-
version = Semantic::Version.new version
|
|
110
|
-
if version.pre == nil
|
|
111
|
-
# Version check doesn't work correctly if one version has
|
|
112
|
-
# the "-###" suffix and the other does not. Versions
|
|
113
|
-
# returned by the server have the suffix.
|
|
114
|
-
version.pre = "0"
|
|
115
|
-
end
|
|
116
|
-
command.instance_variable_set(:@conjur_min_version, version)
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def prompt_for_annotations
|
|
120
|
-
highline.say('Add annotations (a name and value for each one):')
|
|
121
|
-
{}.tap do |annotations|
|
|
122
|
-
until (name = highline.ask(' annotation name (press enter to quit annotations): ')).empty?
|
|
123
|
-
annotations[name] = read_till_eof(' annotation value (^D on its own line to finish):')
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
78
|
def highline
|
|
129
79
|
require 'highline'
|
|
130
80
|
@highline ||= HighLine.new($stdin,$stderr)
|
|
@@ -142,62 +92,35 @@ module Conjur
|
|
|
142
92
|
end
|
|
143
93
|
end.join("\n")
|
|
144
94
|
end
|
|
145
|
-
|
|
146
|
-
def
|
|
147
|
-
c.desc "Filter by kind"
|
|
148
|
-
c.flag [:k, :kind]
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def command_option_as_role c
|
|
95
|
+
|
|
96
|
+
def command_options_for_list(c)
|
|
152
97
|
return if c.flags.member?(:role) # avoid duplicate flags
|
|
153
98
|
c.desc "Role to act as. By default, the current logged-in role is used."
|
|
154
99
|
c.arg_name 'ROLE'
|
|
155
100
|
c.flag [:role]
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def command_options_for_search c
|
|
101
|
+
|
|
159
102
|
c.desc "Full-text search on resource id and annotation values"
|
|
160
103
|
c.flag [:s, :search]
|
|
161
104
|
|
|
105
|
+
c.desc "Maximum number of records to return"
|
|
106
|
+
c.flag [:l, :limit]
|
|
107
|
+
|
|
162
108
|
c.desc "Offset to start from"
|
|
163
109
|
c.flag [:o, :offset]
|
|
164
110
|
|
|
165
|
-
c.desc "
|
|
166
|
-
c.
|
|
167
|
-
|
|
168
|
-
c.desc "Show the number of results, rather than printing them"
|
|
169
|
-
c.switch [:count]
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def command_options_for_list(c)
|
|
173
|
-
command_option_as_role c
|
|
174
|
-
command_options_for_search c
|
|
175
|
-
|
|
176
|
-
c.desc "Show only ids"
|
|
177
|
-
c.switch [:i, :ids]
|
|
178
|
-
|
|
111
|
+
c.desc "Show full object information"
|
|
112
|
+
c.switch [:inspect]
|
|
113
|
+
|
|
179
114
|
c.desc "Show annotations in 'raw' format"
|
|
180
115
|
c.switch [:r, :"raw-annotations"]
|
|
181
116
|
end
|
|
182
|
-
|
|
183
|
-
def process_command_options_for_search options
|
|
184
|
-
opts = options.slice(:search, :count, :limit, :offset, :kind)
|
|
185
|
-
opts[:acting_as] = options[:role] if options[:role]
|
|
186
|
-
opts[:search] = opts[:search].gsub('-',' ') if opts[:search]
|
|
187
|
-
if opts[:kind] && opts[:kind].index(',')
|
|
188
|
-
opts[:kind] = opts[:kind].split(',')
|
|
189
|
-
end
|
|
190
|
-
opts
|
|
191
|
-
end
|
|
192
117
|
|
|
193
118
|
def command_impl_for_list(global_options, options, args)
|
|
194
|
-
opts =
|
|
119
|
+
opts = options.slice(:search, :limit, :options, :kind)
|
|
120
|
+
opts[:acting_as] = options[:role] if options[:role]
|
|
121
|
+
opts[:search]=opts[:search].gsub('-',' ') if opts[:search]
|
|
195
122
|
resources = api.resources(opts)
|
|
196
|
-
if
|
|
197
|
-
puts resources
|
|
198
|
-
elsif options[:ids]
|
|
199
|
-
puts JSON.pretty_generate(resources.map(&:resourceid))
|
|
200
|
-
else
|
|
123
|
+
if options[:inspect]
|
|
201
124
|
resources = resources.map &:attributes
|
|
202
125
|
unless options[:'raw-annotations']
|
|
203
126
|
resources = resources.map do |r|
|
|
@@ -209,6 +132,8 @@ module Conjur
|
|
|
209
132
|
end
|
|
210
133
|
end
|
|
211
134
|
puts JSON.pretty_generate resources
|
|
135
|
+
else
|
|
136
|
+
puts JSON.pretty_generate(resources.map(&:id))
|
|
212
137
|
end
|
|
213
138
|
end
|
|
214
139
|
|
|
@@ -220,160 +145,24 @@ module Conjur
|
|
|
220
145
|
end
|
|
221
146
|
exit_now! message unless valid
|
|
222
147
|
end
|
|
223
|
-
|
|
224
|
-
def retire_options command
|
|
225
|
-
command.arg_name 'role'
|
|
226
|
-
command.desc "Specify a role to give the retired record to (default: the 'attic' user)"
|
|
227
|
-
command.long_desc %Q(When retired, all a record's roles and permissions are revoked.
|
|
228
|
-
|
|
229
|
-
As a final step, the record is 'given' (e.g. 'conjur resource give') to a destination role.
|
|
230
|
-
The default role to receive the record is the user 'attic'. This option can be used to specify
|
|
231
|
-
an alternative destination role.)
|
|
232
|
-
command.flag [:d, :"destination-role"]
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def destination_role options
|
|
236
|
-
destination = options[:"destination-role"]
|
|
237
|
-
if destination
|
|
238
|
-
api.role(destination)
|
|
239
|
-
else
|
|
240
|
-
api.user('attic')
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def elevated?
|
|
245
|
-
api.privilege == 'elevate' && api.global_privilege_permitted?('elevate')
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def validate_retire_privileges record, options
|
|
249
|
-
return true if elevated?
|
|
250
|
-
|
|
251
|
-
if record.respond_to?(:role)
|
|
252
|
-
memberships = current_user.role.memberships.map(&:roleid)
|
|
253
|
-
validate_privileges "You can't administer this record" do
|
|
254
|
-
# The current user has a role which is admin of the record's role
|
|
255
|
-
record.role.members.find{|m| memberships.member?(m.member.roleid) && m.admin_option}
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
validate_privileges "You don't own the record" do
|
|
260
|
-
# The current user has the role which owns the record's resource
|
|
261
|
-
current_user.role.member_of?(record.resource.ownerid)
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
role = destination_role(options)
|
|
265
|
-
exit_now! "Destination role '#{role.roleid}' doesn't exist" unless role.exists?
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
def retire_resource obj
|
|
269
|
-
obj.resource.attributes['permissions'].each do |p|
|
|
270
|
-
role = api.role(p['role'])
|
|
271
|
-
privilege = p['privilege']
|
|
272
|
-
next if obj.respond_to?(:roleid) && role.roleid == obj.roleid && privilege == 'read'
|
|
273
|
-
puts "Denying #{privilege} privilege to #{role.roleid}"
|
|
274
|
-
obj.resource.deny(privilege, role)
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def retire_role obj
|
|
279
|
-
members = obj.role.members
|
|
280
|
-
# Move the invoking role to the end of the roles list, so that it doesn't
|
|
281
|
-
# lose its permissions in the middle of this operation.
|
|
282
|
-
# I'm sure there's a cleaner way to do this.
|
|
283
|
-
self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
|
|
284
|
-
self_member.each do |m|
|
|
285
|
-
members.delete m
|
|
286
|
-
end
|
|
287
|
-
members.concat self_member if self_member
|
|
288
|
-
members.each do |r|
|
|
289
|
-
member = api.role(r.member)
|
|
290
|
-
puts "Revoking from role #{member.roleid}"
|
|
291
|
-
obj.role.revoke_from member
|
|
292
|
-
end
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
def retire_internal_role roleObj
|
|
296
|
-
members = roleObj.members
|
|
297
|
-
# Move the invoking role to the end of the roles list, so that it doesn't
|
|
298
|
-
# lose its permissions in the middle of this operation.
|
|
299
|
-
self_member = members.select{|m| m.member.roleid == current_user.role.roleid}
|
|
300
|
-
self_member.each do |m|
|
|
301
|
-
members.delete m
|
|
302
|
-
end
|
|
303
|
-
members.concat self_member if self_member
|
|
304
|
-
members.each do |r|
|
|
305
|
-
member = api.role(r.member)
|
|
306
|
-
puts "Revoking from role #{member.roleid}"
|
|
307
|
-
roleObj.revoke_from member
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def give_away_resource obj, options
|
|
312
|
-
destination = options[:"destination-role"]
|
|
313
|
-
destination_role = if destination
|
|
314
|
-
api.role(destination)
|
|
315
|
-
else
|
|
316
|
-
api.user('attic')
|
|
317
|
-
end
|
|
318
148
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
# * A list of role ids
|
|
329
|
-
# * A list of RoleGrant
|
|
330
|
-
#
|
|
331
|
-
# +options+ may include:
|
|
332
|
-
#
|
|
333
|
-
# * **V** Show full RoleGrant info
|
|
334
|
-
# * **system** show system (internal) roles
|
|
335
|
-
#
|
|
336
|
-
# @return [Numeric, Hash] Convert the input to a number or Hash.
|
|
337
|
-
def display_members(members, member_field, options)
|
|
338
|
-
# Get this case out of the way
|
|
339
|
-
return display(members) if members.is_a?(Numeric)
|
|
340
|
-
|
|
341
|
-
result, roleid_function = if members.blank?
|
|
342
|
-
[ [], nil ]
|
|
343
|
-
elsif members.first.is_a?(Conjur::RoleGrant)
|
|
344
|
-
if options[:V]
|
|
345
|
-
items = members.collect do |member|
|
|
346
|
-
{
|
|
347
|
-
member: member.member.roleid,
|
|
348
|
-
grantor: member.grantor.roleid,
|
|
349
|
-
admin_option: member.admin_option
|
|
350
|
-
}.tap do |obj|
|
|
351
|
-
obj[:role] = member.role.roleid if member.role
|
|
352
|
-
end
|
|
353
|
-
end
|
|
354
|
-
[
|
|
355
|
-
items, lambda {|member| member[member_field] }
|
|
356
|
-
]
|
|
357
|
-
else
|
|
358
|
-
[ members.map{|m| m.send(member_field) }.map(&:roleid), lambda{|r| r} ]
|
|
359
|
-
end
|
|
149
|
+
def display_members(members, options)
|
|
150
|
+
result = if options[:V]
|
|
151
|
+
members.collect {|member|
|
|
152
|
+
{
|
|
153
|
+
role: member.role.id,
|
|
154
|
+
member: member.member.id,
|
|
155
|
+
admin_option: member.admin_option
|
|
156
|
+
}
|
|
157
|
+
}
|
|
360
158
|
else
|
|
361
|
-
|
|
159
|
+
members.map(&:member).map(&:id)
|
|
362
160
|
end
|
|
363
|
-
|
|
364
|
-
unless options[:system]
|
|
365
|
-
result.reject! do |member|
|
|
366
|
-
roleid_function.call(member) =~ /^.+?:@/
|
|
367
|
-
end
|
|
368
|
-
end
|
|
369
|
-
|
|
370
161
|
display result
|
|
371
162
|
end
|
|
372
163
|
|
|
373
164
|
def display(obj, options = {})
|
|
374
|
-
str = if obj.
|
|
375
|
-
obj.to_s
|
|
376
|
-
elsif obj.respond_to?(:attributes)
|
|
165
|
+
str = if obj.respond_to?(:attributes)
|
|
377
166
|
JSON.pretty_generate obj.attributes
|
|
378
167
|
elsif obj.respond_to?(:id)
|
|
379
168
|
obj.id
|
|
@@ -387,105 +176,9 @@ an alternative destination role.)
|
|
|
387
176
|
puts str
|
|
388
177
|
end
|
|
389
178
|
|
|
390
|
-
def prompt_to_confirm kind, properties
|
|
391
|
-
puts
|
|
392
|
-
puts "A new #{kind} will be created with the following properties:"
|
|
393
|
-
puts
|
|
394
|
-
properties.select{|k,v| !v.blank?}.each do |k,v|
|
|
395
|
-
printf "%-10s: %s\n", k, v
|
|
396
|
-
end
|
|
397
|
-
puts
|
|
398
|
-
|
|
399
|
-
exit(0) unless %w(yes y).member?(highline.ask("Proceed? (yes/no): ").strip.downcase)
|
|
400
|
-
end
|
|
401
|
-
|
|
402
179
|
def integer? v
|
|
403
180
|
Integer(v, 10) rescue false
|
|
404
181
|
end
|
|
405
|
-
|
|
406
|
-
def prompt_for_id kind, label = 'id'
|
|
407
|
-
highline.ask("Enter the #{label}: ") do |q|
|
|
408
|
-
q.readline = true
|
|
409
|
-
q.validate = lambda{|id|
|
|
410
|
-
!id.blank? && !api.send(kind, id).exists?
|
|
411
|
-
}
|
|
412
|
-
q.responses[:not_valid] = "<% if @answer.blank? %>"\
|
|
413
|
-
"#{label} cannot be blank<% else %>"\
|
|
414
|
-
"A #{kind} called '<%= @answer %>' already exists<% end %>"
|
|
415
|
-
end
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
def prompt_for_public_key
|
|
419
|
-
public_key = highline.ask("Enter the public key (press enter to skip): ") do |q|
|
|
420
|
-
q.validate = lambda{|key|
|
|
421
|
-
if key.blank?
|
|
422
|
-
true
|
|
423
|
-
else
|
|
424
|
-
validate_public_key key
|
|
425
|
-
end
|
|
426
|
-
}
|
|
427
|
-
q.responses[:not_valid] = "Public key format is invalid; please try again"
|
|
428
|
-
end
|
|
429
|
-
public_key.blank? ? nil : public_key.strip
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
# http://serverfault.com/questions/453296/how-do-i-validate-a-rsa-ssh-public-key-file-id-rsa-pub
|
|
433
|
-
def validate_public_key key
|
|
434
|
-
if system('which ssh-keygen 2>&1 > /dev/null')
|
|
435
|
-
Conjur.log.debug "Using ssh-keygen to verify the public key\n" if Conjur.log
|
|
436
|
-
require 'tempfile'
|
|
437
|
-
tempfile = Tempfile.new 'public_key'
|
|
438
|
-
tempfile.write(key)
|
|
439
|
-
tempfile.close
|
|
440
|
-
`ssh-keygen -l -f #{tempfile.path}`
|
|
441
|
-
$? == 0
|
|
442
|
-
else
|
|
443
|
-
Conjur.log.debug "ssh-keygen is not available; falling back to simple string testing\n" if Conjur.log
|
|
444
|
-
# Should be a line with at least 2 components,
|
|
445
|
-
# first one being the algo id and second a base64 string.
|
|
446
|
-
# In principle this means:
|
|
447
|
-
# Base64.strict_decode64 key.strip[/\Assh-\w+ (\S+).*/, 1]
|
|
448
|
-
|
|
449
|
-
# Since the pubkeys service is more strict: needs a name and
|
|
450
|
-
# rejects ones with a space, instead reproduce its algorithm here.
|
|
451
|
-
begin
|
|
452
|
-
components = key.strip.split ' '
|
|
453
|
-
Base64.strict_decode64 components[1]
|
|
454
|
-
components.length == 3
|
|
455
|
-
rescue NoMethodError, ArgumentError
|
|
456
|
-
false
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
end
|
|
460
|
-
|
|
461
|
-
def prompt_for_group options = {}
|
|
462
|
-
options[:hint] ||= "press enter to own the record yourself"
|
|
463
|
-
group_ids = api.groups.map(&:id)
|
|
464
|
-
|
|
465
|
-
highline.ask("Enter the group which will own the record (#{options[:hint]}): ", [ "" ] + group_ids) do |q|
|
|
466
|
-
require 'readline'
|
|
467
|
-
Readline.completion_append_character = ""
|
|
468
|
-
Readline.completer_word_break_characters = ""
|
|
469
|
-
|
|
470
|
-
q.readline = true
|
|
471
|
-
q.validate = lambda{|id|
|
|
472
|
-
@group = nil
|
|
473
|
-
id.empty? || (@group = api.group(id)).exists?
|
|
474
|
-
}
|
|
475
|
-
q.responses[:not_valid] = "Group '<%= @answer %>' doesn't exist, or you don't have permission to use it"
|
|
476
|
-
end
|
|
477
|
-
@group ? @group.roleid : nil
|
|
478
|
-
end
|
|
479
|
-
|
|
480
|
-
def prompt_for_idnumber label
|
|
481
|
-
result = highline.ask("Enter a #{label}: ") do |q|
|
|
482
|
-
q.validate = lambda{|id|
|
|
483
|
-
id.blank? || integer?(id)
|
|
484
|
-
}
|
|
485
|
-
q.responses[:not_valid] = "The #{label} must be an integer"
|
|
486
|
-
end
|
|
487
|
-
result.blank? ? nil : result.to_i
|
|
488
|
-
end
|
|
489
182
|
|
|
490
183
|
def prompt_for_password
|
|
491
184
|
require 'highline'
|
|
@@ -509,27 +202,14 @@ an alternative destination role.)
|
|
|
509
202
|
password
|
|
510
203
|
end
|
|
511
204
|
|
|
512
|
-
def current_role
|
|
513
|
-
kind, id = api.username.split('/', 2)
|
|
514
|
-
if id.nil?
|
|
515
|
-
id = kind
|
|
516
|
-
kind = 'user'
|
|
517
|
-
end
|
|
518
|
-
api.role([ kind, id ].join(":"))
|
|
519
|
-
end
|
|
520
|
-
|
|
521
205
|
def has_admin?(role, other_role)
|
|
522
|
-
return true if role.
|
|
523
|
-
memberships = role.memberships.map(&:
|
|
524
|
-
other_role.members.any? { |m| memberships.member?(m.member.
|
|
206
|
+
return true if role.id == other_role.id
|
|
207
|
+
memberships = role.memberships.map(&:id)
|
|
208
|
+
other_role.members.any? { |m| memberships.member?(m.member.id) && m.admin_option }
|
|
525
209
|
rescue RestClient::Forbidden
|
|
526
210
|
false
|
|
527
211
|
end
|
|
528
212
|
|
|
529
|
-
def notify_deprecated
|
|
530
|
-
STDERR.puts 'WARNING! This command is deprecated and will be removed. Use policy instead.'
|
|
531
|
-
end
|
|
532
|
-
|
|
533
213
|
end
|
|
534
214
|
end
|
|
535
215
|
end
|