dnc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +1 -0
- data.tar.gz.sig +1 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +12 -0
- data/.yardopts +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +20 -0
- data/dnc.gemspec +37 -0
- data/lib/dnc.rb +4 -0
- data/lib/dnc/array.rb +16 -0
- data/lib/dnc/dn.rb +209 -0
- data/lib/dnc/string.rb +33 -0
- data/lib/dnc/version.rb +3 -0
- data/spec/fixtures/common_dns.txt +12 -0
- data/spec/lib/dn_spec.rb +73 -0
- data/spec/lib/string_spec.rb +39 -0
- data/spec/spec_helper.rb +23 -0
- metadata +244 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 470a323d926d5b35cf824c6984227b59413b1293
|
4
|
+
data.tar.gz: cd95e84a643c70999b4114a8a9080a8c36aa4268
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e41dda0c97dc5cd0166137cba422eb498db1a2102e8e00fe2f83a20b1d477de4742f6682bb2eb7ced9c36a55e159a21ff6b977d7885d2b517471b730b38be6ad
|
7
|
+
data.tar.gz: a11f87306b43ecb1784ffab751962ee71857e87eeb9a53d43b0993ac9ca46a5dfcaba7b0dbfa1f62954abac2a6e4d03e1b351211acb320dc2f47b61b612c414f
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
�dw8��Il?��"k����D�>ʪ��C���fT�nf_������d���t��aH��b�}��S��+O*Ʒ���\yüAy�`8�j�EW�L]M+G*����
|
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
�t�{P-�� L��"��^q���T�2�+��4[�l
|
data/.coveralls.yml
ADDED
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Steven Haddox
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Distinguished Name (DN) Converter
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'dnc'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install dnc
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'dnc'
|
25
|
+
dn_string = 'CN=Some Valid, O=DN, OU=string'
|
26
|
+
dn = DN.new(dn_string: dn_string)
|
27
|
+
# Or:
|
28
|
+
dn_string.to_dn
|
29
|
+
# And:
|
30
|
+
dn_string.to_dn.to_s
|
31
|
+
```
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it ( https://github.com/[my-github-username]/dnc/fork )
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rubocop/rake_task'
|
4
|
+
require 'coveralls/rake/task'
|
5
|
+
Coveralls::RakeTask.new
|
6
|
+
|
7
|
+
task default: [:spec, :rubocop, 'coveralls:push']
|
8
|
+
|
9
|
+
desc 'Run specs'
|
10
|
+
RSpec::Core::RakeTask.new(:spec)
|
11
|
+
|
12
|
+
desc 'Run rubocop'
|
13
|
+
task :rubocop do
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Display TODOs, FIXMEs, and OPTIMIZEs'
|
18
|
+
task :notes do
|
19
|
+
system("grep -r 'OPTIMIZE:\\|FIXME:\\|TODO:' #{Dir.pwd}")
|
20
|
+
end
|
data/dnc.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dnc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dnc"
|
8
|
+
spec.version = Dnc::VERSION
|
9
|
+
spec.authors = ["Steven Haddox"]
|
10
|
+
spec.email = ["steven@haddox.us"]
|
11
|
+
spec.summary = %q{Distinguished Name Converter}
|
12
|
+
spec.description = %q{Convert multiple X509 DN strings into a consistent(ish) format.}
|
13
|
+
spec.homepage = "https://github.com/stevenhaddox/dnc"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 1.9.3'
|
22
|
+
spec.add_dependency 'logging', '~> 1.8'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'awesome_print', '~> 1.2'
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
26
|
+
spec.add_development_dependency 'coveralls', '~> 0.7'
|
27
|
+
spec.add_development_dependency 'rack', '~> 1.5'
|
28
|
+
spec.add_development_dependency 'rack-test', '~> 0.6'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rubocop', '~> 0.27'
|
31
|
+
spec.add_development_dependency 'rspec', '~> 3.1'
|
32
|
+
spec.add_development_dependency 'simplecov', '~> 0.9'
|
33
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
34
|
+
|
35
|
+
spec.cert_chain = ['certs/stevenhaddox.pem']
|
36
|
+
spec.signing_key = File.expand_path("~/.gem/certs/gem-private_key.pem") if $0 =~ /gem\z/
|
37
|
+
end
|
data/lib/dnc.rb
ADDED
data/lib/dnc/array.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#
|
2
|
+
# Extend the core Array class to include `.wrap`
|
3
|
+
#
|
4
|
+
class Array
|
5
|
+
|
6
|
+
# Duplication of Ruby on Rails Array#wrap method
|
7
|
+
def self.wrap(object)
|
8
|
+
if object.nil?
|
9
|
+
[]
|
10
|
+
elsif object.respond_to?(:to_ary)
|
11
|
+
object.to_ary || [object]
|
12
|
+
else
|
13
|
+
[object]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/dnc/dn.rb
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
# Custom exception for strings that can't be parsed as per RFC1779
|
4
|
+
class DnDelimiterUnparsableError < TypeError; end
|
5
|
+
# Custom exception for strings that can't be parsed as per RFC1779
|
6
|
+
class DnStringUnparsableError < TypeError; end
|
7
|
+
|
8
|
+
# Accepts various DN strings and returns a DN object
|
9
|
+
class DN
|
10
|
+
attr_accessor :original_dn, :dn_string, :delimiter,
|
11
|
+
:cn, :l, :st, :o, :ou, :c, :street, :dc
|
12
|
+
|
13
|
+
# Initialize the instance
|
14
|
+
#
|
15
|
+
# @param opts [Hash] Options hash for new DN instance attribute values
|
16
|
+
# @param opts[:dn_string] [String] The DN string you want to parse into a DN
|
17
|
+
# @param opts[:logger] User provided logger vs Rails / Logging default logger
|
18
|
+
def initialize(opts={})
|
19
|
+
@dn_string = opts[:dn_string]
|
20
|
+
@original_dn = dn_string
|
21
|
+
fail "dnc: dn_string parameter is **required**" if dn_string.nil?
|
22
|
+
@logger = opts[:logger].nil? ? logger : opts[:logger]
|
23
|
+
@delimiter = opts[:delimiter].nil? ? identify_delimiter : opts[:delimiter]
|
24
|
+
format_dn
|
25
|
+
end
|
26
|
+
|
27
|
+
# logger method to return Rails logger if defined, else logging logger
|
28
|
+
def logger
|
29
|
+
return @logger if @logger
|
30
|
+
logger = Logging.logger[self]
|
31
|
+
@logger ||= Kernel.const_defined?('Rails') ? Rails.logger : logger
|
32
|
+
end
|
33
|
+
|
34
|
+
# Split passed DN by identified delimiter
|
35
|
+
def split_by_delimiter
|
36
|
+
dn_string.split(delimiter).reject(&:empty?)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Convert DN object into a string
|
40
|
+
def to_s
|
41
|
+
return_string = ""
|
42
|
+
%w(cn dc l st ou o c street).each do |string_name|
|
43
|
+
unless self.send(string_name.to_sym).nil? || self.send(string_name.to_sym).empty?
|
44
|
+
return_string += "," unless return_string.empty?
|
45
|
+
return_string += self.send("#{string_name}_string".to_sym)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return_string
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Orchestrates reformatting DN to expected element order for LDAP auth.
|
55
|
+
def format_dn
|
56
|
+
dn_string.upcase! # Upcase all DNs for consistency
|
57
|
+
format_dn_element_order unless dn_begins_properly?(dn_string)
|
58
|
+
parse_rdns_to_attrs
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
# Parse @dn_string RDNs and assign them to DN attributes
|
63
|
+
def parse_rdns_to_attrs
|
64
|
+
split_by_delimiter.each do |rdn|
|
65
|
+
unless rdn.include?('+')
|
66
|
+
parse_top_level_rdn(rdn)
|
67
|
+
else
|
68
|
+
parse_nested_rdn(rdn)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
self
|
73
|
+
end
|
74
|
+
|
75
|
+
def parse_top_level_rdn(rdn)
|
76
|
+
rdn_array = rdn.split('=')
|
77
|
+
method = rdn_array[0].downcase.to_sym
|
78
|
+
value = rdn_array[1]
|
79
|
+
unless send(method).nil? || send(method).empty?
|
80
|
+
send("#{method}=", Array.wrap(send(method)))
|
81
|
+
send("#{method}").insert(0, value)
|
82
|
+
else
|
83
|
+
send("#{method}=", value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_nested_rdn(rdn)
|
88
|
+
rdn_keypairs = {}
|
89
|
+
rdn_array = rdn.split('+')
|
90
|
+
rdn_array.each do |string|
|
91
|
+
keypair = string.split('=')
|
92
|
+
rdn_keypairs[keypair[0].to_sym] = keypair[1]
|
93
|
+
end
|
94
|
+
|
95
|
+
send("#{rdn_keypairs.keys.first.downcase}=", rdn_keypairs)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Ensure order of DN elements is proper for CAS server with ',' delimiter
|
99
|
+
def format_dn_element_order
|
100
|
+
formatted_dn = split_by_delimiter.reverse.join(delimiter)
|
101
|
+
if dn_begins_properly?(formatted_dn)
|
102
|
+
dn_string = formatted_dn
|
103
|
+
|
104
|
+
else
|
105
|
+
fail("DN invalid format for LDAP authentication, DN:\r\n#{original_dn}")
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Verify DN starts with 'CN='
|
110
|
+
def dn_begins_properly?(dn_str)
|
111
|
+
dn_str.nil? ? false : (dn_str.start_with?("CN=") || dn_str.start_with?("#{delimiter}CN="))
|
112
|
+
end
|
113
|
+
|
114
|
+
# Regex to match the DN delimiter by getting the 2nd key non-word predecessor
|
115
|
+
def delimiter_regexp
|
116
|
+
/\A.*=.*((([^\w\s\+\)\(])|([_]))\s?)\w+=.*\z/
|
117
|
+
end
|
118
|
+
|
119
|
+
# Identify and set the DN delimiter
|
120
|
+
def identify_delimiter
|
121
|
+
begin
|
122
|
+
logger.debug("DN.identify_delimeter: #{dn_string}")
|
123
|
+
delimiter_regexp.match(dn_string)[1]
|
124
|
+
rescue
|
125
|
+
fail DnDelimiterUnparsableError, "DN delimiter could not be identified
|
126
|
+
\r\nPlease ensure your string complies with RFC1779 formatting."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def cn_string
|
131
|
+
dynamic_strings('cn', cn.class)
|
132
|
+
end
|
133
|
+
|
134
|
+
def l_string
|
135
|
+
dynamic_strings('l', l.class)
|
136
|
+
end
|
137
|
+
|
138
|
+
def st_string
|
139
|
+
dynamic_strings('st', st.class)
|
140
|
+
end
|
141
|
+
|
142
|
+
def o_string
|
143
|
+
dynamic_strings('o', o.class)
|
144
|
+
end
|
145
|
+
|
146
|
+
def ou_string
|
147
|
+
dynamic_strings('ou', ou.class)
|
148
|
+
end
|
149
|
+
|
150
|
+
def c_string
|
151
|
+
dynamic_strings('c', c.class)
|
152
|
+
end
|
153
|
+
|
154
|
+
def street_string
|
155
|
+
dynamic_strings('street', street.class)
|
156
|
+
end
|
157
|
+
|
158
|
+
def dc_string
|
159
|
+
dynamic_strings('dc', dc.class)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Identify which RDN string formatteer to call by value's class
|
163
|
+
def dynamic_strings(getter_method, value_class)
|
164
|
+
case value_class.to_s
|
165
|
+
when Array.to_s
|
166
|
+
dn_array_to_string(getter_method)
|
167
|
+
when Hash.to_s
|
168
|
+
dn_hash_to_string(getter_method)
|
169
|
+
when String.to_s
|
170
|
+
dn_string_to_string(getter_method)
|
171
|
+
else
|
172
|
+
logger.error "Invalid string accessor method class: #{value_class}"
|
173
|
+
fail "Invalid string accessor method class: #{value_class}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# NOTE:
|
178
|
+
# The following methods are a code smell, they handle formatting the values
|
179
|
+
# in DN attrs and converting them into a string format based upon their class
|
180
|
+
|
181
|
+
# Dynamically define a method to return DN array values as string format
|
182
|
+
def dn_array_to_string(getter_method)
|
183
|
+
return_string = ""
|
184
|
+
value = self.send(getter_method.to_sym)
|
185
|
+
value.each do |element|
|
186
|
+
return_string += "," unless return_string.empty?
|
187
|
+
return_string += "#{getter_method.to_s.upcase}=#{element}"
|
188
|
+
end
|
189
|
+
|
190
|
+
return_string
|
191
|
+
end
|
192
|
+
|
193
|
+
# Dynamically define a method to return DN hash values as string format
|
194
|
+
def dn_hash_to_string(getter_method)
|
195
|
+
return_string = ""
|
196
|
+
value = self.send(getter_method.to_sym)
|
197
|
+
value.each do |key, string|
|
198
|
+
return_string += "+" unless return_string.empty?
|
199
|
+
return_string += "#{key}=#{string}"
|
200
|
+
end
|
201
|
+
|
202
|
+
return_string
|
203
|
+
end
|
204
|
+
|
205
|
+
# Dynamically define a method to return DN string values as string format
|
206
|
+
def dn_string_to_string(getter_method)
|
207
|
+
"#{getter_method.to_s.upcase}=#{self.send(getter_method.to_sym)}"
|
208
|
+
end
|
209
|
+
end
|
data/lib/dnc/string.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'dnc'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Extend the core String class to include `.to_dn` && `.to_dn!`
|
5
|
+
#
|
6
|
+
class String
|
7
|
+
|
8
|
+
# Parses the string to return a DN object
|
9
|
+
# Returns nil if a DN instance cannot be created
|
10
|
+
def to_dn
|
11
|
+
begin
|
12
|
+
new_dn = DN.new(dn_string: self.to_s)
|
13
|
+
rescue StandardError
|
14
|
+
new_dn = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
new_dn
|
18
|
+
end
|
19
|
+
|
20
|
+
# Similar to {#to_dn}, but raises an error unless the string can be
|
21
|
+
# explicitly parsed to a DN instance
|
22
|
+
def to_dn!
|
23
|
+
begin
|
24
|
+
new_dn = DN.new(dn_string: self.to_s)
|
25
|
+
rescue StandardError
|
26
|
+
raise DnStringUnparsableError,
|
27
|
+
"Could not force conversion to DN:\n#{inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
new_dn
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
data/lib/dnc/version.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
/C=US/O=RB/OU=DEV/OU=RAILS/OU=People/CN=Last First (initial)%CN=LAST FIRST (INITIAL),OU=PEOPLE,OU=RAILS,OU=DEV,O=RB,C=US
|
2
|
+
/CN=Smith John+initials=cpt/OU=People/OU=RAILS/OU=DEV/O=RB/C=GB%CN=SMITH JOHN+INITIALS=CPT,OU=DEV,OU=RAILS,OU=PEOPLE,O=RB,C=GB
|
3
|
+
CN=John Smith+email=jsmith@mayflower.org,O=ISODE Consortium,C=GB,O=ISODE Consortium%CN=JOHN SMITH+EMAIL=JSMITH@MAYFLOWER.ORG,O=ISODE CONSORTIUM,C=GB,O=ISODE CONSORTIUM
|
4
|
+
/C=GB/O=RB/OU=DEV/OU=RAILS/OU=People/CN=Smith John+initials=cpt%CN=SMITH JOHN+INITIALS=CPT,OU=PEOPLE,OU=RAILS,OU=DEV,O=RB,C=GB
|
5
|
+
/C=US/O=RB/OU=DEV/OU=JS/OU=People/CN=Last First M (initial)%CN=LAST FIRST M (INITIAL),OU=PEOPLE,OU=JS,OU=DEV,O=RB,C=US
|
6
|
+
CN=Marshall T. Rose, O=Dover Beach Consulting, L=Santa Clara, ST=California, C=US%CN=MARSHALL T. ROSE,O=DOVER BEACH CONSULTING,L=SANTA CLARA,ST=CALIFORNIA,C=US
|
7
|
+
CN=FTAM Service, CN=Bells, OU=Computer Science, O=University College London, C=GB%CN=FTAM SERVICE,CN=BELLS,OU=COMPUTER SCIENCE,O=UNIVERSITY COLLEGE LONDON,C=GB
|
8
|
+
CN=Markus Kuhn, O=University of Erlangen, C=DE%CN=MARKUS KUHN,O=UNIVERSITY OF ERLANGEN,C=DE
|
9
|
+
CN=Steve Kille, O=ISODE Consortium, C=DE%CN=STEVE KILLE,O=ISODE CONSORTIUM,C=DE
|
10
|
+
CN=Steve Kille,O=ISODE Consortium,C=GB%CN=STEVE KILLE,O=ISODE CONSORTIUM,C=GB
|
11
|
+
CN=C E B\sid=ceb\foo=bar, C=GB, O=foo%CN=C E B\sid=ceb\foo=bar,C=GB,O=FOO
|
12
|
+
C=UK, ST=England, L=London, O=LShift Ltd., OU=Development, CN=Lee Coomber/UID=123456%CN=LEE COOMBER/UID=123456,OU=DEVELOPMENT,O=LSHIFT LTD.,L=LONDON,ST=ENGLAND,C=UK
|
data/spec/lib/dn_spec.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe DN do
|
4
|
+
let!(:raw_dn) { '/C=US/O=RB/OU=DEV/OU=JS/OU=People/CN=Last First M (initial)' }
|
5
|
+
let!(:dn_to_s) { 'CN=LAST FIRST M (INITIAL),OU=PEOPLE,OU=JS,OU=DEV,O=RB,C=US' }
|
6
|
+
let(:valid_subject) { DN.new(dn_string: raw_dn) }
|
7
|
+
let(:dn_elements) { ["C=US", "O=RB", "OU=DEV", "OU=JS", "OU=PEOPLE", "CN=LAST FIRST M (INITIAL)"] }
|
8
|
+
|
9
|
+
#
|
10
|
+
# Unit specs
|
11
|
+
#
|
12
|
+
describe "#new" do
|
13
|
+
it "should create a new DN object when valid" do
|
14
|
+
expect(valid_subject.class.to_s).to eq(DN.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should identify the most likely delimiter" do
|
18
|
+
expect(valid_subject.delimiter).to eq('/')
|
19
|
+
%w(, ; - _ : \\ % ^ ! & * @ # $).each do |delim|
|
20
|
+
custom_delim_subject = DN.new(dn_string: raw_dn.gsub('/',delim))
|
21
|
+
expect(custom_delim_subject.delimiter).to eq(delim)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should split on identified delimiter" do
|
26
|
+
expect(valid_subject.split_by_delimiter).to eq(dn_elements)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should parse each key value pair and assign an attribute" do
|
30
|
+
expect(valid_subject.c).to eq('US')
|
31
|
+
expect(valid_subject.o).to eq('RB')
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return appropriate attrs as arrays" do
|
35
|
+
[:ou, :dc].each do |array_wrapped_el|
|
36
|
+
dn = DN.new(dn_string: '/C=US/O=RB/OU=DEV/OU=JS/OU=People/DC=example/DC=org/CN=Last First M (initial)')
|
37
|
+
expect(dn.dc).to eq(['ORG', 'EXAMPLE'])
|
38
|
+
expect(dn.ou).to eq(['PEOPLE', 'JS', 'DEV'])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should handle multiple RDN key value pairs in the CN and return an array of elements" do
|
43
|
+
dn = DN.new(dn_string: '/C=US/O=RB/OU=DEV/OU=JS/OU=People/DC=example/DC=org/CN=Last First M (initial)+email=initial@example.org+office=home')
|
44
|
+
expect(dn.cn).to eq({CN: 'LAST FIRST M (INITIAL)', EMAIL: 'INITIAL@EXAMPLE.ORG', OFFICE: 'HOME'})
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "validations" do
|
49
|
+
it "should require a parsable DN string" do
|
50
|
+
expect{ DN.new(dn_string: "") }.to raise_error(DnDelimiterUnparsableError)
|
51
|
+
expect{ DN.new(dn_string: "nope") }.to raise_error(DnDelimiterUnparsableError)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".to_s" do
|
56
|
+
it "should return a properly formatted string for CAS & RFC1779 use" do
|
57
|
+
dn = DN.new(dn_string: '/C=US/O=RB/OU=DEV/OU=JS/OU=People/DC=example/DC=org/CN=Last First M (initial)+email=initial@example.org+office=home')
|
58
|
+
dn_string = 'CN=LAST FIRST M (INITIAL)+EMAIL=INITIAL@EXAMPLE.ORG+OFFICE=HOME,DC=ORG,DC=EXAMPLE,OU=PEOPLE,OU=JS,OU=DEV,O=RB,C=US'
|
59
|
+
expect(dn.to_s).to eq(dn_string)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should parse common DN formats into DN objects" do
|
63
|
+
pending "Finish this"
|
64
|
+
File.readlines('spec/fixtures/common_dns.txt').each do |line|
|
65
|
+
dn_in = line.rstrip.split('%')[0]
|
66
|
+
dn_out = line.rstrip.split('%')[1]
|
67
|
+
#ap dn_in
|
68
|
+
#ap dn_out
|
69
|
+
expect(DN.new(dn_string: dn_in).to_s).to eq(dn_out)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
let!(:raw_dn) { '/DC=org/DC=ruby-lang/CN=Ruby certificate rbcert' }
|
5
|
+
let(:dn) {
|
6
|
+
DN.new({
|
7
|
+
dn_string: raw_dn,
|
8
|
+
})
|
9
|
+
}
|
10
|
+
let(:dn_to_s) { 'CN=RUBY CERTIFICATE RBCERT,DC=RUBY-LANG,DC=ORG' }
|
11
|
+
|
12
|
+
after :all do
|
13
|
+
#expect(@log_output.readline).to eq("DEBUG String: DNC raw_dn:\n")
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".to_dn" do
|
17
|
+
it "should return a DN instance if the string can be parsed" do
|
18
|
+
expect(raw_dn.to_dn.class.to_s).to eq('DN')
|
19
|
+
expect(raw_dn.to_dn.to_yaml).to eq(dn.to_yaml)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should return nil otherwise" do
|
23
|
+
expect("".to_dn).to eql(nil)
|
24
|
+
expect("nope".to_dn).to eql(nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe ".to_dn!" do
|
29
|
+
it "should return a DN instance if the string can be parsed" do
|
30
|
+
expect(raw_dn.to_dn!.class.to_s).to eq('DN')
|
31
|
+
expect(raw_dn.to_dn!.to_yaml).to eq(dn.to_yaml)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should raise a DnStringUnparsableError otherwise" do
|
35
|
+
expect{ "".to_dn! }.to raise_error(DnStringUnparsableError)
|
36
|
+
expect{ "nope".to_dn! }.to raise_error(DnStringUnparsableError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
require 'coveralls'
|
3
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
4
|
+
SimpleCov.start do
|
5
|
+
add_filter '/spec/'
|
6
|
+
end
|
7
|
+
# SimpleCov always comes before **anything** else
|
8
|
+
|
9
|
+
require_relative '../lib/dnc'
|
10
|
+
require 'awesome_print'
|
11
|
+
#require 'rspec/logging_helper'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
config.filter_run_excluding :skip
|
17
|
+
config.order = 'random'
|
18
|
+
|
19
|
+
# Configure RSpec to capture log messages for each test. The output from the
|
20
|
+
# logs will be stored in the @log_output variable. It is a StringIO instance.
|
21
|
+
# include RSpec::LoggingHelper
|
22
|
+
# config.capture_log_messages
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,244 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dnc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Steven Haddox
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDhTCCAm2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBEMRYwFAYDVQQDDA1zdGV2
|
14
|
+
ZW4uaGFkZG94MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJk/IsZAEZ
|
15
|
+
FgNjb20wHhcNMTQxMTE4MDIxMzIwWhcNMTUxMTE4MDIxMzIwWjBEMRYwFAYDVQQD
|
16
|
+
DA1zdGV2ZW4uaGFkZG94MRUwEwYKCZImiZPyLGQBGRYFZ21haWwxEzARBgoJkiaJ
|
17
|
+
k/IsZAEZFgNjb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDanmKr
|
18
|
+
vJDcVGMeDbDouLfKvU5ugOcHTXP04QDYSshaMTeuWSm4OXakxk2rxnR7Laq86R+8
|
19
|
+
h1NbHMdiZdwlHcpZm9/YD6qjbQhnLNGsezMrNpfZwfy9VnUQY4e0OCAca9vQXKTL
|
20
|
+
qC4fiuRD6sQQpyXkiIno0KlJOA4sKtH8vFucPGmhO0FUdlQY5FarDvCvZrtteO6L
|
21
|
+
6/GQFjupFBd9X6zt1XBs28IC+YUw33SN0UJ5JHB45ig0BmeWMXdd4SKWe4ve/2UY
|
22
|
+
asgs2miI3HP0wCPs0EF64/8LbuEUyMjHDr3a7+7KIRxYn2H/yUH5Ndqz6yL5G0sf
|
23
|
+
jUsC32JuE7VlJwFNAgMBAAGjgYEwfzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAd
|
24
|
+
BgNVHQ4EFgQUC8HywyOMPJFCsH7uGW+CeKcZ8+8wIgYDVR0RBBswGYEXc3RldmVu
|
25
|
+
LmhhZGRveEBnbWFpbC5jb20wIgYDVR0SBBswGYEXc3RldmVuLmhhZGRveEBnbWFp
|
26
|
+
bC5jb20wDQYJKoZIhvcNAQEFBQADggEBAFoac9ZKc20ZXw2R2mWUz7FaJJdUvb7o
|
27
|
+
4rKVzFQkJwvAX+NEdP32yCDViGoEqlA13el5fllllmG3E7Qrw+0JA5B3wrZbVfQA
|
28
|
+
v4eX0ZohhW3CXLSz65pd3zfrwPAw0pXs1QKP+IioTuLQoBsGUiIqCPulZvzn/xN2
|
29
|
+
KG7SexyfUEXyJRMMigA/mE8h6bYfgKKUmLQVs1uRaXmOI7dKUF6HZJpda51zJH3v
|
30
|
+
42qdwEXvvkODZAD6KAIXPdmbMfBgPbcd+B/4eUA0PyKo+4dgL1NuqX4MPWToevIZ
|
31
|
+
O8EKLF2X7NmC6FY1bOsSj/J8r1SOkx0rxgF+geRvY1P+hfNjDfxTsjU=
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
date: 2015-01-13 00:00:00.000000000 Z
|
34
|
+
dependencies:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: logging
|
37
|
+
requirement: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.8'
|
42
|
+
type: :runtime
|
43
|
+
prerelease: false
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.8'
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: awesome_print
|
51
|
+
requirement: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.2'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.2'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: bundler
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '1.6'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '1.6'
|
77
|
+
- !ruby/object:Gem::Dependency
|
78
|
+
name: coveralls
|
79
|
+
requirement: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0.7'
|
84
|
+
type: :development
|
85
|
+
prerelease: false
|
86
|
+
version_requirements: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0.7'
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rack
|
93
|
+
requirement: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '1.5'
|
98
|
+
type: :development
|
99
|
+
prerelease: false
|
100
|
+
version_requirements: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '1.5'
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: rack-test
|
107
|
+
requirement: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0.6'
|
112
|
+
type: :development
|
113
|
+
prerelease: false
|
114
|
+
version_requirements: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0.6'
|
119
|
+
- !ruby/object:Gem::Dependency
|
120
|
+
name: rake
|
121
|
+
requirement: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '10.0'
|
126
|
+
type: :development
|
127
|
+
prerelease: false
|
128
|
+
version_requirements: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - "~>"
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '10.0'
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: rubocop
|
135
|
+
requirement: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - "~>"
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0.27'
|
140
|
+
type: :development
|
141
|
+
prerelease: false
|
142
|
+
version_requirements: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - "~>"
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0.27'
|
147
|
+
- !ruby/object:Gem::Dependency
|
148
|
+
name: rspec
|
149
|
+
requirement: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - "~>"
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '3.1'
|
154
|
+
type: :development
|
155
|
+
prerelease: false
|
156
|
+
version_requirements: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - "~>"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '3.1'
|
161
|
+
- !ruby/object:Gem::Dependency
|
162
|
+
name: simplecov
|
163
|
+
requirement: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - "~>"
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0.9'
|
168
|
+
type: :development
|
169
|
+
prerelease: false
|
170
|
+
version_requirements: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - "~>"
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0.9'
|
175
|
+
- !ruby/object:Gem::Dependency
|
176
|
+
name: yard
|
177
|
+
requirement: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - "~>"
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0.8'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - "~>"
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0.8'
|
189
|
+
description: Convert multiple X509 DN strings into a consistent(ish) format.
|
190
|
+
email:
|
191
|
+
- steven@haddox.us
|
192
|
+
executables: []
|
193
|
+
extensions: []
|
194
|
+
extra_rdoc_files: []
|
195
|
+
files:
|
196
|
+
- ".coveralls.yml"
|
197
|
+
- ".gitignore"
|
198
|
+
- ".rubocop.yml"
|
199
|
+
- ".travis.yml"
|
200
|
+
- ".yardopts"
|
201
|
+
- Gemfile
|
202
|
+
- LICENSE.txt
|
203
|
+
- README.md
|
204
|
+
- Rakefile
|
205
|
+
- dnc.gemspec
|
206
|
+
- lib/dnc.rb
|
207
|
+
- lib/dnc/array.rb
|
208
|
+
- lib/dnc/dn.rb
|
209
|
+
- lib/dnc/string.rb
|
210
|
+
- lib/dnc/version.rb
|
211
|
+
- spec/fixtures/common_dns.txt
|
212
|
+
- spec/lib/dn_spec.rb
|
213
|
+
- spec/lib/string_spec.rb
|
214
|
+
- spec/spec_helper.rb
|
215
|
+
homepage: https://github.com/stevenhaddox/dnc
|
216
|
+
licenses:
|
217
|
+
- MIT
|
218
|
+
metadata: {}
|
219
|
+
post_install_message:
|
220
|
+
rdoc_options: []
|
221
|
+
require_paths:
|
222
|
+
- lib
|
223
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: 1.9.3
|
228
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
229
|
+
requirements:
|
230
|
+
- - ">="
|
231
|
+
- !ruby/object:Gem::Version
|
232
|
+
version: '0'
|
233
|
+
requirements: []
|
234
|
+
rubyforge_project:
|
235
|
+
rubygems_version: 2.4.4
|
236
|
+
signing_key:
|
237
|
+
specification_version: 4
|
238
|
+
summary: Distinguished Name Converter
|
239
|
+
test_files:
|
240
|
+
- spec/fixtures/common_dns.txt
|
241
|
+
- spec/lib/dn_spec.rb
|
242
|
+
- spec/lib/string_spec.rb
|
243
|
+
- spec/spec_helper.rb
|
244
|
+
has_rdoc:
|
metadata.gz.sig
ADDED
Binary file
|