Skip to content

Commit 676e150

Browse files
stefanskicyberGitHub Enterprise
authored andcommitted
Merge pull request #21 from Conjur-Enterprise/CNJR-10552-config
CNJR-10552: Config code added to repository
2 parents fd20b31 + 61a23f8 commit 676e150

9 files changed

Lines changed: 488 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99
- Nothing should go in this section, please add to the latest unreleased version
1010
(and update the corresponding date), or add a new version.
1111

12+
## [6.1.0] - 2025-09-05
13+
14+
### Added
15+
- Added Config code and tests to repository (CNJR-10552).
16+
[cyberark/conjur-api-ruby#186](https://github.com/cyberark/conjur-api-ruby/issues/186)
17+
1218
## [6.0.1] - 2025-09-05
1319

1420
### Fixed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ For custom scenarios, the location of the file can be overridden using the `CONJ
4747
You can load the Secrets Manager configuration file using the following Ruby code:
4848

4949
```ruby
50-
require 'conjur/cli'
5150
Conjur::Config.load
5251
Conjur::Config.apply
5352
```
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require 'io/grab'
2+
3+
# Custom matcher to test text written to standard output and standard error
4+
#
5+
# @example
6+
# expect { $stderr.puts "Some random error" }.to write(/Some.* error/).to(:stderr)
7+
#
8+
# @example
9+
# expect { $stderr.puts "Some specific error" }.to write('Some specific error').to(:stderr)
10+
#
11+
# @note http://greyblake.com/blog/2012/12/14/custom-expectations-with-rspec/
12+
RSpec::Matchers.define :write do |message|
13+
supports_block_expectations
14+
15+
chain(:to) do |io|
16+
@io = io
17+
end
18+
19+
match do |block|
20+
stream = case io
21+
when :stdout
22+
$stdout
23+
when :stderr
24+
$stderr
25+
else
26+
io
27+
end
28+
29+
@actual = output = stream.grab &block
30+
31+
case message
32+
when Hash then output.include?(JSON.pretty_generate message)
33+
when String then output.include? message
34+
when Regexp then output.match message
35+
when nil then output
36+
else fail("Allowed types for write `message` are String or Regexp, got `#{message.class}`")
37+
end
38+
end
39+
40+
description do
41+
%Q[write #{message.inspect} to #{@io}]
42+
end
43+
44+
diffable
45+
46+
failure_message do
47+
%Q[expected to #{description} but got #{@actual.inspect}]
48+
end
49+
50+
# default IO is standard output
51+
def io
52+
@io ||= :stdout
53+
end
54+
end

lib/conjur/config.rb

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#
2+
# Copyright (C) 2013 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+
require 'active_support/core_ext/hash/deep_merge'
22+
require 'active_support/core_ext/hash/indifferent_access'
23+
24+
module Conjur
25+
class Config
26+
@@attributes = {}
27+
28+
class << self
29+
def clear
30+
@@attributes = {}
31+
end
32+
33+
def plugin_config_files
34+
[ '/opt/conjur/etc/plugins.yml' ]
35+
end
36+
37+
def user_config_files
38+
$stderr.puts "variable return %s\n", ENV['CONJURRC']
39+
#raise ENV.inspect
40+
if ENV['CONJURRC']
41+
$stderr.puts "variable return %s\n", ENV['CONJURRC']
42+
return ENV['CONJURRC']
43+
else
44+
homefile = File.expand_path "~/.conjurrc"
45+
pwdfile = File.expand_path ".conjurrc"
46+
if homefile != pwdfile && File.file?(pwdfile)
47+
$stderr.puts """NOTE:\t.conjurrc file detected in the current directory.\n"\
48+
"\tIt's no longer consulted in this version. Please explicitly\n"\
49+
"\tset CONJURRC=./.conjurrc if you're sure you want to use it."
50+
end
51+
[ homefile ]
52+
end
53+
end
54+
55+
def default_config_files
56+
['/etc/conjur.conf', user_config_files, plugin_config_files].flatten.uniq
57+
end
58+
59+
def load(config_files = default_config_files)
60+
require 'yaml'
61+
require 'conjur/log'
62+
63+
config_files.each do |f|
64+
if File.file?(f)
65+
if Conjur.log
66+
Conjur.log << "Loading #{f}\n"
67+
end
68+
config = YAML.load(IO.read(f)).stringify_keys rescue {}
69+
if config['cert_file']
70+
config['cert_file'] = File.expand_path(config['cert_file'], File.dirname(f))
71+
end
72+
Conjur::Config.merge config
73+
end
74+
end
75+
end
76+
77+
78+
def apply
79+
require 'conjur/configuration'
80+
81+
keys = Config.keys.dup
82+
keys.delete(:plugins)
83+
84+
cfg = Conjur.configuration
85+
keys.each do |k|
86+
if Conjur.configuration.respond_to?("#{k}_env_var") && (env_var = Conjur.configuration.send("#{k}_env_var")) && (v = ENV[env_var])
87+
if Conjur.log
88+
Conjur.log << "Not overriding environment setting #{k}=#{v}\n"
89+
end
90+
next
91+
end
92+
value = Config[k]
93+
cfg.set k, value if value
94+
end
95+
96+
Conjur.log << "Using authn url #{Conjur.configuration.authn_url}\n" if Conjur.log
97+
98+
Conjur.config.apply_cert_config!
99+
end
100+
101+
102+
def inspect
103+
@@attributes.inspect
104+
end
105+
106+
def plugins
107+
plugins = @@attributes['plugins']
108+
if plugins
109+
plugins.is_a?(Array) ? plugins : plugins.split(',')
110+
else
111+
[]
112+
end
113+
end
114+
115+
def merge(a)
116+
a = {} unless a
117+
@@attributes.deep_merge!(a.stringify_keys)
118+
end
119+
120+
def keys
121+
@@attributes.keys.map(&:to_sym)
122+
end
123+
124+
def [](key)
125+
@@attributes[key.to_s]
126+
end
127+
128+
def member? key
129+
@@attributes.member?(key) || @@attributes.member?(alternate_key(key))
130+
end
131+
132+
def alternate_key key
133+
case key
134+
when String then key.to_sym
135+
when Symbol then key.to_s
136+
else key
137+
end
138+
end
139+
end
140+
end
141+
end

spec/.conjurrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
cert_file: ./conjur-ci.pem
2+
appliance_url: https://conjur.example.com

spec/config_spec.rb

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
require 'spec_helper'
2+
require 'conjur/config'
3+
require 'conjur/command/rspec/output_matchers'
4+
5+
describe Conjur::Config do
6+
include_context "fresh config"
7+
8+
describe ".default_config_files" do
9+
subject { Conjur::Config.default_config_files }
10+
let(:homedir) { '/src/conjur-api' }
11+
around do |example|
12+
realhome = ENV.delete 'HOME'
13+
ENV['HOME'] = homedir
14+
example.run
15+
ENV['HOME'] = realhome
16+
end
17+
18+
let(:deprecation_warning) { /\.conjurrc/ }
19+
20+
shared_examples "no deprecation warning" do
21+
it "does not issue a deprecation warning" do
22+
expect { subject }.to_not write(deprecation_warning).to(:stderr)
23+
end
24+
end
25+
26+
context "when CONJURRC is not set" do
27+
around do |example|
28+
oldrc = ENV.delete 'CONJURRC'
29+
30+
example.run
31+
32+
ENV['CONJURRC'] = oldrc
33+
end
34+
35+
it { is_expected.to include('/etc/conjur.conf') }
36+
it { is_expected.to include("#{homedir}/.conjurrc") }
37+
38+
before do
39+
allow(File).to receive(:expand_path).and_call_original
40+
allow(File).to receive(:expand_path).with('.conjurrc').and_return '.conjurrc'
41+
end
42+
43+
context "When .conjurrc is present" do
44+
before { allow(File).to receive(:file?).with('.conjurrc').and_return true }
45+
it "Issues a deprecation warning" do
46+
expect { subject }.to write(deprecation_warning).to(:stderr)
47+
end
48+
49+
it "doesn't use the file" do
50+
expect(subject).to_not include '.conjurrc'
51+
end
52+
53+
context "but the current directory is home" do
54+
before do
55+
allow(File).to receive(:expand_path).and_call_original
56+
allow(File).to receive(:expand_path).and_call_original
57+
allow(File).to receive(:expand_path).with('.conjurrc').and_return("#{homedir}/.conjurrc")
58+
end
59+
60+
include_examples "no deprecation warning"
61+
end
62+
end
63+
64+
context "When .conjurrc is missing" do
65+
before { allow(File).to receive(:file?).with('.conjurrc').and_return false }
66+
include_examples "no deprecation warning"
67+
end
68+
end
69+
70+
context "when CONJURRC is set but not available" do
71+
oldrc = ENV['CONJURRC']
72+
73+
before { ENV['CONJURRC']='stub_conjurrc' }
74+
75+
it { is_expected.to include('/etc/conjur.conf') }
76+
it { is_expected.not_to include("#{homedir}/.conjurrc") }
77+
it { is_expected.to include('stub_conjurrc') }
78+
79+
include_examples "no deprecation warning"
80+
ENV['CONJURRC'] = oldrc
81+
end
82+
83+
context "when CONJURRC is set to conjurrc" do
84+
oldrc = ENV['CONJURRC']
85+
86+
before { ENV['CONJURRC']='conjurrc' }
87+
88+
before { allow(File).to receive(:file?).with('conjurrc').and_return true }
89+
it { is_expected.to include('/etc/conjur.conf') }
90+
it { is_expected.to include('conjurrc') }
91+
it { is_expected.not_to include("#{homedir}/conjurrc") }
92+
93+
include_examples "no deprecation warning"
94+
ENV['CONJURRC'] = oldrc
95+
end
96+
end
97+
98+
let(:load!) { Conjur::Config.load([ File.expand_path('conjurrc', File.dirname(__FILE__)) ]) }
99+
let(:cert_path) { File.expand_path('conjur-ci.pem', File.dirname(__FILE__)) }
100+
101+
describe "#load" do
102+
it "resolves the cert_file" do
103+
load!
104+
105+
expect(Conjur::Config[:cert_file]).to eq(cert_path)
106+
end
107+
end
108+
109+
describe "#apply" do
110+
before {
111+
allow(OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE).to receive(:add_file)
112+
allow(File).to receive(:open)
113+
}
114+
115+
context "ssl_certificate string" do
116+
let(:ssl_certificate) do
117+
"""-----BEGIN CERTIFICATE-----
118+
MIIDPjCCAiagAwIBAgIVAKW1gdmOFrXt6xB0iQmYQ4z8Pf+kMA0GCSqGSIb3DQEB
119+
CwUAMD0xETAPBgNVBAoTCGN1Y3VtYmVyMRIwEAYDVQQLEwlDb25qdXIgQ0ExFDAS
120+
BgNVBAMTC2N1a2UtbWFzdGVyMB4XDTE1MTAwNzE2MzAwNloXDTI1MTAwNDE2MzAw
121+
NlowFjEUMBIGA1UEAwwLY3VrZS1tYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
122+
DwAwggEKAoIBAQC9e8bGIHOLOypKA4lsLcAOcDLAq+ICuVxn9Vg0No0m32Ok/K7G
123+
uEGtlC8RidObntblUwqdX2uP7mqAQm19j78UTl1KT97vMmmFrpVZ7oQvEm1FUq3t
124+
FBmJglthJrSbpdZjLf7a7eL1NnunkfBdI1DK9QL9ndMjNwZNFbXhld4fC5zuSr/L
125+
PxawSzTEsoTaB0Nw0DdRowaZgrPxc0hQsrj9OF20gTIJIYO7ctZzE/JJchmBzgI4
126+
CdfAYg7zNS+0oc0ylV0CWMerQtLICI6BtiQ482bCuGYJ00NlDcdjd3w+A2cj7PrH
127+
wH5UhtORL5Q6i9EfGGUCDbmfpiVD9Bd3ukbXAgMBAAGjXDBaMA4GA1UdDwEB/wQE
128+
AwIFoDAdBgNVHQ4EFgQU2jmj7l5rSw0yVb/vlWAYkK/YBwkwKQYDVR0RBCIwIIIL
129+
Y3VrZS1tYXN0ZXKCCWxvY2FsaG9zdIIGY29uanVyMA0GCSqGSIb3DQEBCwUAA4IB
130+
AQBCepy6If67+sjuVnT9NGBmjnVaLa11kgGNEB1BZQnvCy0IN7gpLpshoZevxYDR
131+
3DnPAetQiZ70CSmCwjL4x6AVxQy59rRj0Awl9E1dgFTYI3JxxgLsI9ePdIRVEPnH
132+
dhXqPY5ZIZhvdHlLStjsXX7laaclEtMeWfSzxe4AmP/Sm/er4ks0gvLQU6/XJNIu
133+
RnRH59ZB1mZMsIv9Ii790nnioYFR54JmQu1JsIib77ZdSXIJmxAtraJSTLcZbU1E
134+
+SM3XCE423Xols7onyluMYDy3MCUTFwoVMRBcRWCAk5gcv6XvZDfLi6Zwdne6x3Y
135+
bGenr4vsPuSFsycM03/EcQDT
136+
-----END CERTIFICATE-----
137+
"""
138+
end
139+
let(:certificate){ double('Certificate') }
140+
before{
141+
Conjur::Config.class_variable_set('@@attributes', {'ssl_certificate' => ssl_certificate})
142+
}
143+
144+
it 'trusts the certificate string' do
145+
expect(OpenSSL::X509::Certificate).to receive(:new).with(ssl_certificate).once.and_return certificate
146+
expect(OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE).to receive(:add_cert).with(certificate).once
147+
Conjur::Config.apply
148+
end
149+
end
150+
151+
context "cert_file" do
152+
let(:cert_file) { "/path/to/cert.pem" }
153+
before {
154+
Conjur::Config.class_variable_set("@@attributes", { 'cert_file' => cert_file })
155+
}
156+
157+
it "trusts the cert_file" do
158+
expect(OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE).to receive(:add_file).with cert_file
159+
Conjur::Config.apply
160+
end
161+
162+
it "propagates the cert_file to Configuration.cert_file" do
163+
Conjur::Config.apply
164+
expect(Conjur.configuration.cert_file).to eq(cert_file)
165+
end
166+
end
167+
168+
it "shadows rc with envars" do
169+
url = 'https://other-conjur.example.com'
170+
ENV['CONJUR_APPLIANCE_URL'] = url
171+
load!
172+
Conjur::Config.apply
173+
expect(Conjur.configuration.appliance_url).to eq(url)
174+
end
175+
end
176+
end

0 commit comments

Comments
 (0)