ruby-jss 1.0.2 → 1.0.3b1

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.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0196bdb655afca3d5ed45a3a3843664581141d379b5549814945d1868ed51fa4'
4
- data.tar.gz: f818199e33219b7c21b1353874b4d3c5c2f92943acbd01e2a9b332fc868628a7
3
+ metadata.gz: 26f81c239772f5d60e91e894de2d3617e76600a8a259fb8185612ffc7eab2d02
4
+ data.tar.gz: 849b3a3ef79be5f35594612cca9820a238489e5ceda4ca853e8f1d706e3e48a1
5
5
  SHA512:
6
- metadata.gz: b90fb35e0f5f132edf4b396d0608225f30722322fc9cbea5826c127b2b2e77fa2bce69345952b4440454be3a3ce110530c54b349ea472220bc5d4ad1e83f32d2
7
- data.tar.gz: d2952cb9ff7f44b3617946f93d522fba7d990f98754542f1413ccd1736ea33fed85e4e4fc24b96dc347aa32907793888dacf6b26cfd068366d63f5b460d3b6d4
6
+ metadata.gz: 27bf2bf3d489fff5ea4853b445be8e6dee1b078b77f4f8d51564f7f8c22a63edd54452202bbee7c5649ce39960c4cd86df4637bb7ad26405cac7cf974229f27c
7
+ data.tar.gz: e79a56e46ac43760749197e7e200a9fcf3c9dfedbf32cbc15fec460ecc9e7a820db3bea8a8f71295b573265d9e4f157bdbda0d2e0a87908393dc3b85efc9f41d
data/CHANGES.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Change History
2
2
 
3
+ ## v 1.0.2b1 2018-12-04
4
+
5
+ - fix: default port number choice for on-prem vs. JamfCloud connections
6
+ - update: use new(er) API resources for LDAP lookups, don't go directly to LDAP via net/ldap
7
+ - add: LDAPServer.server_for_user and .server_for_group class methods, return the id of the first LDAP server containing the given user or group
8
+ - fix Client.primary_console_user returns nil when at the loginwindow
9
+ - add: Client.homdir(user) and Client.do_not_disturb?(user)
10
+ - fix: Computer#certificates attribute is now readable
11
+ - add: Package.all_filenames, .orphaned_files, and .missing_files class methods. WARNING - these are very slow since they must instantiate every package.
12
+ - fix: error when SelfService icon is not available where expected, returns nil instead. (Thanks to @cybertunnel for finding this)
13
+
3
14
  ## v 1.0.2 2018-10-16
4
15
 
5
16
  - add: Support for parentheses (opening_paren and closing_paren) in JSS::Criteriable::Criterion objects
@@ -290,8 +290,8 @@ Options:
290
290
 
291
291
  Notes:
292
292
 
293
- - If no API settings are provided, they will be read from /etc/jss_gem.conf
294
- and ~/.jss_gem.conf. See the JSS Gem docs for details.
293
+ - If no API settings are provided, they will be read from /etc/ruby-jss.conf
294
+ and ~/.ruby-jss.conf. See the ruby-jss docs for details.
295
295
 
296
296
  - The password for the connection will be read from STDIN or prompted if needed
297
297
 
@@ -307,7 +307,7 @@ Notes:
307
307
  (spaces, tabs, & returns in any number or combination)
308
308
 
309
309
  FULLHELP
310
- return
310
+
311
311
  end
312
312
 
313
313
  #####################################
@@ -425,6 +425,7 @@ module JSS
425
425
  # @return [true]
426
426
  #
427
427
  def connect(args = {})
428
+ args[:no_port_specified] = args[:port].to_s.empty?
428
429
  args = apply_connection_defaults args
429
430
 
430
431
  # confirm we know basics
@@ -993,7 +994,7 @@ module JSS
993
994
  # @return [Hash] The args with defaults applied
994
995
  #
995
996
  def apply_module_defaults(args)
996
- args[:port] ||= args[:server].to_s.end_with?(JAMFCLOUD_DOMAIN) ? JAMFCLOUD_PORT : SSL_PORT
997
+ args[:port] = args[:server].to_s.end_with?(JAMFCLOUD_DOMAIN) ? JAMFCLOUD_PORT : SSL_PORT if args[:no_port_specified]
997
998
  args[:timeout] ||= DFT_TIMEOUT
998
999
  args[:open_timeout] ||= DFT_OPEN_TIMEOUT
999
1000
  args[:ssl_version] ||= DFT_SSL_VERSION
@@ -415,9 +415,7 @@ module JSS
415
415
  end_date ||= start_date
416
416
  start_date = Time.parse start_date if start_date.is_a? String
417
417
  end_date = Time.parse end_date if end_date.is_a? String
418
- unless ([start_date.class, end_date.class] - APPLICATION_USAGE_DATE_CLASSES).empty?
419
- raise JSS::InvalidDataError, 'Invalid Start or End Date'
420
- end
418
+ raise JSS::InvalidDataError, 'Invalid Start or End Date' unless ([start_date.class, end_date.class] - APPLICATION_USAGE_DATE_CLASSES).empty?
421
419
  start_date = start_date.strftime APPLICATION_USAGE_DATE_FMT
