1# = uri/ldap.rb
2#
3# Author::
4#  Takaaki Tateishi <ttate@jaist.ac.jp>
5#  Akira Yamada <akira@ruby-lang.org>
6# License::
7#   URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada.
8#   You can redistribute it and/or modify it under the same term as Ruby.
9# Revision:: $Id: ldap.rb 31555 2011-05-13 20:03:21Z drbrain $
10#
11# See URI for general documentation
12#
13
14require 'uri/generic'
15
16module URI
17
18  #
19  # LDAP URI SCHEMA (described in RFC2255)
20  # ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]]
21  #
22  class LDAP < Generic
23
24    # A Default port of 389 for URI::LDAP
25    DEFAULT_PORT = 389
26
27    # An Array of the available components for URI::LDAP
28    COMPONENT = [
29      :scheme,
30      :host, :port,
31      :dn,
32      :attributes,
33      :scope,
34      :filter,
35      :extensions,
36    ].freeze
37
38    # Scopes available for the starting point.
39    #
40    # * SCOPE_BASE - the Base DN
41    # * SCOPE_ONE  - one level under the Base DN, not including the base DN and
42    #                not including any entries under this.
43    # * SCOPE_SUB  - subtress, all entries at all levels
44    #
45    SCOPE = [
46      SCOPE_ONE = 'one',
47      SCOPE_SUB = 'sub',
48      SCOPE_BASE = 'base',
49    ].freeze
50
51    #
52    # == Description
53    #
54    # Create a new URI::LDAP object from components, with syntax checking.
55    #
56    # The components accepted are host, port, dn, attributes,
57    # scope, filter, and extensions.
58    #
59    # The components should be provided either as an Array, or as a Hash
60    # with keys formed by preceding the component names with a colon.
61    #
62    # If an Array is used, the components must be passed in the order
63    # [host, port, dn, attributes, scope, filter, extensions].
64    #
65    # Example:
66    #
67    #     newuri = URI::LDAP.build({:host => 'ldap.example.com',
68    #       :dn> => '/dc=example'})
69    #
70    #     newuri = URI::LDAP.build(["ldap.example.com", nil,
71    #       "/dc=example;dc=com", "query", nil, nil, nil])
72    #
73    def self.build(args)
74      tmp = Util::make_components_hash(self, args)
75
76      if tmp[:dn]
77        tmp[:path] = tmp[:dn]
78      end
79
80      query = []
81      [:extensions, :filter, :scope, :attributes].collect do |x|
82        next if !tmp[x] && query.size == 0
83        query.unshift(tmp[x])
84      end
85
86      tmp[:query] = query.join('?')
87
88      return super(tmp)
89    end
90
91    #
92    # == Description
93    #
94    # Create a new URI::LDAP object from generic URI components as per
95    # RFC 2396. No LDAP-specific syntax checking is performed.
96    #
97    # Arguments are +scheme+, +userinfo+, +host+, +port+, +registry+, +path+,
98    # +opaque+, +query+ and +fragment+, in that order.
99    #
100    # Example:
101    #
102    #     uri = URI::LDAP.new("ldap", nil, "ldap.example.com", nil,
103    #       "/dc=example;dc=com", "query", nil, nil, nil, nil)
104    #
105    #
106    # See also URI::Generic.new
107    #
108    def initialize(*arg)
109      super(*arg)
110
111      if @fragment
112        raise InvalidURIError, 'bad LDAP URL'
113      end
114
115      parse_dn
116      parse_query
117    end
118
119    # private method to cleanup +dn+ from using the +path+ component attribute
120    def parse_dn
121      @dn = @path[1..-1]
122    end
123    private :parse_dn
124
125    # private method to cleanup +attributes+, +scope+, +filter+ and +extensions+,
126    # from using the +query+ component attribute
127    def parse_query
128      @attributes = nil
129      @scope      = nil
130      @filter     = nil
131      @extensions = nil
132
133      if @query
134        attrs, scope, filter, extensions = @query.split('?')
135
136        @attributes = attrs if attrs && attrs.size > 0
137        @scope      = scope if scope && scope.size > 0
138        @filter     = filter if filter && filter.size > 0
139        @extensions = extensions if extensions && extensions.size > 0
140      end
141    end
142    private :parse_query
143
144    # private method to assemble +query+ from +attributes+, +scope+, +filter+ and +extensions+.
145    def build_path_query
146      @path = '/' + @dn
147
148      query = []
149      [@extensions, @filter, @scope, @attributes].each do |x|
150        next if !x && query.size == 0
151        query.unshift(x)
152      end
153      @query = query.join('?')
154    end
155    private :build_path_query
156
157    # returns dn.
158    def dn
159      @dn
160    end
161
162    # private setter for dn +val+
163    def set_dn(val)
164      @dn = val
165      build_path_query
166      @dn
167    end
168    protected :set_dn
169
170    # setter for dn +val+
171    def dn=(val)
172      set_dn(val)
173      val
174    end
175
176    # returns attributes.
177    def attributes
178      @attributes
179    end
180
181    # private setter for attributes +val+
182    def set_attributes(val)
183      @attributes = val
184      build_path_query
185      @attributes
186    end
187    protected :set_attributes
188
189    # setter for attributes +val+
190    def attributes=(val)
191      set_attributes(val)
192      val
193    end
194
195    # returns scope.
196    def scope
197      @scope
198    end
199
200    # private setter for scope +val+
201    def set_scope(val)
202      @scope = val
203      build_path_query
204      @scope
205    end
206    protected :set_scope
207
208    # setter for scope +val+
209    def scope=(val)
210      set_scope(val)
211      val
212    end
213
214    # returns filter.
215    def filter
216      @filter
217    end
218
219    # private setter for filter +val+
220    def set_filter(val)
221      @filter = val
222      build_path_query
223      @filter
224    end
225    protected :set_filter
226
227    # setter for filter +val+
228    def filter=(val)
229      set_filter(val)
230      val
231    end
232
233    # returns extensions.
234    def extensions
235      @extensions
236    end
237
238    # private setter for extensions +val+
239    def set_extensions(val)
240      @extensions = val
241      build_path_query
242      @extensions
243    end
244    protected :set_extensions
245
246    # setter for extensions +val+
247    def extensions=(val)
248      set_extensions(val)
249      val
250    end
251
252    # Checks if URI has a path
253    # For URI::LDAP this will return +false+
254    def hierarchical?
255      false
256    end
257  end
258
259  @@schemes['LDAP'] = LDAP
260end
261