adap 0.0.10

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
+ SHA256:
3
+ metadata.gz: 3a74e61b5e3e18366156d599bc06934cac304203d0160874850be5dd0726a533
4
+ data.tar.gz: 44ae5471294aa70674cca0bcb8123f1ade2bc458b6e1f550cf4af1eaa4a56ee1
5
+ SHA512:
6
+ metadata.gz: b68ffd625a78bc5cd5f1180ff411c963e6dfd2cde48f4a32df30f837d601d5471a9e8779f6dc307e838e4cef9a55bf6bc4e0b295def2fe27dbb6f67aa2d4ec4f
7
+ data.tar.gz: ae1eda60ee121c4b9c44fba035385955bd447e26f66420a47233058b15626cf63a418d2018d738dc929fa878f48eff211c110c291f995a6e6fad0a97e12b1dc6
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ adap-*.gem
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.5
7
+ before_install: gem install bundler -v 2.0.2
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in adap.gemspec
4
+ gemspec
5
+
6
+ gem "unix-crypt", "~> 1.3"
7
+
8
+ gem "net-ldap", "~> 0.16.2"
9
+
10
+ gem "mocha", "~> 1.10"
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ adap (0.0.7)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.14.0)
10
+ mocha (1.11.2)
11
+ net-ldap (0.16.2)
12
+ rake (10.5.0)
13
+ unix-crypt (1.3.0)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ adap!
20
+ bundler (~> 2.0)
21
+ minitest (~> 5.0)
22
+ mocha (~> 1.10)
23
+ net-ldap (~> 0.16.2)
24
+ rake (~> 10.0)
25
+ unix-crypt (~> 1.3)
26
+
27
+ BUNDLED WITH
28
+ 2.1.4
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Adap
2
+ Adap is a program that synchronize user data on Samba Active Directory (AD) to LDAP.
3
+
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'adap'
10
+ ```
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install adap
19
+
20
+ ## Usage
21
+
22
+ To build this modules, run the command like below.
23
+
24
+ ```
25
+ gem build adap.gemspec
26
+ ```
27
+
28
+ Then include this module and use it like below.
29
+
30
+ ```ruby
31
+ require "adap"
32
+
33
+ adap = Adap.new({
34
+ :ad_host => "localhost", # Host name or IP of your Active Directory(AD)
35
+ :ad_binddn => "CN=Administrator,CN=Users,DC=mysite,DC=example,DC=com", # Bind dn of your AD
36
+ :ad_basedn => "CN=Users,DC=mysite,DC=example,DC=com", # Base dn of your AD
37
+ :ad_password => "ad_secret", # Password of your AD's bind dn
38
+ :ldap_host => "ldap_server", # Host name or IP of your LDAP
39
+ :ldap_binddn => "uid=Administrator,ou=Users,dc=mysite,dc=example,dc=com", # Bind dn of your LDAP
40
+ :ldap_basedn => "dc=mysite,dc=example,dc=com", # Base dn of your LDAP
41
+ :ldap_password => "ldap_secret" # Password of your LDAP's bind dn
42
+ })
43
+
44
+ # This operation will synchronize a user taro-suzuki to LDAP from AD
45
+ adap.sync_user("taro-suzuki")
46
+ ```
47
+
48
+ ## Requirements and limitations
49
+
50
+ This program has some requirements and limitations like below.
51
+
52
+ ### Not all attributes are synchronized
53
+
54
+ Data synchronized to LDAP from AD are limited such as dn, cn uid and uidNumber etc.
55
+ These attributes are enough to authenticate users to login to Unix-like systems that used an LDAP for authenticating users.
56
+
57
+ ### AD must be set not to require strong auth
58
+
59
+ AD must allow setting `ldap server require strong auth = no` for getting user data.
60
+
61
+ * smb.conf of your AD
62
+ ```
63
+ ldap server require strong auth = no
64
+ ```
65
+
66
+ This program will fail to get user data from AD if you did not allow this setting.
67
+
68
+ ### AD must allow CryptSHA256 or CryptSHA512 to store password and they have to be same as a storing method in LDAP
69
+
70
+ AD must allow storing password as CryptSHA256 or CryptSHA512 by setting smb.conf like below.
71
+
72
+ * your AD's smb.conf
73
+ ```
74
+ password hash userPassword schemes = CryptSHA256 CryptSHA512
75
+ ```
76
+
77
+ And LDAP have to be configured to store password as sha256(CryptSHA256) or sha512(CryptSHA512).
78
+
79
+ For example, you use OpneLDAP, you have to set configuration like below when you store password as sha256.
80
+
81
+ ```
82
+ $ ldapmodify -Y EXTERNAL -H ldapi:/// << 'EOF'
83
+ dn: cn=config
84
+ add: olcPasswordHash
85
+ olcPasswordHash: {CRYPT}
86
+ -
87
+ add: olcPasswordCryptSaltFormat
88
+ olcPasswordCryptSaltFormat: $5$%.16s
89
+ EOF
90
+ ```
91
+
92
+ This instruction allows us to save password as sha256 with a salt that length is 16 characters.
93
+ Or you can store user's password as sha512 with a salt that length is 16 characters like below.
94
+
95
+ ```
96
+ $ ldapmodify -Y EXTERNAL -H ldapi:/// << 'EOF'
97
+ dn: cn=config
98
+ add: olcPasswordHash
99
+ olcPasswordHash: {CRYPT}
100
+ -
101
+ add: olcPasswordCryptSaltFormat
102
+ olcPasswordCryptSaltFormat: $6$%.16s
103
+ EOF
104
+ ```
105
+
106
+ ### This program must be located in AD server
107
+
108
+ This program must be located in AD server because samba-tool on AD only support getting hashed password only from `ldapi://` or `tdb://`.
109
+
110
+ ### This program only supports syncing user data. Syncing group data does not support yet
111
+
112
+ Syncing group data might be supported and implemented if some people demand it.
113
+
114
+ ## Development
115
+
116
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
117
+
118
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
119
+
120
+ ## Contributing
121
+
122
+ Bug reports and pull requests are welcome on GitHub at https://github.com/TsutomuNakamura/adap.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/adap.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "adap/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "adap"
7
+ spec.version = ModAdap::VERSION
8
+ spec.authors = ["Tsutomu Nakamura"]
9
+ spec.email = ["tsuna.0x00@gmail.com"]
10
+
11
+ spec.summary = %q{LDAP migration tool from AD to NT schema}
12
+ spec.description = %q{This tool migrate AD LDAP entries to NT LDAP entries.}
13
+ spec.homepage = "https://github.com/TsutomuNakamura/adap"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 2.0"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "adap"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,21 @@
1
+ version: '3'
2
+
3
+ services:
4
+ ruby:
5
+ container_name: ad-ruby
6
+ image: tsutomu/adap:docker_bundle_v2.1.2
7
+ hostname: ruby
8
+ depends_on:
9
+ - nt
10
+ entrypoint:
11
+ - /opt/entrypoint.sh
12
+ volumes:
13
+ - .:/opt/app
14
+ privileged: true
15
+
16
+ nt:
17
+ container_name: nt
18
+ hostname: nt
19
+ image: tsutomu/docker-samba-nt
20
+ privileged: true
21
+
@@ -0,0 +1,13 @@
1
+ FROM tsutomu/docker-samba-ad
2
+ LABEL maintainer "Tsutomu Nakamura<tsuna.0x00@gmail.com>"
3
+
4
+ RUN DEBIAN_FRONTEND=noninteractive apt-get update \
5
+ && DEBIAN_FRONTEND=noninteractive apt-get -y install \
6
+ curl ruby-full ruby-bundler \
7
+ && gem update --system \
8
+ && rm -rf /usr/share/rubygems-integration/all/specifications/rake-* \
9
+ && apt-get clean
10
+
11
+ ENV LC_ALL=C.UTF-8
12
+ ENV LANG=C.UTF-8
13
+
@@ -0,0 +1,14 @@
1
+ # For testing
2
+ after the container up, you can test connectivity by executing command like below.
3
+
4
+ ```
5
+ ldapsearch -x -LLL -o 'ldif-wrap=no' -h ad -w "p@ssword0" \
6
+ -D "CN=Administrator,CN=Users,DC=mysite,DC=example,DC=com" \
7
+ -b "CN=Users,DC=mysite,DC=example,DC=com" \
8
+ '(objectCategory=CN=Person,CN=Schema,CN=Configuration,DC=mysite,DC=example,DC=com)'
9
+ ```
10
+
11
+ ```
12
+ samba-tool user getpassword taro-suzuki --attribute virtualCryptSHA512
13
+ ```
14
+
File without changes
data/lib/adap/adap.rb ADDED
@@ -0,0 +1,367 @@
1
+ require 'net-ldap'
2
+
3
+ class Adap
4
+
5
+ # :unixhomedirectory and :homedirectory are the attributes that has same meaning between AD and LDAP.
6
+ USER_REQUIRED_ATTRIBUTES = [:cn, :sn, :uid, :uidnumber, :gidnumber, :displayname, :loginshell, :gecos, :givenname, :unixhomedirectory, :homedirectory]
7
+ #USER_REQUIRED_ATTRIBUTES = ['cn', 'sn', 'uid', 'uidNumber', 'gidNumber', 'homeDirectory', 'loginShell', 'gecos', 'givenName']
8
+ GROUP_OF_USER_REQUIRED_ATTRIBUTES = [:objectclass, :gidnumber, :cn, :description, :memberuid]
9
+
10
+ #
11
+ # params {
12
+ # :ad_host required IP or hostname of AD.
13
+ # :ad_port optional (default:389) Port of AD host.
14
+ # :ad_binddn required Binddn of AD.
15
+ # :ad_basedn required Basedn of AD users.
16
+ # :ad_password optional (default:(empty)) Password of AD with :ad_binddn.
17
+ # :ldap_host required IP or hostname of NT.
18
+ # :ldap_port optional (default:389) Port of NT host.
19
+ # :ldap_binddn required Binddn of NT.
20
+ # :ldap_basedn required Basedn of NT users.
21
+ # :ldap_password optional (default:(same as :ad_password)) Password of NT with :ldap_binddn
22
+ # :password_hash_algorithm (default:virtualCryptSHA512) Password hash algorithm. It must be same between AD and LDAP, and only support virtualCryptSHA512 or virtualCryptSHA256 now.
23
+ # }
24
+ #
25
+ def initialize(params)
26
+ raise "Initialize Adap was failed. params must not be nil" if params == nil
27
+ #raise 'Adap requires keys of parameter "ad_host" "ad_binddn" "ad_basedn"' \
28
+ [:ad_host, :ad_binddn, :ad_basedn, :ldap_host, :ldap_binddn, :ldap_basedn].each { |k|
29
+ raise 'Adap requires keys in params ":ad_host", ":ad_binddn", ":ad_basedn", ":ldap_host", ":ldap_binddn", ":ldap_basedn"' if !params.key?(k)
30
+ }
31
+
32
+ @ad_host = params[:ad_host]
33
+ @ad_port = (params[:ad_port] ? params[:ad_port] : 389)
34
+ @ad_binddn = params[:ad_binddn]
35
+ @ad_basedn = params[:ad_basedn]
36
+ @ad_auth = (params.has_key?(:ad_password) ? { :method => :simple, :username => @ad_binddn, :password => params[:ad_password] } : nil)
37
+ @ldap_host = params[:ldap_host]
38
+ @ldap_port = (params[:ldap_port] ? params[:ldap_port] : 389)
39
+ @ldap_binddn = params[:ldap_binddn]
40
+ @ldap_basedn = params[:ldap_basedn]
41
+ @ldap_user_basedn = params[:ldap_user_basedn]
42
+ @ldap_auth = (params.has_key?(:ldap_password) ? { :method => :simple, :username => @ldap_binddn, :password => params[:ldap_password] } : nil )
43
+ @password_hash_algorithm = (params[:password_hash_algorithm] ? params[:password_hash_algorithm] : 'virtualCryptSHA512')
44
+
45
+ @ad_client = Adap::get_ad_client_instance(@ad_host, @ad_port, @ad_auth)
46
+ @ldap_client = Adap::get_ldap_client_instance(@ldap_host, @ldap_port, @ldap_auth)
47
+ end
48
+
49
+ def self.get_ad_client_instance(ad_host, ad_port, ad_auth)
50
+ Net::LDAP.new(:host => ad_host, :port => ad_port, :auth => ad_auth)
51
+ end
52
+
53
+ def self.get_ldap_client_instance(ldap_host, ldap_port, ldap_auth)
54
+ Net::LDAP.new(:host => ldap_host, :port => ldap_port, :auth => ldap_auth)
55
+ end
56
+
57
+ def get_ad_dn(username)
58
+ "CN=#{username},CN=Users,#{@ad_basedn}"
59
+ end
60
+
61
+ def get_ldap_dn(username)
62
+ "uid=#{username},ou=Users,#{@ldap_basedn}"
63
+ end
64
+
65
+ def create_ldap_attributes(entry)
66
+ attributes = {
67
+ :objectclass => ["top", "person", "organizationalPerson", "inetOrgPerson", "posixAccount", "shadowAccount"]
68
+ }
69
+
70
+ entry.each do |attribute, values|
71
+ # Change string to lower case symbols to compare each attributes correctly
72
+ attribute = attribute.downcase.to_sym
73
+
74
+ if USER_REQUIRED_ATTRIBUTES.include?(attribute) then
75
+ if attribute == :unixhomedirectory then
76
+ attributes[:homedirectory] = values
77
+ else
78
+ attributes[attribute] = values
79
+ end
80
+ end
81
+ end
82
+
83
+ attributes
84
+ end
85
+
86
+ def get_password(username)
87
+ password = get_raw_password(username, @password_hash_algorithm)
88
+
89
+ if password == nil || password.empty?
90
+ raise "Failed to get password of #{username} from AD. Did you enabled AD password option virtualCryptSHA512 and/or virtualCryptSHA256?"
91
+ end
92
+ password = password.chomp
93
+
94
+ password
95
+ end
96
+
97
+ def get_raw_password(username, algo)
98
+ `samba-tool user getpassword #{username} --attribute #{algo} 2> /dev/null | grep -E '^virtualCrypt' -A 1 | tr -d ' \n' | cut -d ':' -f 2`
99
+ end
100
+
101
+ def sync_user(uid)
102
+ ad_entry = nil
103
+ ldap_entry = nil
104
+ ad_dn = get_ad_dn(uid)
105
+ ldap_dn = get_ldap_dn(uid)
106
+
107
+ # dn: CN=user-name,CN=Users,DC=mysite,DC=example,DC=com
108
+ @ad_client.search(:base => ad_dn) do |entry|
109
+ ad_entry = entry
110
+ end
111
+ ret_code = @ad_client.get_operation_result.code
112
+
113
+ return {
114
+ :code => ret_code,
115
+ :operations => nil,
116
+ :message => "Failed to get a user #{ad_dn} from AD - " + @ad_client.get_operation_result.error_message
117
+ } if ret_code != 0 && ret_code != 32
118
+
119
+ @ldap_client.search(:base => ldap_dn) do |entry|
120
+ ldap_entry = entry
121
+ end
122
+ ret_code = @ldap_client.get_operation_result.code
123
+
124
+ return {
125
+ :code => ret_code,
126
+ :operations => nil,
127
+ :message => "Failed to get a user #{ldap_dn} from LDAP - " + @ldap_client.get_operation_result.error_message
128
+ } if ret_code != 0 && ret_code != 32
129
+
130
+ ret = nil
131
+ if !ad_entry.nil? and ldap_entry.nil? then
132
+ ret = add_user(ldap_dn, ad_entry, get_password(uid))
133
+ elsif ad_entry.nil? and !ldap_entry.nil? then
134
+ ret = delete_user(ldap_dn)
135
+ elsif !ad_entry.nil? and !ldap_entry.nil? then
136
+ ret = modify_user(ldap_dn, ad_entry, ldap_entry, get_password(uid))
137
+ else
138
+ # ad_entry.nil? and ldap_entry.nil? then
139
+ return {:code => 0, :operations => nil, :message => "There are not any data of #{uid} to sync."}
140
+ end
141
+
142
+ return ret if ret[:code] != 0
143
+
144
+ # Sync groups belonging the user next if syncing data of the user has succeeded.
145
+ ret_sync_group = sync_group_of_user(uid, get_primary_gidnumber(ad_entry))
146
+
147
+ return {
148
+ :code => ret_sync_group[:code],
149
+ :operations => ret[:operations].concat(ret_sync_group[:operations]),
150
+ :message => (
151
+ ret_sync_group[:code] == 0 ?
152
+ nil : "Syncing a user #{uid} has succeeded but syncing its groups have failed. Message: " + ret_sync_group[:message]
153
+ )
154
+ }
155
+ end
156
+
157
+ def add_user(ldap_user_dn, ad_entry, password)
158
+ attributes = create_ldap_attributes(ad_entry)
159
+
160
+ @ldap_client.add(
161
+ :dn => ldap_user_dn,
162
+ :attributes => attributes
163
+ )
164
+ ret_code = @ldap_client.get_operation_result.code
165
+
166
+ return {
167
+ :code => ret_code,
168
+ :operations => [:add_user],
169
+ :message => "Failed to add a user #{ldap_user_dn} in add_user() - " + @ldap_client.get_operation_result.error_message
170
+ } if ret_code != 0
171
+
172
+ @ldap_client.modify(
173
+ :dn => ldap_user_dn,
174
+ :operations => [
175
+ [:add, :userPassword, password]
176
+ ]
177
+ )
178
+ ret_code = @ldap_client.get_operation_result.code
179
+
180
+ return {
181
+ :code => ret_code,
182
+ :operations => [:add_user],
183
+ :message => "Failed to modify a user #{ldap_user_dn} in add_user() - " + @ldap_client.get_operation_result.error_message
184
+ } if ret_code != 0
185
+
186
+ return {:code => ret_code, :operations => [:add_user], :message => nil}
187
+ end
188
+
189
+ def modify_user(ldap_user_dn, ad_entry, ldap_entry, password)
190
+ # An attribute objectClass will not be sync because it assumed already added by add_user() function or another method in LDAP.
191
+ operations = create_modify_operations(ad_entry, ldap_entry, password)
192
+
193
+ @ldap_client.modify(
194
+ :dn => ldap_user_dn,
195
+ :operations => operations
196
+ )
197
+ ret_code = @ldap_client.get_operation_result.code
198
+
199
+ return {
200
+ :code => ret_code,
201
+ :operations => [:modify_user],
202
+ :message => "Failed to modify a user #{ldap_user_dn} in modify_user() - " + @ldap_client.get_operation_result.error_message
203
+ } if ret_code != 0
204
+
205
+ return {:code => ret_code, :operations => [:modify_user], :message => nil}
206
+ end
207
+
208
+ def create_modify_operations(ad_entry, ldap_entry, password)
209
+ operations = []
210
+
211
+ ad_entry.each do |key, value|
212
+ ad_key_sym = key.downcase.to_sym
213
+ ldap_key = (key != "unixHomeDirectory" ? key : "homeDirectory")
214
+ ldap_key_sym = ldap_key.downcase.to_sym
215
+
216
+ if USER_REQUIRED_ATTRIBUTES.include?(ad_key_sym)
217
+ next if value == ldap_entry[ldap_key]
218
+ operations.push((ldap_entry[ldap_key] != nil ? [:replace, ldap_key_sym, value] : [:add, ldap_key_sym, value]))
219
+ end
220
+ end
221
+
222
+ ldap_entry.each do |key, value|
223
+ ldap_key_sym = key.downcase.to_sym
224
+ ad_key = (key != "homeDirectory" ? key : "unixHomeDirectory")
225
+
226
+ if USER_REQUIRED_ATTRIBUTES.include?(ldap_key_sym)
227
+ operations.push([:delete, ldap_key_sym, nil]) if ad_entry[ad_key] == nil
228
+ end
229
+ end
230
+
231
+ # AD does not have password as simple ldap attribute.
232
+ # So password will always be updated for this reason.
233
+ operations.push([:replace, :userpassword, password])
234
+
235
+ operations
236
+ end
237
+
238
+ def delete_user(ldap_user_dn)
239
+ @ldap_client.delete(:dn => ldap_user_dn)
240
+ ret_code = @ldap_client.get_operation_result.code
241
+
242
+ return {
243
+ :code => ret_code,
244
+ :operations => [:delete_user],
245
+ :message => "Failed to delete a user #{ldap_user_dn} in delete_user() - " + @ldap_client.get_operation_result.error_message
246
+ } if ret_code != 0
247
+
248
+ return {:code => ret_code, :operations => [:delete_user], :message => nil}
249
+ end
250
+
251
+ def sync_group_of_user(uid, primary_gid)
252
+ ad_group_map = {}
253
+ ldap_group_map = {}
254
+
255
+ # Creating AD ldapsearch filter
256
+
257
+ ad_filter = if primary_gid == nil then
258
+ Net::LDAP::Filter.construct(
259
+ "(&(objectCategory=CN=Group,CN=Schema,CN=Configuration,#{@ad_basedn})(member=CN=#{uid},CN=Users,#{@ad_basedn}))")
260
+ else
261
+ Net::LDAP::Filter.construct(
262
+ "(&(objectCategory=CN=Group,CN=Schema,CN=Configuration,#{@ad_basedn})(|(member=CN=#{uid},CN=Users,#{@ad_basedn})(gidNumber=#{primary_gid})))")
263
+ end
264
+
265
+ # Get groups from AD
266
+ @ad_client.search(:base => @ad_basedn, :filter => ad_filter) do |entry|
267
+ ad_group_map[entry[:name]] = nil
268
+ end
269
+ ret_code = @ad_client.get_operation_result.code
270
+
271
+ return {
272
+ :code => ret_code,
273
+ :operations => [:search_groups_from_ad],
274
+ :message => "Failed to get groups of a user #{uid} from AD to sync them. " + @ad_client.get_operation_result.error_message
275
+ } if ret_code != 0
276
+
277
+ # Create LDAP ldapsearch filter
278
+ ldap_filter = Net::LDAP::Filter.construct("(memberUid=#{uid})")
279
+
280
+ # Get groups from LDAP
281
+ @ldap_client.search(:base => "ou=Users," + @ldap_basedn, :filter => ldap_filter) do |entry|
282
+ ldap_group_map[entry[:cn]] = nil
283
+ end
284
+ ret_code = @ldap_client.get_operation_result.code
285
+
286
+ return {
287
+ :code => ret_code,
288
+ :operations => [:search_groups_from_ldap],
289
+ :message => "Failed to get groups of a user #{uid} from LDAP to sync them. " + @ldap_client.get_operation_result.error_message
290
+ } if ret_code != 0
291
+
292
+ # Comparing name of AD's entry and cn of LDAP's entry
293
+ operation_with_dn = create_sync_group_of_user_operation(ad_group_map, ldap_group_map, uid)
294
+
295
+ return {
296
+ :code => 0,
297
+ :operations => [:modify_group_of_user],
298
+ :message => "There are not any groups of user to sync"
299
+ } if operation_with_dn.length == 0
300
+
301
+ # ldap_user_dn = get_ldap_dn(uid)
302
+
303
+ operation_with_dn.each_key do |key|
304
+ @ldap_client.modify({
305
+ :dn => key,
306
+ :operations => operation_with_dn[key]
307
+ })
308
+ ret_code = @ldap_client.get_operation_result.code
309
+
310
+ return {
311
+ :code => ret_code,
312
+ :operations => [:modify_group_of_user],
313
+ :message => "Failed to modify group \"#{key}\" of user #{uid}. " + @ldap_client.get_operation_result.error_message
314
+ } if ret_code != 0
315
+ end
316
+
317
+ return {:code => 0, :operations => [:modify_group_of_user], :message => nil}
318
+ end
319
+
320
+ # {
321
+ # "cn=foo,ou=Groups,dc=mysite,dc=example,dc=com": [
322
+ # [:add, :memberuid, uid]
323
+ # ],
324
+ # "cn=bar,ou=Groups,dc=mysite,dc=example,dc=com": [
325
+ # [:delete, :memberuid, uid]
326
+ # ],
327
+ # "cn=baz,ou=Groups,dc=mysite,dc=example,dc=com": [
328
+ # [:add, :memberuid, uid]
329
+ # ]
330
+ # }
331
+ def create_sync_group_of_user_operation(ad_group_map, ldap_group_map, uid)
332
+ operations_with_dn = {}
333
+
334
+ ad_group_map.each_key do |key|
335
+ operations_with_dn["cn=#{key},ou=Groups,#{@ldap_basedn}"] = [[:add, :memberuid, uid]] if !ldap_group_map.has_key?(key)
336
+ end
337
+
338
+ ldap_group_map.each_key do |key|
339
+ operations_with_dn["cn=#{key},ou=Groups,#{@ldap_basedn}"] = [[:delete, :memberuid, uid]] if !ad_group_map.has_key?(key)
340
+ end
341
+
342
+ operations_with_dn
343
+ end
344
+
345
+ def get_primary_gidnumber(entry)
346
+ return nil if entry == nil
347
+
348
+ if entry[:gidnumber] == nil then
349
+ ad_result = get_primary_gidnumber_from_ad(entry[:uid].first)
350
+ return ad_result
351
+ end
352
+
353
+ return entry[:gidnumber].first
354
+ end
355
+
356
+ def get_primary_gidnumber_from_ad(uid)
357
+ return nil if uid ==nil
358
+
359
+ @ad_client.search(:base => "CN=#{uid},CN=Users,#{@ad_basedn}") do |entry|
360
+ primary_gid = entry[:gidnumber].first
361
+ end
362
+
363
+ return primary_gid
364
+ end
365
+
366
+ end
367
+
@@ -0,0 +1,3 @@
1
+ module ModAdap
2
+ VERSION = "0.0.10"
3
+ end
data/lib/adap.rb ADDED
@@ -0,0 +1,7 @@
1
+ require "adap/version"
2
+ require "adap/adap.rb"
3
+
4
+ module ModAdap
5
+ class Error < StandardError; end
6
+ # Your code goes here...
7
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: adap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.10
5
+ platform: ruby
6
+ authors:
7
+ - Tsutomu Nakamura
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-01-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: This tool migrate AD LDAP entries to NT LDAP entries.
56
+ email:
57
+ - tsuna.0x00@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Gemfile
65
+ - Gemfile.lock
66
+ - README.md
67
+ - Rakefile
68
+ - adap.gemspec
69
+ - bin/console
70
+ - bin/setup
71
+ - docker-compose.yml
72
+ - dockerfile/Dockerfile
73
+ - dockerfile/README.md
74
+ - dockerfile/entrypoint.sh
75
+ - lib/adap.rb
76
+ - lib/adap/adap.rb
77
+ - lib/adap/version.rb
78
+ homepage: https://github.com/TsutomuNakamura/adap
79
+ licenses: []
80
+ metadata:
81
+ homepage_uri: https://github.com/TsutomuNakamura/adap
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.1.2
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: LDAP migration tool from AD to NT schema
101
+ test_files: []