422
420
  end_date = end_date.strftime APPLICATION_USAGE_DATE_FMT
423
421
  data = api.get_rsrc(APPLICATION_USAGE_RSRC + "/id/#{id}/#{start_date}_#{end_date}")
@@ -720,6 +718,16 @@ module JSS
720
718
  #
721
719
  attr_reader :software
722
720
 
721
+ # @return [Array<Hash>] Data about all the certificates on the computer.
722
+ #
723
+ # Each Hash represents a certificate and has these keys:
724
+ # common_name: [String] the name of the cert
725
+ # identity: [Boolean] Is this an identiry cert?
726
+ # expires: [Time] the certificate expiration time
727
+ # name: [String] Display name for the certificate, if any
728
+ #
729
+ attr_reader :certificates
730
+
723
731
  # Constructor
724
732
  #####################################
725
733
 
@@ -763,11 +771,19 @@ module JSS
763
771
  @sus = @init_data[:general][:sus]
764
772
 
765
773
  @configuration_profiles = @init_data[:configuration_profiles]
766
- @certificates = @init_data[:certificates]
774
+
767
775
  @groups_accounts = @init_data[:groups_accounts]
768
776
  @hardware = @init_data[:hardware]
769
777
  @peripherals = @init_data[:peripherals]
770
778
  @software = @init_data[:software]
779
+ @certificates = @init_data[:certificates].map do |cert|
780
+ {
781
+ expires: JSS.epoch_to_time(cert[:expires_epoch]),
782
+ common_name: cert[:common_name],
783
+ identity: cert[:identity],
784
+ name: cert[:name]
785
+ }
786
+ end
771
787
 
772
788
  # Freeze immutable things.
773
789
  # These are updated via recon, and aren't sent
@@ -956,7 +972,6 @@ module JSS
956
972
  @unmange_at_update = true
957
973
  end
958
974
 
959
- #
960
975
  def asset_tag=(new_val)
961
976
  return nil if @asset_tag == new_val
962
977
  new_val.strip!
@@ -964,7 +979,6 @@ module JSS
964
979
  @need_to_update = true
965
980
  end
966
981
 
967
- #
968
982
  def barcode1=(new_val)
969
983
  return nil if @barcode1 == new_val
970
984
  new_val.strip!
@@ -972,7 +986,6 @@ module JSS
972
986
  @need_to_update = true
973
987
  end
974
988
 
975
- #
976
989
  def barcode2=(new_val)
977
990
  return nil if @barcode2 == new_val
978
991
  new_val.strip!
@@ -980,35 +993,30 @@ module JSS
980
993
  @need_to_update = true
981
994
  end
982
995
 
983
- #
984
996
  def ip_address=(new_val)
985
997
  return nil if @ip_address == new_val
986
998
  @ip_address = new_val.empty? ? new_val : JSS::Validate.ip_address(new_val)
987
999
  @need_to_update = true
988
1000
  end
989
1001
 
990
- #
991
1002
  def mac_address=(new_val)
992
1003
  return nil if new_val == @mac_address
993
1004
  @mac_address = new_val.empty? ? new_val : JSS::Validate.mac_address(new_val)
994
1005
  @need_to_update = true
995
1006
  end
996
1007
 
997
- #
998
1008
  def alt_mac_address=(new_val)
999
1009
  return nil if new_val == @alt_mac_address
1000
1010
  @alt_mac_address = new_val.empty? ? new_val : JSS::Validate.mac_address(new_val)
1001
1011
  @need_to_update = true
1002
1012
  end
1003
1013
 
1004
- #
1005
1014
  def serial_number=(new_val)
1006
1015
  return nil if new_val == @serial_number
1007
1016
  @serial_number = new_val.empty? ? new_val : JSS::Validate.unique_identifier(JSS::Computer, :serial_number, new_val, api: api)
1008
1017
  @need_to_update = true
1009
1018
  end
1010
1019
 
1011
- #
1012
1020
  def udid=(new_val)
1013
1021
  return nil if new_val == @udid
1014
1022
  @udid = new_val.empty? ? new_val : JSS::Validate.unique_identifier(JSS::Computer, :udid, new_val, api: api)
@@ -1,272 +1,275 @@
1
- ### Copyright 2018 Pixar
2
-
3
- ###
4
- ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
- ### with the following modification; you may not use this file except in
6
- ### compliance with the Apache License and the following modification to it:
7
- ### Section 6. Trademarks. is deleted and replaced with:
8
- ###
9
- ### 6. Trademarks. This License does not grant permission to use the trade
10
- ### names, trademarks, service marks, or product names of the Licensor
11
- ### and its affiliates, except as required to comply with Section 4(c) of
12
- ### the License and to reproduce the content of the NOTICE file.
13
- ###
14
- ### You may obtain a copy of the Apache License at
15
- ###
16
- ### http://www.apache.org/licenses/LICENSE-2.0
17
- ###
18
- ### Unless required by applicable law or agreed to in writing, software
19
- ### distributed under the Apache License with the above modification is
20
- ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
- ### KIND, either express or implied. See the Apache License for the specific
22
- ### language governing permissions and limitations under the Apache License.
23
- ###
24
- ###
25
-
26
- ###
27
- module JSS
28
-
29
- #####################################
30
- ### Module Variables
31
- #####################################
32
-
33
- #####################################
34
- ### Module Methods
35
- #####################################
1
+ # Copyright 2018 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
36
25
 
