cs-ruby-ldap 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 19e574263bc68b0b3eadebc634b65cc0188fd5d4
4
+ data.tar.gz: 4ca1ff90e220d24d8a4db6cf1f703d48f3b5d1c7
5
+ SHA512:
6
+ metadata.gz: 8e7bd944ff3e8c3162e04212e241bf239d2ba725af6cd8ef9a28532c25c5c7d0b572619276777107b958036f09904966920f97eda5ae541ea994c43b404f9075
7
+ data.tar.gz: ca4fa065811f6a649448c5ed5189d63c01e12259fc1391db229f98f536ec028a47038972eabe47b9383566d283ff17a5710570750ccafa2ae93d9371e296e43d
data/README.rdoc ADDED
File without changes
@@ -0,0 +1,209 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Utilities for manipulating slapd
3
+ #
4
+ # Author:: Benjamin Staffin <ben@cloudscaling.com>
5
+ #
6
+ # Copyright 2014, The Cloudscaling Group, Inc.
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ require 'base64'
22
+ require 'digest'
23
+ require 'mixlib/shellout'
24
+ require 'net-ldap'
25
+ require 'openssl'
26
+ require 'stringio'
27
+
28
+ module Cloudscaling
29
+ # Tools for manipulating slapd using a ldapi:/// connection
30
+ class LDAPConfigUtils
31
+ # Read directly from an on-disk database, even if slapd is not running.
32
+ def self.slapcat(subtree_dn, filter)
33
+ scat = Mixlib::ShellOut.new('slapcat', '-o', 'ldif-wrap=no',
34
+ '-H', "ldap:///#{subtree_dn}???#{filter}")
35
+ scat.run_command
36
+ scat.error!
37
+ scat.stdout
38
+ end
39
+
40
+ # Mimic Net::LDAP#search using ldapsearch subprocess so we can use
41
+ # things like -Y EXTERNAL -H ldapi:///
42
+ # Returns a Net::LDAP::Dataset object representing the result.
43
+ def self.ldapsearch(args)
44
+ cmdargs = %w(ldapsearch -LLLQ -Y EXTERNAL -H ldapi:/// -o ldif-wrap=no)
45
+ filter = attributes = nil
46
+ args.each do |arg, val|
47
+ case arg.to_s
48
+ when 'base'
49
+ cmdargs += ['-b', val]
50
+ when 'scope'
51
+ unless [:base, :one, :sub, :children].include? val
52
+ fail "Unrecognized scope: #{val}"
53
+ end
54
+ cmdargs += ['-s', String(val)]
55
+ when 'filter'
56
+ filter = val
57
+ when 'attributes'
58
+ unless val.is_a?(Array)
59
+ fail("attributes must be an Array, not #{val.class}")
60
+ end
61
+ attributes = val
62
+ else
63
+ fail "Unrecognized option: #{arg}"
64
+ end
65
+ end
66
+ cmdargs << filter if filter
67
+ cmdargs += attributes if attributes
68
+ lsearch = Mixlib::ShellOut.new(*cmdargs, returns: [0, 32])
69
+ # exit 32 --> noSuchObject
70
+ lsearch.run_command
71
+ lsearch.error!
72
+ Net::LDAP::Dataset.read_ldif(StringIO.new(lsearch.stdout))
73
+ end
74
+
75
+ # Use this to modify anything in cn=config
76
+ # ldif: String or Array of Strings
77
+ # op: :modify (default) or :add
78
+ def self.ldapmodify_ldif(ldif, op = :modify)
79
+ ldif = ldif.join("\n") if ldif.is_a?(Array)
80
+ extraargs = []
81
+ case op
82
+ when :add
83
+ extraargs << '-a'
84
+ end
85
+ # Chef::Log.info("applying ldif: #{ldif}")
86
+ lmod = Mixlib::ShellOut.new(
87
+ 'ldapmodify', *extraargs, '-Q', '-Y', 'EXTERNAL', '-H', 'ldapi:///',
88
+ input: ldif)
89
+ lmod.run_command
90
+ lmod.error!
91
+ end
92
+
93
+ # Check for existence of a cn=config entry
94
+ def self.dn_exists?(dn)
95
+ true unless ldapsearch(base: dn,
96
+ attributes: 'dn',
97
+ scope: :base
98
+ ).empty?
99
+ end
100
+
101
+ def self.ldapadd(dn, attrs)
102
+ ldif = ["dn: #{dn}"]
103
+ attrs.each do |attr, value|
104
+ ldif << "#{attr}: #{value}"
105
+ end
106
+ ldapmodify_ldif(ldif, :add)
107
+ end
108
+
109
+ # Add or update a cn=config entry
110
+ def self.add_or_update_entry(dn, attrs)
111
+ unless dn_exists?(dn)
112
+ ldapadd(dn, attrs)
113
+ return
114
+ end
115
+ ldif = ["dn: #{dn}",
116
+ 'changeType: modify']
117
+ attrs.each do |attr, value|
118
+ ldif << "replace: #{attr}"
119
+ case value
120
+ when Array
121
+ value.each { |vvv| ldif << "#{attr}: #{vvv}" }
122
+ else
123
+ ldif << "#{attr}: #{value}"
124
+ end
125
+ ldif << '-'
126
+ end
127
+ ldapmodify_ldif(ldif)
128
+ end
129
+ end
130
+
131
+ # Tools for interacting with LDAP using a TCP connection
132
+ class LDAPUtils
133
+ def initialize(server, port = 389, auth_dn = nil, password = nil,
134
+ tls_enable = true)
135
+ args = { host: server,
136
+ port: port }
137
+ auth_dn && args.update(auth: { method: :simple,
138
+ username: auth_dn,
139
+ password: password })
140
+ tls_enable && args.update(encryption: { method: :start_tls })
141
+ @ldap = Net::LDAP.new(args)
142
+ @ldap.bind || fail("LDAP bind failed: #{@ldap.get_operation_result}")
143
+ end
144
+
145
+ def self.openssl_extract_cert(rawcert)
146
+ OpenSSL::X509::Certificate.new(rawcert).to_s
147
+ end
148
+
149
+ def self.openssl_extract_key(rawcert, passphrase = nil)
150
+ OpenSSL::PKey.read(rawcert, passphrase).to_s
151
+ end
152
+
153
+ # Generate a rfc2307 SSHA-hashed password in the format that slapd likes
154
+ # http://www.openldap.org/faq/data/cache/347.html
155
+ def self.ssha_password(clear_password, salt = nil)
156
+ unless salt
157
+ len = 16
158
+ base = 16
159
+ # rubocop:disable Style/SingleLineBlockParams
160
+ salt = len.times.reduce('') { |a| a << rand(base).to_s(base) }
161
+ end
162
+ digest = Digest::SHA1.digest("#{clear_password}#{salt}")
163
+ hash = Base64.encode64("#{digest}#{salt}").chomp
164
+ "{SSHA}#{hash}"
165
+ end
166
+
167
+ def self.first_item(dn)
168
+ m = dn.match(/^([^=]+)=([^,]+)/)
169
+ { m[1] => m[2] }
170
+ end
171
+
172
+ def dn_exists?(dn)
173
+ @ldap.search(base: dn,
174
+ scope: Net::LDAP::SearchScope_BaseObject,
175
+ return_result: false)
176
+ end
177
+
178
+ # Add an entry to the directory
179
+ # dn: Distinguished name of the new entry
180
+ # attrs: hash of entry attributes
181
+ def add_entry(dn, attrs)
182
+ # Chef::Log.info "Add LDAP entry: #{dn} #{attrs}"
183
+ @ldap.add(dn: dn, attributes: attrs) ||
184
+ fail("LDAP add failure: #{@ldap.get_operation_result}")
185
+ end
186
+
187
+ # Add or update a directory entry.
188
+ # If the entry exists, any attributes provided to this function will be
189
+ # updated with a "replace" operation, which will add the attributes if
190
+ # necessary, and replace all values of the attribute otherwise.
191
+ def add_or_update_entry(dn, attrs)
192
+ ret = @ldap.search(base: dn,
193
+ scope: Net::LAP::SearchScope_BaseObject,
194
+ return_result: true)
195
+ entries && entries.size > 1 && fail("#{dn} matches more than one entry")
196
+ if ret.nil?
197
+ add_entry(dn, attrs)
198
+ else
199
+ entry = ret.first
200
+ ops = attrs.each_with_object(Array.new) do |(k, v), acc|
201
+ acc << [:replace, k, v] unless entry[k] == [v].flatten
202
+ end
203
+ # Chef::Log.info("Update LDAP entry: dn=#{dn}")
204
+ @ldap.modify(dn: dn, operations: ops) ||
205
+ fail("LDAP update failure: #{@ldap.get_operation_result}")
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,2 @@
1
+ # -*- ruby encoding: utf-8 -*-
2
+ require 'cloudscaling/ldap'
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cs-ruby-ldap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Benjamin Staffin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: net-ldap
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mixlib-shellout
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.4'
41
+ description: Cloudscaling LDAP tools and helpers
42
+ email: ben@cloudscaling.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - README.rdoc
48
+ - lib/cloudscaling/ldap.rb
49
+ - lib/cs_ruby_ldap.rb
50
+ homepage: http://pd.cloudscaling.com
51
+ licenses:
52
+ - Apache 2.0
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '2.0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.2.2
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Cloudscaling LDAP tools and helpers
74
+ test_files: []