ruby-jss 1.0.2 → 1.0.3b1

Sign up to get free protection for your applications and to get access to all the features.

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