26
+ module JSS
37
27
 
38
- #####################################
39
- ### Classes
28
+ # Classes
40
29
  #####################################
41
30
 
42
- ###
43
- ### An LDAP server in the JSS.
44
- ###
45
- ### This class doesn't curretly provide creation or updaing of LDAP server
46
- ### definitions in the JSS. Please use the JSS web UI.
47
- ###
48
- ### However, it does provide methods for querying users and usergroups from
49
- ### LDAP servers, and checking group membership.
50
- ###
51
- ### When an LDAPServer instance is created, if it
52
- ### uses anonymous binding for lookups (the Authentication Type is set to 'none') then
53
- ### the LDAP connection is established immediately. Otherwise, you must use the {#connect}
54
- ### method, and provide the appropriate password for the lookup account defined.
55
- ###
56
- ### Since LDAP server connections are used to verify the validity of LDAP users & groups used in
57
- ### scopes, if you don't connect to all LDAP servers before modifying any scope's user & group
58
- ### limitations or exceptions, those new values may not be verifiable. Unverified limitations and
59
- ### exceptions, when sent to the API, will result in a REST 409 Conflict error if the user or
60
- ### group doesn't exist. Unfortunately, 409 Conflict errors are very generic and don't indicate the
61
- ### source of the problem (in this case, a non-existent user or group limitation or exception to the
62
- ### scope). The {JSS::Scopable} module tries to catch these errors and raise a more useful
63
- ### exception when they happen.
64
- ###
65
- ### The class method {LDAPServer.all_ldaps} returns a Hash of JSS::LDAPServer instances.
66
- ### one for each server defined in the JSS.
67
- ###
68
- ### The class methods {LDAPServer.user_in_ldap?} and {LDAPServer.group_in_ldap?} can be
69
- ### used to check all defined LDAP servers for a user or group. They are used by
70
- ### {JSS::Scopable::Scope} when adding user and groups to scope limitations and exceptions.
71
- ###
72
- ### Within an LDAPServer instance, the methods {#find_user} and {#find_group} will return
73
- ### all matches in the server for a given search term.
74
- ###
75
- ### @see JSS::APIObject
76
- ###
31
+ # An LDAP server in the JSS.
32
+ #
33
+ # This class doesn't curretly provide creation or updaing of LDAP server
34
+ # definitions in the JSS. Please use the JSS web UI.
35
+ #
36
+ # However, it does provide methods for querying users and usergroups from
37
+ # LDAP servers, and checking group membership.
38
+ #
39
+ # The class methods {LDAPServer.user_in_ldap?} and {LDAPServer.group_in_ldap?} can be
40
+ # used to check all defined LDAP servers for a user or group. They are used by
41
+ # {JSS::Scopable::Scope} when adding user and groups to scope limitations and exceptions.
42
+ #
43
+ # Within an LDAPServer instance, the methods {#find_user} and {#find_group} will return
44
+ # all matches in the server for a given search term.
45
+ #
46
+ # @see JSS::APIObject
47
+ #
77
48
  class LDAPServer < JSS::APIObject
78
49
 
79
-
80
- ### Class Methods
50
+ # Constants
81
51
  #####################################
82
52
 
83
- ### DEPRECATED: Please Use ::all_objects
84
- ###
85
- ### @param refresh[Boolean] should the LDAP server data be re-read from the API?
86
- ###
87
- ### @return [Hash{String => JSS::LDAPServer}] JSS::LDAPServer instances for all defined servers
88
- ###
89
- def self.all_ldaps(refresh = false, api: JSS.api)
90
- hash = {}
91
- all_objects(refresh, api: api) { |ls| hash[ls.name] = s }
92
- hash
93
- end
94
-
95
- ###
96
- ### @param user[String] a username to search for in all LDAP servers
97
- ###
98
- ### @return [Boolean] does the user exist in any LDAP server?
99
- ###
100
- def self.user_in_ldap?(user, api: JSS.api)
101
- all_objects(refresh, api: api).each do |ldap|
102
- next if ldap.find_user(user, :exact).empty?
103
- return true
104
- end
105
- false
106
- end
107
-
108
- ###
109
- ### @param group[String] a group to search for in all LDAP servers
110
- ###
111
- ### @return [Boolean] does the group exist in any LDAP server?
112
- ###
113
- def self.group_in_ldap? (group, api: JSS.api)
114
- all_objects(refresh, api: api).each do |ldap|
115
- next if ldap.find_group(group, :exact).empty?
116
- return true
117
- end
118
- false
119
- end
120
-
121
-
53
+ # The base for REST resources of this class
54
+ RSRC_BASE = 'ldapservers'.freeze
122
55
 
