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 +7 -0
- data/README.rdoc +0 -0
- data/lib/cloudscaling/ldap.rb +209 -0
- data/lib/cs_ruby_ldap.rb +2 -0
- metadata +74 -0
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
|
data/lib/cs_ruby_ldap.rb
ADDED
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: []
|