rom-ldap 0.2.2

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.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +251 -0
  3. data/CONTRIBUTING.md +18 -0
  4. data/README.md +172 -0
  5. data/TODO.md +33 -0
  6. data/config/responses.yml +328 -0
  7. data/lib/dry/monitor/ldap/colorizers/default.rb +17 -0
  8. data/lib/dry/monitor/ldap/colorizers/rouge.rb +31 -0
  9. data/lib/dry/monitor/ldap/logger.rb +58 -0
  10. data/lib/rom-ldap.rb +1 -0
  11. data/lib/rom/ldap.rb +22 -0
  12. data/lib/rom/ldap/alias.rb +30 -0
  13. data/lib/rom/ldap/associations.rb +6 -0
  14. data/lib/rom/ldap/associations/core.rb +23 -0
  15. data/lib/rom/ldap/associations/many_to_many.rb +18 -0
  16. data/lib/rom/ldap/associations/many_to_one.rb +22 -0
  17. data/lib/rom/ldap/associations/one_to_many.rb +32 -0
  18. data/lib/rom/ldap/associations/one_to_one.rb +19 -0
  19. data/lib/rom/ldap/associations/self_ref.rb +35 -0
  20. data/lib/rom/ldap/attribute.rb +327 -0
  21. data/lib/rom/ldap/client.rb +185 -0
  22. data/lib/rom/ldap/client/authentication.rb +118 -0
  23. data/lib/rom/ldap/client/operations.rb +233 -0
  24. data/lib/rom/ldap/commands.rb +6 -0
  25. data/lib/rom/ldap/commands/create.rb +41 -0
  26. data/lib/rom/ldap/commands/delete.rb +17 -0
  27. data/lib/rom/ldap/commands/update.rb +35 -0
  28. data/lib/rom/ldap/constants.rb +193 -0
  29. data/lib/rom/ldap/dataset.rb +286 -0
  30. data/lib/rom/ldap/dataset/conversion.rb +62 -0
  31. data/lib/rom/ldap/dataset/dsl.rb +299 -0
  32. data/lib/rom/ldap/dataset/persistence.rb +44 -0
  33. data/lib/rom/ldap/directory.rb +126 -0
  34. data/lib/rom/ldap/directory/capabilities.rb +71 -0
  35. data/lib/rom/ldap/directory/entry.rb +200 -0
  36. data/lib/rom/ldap/directory/env.rb +155 -0
  37. data/lib/rom/ldap/directory/operations.rb +282 -0
  38. data/lib/rom/ldap/directory/password.rb +122 -0
  39. data/lib/rom/ldap/directory/root.rb +187 -0
  40. data/lib/rom/ldap/directory/tokenization.rb +66 -0
  41. data/lib/rom/ldap/directory/transactions.rb +31 -0
  42. data/lib/rom/ldap/directory/vendors/active_directory.rb +129 -0
  43. data/lib/rom/ldap/directory/vendors/apache_ds.rb +27 -0
  44. data/lib/rom/ldap/directory/vendors/e_directory.rb +16 -0
  45. data/lib/rom/ldap/directory/vendors/open_directory.rb +12 -0
  46. data/lib/rom/ldap/directory/vendors/open_dj.rb +25 -0
  47. data/lib/rom/ldap/directory/vendors/open_ldap.rb +35 -0
  48. data/lib/rom/ldap/directory/vendors/three_eight_nine.rb +16 -0
  49. data/lib/rom/ldap/directory/vendors/unknown.rb +22 -0
  50. data/lib/rom/ldap/dsl.rb +76 -0
  51. data/lib/rom/ldap/errors.rb +47 -0
  52. data/lib/rom/ldap/expression.rb +77 -0
  53. data/lib/rom/ldap/expression_encoder.rb +174 -0
  54. data/lib/rom/ldap/extensions.rb +50 -0
  55. data/lib/rom/ldap/extensions/active_support_notifications.rb +26 -0
  56. data/lib/rom/ldap/extensions/compatibility.rb +11 -0
  57. data/lib/rom/ldap/extensions/dsml.rb +165 -0
  58. data/lib/rom/ldap/extensions/msgpack.rb +23 -0
  59. data/lib/rom/ldap/extensions/optimised_json.rb +25 -0
  60. data/lib/rom/ldap/extensions/rails_log_subscriber.rb +38 -0
  61. data/lib/rom/ldap/formatter.rb +26 -0
  62. data/lib/rom/ldap/functions.rb +207 -0
  63. data/lib/rom/ldap/gateway.rb +145 -0
  64. data/lib/rom/ldap/ldif.rb +74 -0
  65. data/lib/rom/ldap/ldif/exporter.rb +77 -0
  66. data/lib/rom/ldap/ldif/importer.rb +95 -0
  67. data/lib/rom/ldap/mapper_compiler.rb +19 -0
  68. data/lib/rom/ldap/matchers.rb +69 -0
  69. data/lib/rom/ldap/message_queue.rb +7 -0
  70. data/lib/rom/ldap/oid.rb +101 -0
  71. data/lib/rom/ldap/parsers/abstract_syntax.rb +91 -0
  72. data/lib/rom/ldap/parsers/attribute.rb +290 -0
  73. data/lib/rom/ldap/parsers/filter_syntax.rb +133 -0
  74. data/lib/rom/ldap/pdu.rb +285 -0
  75. data/lib/rom/ldap/plugin/pagination.rb +145 -0
  76. data/lib/rom/ldap/plugins.rb +7 -0
  77. data/lib/rom/ldap/projection_dsl.rb +38 -0
  78. data/lib/rom/ldap/relation.rb +135 -0
  79. data/lib/rom/ldap/relation/exporting.rb +72 -0
  80. data/lib/rom/ldap/relation/reading.rb +461 -0
  81. data/lib/rom/ldap/relation/writing.rb +64 -0
  82. data/lib/rom/ldap/responses.rb +17 -0
  83. data/lib/rom/ldap/restriction_dsl.rb +45 -0
  84. data/lib/rom/ldap/schema.rb +123 -0
  85. data/lib/rom/ldap/schema/attributes_inferrer.rb +59 -0
  86. data/lib/rom/ldap/schema/dsl.rb +13 -0
  87. data/lib/rom/ldap/schema/inferrer.rb +50 -0
  88. data/lib/rom/ldap/schema/type_builder.rb +133 -0
  89. data/lib/rom/ldap/scope.rb +19 -0
  90. data/lib/rom/ldap/search_request.rb +249 -0
  91. data/lib/rom/ldap/socket.rb +210 -0
  92. data/lib/rom/ldap/tasks/ldap.rake +103 -0
  93. data/lib/rom/ldap/tasks/ldif.rake +80 -0
  94. data/lib/rom/ldap/transaction.rb +29 -0
  95. data/lib/rom/ldap/type_map.rb +88 -0
  96. data/lib/rom/ldap/types.rb +158 -0
  97. data/lib/rom/ldap/version.rb +17 -0
  98. data/lib/rom/plugins/relation/ldap/active_directory.rb +182 -0
  99. data/lib/rom/plugins/relation/ldap/auto_restrictions.rb +69 -0
  100. data/lib/rom/plugins/relation/ldap/e_directory.rb +27 -0
  101. data/lib/rom/plugins/relation/ldap/instrumentation.rb +35 -0
  102. data/lib/rouge/lexers/ldap.rb +72 -0
  103. data/lib/rouge/themes/ldap.rb +49 -0
  104. metadata +231 -0
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'rom/gateway'
5
+ require 'rom/ldap/directory'
6
+ require 'rom/ldap/dataset'
7
+
8
+ module ROM
9
+ module LDAP
10
+ # @abstract
11
+ # Responsible for initialising connection, binding to server.
12
+ # Wrapping the connection in the directory and then dataset abstractions,
13
+ # and passing them to the relations.
14
+ #
15
+ class Gateway < ROM::Gateway
16
+
17
+ adapter :ldap
18
+
19
+ # @!attribute [r] directory
20
+ # @return [String]
21
+ attr_reader :directory
22
+
23
+ # @!attribute [r] logger
24
+ # @return [Object] configured gateway logger
25
+ attr_reader :logger
26
+
27
+ # Initialize an LDAP gateway
28
+ #
29
+ # Gateways are typically initialized via ROM::Configuration object
30
+ #
31
+ # @overload initialize(uri, options)
32
+ # Connects to a directory via options
33
+ #
34
+ # @example
35
+ # ROM.container(:ldap, uri, {})
36
+ #
37
+ # @param uri [String] 'ldap://127.0.0.1:389' or nil
38
+ #
39
+ # @option options :username [String] BINDDN Directory admin username.
40
+ #
41
+ # @option options :password [String] BINDDN Directory admin password.
42
+ #
43
+ # @option options :base [String] BASE Directory search base.
44
+ #
45
+ # @option options :timeout [Integer] Connection timeout in seconds.
46
+ #
47
+ # @option options :logger [Object] Defaults to $stdout
48
+ #
49
+ # @return [LDAP::Gateway]
50
+ #
51
+ def initialize(uri = nil, **options)
52
+ @directory = Directory.new(uri, options)
53
+ @logger = options.fetch(:logger) { ::Logger.new(STDOUT) }
54
+
55
+ options.fetch(:extensions, EMPTY_ARRAY).each do |ext|
56
+ next unless LDAP.available_extension?(ext)
57
+
58
+ LDAP.load_extensions(ext)
59
+ end
60
+
61
+ super()
62
+ end
63
+
64
+ # Used by attribute_inferrer to query attributes.
65
+ #
66
+ # @param filter [String]
67
+ #
68
+ # @return [Array<Directory::Entry>]
69
+ #
70
+ # @api public
71
+ #
72
+ def [](filter)
73
+ directory.query_attributes(filter)
74
+ end
75
+ alias_method :call, :[]
76
+
77
+ # Directory attributes identifiers and descriptions.
78
+ #
79
+ # @see Schema::Inferrer
80
+ # @see Schema::TypeBuilder
81
+ #
82
+ # @return [Array<String>]
83
+ #
84
+ # @api public
85
+ #
86
+ def attribute_types
87
+ directory.attribute_types
88
+ end
89
+
90
+ # Check for presence of entries under new filter.
91
+ #
92
+ # @param name [String] An ldap compatible filter string.
93
+ #
94
+ # @return [Boolean]
95
+ #
96
+ # @api public
97
+ #
98
+ def dataset?(name)
99
+ dataset(name).any?
100
+ end
101
+
102
+ # An enumerable object for chainable queries.
103
+ #
104
+ # @param name [String] An ldap compatible filter string.
105
+ # Used as the param to schema block in relation classes.
106
+ #
107
+ # @return [Dataset] Scoped by name filter.
108
+ #
109
+ # @api public
110
+ #
111
+ def dataset(name)
112
+ Dataset.new(name: name, directory: directory)
113
+ end
114
+
115
+ # @param logger [Logger]
116
+ #
117
+ # @api public
118
+ #
119
+ def use_logger(logger)
120
+ directory.logger = @logger = logger
121
+ end
122
+
123
+ # Underlying directory type
124
+ #
125
+ # @return [Symbol]
126
+ #
127
+ # @api public
128
+ #
129
+ def directory_type
130
+ directory.type
131
+ end
132
+
133
+ # Disconnect from the server.
134
+ #
135
+ # @return [?]
136
+ #
137
+ # @api public
138
+ #
139
+ def disconnect
140
+ directory.disconnect
141
+ end
142
+
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/ldap/ldif/importer'
4
+ require 'rom/ldap/ldif/exporter'
5
+
6
+ require 'rom/initializer'
7
+ #
8
+ # @see https://docs.oracle.com/cd/E10773_01/doc/oim.1014/e10531/ldif_appendix.htm
9
+ #
10
+ module ROM
11
+ module LDAP
12
+ module_function
13
+
14
+ #
15
+ # LDAP Data Interchange Format (LDIF)
16
+ #
17
+ # Refines Array and Hash with #to_ldif method.
18
+ #
19
+ # @see Directory::Entry
20
+ # @see Relation::Exporting
21
+ #
22
+ module LDIF
23
+ # @example
24
+ #
25
+ # ROM::LDAP::LDIF("version: 3\n") => [{}]
26
+ #
27
+ # @param ldif [String]
28
+ #
29
+ # @return [Array<Hash>]
30
+ #
31
+ # @api private
32
+ def self.to_tuples(ldif, &block)
33
+ Importer.new(ldif).to_tuples(&block)
34
+ end
35
+
36
+ # Extend functionality of Hash class.
37
+ #
38
+ refine ::Hash do
39
+ # Convert hash to LDIF format
40
+ #
41
+ # @return [String]
42
+ #
43
+ # @api public
44
+ def to_ldif
45
+ Exporter.new([self]).to_ldif
46
+ end
47
+ end
48
+
49
+ # Extend functionality of Array class.
50
+ #
51
+ refine ::Array do
52
+ # Convert array to LDIF format
53
+ #
54
+ # @return [String]
55
+ #
56
+ # @api public
57
+ def to_ldif
58
+ Exporter.new(self).to_ldif
59
+ end
60
+ end
61
+ end
62
+
63
+ # Parser for LDIF format
64
+ # rubocop:disable Naming/MethodName
65
+ #
66
+ # alias for LDIF.to_tuples
67
+ #
68
+ # @api public
69
+ def LDIF(ldif, &block)
70
+ LDIF.to_tuples(ldif, &block)
71
+ end
72
+ # rubocop:enable Naming/MethodName
73
+ end
74
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/initializer'
4
+ require 'rom/ldap/functions'
5
+
6
+ module ROM
7
+ module LDAP
8
+ module LDIF
9
+ # Export Entry objects as LDIF files.
10
+ #
11
+ # @param tuple [Array<Hash>]
12
+ #
13
+ # @api private
14
+ class Exporter
15
+
16
+ extend Initializer
17
+
18
+ param :tuples, type: Types::Strict::Array.of(Types::Strict::Hash)
19
+
20
+ # @return [String]
21
+ #
22
+ def to_ldif
23
+ tuples.map { |tuple| create_entry(tuple) }.join(NEW_LINE)
24
+ end
25
+
26
+ private
27
+
28
+ # @param tuple [Hash]
29
+ #
30
+ # @return [Array<String>]
31
+ #
32
+ def create_entry(tuple)
33
+ ary = []
34
+ tuple.each do |key, values|
35
+ values.each { |value| ary << key_value_pair(key, value) }
36
+ end
37
+ ary << NEW_LINE
38
+ ary
39
+ end
40
+
41
+ # @param key [String]
42
+ # @param value [String]
43
+ #
44
+ # @return [String]
45
+ #
46
+ def key_value_pair(key, value)
47
+ if /userpassword/i.match?(key)
48
+ value = Functions[:to_base64].call(value)
49
+ "#{key}:: #{value}"
50
+ elsif value_is_binary?(value)
51
+ value = Functions[:to_base64].call(value, strict: false)
52
+ value = value.gsub(/#{NEW_LINE}/m, "#{NEW_LINE} ")
53
+ "#{key}:: #{value}"
54
+ else
55
+ "#{key}: #{value}"
56
+ end
57
+ end
58
+
59
+ # jpegphoto:<file:///tmp/myphoto.jpg
60
+ # userpassword::qwerty
61
+ #
62
+ # @param value [String]
63
+ #
64
+ # @return [Boolean]
65
+ def value_is_binary?(value)
66
+ return true if value.start_with?(':', '<')
67
+
68
+ value.each_byte do |byte|
69
+ return true if (byte < 32) || (byte > 126)
70
+ end
71
+ false
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/initializer'
4
+
5
+ module ROM
6
+ module LDAP
7
+ module LDIF
8
+ # LDIF to importable tuples.
9
+ #
10
+ # @param ldif [String]
11
+ #
12
+ # @api private
13
+ class Importer
14
+
15
+ extend Initializer
16
+
17
+ param :ldif, type: Types::Strict::String
18
+
19
+ # @example =>
20
+ #
21
+ # [{
22
+ # :dn => "ou=users, dc=rom, dc=ldap",
23
+ # "ou" => "users",
24
+ # "objectClass" => "organizationalUnit"
25
+ # }]
26
+ #
27
+ # @return [Array<Hash>]
28
+ #
29
+ def to_tuples
30
+ dataset.map do |entry|
31
+ next if entry.any? { |l| l.match?(/^version/) }
32
+
33
+ abort 'update statements not allowed' if entry.any? { |l| l.match?(/^changetype/) }
34
+
35
+ tuple = parse(entry)
36
+
37
+ block_given? ? yield(tuple) : tuple
38
+ end.compact
39
+ end
40
+
41
+ private
42
+
43
+ def dataset
44
+ ldif
45
+ .split(NEW_LINE)
46
+ .reject(&method(:comment?))
47
+ .chunk(&method(:divider?))
48
+ .reject(&:first)
49
+ .map(&:pop)
50
+ end
51
+
52
+ def comment?(line)
53
+ line.match?(/^#/)
54
+ end
55
+
56
+ def divider?(line)
57
+ line.eql?(EMPTY_STRING)
58
+ end
59
+
60
+ # @param entry [Array<String>]
61
+ #
62
+ # @return [Hash]
63
+ #
64
+ def parse(entry)
65
+ entry.map(&method(:key_pair)).inject(&method(:merge))
66
+ end
67
+
68
+ #
69
+ # @return [Hash]
70
+ #
71
+ def key_pair(line)
72
+ _, key, _, value = line.match(LDIF_LINE_REGEX).to_a
73
+
74
+ key = key.to_sym if key.eql?('dn')
75
+ value = File.binread(Regexp.last_match(1)) if value.match(BIN_FILE_REGEX)
76
+ value = Functions[:identify_value].call(value)
77
+
78
+ { key => value }
79
+ end
80
+
81
+ def merge(original, additional)
82
+ key, value = additional.to_a.first
83
+
84
+ if original.key?(key)
85
+ original[key] = [*original[key], value]
86
+ original
87
+ else
88
+ original.merge(additional)
89
+ end
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/mapper_compiler'
4
+
5
+ module ROM
6
+ module LDAP
7
+ class MapperCompiler < ROM::MapperCompiler
8
+ # def visit_attribute(node)
9
+ # name, _, meta_options = node
10
+
11
+ # if meta_options[:wrapped]
12
+ # [name, from: meta_options[:alias]]
13
+ # else
14
+ # [name]
15
+ # end
16
+ # end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ROM
4
+ module LDAP
5
+ # Type Builder
6
+ #
7
+ # @see https://ldap.com/matching-rules/
8
+ #
9
+ # Matching Rules - 2.5.13
10
+ #
11
+ # @return [Array<String>]
12
+ #
13
+ # @api private
14
+ STRING_MATCHERS = %w[
15
+ 2.5.13.0 objectIdentifierMatch
16
+ 2.5.13.1 distinguishedNameMatch
17
+ 2.5.13.2 caseIgnoreMatch
18
+ 2.5.13.3 caseIgnoreOrderingMatch
19
+ 2.5.13.4 caseIgnoreSubstringsMatch
20
+ 2.5.13.5 caseExactMatch
21
+ 2.5.13.6 caseExactOrderingMatch
22
+ 2.5.13.7 caseExactSubstringsMatch
23
+ 2.5.13.8 numericStringMatch
24
+ 2.5.13.9 numericStringOrderingMatch
25
+ 2.5.13.10 numericStringSubstringsMatch
26
+ 2.5.13.11 caseIgnoreListMatch
27
+ 2.5.13.12 caseIgnoreListSubstringsMatch
28
+ 2.5.13.17 octetStringMatch
29
+ 2.5.13.18 octetStringOrderingMatch
30
+ 2.5.13.20 telephoneNumberMatch
31
+ 2.5.13.21 telephoneNumberSubstringsMatch
32
+ 2.5.13.24 protocolInformationMatch
33
+ 2.5.13.30 objectIdentifierFirstComponentMatch
34
+
35
+ 1.3.6.1.1.16.2 uuidMatch
36
+ 1.3.6.1.1.16.3 uuidOrderingMatch
37
+
38
+ 1.3.6.1.4.1.1466.109.114.1 caseExactIA5Match
39
+ 1.3.6.1.4.1.1466.109.114.2 caseIgnoreIA5Match
40
+ 1.3.6.1.4.1.1466.109.114.3 caseIgnoreIA5SubstringsMatch
41
+ ].freeze
42
+
43
+ # @return [Array<String>]
44
+ #
45
+ # @api private
46
+ BOOLEAN_MATCHERS = %w[
47
+ 2.5.13.13 booleanMatch
48
+ ].freeze
49
+
50
+ #
51
+ # @return [Array<String>]
52
+ #
53
+ # @api private
54
+ INTEGER_MATCHERS = %w[
55
+ 2.5.13.14 integerMatch
56
+ 2.5.13.15 integerOrderingMatch
57
+ ].freeze
58
+
59
+ # @return [Array<String>]
60
+ #
61
+ # @api private
62
+ TIME_MATCHERS = %w[
63
+ csnMatch
64
+ 2.5.13.25 uTCTimeMatch
65
+ 2.5.13.27 generalizedTimeMatch
66
+ 2.5.13.28 generalizedTimeOrderingMatch
67
+ ].freeze
68
+ end
69
+ end