123
- #####################################
124
- ### Class Constants
125
- #####################################
126
-
127
- ### The base for REST resources of this class
128
- RSRC_BASE = "ldapservers"
129
-
130
- ### the hash key used for the JSON list output of all objects in the JSS
56
+ # the hash key used for the JSON list output of all objects in the JSS
131
57
  RSRC_LIST_KEY = :ldap_servers
132
58
 
133
- ### The hash key used for the JSON object output.
134
- ### It's also used in various error messages
59
+ # The hash key used for the JSON object output.
60
+ # It's also used in various error messages
135
61
  RSRC_OBJECT_KEY = :ldap_server
136
62
 
137
- ### these keys, as well as :id and :name, are present in valid API JSON data for this class
138
- VALID_DATA_KEYS = []
63
+ # these keys, as well as :id and :name, are present in valid API JSON data for this class
64
+ VALID_DATA_KEYS = [].freeze
139
65
 
140
- ### the default LDAP port
66
+ # the default LDAP port
141
67
  DEFAULT_PORT = 389
142
68
 
143
- ### possible values for search scope
144
- SEARCH_SCOPES = ["All Subtrees", "First Level Only"]
69
+ # possible values for search scope
70
+ SEARCH_SCOPES = ['All Subtrees', 'First Level Only'].freeze
145
71
 
146
- ### possible authentication types
147
- AUTH_TYPES = {'none' => :anonymous, 'simple' => :simple, 'CRAM-MD5' => :cram_md5, 'DIGEST-MD5' => :digest_md5 }
72
+ # possible authentication types
73
+ AUTH_TYPES = { 'none' => :anonymous, 'simple' => :simple, 'CRAM-MD5' => :cram_md5, 'DIGEST-MD5' => :digest_md5 }.freeze
148
74
 
149
- ### possible referral responses
150
- REFERRAL_RESPONSES = ['', nil, 'follow', 'ignore']
75
+ # possible referral responses
76
+ REFERRAL_RESPONSES = ['', nil, 'follow', 'ignore'].freeze
151
77
 
152
- ### possible objectclass mapping options
153
- OBJECT_CLASS_MAPPING_OPTIONS = ["any", "all"]
78
+ # possible objectclass mapping options
79
+ OBJECT_CLASS_MAPPING_OPTIONS = %w[any all].freeze
154
80
 
155
81
  # the object type for this object in
156
82
  # the object history table.
157
83
  # See {APIObject#add_object_history_entry}
158
84
  OBJECT_HISTORY_OBJECT_TYPE = 80
159
85
 
86
+ # Class Methods
160
87
  #####################################
161
- ### Attributes
88
+
89
+ # Does a user exist in any ldap server?
90
+ #
91
+ # @param user[String] a username to search for in all LDAP servers
92
+ #
93
+ # @param api[JSS::APIConnection] the API connection to use for the search#
94
+
95
+ # @return [Integer, nil] the id of the first LDAP server with the user,
96
+ # nil if not found
97
+ #
98
+ def self.server_for_user(user, api: JSS.api)
99
+ all_objects(:refresh, api: api).each do |ldap|
100
+ next if ldap.find_user(user, :exact).empty?
101
+ return ldap.id
102
+ end
103
+ nil
104
+ end
105
+
106
+ # For Backward Compatibility,
107
+ #
108
+ # @param user[String] a username to search for in all LDAP servers
109
+ #
110
+ # @param api[JSS::APIConnection] the API connection to use for the search
111
+ #
112
+ # @return [Boolean] Does the user exist in any LDAP server?
113
+ #
114
+ def self.user_in_ldap?(user, api: JSS.api)
115
+ server_for_user(user, api: api) ? true : false
116
+ end
117
+
118
+ # Does a group exist in any ldap server?
119
+ #
120
+ # @param group[String] a group to search for in all LDAP servers
121
+ #
122
+ # @param api[JSS::APIConnection] the API connection to use for the search
123
+ #
124
+ # @return [Integer, nil] the id of the first LDAP server with the group,
125
+ # nil if not found
126
+ #
127
+ def self.server_for_group(group, api: JSS.api)
128
+ all_objects(:refresh, api: api).each do |ldap|
129
+ next if ldap.find_group(group, :exact).empty?
130
+ return ldap.id
131
+ end
132
+ nil
133
+ end
134
+
135
+ # For Backward Compatibility,
136
+ #
137
+ # @param user[String] a group name to search for in all LDAP servers
138
+ #
139
+ # @param api[JSS::APIConnection] the API connection to use for the search
140
+ #
141
+ # @return [Boolean] Does the group exist in any LDAP server?
142
+ #
143
+ def self.group_in_ldap?(group, api: JSS.api)
144
+ server_for_group(group, api: api) ? true : false
145
+ end
146
+
147
+ # On a given server, does a given group contain a given user?
148
+ #
149
+ # This class method allows the check to happen without instanting
150
+ # the LDAPServer.
151
+ #
152
+ # @param server[String, Integer] The name or id of the LDAP server to use
153
+ #
154
+ # @param user[String] the username to check for memebership in the group
155
+ #
156
+ # @param group[String] the group name to see if the user is a member
157
+ #
158
+ # @param api[JSS::APIConnection] the API connection to use for the search
159
+ #
160
+ # @return [Boolean] is the user a member of the group?
161
+ #
162
+ def self.check_membership(ldap_server, user, group, api: JSS.api)
163
+ ldap_server_id = valid_id ldap_server
164
+ raise JSS::NoSuchItemError, "No LDAPServer matching #{ldap_server}" unless ldap_server_id
165
+ rsrc = "#{RSRC_BASE}/id/#{ldap_server_id}/group/#{CGI.escape group}/user/#{CGI.escape user}"
166
+ member_check = api.get_rsrc rsrc
167
+ return false if member_check[:ldap_users].empty?
168
+ true
169
+ end
170
+
171
+ # Attributes
162
172
  #####################################
