wobaduser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b28543ca5d946a4e698ad38bd4b29c492a7ac1b8
4
+ data.tar.gz: adccab4655ae479b566d8fa8ff23adee912b5011
5
+ SHA512:
6
+ metadata.gz: f97d54312d98d9faa83f77079c4131f4c7aa76f7be52ebf2709d59aa4987b879599a8deff312d7fbbe60f512566e86ed5ea5b5a8fd2a2b3a71cefd0ff777aa0e
7
+ data.tar.gz: 2ffd81aa1db62405afd8eb596c06c27cb123c84ffc99fdc0b02081f6c05284a74c49dacea90492c69f0c42c91fcbcbd04bb46453be9aa7e99023f90415ed5426
@@ -0,0 +1,25 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ .*.swp
24
+ utils/
25
+ .localenv
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in wobaduser.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-2018 Wolfgang Barth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,14 @@
1
+ wobaduser
2
+ =========
3
+
4
+ Lightweight Active Directory access.
5
+
6
+ If you are looking for a full featured solution to access and modify Active Directory, see [https://github.com/ajrkerr/active_directory](https://github.com/ajrkerr/active_directory)
7
+
8
+ Licence
9
+ -------
10
+
11
+ wobauth Copyright (C) 2014-2018 Wolfgang Barth
12
+
13
+ MIT license, see [LICENSE](LICENSE)
14
+
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,5 @@
1
+ class String
2
+ def as_utf8
3
+ self.force_encoding('UTF-8')
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ require "wobaduser/version"
2
+ require 'net/ldap'
3
+ require 'active_support/core_ext/module'
4
+ require 'active_support/core_ext/hash'
5
+ require 'string_addons'
6
+
7
+ module Wobaduser
8
+ autoload :LDAP, 'wobaduser/ldap'
9
+ autoload :Base, 'wobaduser/base'
10
+ autoload :User, 'wobaduser/user'
11
+
12
+ def self.setup
13
+ yield self
14
+ end
15
+
16
+ # timeout for ldap connections
17
+ mattr_accessor :timeout
18
+ @@timeout = 10
19
+ end
@@ -0,0 +1,174 @@
1
+ # additional copyright info:
2
+ # the metaprogramming for generate_single_value_readers and
3
+ # generate_multi_value_readers was published 2008 by Ernie Miller on
4
+ # http://erniemiller.org/2008/04/04/simplified-active-directory-authentication/
5
+ # with an excellent explanation.
6
+ #
7
+ require 'immutable-struct'
8
+
9
+ module Wobaduser
10
+ class Base
11
+ SearchResult = ImmutableStruct.new( :success?, :errors, :entries )
12
+
13
+ ###################################################################
14
+ # ATTR_SV is for single valued attributes only. result is a string.
15
+ # ATTR_MV is for multi valued attributes. result is an array.
16
+ # Concrete values should be set in subclasses. See Wobaduser::User
17
+ # for an example.
18
+ #
19
+ ATTR_SV={}
20
+ ATTR_MV={}
21
+ #
22
+ ###################################################################
23
+
24
+ attr_reader :errors, :entry
25
+
26
+ # Create an new Wobaduser object
27
+ # not to be intended to call directly, but possible. Better to use
28
+ # Wobaduser::User.new or Wobaduser::Group.new. There are to modes:
29
+ # 1) use Wobaduser::LDAP + LDAP-Filter
30
+ # 2) use a retrieved LDAP entry, i.e. used in Wobaduser::Base.search
31
+ #
32
+ # [+:entry+] ldap entry
33
+ # [+:ldap+] instance of Wobaduser::LDAP
34
+ # [+:filter+] ldap filter (as string, *not* as Net::LDAP::Filter)
35
+ # [+:ldap_options+] additional ldap options for search
36
+ #
37
+ # :entry and (:ldap, :filter) are mutually exclusive
38
+ #
39
+ def initialize(options = {})
40
+ options.symbolize_keys!
41
+ keys = options.keys
42
+ if keys.include?(:entry) && (keys & [:ldap, :filter, :ldap_options]).any?
43
+ raise ArgumentError, ":entry and one of (:ldap, :filter, :ldap_options) are mutually exclusive!"
44
+ end
45
+ reset_errors
46
+ get_ldap_entry(options)
47
+ unless entry.nil?
48
+ self.class.class_eval do
49
+ generate_single_value_readers
50
+ generate_multi_value_readers
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.search(options = {})
56
+ search = search_ldap_entries(options)
57
+ if search.success?
58
+ entries = search.entries.map {|entry| self.new(entry: entry)}
59
+ result = SearchResult.new(success: true, errors: [], entries: entries)
60
+ else
61
+ result = SearchResult.new(success: false, errors: search.errors, entries: [])
62
+ end
63
+ end
64
+
65
+ def self.filter
66
+ Net::LDAP::Filter.present('objectClass')
67
+ end
68
+
69
+ def valid?
70
+ @entry.kind_of? Net::LDAP::Entry
71
+ end
72
+
73
+ protected
74
+
75
+ #
76
+ # method generator for single value attributes defined in ATTR_SV
77
+ #
78
+ def self.generate_single_value_readers
79
+ return if ATTR_SV.nil?
80
+ self::ATTR_SV.each_pair do |k, v|
81
+ val, block = Array(v)
82
+ define_method(k) do
83
+ if @entry.attribute_names.include?(val)
84
+ attribute = @entry.send(val)
85
+ attribute = attribute.first if attribute.is_a? Array
86
+ if block.is_a?(Proc)
87
+ final = block[attribute.to_s]
88
+ else
89
+ final = attribute.to_s
90
+ end
91
+ final = final.force_encoding('UTF-8') if final.is_a? String
92
+ return final
93
+ else
94
+ return ''
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ #
101
+ # method generator for multi value attributes defined in ATTR_MV
102
+ #
103
+ def self.generate_multi_value_readers
104
+ return if ATTR_SV.nil?
105
+ self::ATTR_MV.each_pair do |k, v|
106
+ val, block = Array(v)
107
+ define_method(k) do
108
+ if @entry.attribute_names.include?(val)
109
+ if block.is_a?(Proc)
110
+ finals = @entry.send(val).collect(&block)
111
+ else
112
+ finals = @entry.send(val)
113
+ end
114
+ finals = finals.map{|v| v.is_a?(String) ? v.to_s.force_encoding('UTF-8') : v } if finals.is_a? Array
115
+ return finals.compact
116
+ else
117
+ return []
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ def self.search_ldap_entries(options)
124
+ ldap = options.fetch(:ldap)
125
+ if ldap.nil?
126
+ raise "ldap connection not yet available"
127
+ end
128
+ filter = options.fetch(:filter)
129
+ ldap_options = options.fetch(:ldap_options, {}).
130
+ merge(filter: build_filter(filter))
131
+ entries = ldap.search(ldap_options)
132
+ if ldap.errors.any?
133
+ result = SearchResult.new(success: false, errors: ldap.errors, entries: [])
134
+ else
135
+ result = SearchResult.new(success: true, errors: [], entries: entries)
136
+ end
137
+ end
138
+
139
+ def self.build_filter(filter)
140
+ unless filter.kind_of? Net::LDAP::Filter
141
+ filter = Net::LDAP::Filter.construct(filter)
142
+ end
143
+ filter & self.filter
144
+ end
145
+
146
+ protected
147
+
148
+ def add_error(message)
149
+ @errors << message
150
+ end
151
+
152
+ def reset_errors
153
+ @errors = []
154
+ end
155
+
156
+ private
157
+
158
+ def get_ldap_entry(options)
159
+ if options.keys.include?(:entry)
160
+ @entry = options.fetch(:entry)
161
+ reset_errors
162
+ else
163
+ result = Wobaduser::Base.search_ldap_entries(options)
164
+ if result.success?
165
+ @entry = result.entries.first
166
+ else
167
+ add_error(result.errors.join(", "))
168
+ @entry = nil
169
+ end
170
+ end
171
+ end
172
+
173
+ end
174
+ end
@@ -0,0 +1,84 @@
1
+ require 'timeout'
2
+ require 'net/ldap'
3
+
4
+ module Wobaduser
5
+ class LDAP
6
+ attr_reader :ldap_options, :errors
7
+
8
+ # Wobaduser::LDAP.new({ldap_options: {}, bind: true})
9
+ #
10
+ # [+:ldap_options+] for possible ldap options see Net::LDAP::new
11
+ # [+:bind+] true: bind on initialize, false: bind on later operations
12
+ #
13
+ def initialize(options = {})
14
+ options.symbolize_keys!
15
+ reset_errors
16
+ @ldap_options = options.fetch(:ldap_options).symbolize_keys!
17
+ do_bind = options.fetch(:bind, true)
18
+ connection(ldap_options: @ldap_options, bind: do_bind)
19
+ end
20
+
21
+ # execute ldap search operation
22
+ #
23
+ # for possible ldap options see Net::LDAP#search
24
+ #
25
+ def search(options = {})
26
+ reset_errors
27
+ options.symbolize_keys!
28
+ begin
29
+ result = connection.search(options)
30
+ add_error(operation_error)
31
+ rescue => e
32
+ result = []
33
+ add_error(e.message)
34
+ end
35
+ result
36
+ end
37
+
38
+ # returns last ldap operation error, if any
39
+ #
40
+ def operation_error
41
+ (connection.get_operation_result.code == 0) ? nil : connection.get_operation_result
42
+ end
43
+
44
+ private
45
+ attr_reader :connection
46
+
47
+ def add_error(message)
48
+ @errors << message
49
+ end
50
+
51
+ def reset_errors
52
+ @errors = []
53
+ end
54
+
55
+ def connected?
56
+ !!@connected
57
+ end
58
+
59
+ def connection(options ={})
60
+ return @connection if @connected
61
+ @connected = false
62
+ reset_errors
63
+ options.symbolize_keys!
64
+ @connection ||= Net::LDAP.new(options.fetch(:ldap_options))
65
+ if options.fetch(:bind, true)
66
+ begin
67
+ Timeout::timeout(Wobaduser.timeout) {
68
+ if @connection.bind
69
+ @connected = true
70
+ else
71
+ add_error(operation_error)
72
+ end
73
+ }
74
+ rescue Timeout::Error => e
75
+ add_error("Timeout: could not bind to server within #{Wobaduser.timeout} seconds")
76
+ rescue Net::LDAP::Error => e
77
+ add_error(e.message)
78
+ end
79
+ end
80
+ @connection
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,72 @@
1
+ module Wobaduser
2
+ class User < Base
3
+
4
+ ########################################################################
5
+ # ATTR_SV is for single valued attributes only. Generated readers will
6
+ # convert the value to a string before returning or calling your Proc.
7
+
8
+ ATTR_SV = {
9
+ # method name ldap attribute
10
+ :username => :userprincipalname,
11
+ :userprincipalname => :userprincipalname,
12
+ :givenname => :givenname,
13
+ :sn => :sn,
14
+ :cn => :cn,
15
+ :dn => :dn,
16
+ :displayname => :displayname,
17
+ :mail => :mail,
18
+ :title => :title,
19
+ :telephonenumber => :telephonenumber,
20
+ :facsimiletelephonenumber => :facsimiletelephonenumber,
21
+ :mobile => :mobile,
22
+ :description => :description,
23
+ :department => :department,
24
+ :company => :company,
25
+ :postalcode => :postalcode,
26
+ :l => :l,
27
+ :streetaddress => :streetaddress,
28
+ :samaccountname => :samaccountname,
29
+ :primarygroupid => :primarygroupid,
30
+ :guid => [ :objectguid, Proc.new {|p| Base64.encode64(p).chomp } ],
31
+ :useraccountcontrol => :useraccountcontrol,
32
+ :is_valid? => [ :useraccountcontrol, Proc.new {|c| (c.to_i & 2) == 0 } ],
33
+ }
34
+
35
+ # ATTR_MV is for multi-valued attributes. Generated readers will always
36
+ # return an array.
37
+
38
+ ATTR_MV = {
39
+ # method name ldap attribute
40
+ :members => :member,
41
+ :objectclass => :objectclass,
42
+ :groups => [ :memberof,
43
+ # Get the simplified name of first-level groups.
44
+ # TODO: Handle escaped special characters
45
+ Proc.new {|g| g.sub(/.*?CN=(.*?),.*/, '\1')} ],
46
+ # :mailaliases => [ :proxyAddresses, Proc.new{|p| p.lowercase.gsub(/\Asmtp:/,'')}]
47
+ :mailaliases => [ :proxyaddresses, Proc.new {|p|
48
+ p = p.downcase
49
+ next unless p=~ /\Asmtp:/
50
+ p.gsub(/\Asmtp:/, '')
51
+ }],
52
+ }
53
+ #
54
+ ########################################################################
55
+
56
+ def filter(valid = false)
57
+ filter = Net::LDAP::Filter.eq('objectClass', 'user')
58
+ if valid
59
+ filter & ~(Net::LDAP::Filter.ex('UserAccountControl:1.2.840.113556.1.4.803', 2))
60
+ else
61
+ filter
62
+ end
63
+ end
64
+
65
+ def all_groups
66
+ filter = Net::LDAP::Filter.present("cn") & Net::LDAP::Filter.eq("objectClass", "group") &
67
+ Net::LDAP::Filter.ex("member:1.2.840.113556.1.4.1941", @entry.dn)
68
+ @ldap.search(filter: filter, attributes: ['cn']).map(&:cn).flatten.map(&:as_utf8)
69
+ end
70
+ end
71
+ end
72
+
@@ -0,0 +1,4 @@
1
+ module Wobaduser
2
+ VERSION = "1.0.0"
3
+ RELEASEDATE = "2018-07-21"
4
+ end
@@ -0,0 +1,15 @@
1
+ LDAP_HOST=
2
+ LDAP_PORT=
3
+ LDAP_BASE=
4
+ LDAP_USER=
5
+ LDAP_PASSWD=
6
+ # SN must match UPN2 & UPN3
7
+ # GIVENNAME must match UPN2 & UPN3
8
+ # EMAIL must match UPN2
9
+ USERPRINCIPALNAME=
10
+ USERPRINCIPALNAME2=
11
+ USERPRINCIPALNAME3=
12
+ LDAP_SEARCH_SN=
13
+ LDAP_SEARCH_GIVENNAME=
14
+ LDAP_SEARCH_EMAIL=
15
+
@@ -0,0 +1,78 @@
1
+ require 'timeout'
2
+ require 'spec_helper'
3
+
4
+ describe 'LdapSetup' do
5
+ context "with dummy options" do
6
+ let(:ldap_options) {{ "host" => '127.0.0.1', "base" => 'dc=example,dc=com', :port => 3268}}
7
+ let(:ldap) { Wobaduser::LDAP.new(ldap_options: ldap_options, bind: false) }
8
+ let(:filter) { Net::LDAP::Filter.eq("userprincipalname", "doesnotexist") }
9
+
10
+ it "set ldap options as symbols" do
11
+ expect(ldap.ldap_options).to be_a_kind_of Hash
12
+ opts = ldap.ldap_options
13
+ expect(opts).to include(:host, :base, :port)
14
+ expect(opts).not_to include("host", "base", "port")
15
+ end
16
+
17
+ it { expect(ldap).to respond_to(:search) }
18
+ it { expect(ldap).to respond_to(:errors) }
19
+ it { expect(ldap).not_to respond_to(:connection) }
20
+ it { expect(ldap).not_to respond_to(:connected?) }
21
+
22
+ it "ldap search doesn't raise an error" do
23
+ expect{
24
+ ldap.search(filter: filter)
25
+ }.not_to raise_error
26
+ end
27
+
28
+ it "ldap search delivers some errors" do
29
+ ldap.search(filter: filter)
30
+ expect(ldap.errors.any?).to be_truthy
31
+ end
32
+
33
+ it "connect to a existing host without ldap server should get connection refused" do
34
+ ldap = nil
35
+ Wobaduser.timeout = 2
36
+ expect {
37
+ ldap = Wobaduser::LDAP.new(ldap_options: ldap_options, bind: true)
38
+ }.not_to raise_error
39
+ expect(ldap.errors.any?).to be_truthy
40
+ expect(ldap.errors.join(" ")).to match /Connection refused/
41
+ end
42
+
43
+ it "connect to a nonexistent host should timeout" do
44
+ Wobaduser.timeout = 2
45
+ ldap_options['host'] = '1.2.3.4'
46
+ ldap = Wobaduser::LDAP.new(ldap_options: ldap_options, bind: true)
47
+ expect(ldap.errors.join(" ")).to match /Timeout: could not bind to server/
48
+ end
49
+ end
50
+
51
+ context "with real environment" do
52
+ let(:ldap_options) { {
53
+ host: ENV['LDAP_HOST'],
54
+ base: ENV['LDAP_BASE'],
55
+ port: ENV['LDAP_PORT'],
56
+ auth: {
57
+ method: :simple,
58
+ username: ENV['LDAP_USER'],
59
+ password: ENV['LDAP_PASSWD'],
60
+ }
61
+ } }
62
+ let(:ldap) { Wobaduser::LDAP.new(ldap_options: ldap_options, bind: true) }
63
+
64
+ it "Wobaduser::LDAP should delegate search" do
65
+ expect(ldap).to respond_to(:search)
66
+ end
67
+
68
+ it "search should return Net::LDAP::Entries" do
69
+ filter = Net::LDAP::Filter.eq("userprincipalname", ENV['USERPRINCIPALNAME'])
70
+ entry = ldap.search(filter: filter).first
71
+ expect(ldap.errors.any?).to be_falsey
72
+ expect(entry).to be_a_kind_of Net::LDAP::Entry
73
+ expect(entry).to respond_to(:userprincipalname)
74
+ expect(entry.userprincipalname).to include(ENV['USERPRINCIPALNAME'])
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,26 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'wobaduser'
5
+ require 'dotenv'
6
+
7
+ Dotenv.load( File.expand_path(__FILE__ + '/../.localenv'),
8
+ File.expand_path(__FILE__ + '/../.env'))
9
+
10
+ RSpec.configure do |config|
11
+ config.run_all_when_everything_filtered = true
12
+ # config.filter_run :focus
13
+ config.mock_framework = :rspec
14
+
15
+ config.around(:each) do |example|
16
+ Timeout::timeout(Wobaduser.timeout + 1) {
17
+ example.run
18
+ }
19
+ end
20
+
21
+ # Run specs in random order to surface order dependencies. If you find an
22
+ # order dependency and want to debug it, you can fix the order by providing
23
+ # the seed, which is printed after each run.
24
+ # --seed 1234
25
+ config.order = 'random'
26
+ end
@@ -0,0 +1,126 @@
1
+ require 'timeout'
2
+ require 'spec_helper'
3
+
4
+ describe 'User' do
5
+ context "with different options on #new" do
6
+ let(:ldap_options) {{"host" => '127.0.0.1', "base" => 'dc=example,dc=com', :port => 3268}}
7
+ let(:ldap) { Wobaduser::LDAP.new(ldap_options: ldap_options) }
8
+ let(:entry) { instance_double("Net::LDAP::Entry") }
9
+ let(:filter) { Net::LDAP::Filter.eq("userprincipalname", "doesnotexist") }
10
+
11
+ it ":entry does not raise an ArgumentError" do
12
+ expect {
13
+ Wobaduser::User.new(entry: entry)
14
+ }.not_to raise_error
15
+ end
16
+
17
+ it ":ldap + :filter does not raise an ArgumentError" do
18
+ expect {
19
+ Wobaduser::User.new(ldap: ldap, filter: filter)
20
+ }.not_to raise_error
21
+ end
22
+
23
+ [:ldap, :filter, :ldap_options].each do |option|
24
+ it ":entry and #{option} raises an ArgumentError" do
25
+ expect {
26
+ Wobaduser::User.new(entry: entry, option => nil)
27
+ }.to raise_error(ArgumentError)
28
+ end
29
+ end
30
+ end
31
+
32
+ context "with dummy options" do
33
+ let(:ldap_options) {{"host" => '127.0.0.1', "base" => 'dc=example,dc=com', :port => 3268}}
34
+ let(:ldap) { Wobaduser::LDAP.new(ldap_options: ldap_options, bind: false) }
35
+ let(:user) { Wobaduser::User.new(ldap: ldap, filter: filter) }
36
+ let(:filter) { Net::LDAP::Filter.eq("userprincipalname", "doesnotexist") }
37
+
38
+ it { expect(ldap.errors.any?).to be_falsey }
39
+
40
+ it { expect { user }.not_to raise_error }
41
+ it { expect(user.errors.any?).to be_truthy }
42
+ end
43
+
44
+ context "with real environment" do
45
+ let(:ldap_options) {{
46
+ host: ENV['LDAP_HOST'],
47
+ base: ENV['LDAP_BASE'],
48
+ port: ENV['LDAP_PORT'],
49
+ auth: {
50
+ method: :simple,
51
+ username: ENV['LDAP_USER'],
52
+ password: ENV['LDAP_PASSWD'],
53
+ }
54
+ }}
55
+ let(:ldap) {Wobaduser::LDAP.new(ldap_options: ldap_options, bind: true)}
56
+
57
+ context "and valid USERPRINCIPALNAME" do
58
+ let(:filter) {Net::LDAP::Filter.eq("userprincipalname", ENV['USERPRINCIPALNAME'])}
59
+
60
+ it "valid user should respond to various attribute methods" do
61
+ user = Wobaduser::User.new(ldap: ldap, filter: filter)
62
+ expect(user.valid?).to be_truthy
63
+ expect(user).to respond_to(:userprincipalname)
64
+ expect(user.userprincipalname).to include(ENV['USERPRINCIPALNAME'])
65
+ Wobaduser::User::ATTR_SV.each do |key,value|
66
+ expect(user.send(key)).to be_a_kind_of String unless key == :is_valid?
67
+ end
68
+ Wobaduser::User::ATTR_MV.each do |key,value|
69
+ expect(user.send(key)).to be_a_kind_of Array
70
+ end
71
+ end
72
+ end
73
+
74
+ context "and invalid USERPRINCIPALNAME" do
75
+ let(:filter) {Net::LDAP::Filter.eq("userprincipalname", "doesnotexist")}
76
+
77
+ it "valid user should respond to various attribute methods" do
78
+ user = Wobaduser::User.new(ldap: ldap, filter: filter)
79
+ expect(user.valid?).to be_falsey
80
+ end
81
+ end
82
+
83
+ describe "::search" do
84
+ let(:entries) { Wobaduser::User.search(ldap: ldap, filter: filter) }
85
+ let(:users) { entries.entries }
86
+
87
+ context "search for sn" do
88
+ let(:filter) {Net::LDAP::Filter.eq("sn", "#{ENV['LDAP_SEARCH_SN']}*")}
89
+
90
+ it { expect(entries.success?).to be_truthy }
91
+ it { expect(users).to be_a_kind_of Array }
92
+ it { expect(users.map {|u| u.userprincipalname}).to include(
93
+ ENV['USERPRINCIPALNAME3'], ENV['USERPRINCIPALNAME2']) }
94
+ end
95
+
96
+ context "search for givenname" do
97
+ let(:filter) {Net::LDAP::Filter.eq("givenname", ENV['LDAP_SEARCH_GIVENNAME'])}
98
+
99
+ it { expect(entries.success?).to be_truthy }
100
+ it { expect(users).to be_a_kind_of Array }
101
+ it { expect(users.map {|u| u.userprincipalname}).to include(
102
+ ENV['USERPRINCIPALNAME3'], ENV['USERPRINCIPALNAME2']) }
103
+ end
104
+
105
+ context "search for mail" do
106
+ let(:filter) {Net::LDAP::Filter.eq("mail", ENV['LDAP_SEARCH_EMAIL'])}
107
+
108
+ it { expect(entries.success?).to be_truthy }
109
+ it { expect(users).to be_a_kind_of Array }
110
+ it { expect(users.map {|u| u.userprincipalname}).to include(
111
+ ENV['USERPRINCIPALNAME2']) }
112
+ end
113
+
114
+ context "with invalid filter" do
115
+ let(:filter) {"bla"}
116
+ let(:entries) { Wobaduser::User.search(ldap: ldap, filter: filter) }
117
+
118
+ it "raises a FilterSyntaxInvalidError" do
119
+ expect {
120
+ entries.success?
121
+ }.to raise_error(Net::LDAP::FilterSyntaxInvalidError)
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'wobaduser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "wobaduser"
8
+ spec.version = Wobaduser::VERSION
9
+ spec.authors = ["Wolfgang Barth"]
10
+ spec.email = ["wob@swobspace.net"]
11
+ spec.summary = %q{Lightweight Active Directory LDAP read access}
12
+ spec.description = %q{Lightweight Active Directory LDAP read access}
13
+ spec.homepage = ""
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.add_dependency "net-ldap"
22
+ spec.add_dependency "activesupport"
23
+ spec.add_dependency "immutable-struct"
24
+
25
+ spec.add_development_dependency "bundler", "> 1.6"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "dotenv"
29
+ end
metadata ADDED
@@ -0,0 +1,165 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wobaduser
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Wolfgang Barth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-07-21 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'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: immutable-struct
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: dotenv
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Lightweight Active Directory LDAP read access
112
+ email:
113
+ - wob@swobspace.net
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - Gemfile
121
+ - LICENSE
122
+ - README.md
123
+ - Rakefile
124
+ - lib/string_addons.rb
125
+ - lib/wobaduser.rb
126
+ - lib/wobaduser/base.rb
127
+ - lib/wobaduser/ldap.rb
128
+ - lib/wobaduser/user.rb
129
+ - lib/wobaduser/version.rb
130
+ - spec/.env
131
+ - spec/ldap_setup_spec.rb
132
+ - spec/spec_helper.rb
133
+ - spec/user_spec.rb
134
+ - utils/test.rb
135
+ - utils/test2.rb
136
+ - wobaduser.gemspec
137
+ homepage: ''
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.6.14.1
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Lightweight Active Directory LDAP read access
161
+ test_files:
162
+ - spec/.env
163
+ - spec/ldap_setup_spec.rb
164
+ - spec/spec_helper.rb
165
+ - spec/user_spec.rb