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 +4 -4
- data/CHANGES.md +11 -0
- data/bin/cgrouper +3 -3
- data/lib/jss/api_connection.rb +2 -1
- data/lib/jss/api_object/computer.rb +20 -12
- data/lib/jss/api_object/ldap_server.rb +249 -444
- data/lib/jss/api_object/package.rb +390 -342
- data/lib/jss/api_object/self_servable/icon.rb +5 -1
- data/lib/jss/client.rb +30 -1
- data/lib/jss/client/management_action.rb +1 -1
- data/lib/jss/composer.rb +1 -1
- data/lib/jss/version.rb +1 -1
- metadata +17 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 26f81c239772f5d60e91e894de2d3617e76600a8a259fb8185612ffc7eab2d02
|
4
|
+
data.tar.gz: 849b3a3ef79be5f35594612cca9820a238489e5ceda4ca853e8f1d706e3e48a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/bin/cgrouper
CHANGED
@@ -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/
|
294
|
-
and ~/.
|
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
|
-
|
310
|
+
|
311
311
|
end
|
312
312
|
|
313
313
|
#####################################
|
data/lib/jss/api_connection.rb
CHANGED
@@ -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]
|
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
|
-
|
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
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
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
|
-
|
134
|
-
|
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
|
-
|
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
|
-
|
66
|
+
# the default LDAP port
|
141
67
|
DEFAULT_PORT = 389
|
142
68
|
|
143
|
-
|
144
|
-
SEARCH_SCOPES = [
|
69
|
+
# possible values for search scope
|
70
|
+
SEARCH_SCOPES = ['All Subtrees', 'First Level Only'].freeze
|
145
71
|
|
146
|
-
|
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
|
-
|
150
|
-
REFERRAL_RESPONSES = ['', nil, 'follow', 'ignore']
|
75
|
+
# possible referral responses
|
76
|
+
REFERRAL_RESPONSES = ['', nil, 'follow', 'ignore'].freeze
|
151
77
|
|
152
|
-
|
153
|
-
OBJECT_CLASS_MAPPING_OPTIONS = [
|
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
|
-
|
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
|
-
|
165
|
-
|
174
|
+
# These attributes all come from the :connection hash of the
|
175
|
+
# API data
|
166
176
|
|
167
|
-
|
177
|
+
# @return [String] the hostname of the server
|
168
178
|
attr_reader :hostanme
|
169
179
|
|
170
|
-
|
180
|
+
# @return [Integer] the port for ldap
|
171
181
|
attr_reader :port
|
172
182
|
|
173
|
-
|
183
|
+
# @return [Boolean] should the connection use ssl?
|
174
184
|
attr_reader :use_ssl
|
175
185
|
|
176
|
-
|
186
|
+
# @return [String] what authentication method should be used?
|
177
187
|
attr_reader :authentication_type
|
178
188
|
|
179
|
-
|
189
|
+
# @return [String] the Distinguished Name of the account used for connections/lookups?
|
180
190
|
attr_reader :lookup_dn
|
181
191
|
|
182
|
-
|
192
|
+
# @return [String] the password for the connection/lookup account, as a SHA256 digest.
|
183
193
|
attr_reader :lookup_pw_sha256
|
184
194
|
|
185
|
-
|
195
|
+
# @return [Integer] timeout, in seconds, for opening LDAP connections
|
186
196
|
attr_reader :open_close_timeout
|
187
197
|
|
188
|
-
|
198
|
+
# @return [Integer] timeout, in seconds, for search queries
|
189
199
|
attr_reader :search_timeout
|
190
200
|
|
191
|
-
|
201
|
+
# @return [String] the referral response from the server
|
192
202
|
attr_reader :referral_response
|
193
203
|
|
194
|
-
|
204
|
+
# @return [Boolean] should searches use wildcards?
|
195
205
|
attr_reader :use_wildcards
|
196
206
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
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
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
259
|
-
attr_reader :connected
|
260
|
-
|
261
|
-
#####################################
|
262
|
-
### Constructor
|
266
|
+
# Constructor
|
263
267
|
#####################################
|
264
268
|
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
def initialize
|
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
|
285
|
-
@user_group_mappings = @init_data[:mappings_for_users
|
286
|
-
@user_group_membership_mappings = @init_data[:mappings_for_users
|
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
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
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
|
-
|
439
|
-
|
440
|
-
|
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
|
-
|
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
|