163
173
 
164
- ### These attributes all come from the :connection hash of the
165
- ### API data
174
+ # These attributes all come from the :connection hash of the
175
+ # API data
166
176
 
167
- ### @return [String] the hostname of the server
177
+ # @return [String] the hostname of the server
168
178
  attr_reader :hostanme
169
179
 
170
- ### @return [Integer] the port for ldap
180
+ # @return [Integer] the port for ldap
171
181
  attr_reader :port
172
182
 
173
- ### @return [Boolean] should the connection use ssl?
183
+ # @return [Boolean] should the connection use ssl?
174
184
  attr_reader :use_ssl
175
185
 
176
- ### @return [String] what authentication method should be used?
186
+ # @return [String] what authentication method should be used?
177
187
  attr_reader :authentication_type
178
188
 
179
- ### @return [String] the Distinguished Name of the account used for connections/lookups?
189
+ # @return [String] the Distinguished Name of the account used for connections/lookups?
180
190
  attr_reader :lookup_dn
181
191
 
182
- ### @return [String] the password for the connection/lookup account, as a SHA256 digest.
192
+ # @return [String] the password for the connection/lookup account, as a SHA256 digest.
183
193
  attr_reader :lookup_pw_sha256
184
194
 
185
- ### @return [Integer] timeout, in seconds, for opening LDAP connections
195
+ # @return [Integer] timeout, in seconds, for opening LDAP connections
186
196
  attr_reader :open_close_timeout
187
197
 
188
- ### @return [Integer] timeout, in seconds, for search queries
198
+ # @return [Integer] timeout, in seconds, for search queries
189
199
  attr_reader :search_timeout
190
200
 
191
- ### @return [String] the referral response from the server
201
+ # @return [String] the referral response from the server
192
202
  attr_reader :referral_response
193
203
 
194
- ### @return [Boolean] should searches use wildcards?
204
+ # @return [Boolean] should searches use wildcards?
195
205
  attr_reader :use_wildcards
196
206
 
197
-
198
- ### @return [Hash<Symbol=>String>]
199
- ###
200
- ### The LDAP attributes mapped to various user data
201
- ###
202
- ### The hash keys are:
203
- ### - :search_base =>
204
- ### - :search_scope =>
205
- ### - :object_classes =>
206
- ### - :map_object_class_to_any_or_all =>
207
- ### - :map_username =>
208
- ### - :map_user_id =>
209
- ### - :map_department =>
210
- ### - :map_building =>
211
- ### - :map_room =>
212
- ### - :map_realname =>
213
- ### - :map_phone =>
214
- ### - :map_email_address =>
215
- ### - :map_position =>
216
- ### - :map_user_uuid =>
217
- ### - :append_to_email_results =>
218
- ###
207
+ # @return [Hash<Symbol=>String>]
208
+ #
209
+ # The LDAP attributes mapped to various user data
210
+ #
211
+ # The hash keys are:
212
+ # - :search_base =>
213
+ # - :search_scope =>
214
+ # - :object_classes =>
215
+ # - :map_object_class_to_any_or_all =>
216
+ # - :map_username =>
217
+ # - :map_user_id =>
218
+ # - :map_department =>
219
+ # - :map_building =>
220
+ # - :map_room =>
221
+ # - :map_realname =>
222
+ # - :map_phone =>
223
+ # - :map_email_address =>
224
+ # - :map_position =>
225
+ # - :map_user_uuid =>
226
+ # - :append_to_email_results =>
227
+ #
219
228
  attr_reader :user_mappings
220
229
 
221
-
222
- ### @return [Hash<Symbol=>String>]
223
- ###
224
- ### The LDAP attributes mapped to various user group data
225
- ###
226
- ### The hash keys are:
227
- ### - :search_base =>
228
- ### - :search_scope =>
229
- ### - :object_classes =>
230
- ### - :map_object_class_to_any_or_all =>
231
- ### - :map_group_id =>
232
- ### - :map_group_name =>
233
- ### - :map_group_uuid =>
234
- ###
230
+ # @return [Hash<Symbol=>String>]
231
+ #
232
+ # The LDAP attributes mapped to various user group data
233
+ #
234
+ # The hash keys are:
235
+ # - :search_base =>
236
+ # - :search_scope =>
237
+ # - :object_classes =>
238
+ # - :map_object_class_to_any_or_all =>
239
+ # - :map_group_id =>
240
+ # - :map_group_name =>
241
+ # - :map_group_uuid =>
242
+ #
235
243
  attr_reader :user_group_mappings
