rom-ldap 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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