236
244
 
237
- ### @return [Hash<Symbol=>String>]
238
- ###
239
- ### The LDAP attributes used to identify a user as a member of a group
240
- ###
241
- ### The hash keys are:
242
- ### - :user_group_membership_stored_in =>
243
- ### - :map_user_membership_use_dn =>
244
- ### - :map_group_membership_to_user_field =>
245
- ### - :group_id =>
246
- ### - :map_object_class_to_any_or_all =>
247
- ### - :append_to_username =>
248
- ### - :username =>
249
- ### - :object_classes =>
250
- ### - :use_dn =>
251
- ### - :search_base =>
252
- ### - :recursive_lookups =>
253
- ### - :search_scope =>
254
- ### - :map_user_membership_to_group_field =>
255
- ###
245
+ # @return [Hash<Symbol=>String>]
246
+ #
247
+ # The LDAP attributes used to identify a user as a member of a group
248
+ #
249
+ # The hash keys are:
250
+ # - :user_group_membership_stored_in =>
251
+ # - :map_user_membership_use_dn =>
252
+ # - :map_group_membership_to_user_field =>
253
+ # - :group_id =>
254
+ # - :map_object_class_to_any_or_all =>
255
+ # - :append_to_username =>
256
+ # - :username =>
257
+ # - :object_classes =>
258
+ # - :use_dn =>
259
+ # - :search_base =>
260
+ # - :recursive_lookups =>
261
+ # - :search_scope =>
262
+ # - :map_user_membership_to_group_field =>
263
+ #
256
264
  attr_reader :user_group_membership_mappings
257
265
 
258
- ### @return [Boolean] we we connected to this server at the moment?
259
- attr_reader :connected
260
-
261
- #####################################
262
- ### Constructor
266
+ # Constructor
263
267
  #####################################
264
268
 
265
- ###
266
- ### See JSS::APIObject#initialize
267
- ###
268
- def initialize (args = {})
269
- require 'net/ldap'
269
+ #
270
+ # See JSS::APIObject#initialize
271
+ #
272
+ def initialize(args = {})
270
273
  super
271
274
 
272
275
  @hostname = @init_data[:connection][:hostname]
@@ -281,252 +284,54 @@ module JSS
281
284
  @lookup_dn = @init_data[:connection][:account][:distinguished_username]
282
285
  @lookup_pw_sha256 = @init_data[:connection][:account][:password_sha256]
283
286
 
284
- @user_mappings = @init_data[:mappings_for_users ][:user_mappings]
285
- @user_group_mappings = @init_data[:mappings_for_users ][:user_group_mappings]
286
- @user_group_membership_mappings = @init_data[:mappings_for_users ][:user_group_membership_mappings]
287
-
288
- # the ldap attributes to retrieve with user lookups
289
- # (all those defined in the user mappings)
290
- @user_attrs_to_get = {
291
- :username => @user_mappings[:map_username],
292
- :user_id => @user_mappings[:map_user_id],
293
- :department => @user_mappings[:map_department],
294
- :building => @user_mappings[:map_building],
295
- :room => @user_mappings[:map_room],
296
- :realname => @user_mappings[:map_realname],
297
- :phone => @user_mappings[:map_phone],
298
- :email_address => @user_mappings[:map_email_address],
299
- :position => @user_mappings[:map_position],
300
- :user_uuid => @user_mappings[:map_user_uuid]
301
- }.delete_if{|k,v| v.nil? }
302
-
303
- # and for groups....
304
- @user_group_attrs_to_get = {
305
- :group_id => @user_group_mappings[:map_group_id],
306
- :group_name => @user_group_mappings[:map_group_name],
307
- :group_uuid => @user_group_mappings[:map_group_uuid]
308
- }.delete_if{|k,v| v.nil? }
287
+ @user_mappings = @init_data[:mappings_for_users][:user_mappings]
288
+ @user_group_mappings = @init_data[:mappings_for_users][:user_group_mappings]
289
+ @user_group_membership_mappings = @init_data[:mappings_for_users][:user_group_membership_mappings]
309
290
 
310
291
  @connection = nil
311
292
  @connected = false
312
-
313
- # If we are using anonymous binding, connect now
314
- connect if @authentication_type == :anonymous
315
293
  end
316
294
 
295
+ # Public Instance Methods
317
296
  #####################################
318
- ### Public Instance Methods
319
- #####################################
320
-
321
- ###
322
- ###
323
- ### @param user[String] the username to search for
324
- ###
325
- ### @param exact[Boolean] if true, force an exact match, otherwise use wildcards if @use_wildcards is true
326
- ###
327
- ### @param additional_filter[Net::LDAP::Fliter] an additional filter to be AND'd to the existing filter.
328
- ###
329
- ### @return [Array<Hash>] The @user_attrs_to_get for all usernames matching the query
330
- ###
331
- def find_user(user, exact = false, additional_filter = nil)
332
-
333
- raise JSS::InvalidConnectionError, "Not connected to LDAP server '#{@name}'. Please use #connect first." unless @connected
334
-
335
- if @use_wildcards and not exact
336
- user_filter = Net::LDAP::Filter.contains(@user_mappings[:map_username], user)
337
- else
338
- user_filter = Net::LDAP::Filter.eq(@user_mappings[:map_username], user)
339
- end
340
-
341
- # limit the object classes
342
- ocs = @user_mappings[:object_classes].to_s.chomp.split(/,\s*/)
343
- anyall = @user_mappings[:map_object_class_to_any_or_all]
344
- oc_filter = Net::LDAP::Filter.eq("objectclass", ocs.shift)
345
- ocs.each do |oc|
346
- if anyall == "any"
347
- oc_filter = oc_filter | Net::LDAP::Filter.eq("objectclass", oc)
348
- else
349
- oc_filter = oc_filter & Net::LDAP::Filter.eq("objectclass", oc)
350
- end
351
- end
352
-
353
- full_filter = oc_filter & user_filter
354
- full_filter = full_filter & additional_filter if additional_filter
355
- treebase = @user_mappings[:search_base]
356
- ldap_attribs = @user_attrs_to_get.values
357
-
358
- # should we grab membership from the user?
359
- if @user_group_membership_mappings[:user_group_membership_stored_in] == "user object" and \
360
- @user_group_membership_mappings[:map_group_membership_to_user_field]
361
- get_groups = true
362
- ldap_attribs << @user_group_membership_mappings[:map_group_membership_to_user_field]
363
- end
364
-
365
- results = []
366
297
 
367
- @connection.search(:base => treebase, :filter => full_filter, :attributes => ldap_attribs ) do |entry|
368
- userhash = {:dn => entry.dn}
369
- @user_attrs_to_get.each do |k,attr|
370
- userhash[k] = entry[attr][0]
371
- end
372
- userhash[:groups] = entry[@user_group_membership_mappings[:map_group_membership_to_user_field]] if get_groups
373
- # to do - if the groups are dns, convert to groupnames
374
- results << userhash
375
- end
376
- results
298
+ # Search for a user in this ldap server
299
+ #
300
+ # @param user[String] the username to search for
301
+ #
302
+ # @param exact[Boolean] if true, force an exact match, otherwise use wildcards
303
+ #
304
+ # @return [Array<Hash>] The mapped LDAP data for all usernames matching the query
305
+ #
306
+ def find_user(user, exact = false)
307
+ raise JSS::NoSuchItemError, 'LDAPServer not yet saved in the JSS' unless @in_jss
308
+ raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/user/#{user}")[:ldap_users]
309
+ exact ? raw.select { |u| u[:username] == user } : raw
377
310
  end
378
311
 
379
- ###
380
- ###
381
- ### @param group[String] the group name to search for
382
- ###
383
- ### @param exact[Boolean] if true, force an exact match, otherwuse use wildcards if @use_wildcards is true
384
- ###
385
- ### @param additional_filter[Net::LDAP::Fliter] an additional filter to be AND'd to the existing filter.
386
- ###
387
- ### @return [Array<Hash>] The @user_group_attrs_to_get for all groups matching the query
388
- ###
389
- def find_group(group, exact = false, additional_filter = nil)
390
-
391
- raise JSS::InvalidConnectionError, "Not connected to LDAP server '#{@name}'. Please use #connect first." unless @connected
392
-
393
- if @use_wildcards and not exact
394
- group_filter = Net::LDAP::Filter.contains(@user_group_mappings[:map_group_name], group)
395
- else
396
- group_filter = Net::LDAP::Filter.eq(@user_group_mappings[:map_group_name], group)
397
- end
398
-
399
- # limit the object classes
400
- ocs = @user_group_mappings[:object_classes].to_s.chomp.split(/,\s*/)
401
- anyall = @user_group_mappings[:map_object_class_to_any_or_all]
402
- oc_filter = Net::LDAP::Filter.eq("objectclass", ocs.shift)
403
- ocs.each do |oc|
404
- if anyall == "any"
405
- oc_filter = oc_filter | Net::LDAP::Filter.eq("objectclass", oc)
406
- else
407
- oc_filter = oc_filter & Net::LDAP::Filter.eq("objectclass", oc)
408
- end
409
- end
410
-
411
- full_filter = oc_filter & group_filter
412
- full_filter = full_filter & additional_filter if additional_filter
413
- treebase = @user_group_mappings[:search_base]
414
- ldap_attribs = @user_group_attrs_to_get.values
415
-
416
- # should we grab membership from the group?
417
- if @user_group_membership_mappings[:user_group_membership_stored_in] == "group object" and \
418
- @user_group_membership_mappings[:map_user_membership_to_group_field]
419
- get_members = true
420
- ldap_attribs << @user_group_membership_mappings[:map_user_membership_to_group_field]
421
- end
422
-
423
- results = []
424
- @connection.search(:base => treebase, :filter => full_filter, :attributes => ldap_attribs ) do |entry|
425
- hash = {:dn => entry.dn}
426
- @user_group_attrs_to_get.each do |k,attr|
427
- hash[k] = entry[attr][0]
428
- end
429
- hash[:members] = entry[@user_group_membership_mappings[:map_user_membership_to_group_field]] if get_members
430
- # to do, if the members are dns, convert to usernames
431
- results << hash
432
- end
433
- results
312
+ # @param group[String] the group name to search for
313
+ #
314
+ # @param exact[Boolean] if true, force an exact match, otherwuse use wildcards
315
+ #
316
+ # @return [Array<Hash>] The groupname and uid for all groups matching the query
317
+ #
318
+ def find_group(group, exact = false)
319
+ raise JSS::NoSuchItemError, 'LDAPServer not yet saved in the JSS' unless @in_jss
320
+ raw = api.get_rsrc("#{RSRC_BASE}/id/#{@id}/group/#{group}")[:ldap_groups]
321
+ exact ? raw.select { |u| u[:groupname] == group } : raw
434
322
  end
435
323
 
436
-
437
- ###
438
- ### @param user[String] the username to check for memebership in the group
439
- ###
440
- ### @param group[String] the group name to see if the user is a member
441
- ###
442
- ### @return [Boolean, nil] is the user a member? Nil if unable to check
443
- ###
444
- ### @todo Implement checking groups membership in 'other' ldap area
445
- ###
324
+ # @param user[String] the username to check for memebership in the group
325
+ #
326
+ # @param group[String] the group name to see if the user is a member
327
+ #
328
+ # @return [Boolean, nil] is the user a member? Nil if unable to check
329
+ #
446
330
  def check_membership(user, group)
447
-
448
- raise JSS::InvalidConnectionError, "Not connected to LDAP server '#{@name}'. Please use #connect first." unless @connected
449
-
450
- found_user = find_user(user, :exact)[0]
451
- found_group = find_group(group, :exact)[0]
452
-
453
- raise JSS::NoSuchItemError, "No user '#{user}' in LDAP." unless found_user
454
- raise JSS::NoSuchItemError, "No group '#{group}' in LDAP." unless found_group
455
-
456
- if @user_group_membership_mappings[:user_group_membership_stored_in] == "group object"
457
- if @user_group_membership_mappings[:map_user_membership_use_dn]
458
- return found_group[:members].include? found_user[:dn]
459
- else
460
- return found_group[:members].include? user
461
- end
462
-
463
-
464
- elsif @user_group_membership_mappings[:user_group_membership_stored_in] == "user object"
465
- if @user_group_membership_mappings[:use_dn]
466
- return found_user[:groups].include? found_group[:dn]
467
- else
468
- return found_user[:groups].include? group
469
- end
470
-
471
-
472
- else
473
- ### To do!!
474
- return nil
475
- # implement a search based on the "other" settings
476
- # This will be 3 searchs
477
- # - one for the username mapping in users
478
- # - one for the gid in groups
479
- # - one for a record linking them in the "other" search base
480
- end
331
+ raise JSS::NoSuchItemError, 'LDAPServer not yet saved in the JSS' unless @in_jss
332
+ self.class.check_membership @id, user, group, api: @api
481
333
  end
482
334
 
483
-
484
- ###
485
- ### The connect to this LDAP server for subsequent use of the {#find_user}, {#find_group}
486
- ### and {#check_membership} methods
487
- ###
488
- ### @param pw[String,Symbol] the LDAP connection password for this server. Can be nil if
489
- ### authentication type is 'none'.
490
- ### If :prompt, the user is promted on the commandline to enter the password for the :user.
491
- ### If :stdin#, the password is read from a line of std in represented by the digit at #,
492
- ### so :stdin3 reads the passwd from the third line of standard input. defaults to line 2,
493
- ### if no digit is supplied. see {JSS.stdin}
494
- ###
495
- ###
496
- ### @return [Boolean] did we connect to the LDAP server with the defined credentials
497
- ###
498
- def connect(pw = nil)
499
-
500
- unless @authentication_type == :anonymous
501
- # how do we get the password?
502
- password = if pw == :prompt
503
- JSS.prompt_for_password "Enter the password for the LDAP connection account '#{@lookup_dn}':"
504
- elsif pw.is_a?(Symbol) and pw.to_s.start_with?('stdin')
505
- pw.to_s =~ /^stdin(\d+)$/
506
- line = $1
507
- line ||= 2
508
- JSS.stdin line
509
- else
510
- pw
511
- end
512
-
513
-
514
- raise JSS::InvalidDataError, "Incorrect password for LDAP connection account '#{@lookup_dn}'" unless @lookup_pw_sha256 == Digest::SHA2.new(256).update(password.to_s).to_s
515
- end # unless
516
-
517
- @connection = Net::LDAP.new :host => @hostname, :port => @port, :auth => {:method => @authentication_type, :username => @lookup_dn, :password => password }
518
-
519
- @connected = true
520
- end # connect
521
-
522
-
523
-
524
- ###
525
- ### Aliases
526
- ###
527
-
528
- alias connected? connected
529
-
530
335
  end # class ldap server
531
336
 
532
337
  end # module