ruby-jss 1.3.3 → 1.4.1

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: ee61895968b4dc50815f33bdd50cd54c1a1fcad700243f4be3cded694c11f745
4
- data.tar.gz: a7a3ce9847874fa7c26c859f6fa26e5a8802b60f42c300f347e2c40b5c38d949
3
+ metadata.gz: ef0484427943d39089bb11eb73af827c5137d79550c34ea5fdf7ca683c7c69f0
4
+ data.tar.gz: '09abd63abb133ee6c34aa2fcb9ae8ccc3e11b3248061ee410223bd8f25f5a7ae'
5
5
  SHA512:
6
- metadata.gz: f95b9d7f49983c9899a18b7e1edb6130250e402a5284e65a9ae64829176d2c3f5fe3d35584cf6dcee85b7486e1a037c8a5ae3ef58b72d169f4ac05cebb5d8858
7
- data.tar.gz: 1b5131ec5bec8b940c4daf1403f9595a4749b0b5d36cfc7bf07a2aca0adbee2446ff44dfe078e8e1651f5fa25ed6725d763ec0f5798d06ec16d70b57ea466b86
6
+ metadata.gz: 962236f6ca861fbfa3f706df0d8d977f15fb1436238f7d9c061ae8043c4148b956fc187a9858f9444b8461cc7d707067278e6a9030c676af26febdc565b0cd0f
7
+ data.tar.gz: 7f7027d207de8ea3fd3d1c010483a9e261d4962857ec48ac6f4df0ba4d38526720ff3700078900e1df2182f191a0aaf7c3284ef3438714e1fbc9118f3aeb03ca
data/CHANGES.md CHANGED
@@ -4,6 +4,53 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## \[1.4.1] - 2020-10-01
8
+
9
+ ### Added
10
+
11
+ - Support for JP API connections to https://tryitout.jamfcloud.com/, the open API test server provided by Jamf. It uses internal tokens, so the /auth endpoint is disabled. Now as with the Classic API, `Jamf::Connection.connect` will accpt any name & password when connecting to that host.
12
+
13
+ ## \[1.4.0] - 2020-09-14
14
+
15
+ ### Added
16
+
17
+ - Class JSS::VPPAccount, implementing the 'vppacconts' endpoint.
18
+
19
+ - Constant JSS::APP_STORE_COUNTRY_CODES, a Hash with keys being the official country names used by the App Store, and values being the two-letter codes for those names. This static Hash is derived from a Jamf Pro API end point, and will be updated as needed. These codes are used by JSS::VPPAccount
20
+
21
+ - Module Method JSS.country_code_match(str) whic allows you to filter the JSS::APP_STORE_COUNTRY_CODES Hash to only those key-value pairs that include the given string.
22
+
23
+ - Mixin Class Method VPPable.all_vpp_device_assignable, returns a Hash of Hashes showing the total, used, and remaining licenses for all members of the target class that are VPP-assignable by device.
24
+
25
+ - Scopable::Scope#in_scope?(machine) Given a JSS::Computer or MobileDevice, or an identifier for one, it is in the scope? WARNING: For scopes that include Jamf Users or User Groups as targets or exclusions, this method may return an incorrect value. See the discussion in the comments/documentation for the Scopable::Scope class under `IMPORTANT - Users & User Groups in Targets and Exclusions`
26
+
27
+ - Scopable::Scope#scoped_machines returns a Hash of ids=>names for all machines in this scope. WARNING: This must instantiate all machines in the target class. It will still be slow, at least the first time for each target class. On the upside, the instantiated machines will be cached, so generating this list for other scopes with the same target class will be much much faster. In tests, with 1600 Computers in the JSS, it took about 7 minutes the first time, but less than 1 second after caching.
28
+ See also the warning for #in_scope? above, which applies here as well.
29
+
30
+ - JSS::Policy objects support 'Policy Retry' via the getter/setter methods #retry_event, #retry_attempts, and #notify_failed_retries. You can only set these values if the #frequency is :once_per_computer. To turn off policy-retry, either set the retry_event to :none, or set the retry_attempts to 0
31
+
32
+ ### Changed
33
+
34
+ - Prettier XML for JSS::APIObject#ppx
35
+
36
+ - Improved JSS::Validate.boolean. Accepts: true, false, 'true', 'false', 'yes', 'no', 't','f', 'y', or 'n' as Strings or Symbols, case insensitive
37
+
38
+ - The JSS::MacApplication class is more fully implemented
39
+
40
+ - JSS::Scopable::Scope now uses the word 'targets' consistently to match the UI's 'Targets' tab. The previous word 'inclusions' still works as before.
41
+
42
+ - When using the Jamf module to access the Jamf Pro API, the minumum JamfPro version is now 10.23.0. WARNING: Like the Jamf Pro API itself, the Jamf module that accesses it is in beta and may have breaking changes at any time.
43
+
44
+ ### Fixed
45
+
46
+ - JSS::ExtensionAttribute: when used as a display field in an AdvancedSearch, the name of the EA in the search result Hash comes from the API as a String (turned into a Symbol) that is the EA name with colons removed and spaces & dashes turned to underscores. Previously ruby-jss didn't remove the colons
47
+
48
+ - Used an XML workaround for the common classic API bug where an XML array comes as a single-item JSON hash. This time in the JSS::User class's user_groups method.
49
+
50
+ - The Jamf Pro API endpoints for /v1/device-enrollment changed to /v1/device-enrollments and /v1/device-enrollment/sync/<id> changed to /v1/device-enrollments/<id>/syncs. /v1/devive-enrollments/syncs.
51
+
52
+ - The Jamf Pro API endpoint for bulk-deleting Departments changed from 'delete-departments' to 'delete-multiple'
53
+
7
54
  ## \[1.3.3] - 2020-08-07
8
55
 
9
56
  ### Fixed
@@ -48,7 +48,7 @@ module Jamf
48
48
  RSRC_BASE = 'uapi'.freeze
49
49
 
50
50
  # The API version must be this or higher
51
- MIN_JAMF_VERSION = Gem::Version.new('10.15.0')
51
+ MIN_JAMF_VERSION = Gem::Version.new('10.23.0')
52
52
 
53
53
  HTTPS_SCHEME = 'https'.freeze
54
54
 
@@ -44,6 +44,15 @@ module Jamf
44
44
  # transaction authorization.
45
45
  AUTH_TOKEN_PFX = 'jamf-token '.freeze
46
46
 
47
+ # Recognize the tryitout server, cuz its /auth endpoint
48
+ # is disabled, and it needs no tokens
49
+ JAMF_TRYITOUT_HOST = "tryitout#{Jamf::Connection::JAMFCLOUD_DOMAIN}".freeze
50
+
51
+ JAMF_TRYITOUT_TOKEN_BODY = {
52
+ token: 'This is a fake token, tryitout.jamfcloud.com uses internal tokens',
53
+ expires: 2000000000000
54
+ }.freeze
55
+
47
56
  # @return [String] The user who generated this token
48
57
  attr_reader :user
49
58
 
@@ -81,7 +90,9 @@ module Jamf
81
90
  @timeout = params[:timeout] || Jamf::Connection::DFT_TIMEOUT
82
91
  @ssl_options = params[:ssl_options] || {}
83
92
 
84
- if params[:pw]
93
+ if @base_url.host == JAMF_TRYITOUT_HOST
94
+ init_jamf_tryitout
95
+ elsif params[:pw]
85
96
  init_from_pw params[:pw]
86
97
  elsif params[:token_string]
87
98
  init_from_token_string params[:token_string]
@@ -90,6 +101,15 @@ module Jamf
90
101
  end
91
102
  end # init
92
103
 
104
+ # Initialize from password
105
+ def init_jamf_tryitout
106
+ @token_response_body = JAMF_TRYITOUT_TOKEN_BODY
107
+ @auth_token = AUTH_TOKEN_PFX + @token_response_body[:token]
108
+ @expires = Jamf::Timestamp.new @token_response_body[:expires]
109
+ @login_time = Jamf::Timestamp.new Time.now
110
+ @valid = true
111
+ end # init_from_pw
112
+
93
113
  # Initialize from password
94
114
  def init_from_pw(pw)
95
115
  resp = token_connection(
@@ -46,7 +46,7 @@ module Jamf
46
46
  RSRC_PATH = 'departments'.freeze
47
47
 
48
48
  # TODO: Jamf - will this be standard for collections?
49
- BULK_DELETE_RSRC = 'delete-departments'.freeze
49
+ BULK_DELETE_RSRC = 'delete-multiple'.freeze
50
50
 
51
51
  # Object Model / Attributes
52
52
  # See APIObject class documentation for details
@@ -52,7 +52,7 @@ module Jamf
52
52
 
53
53
  RSRC_VERSION = 'v1'.freeze
54
54
 
55
- RSRC_PATH = 'device-enrollment'.freeze
55
+ RSRC_PATH = 'device-enrollments'.freeze
56
56
 
57
57
  # Object Model / Attributes
58
58
  # See APIObject class documentation for details
@@ -140,7 +140,7 @@ module Jamf
140
140
 
141
141
  DEVICES_RSRC = 'devices'.freeze
142
142
 
143
- SYNC_RSRC = 'sync'.freeze
143
+ SYNC_RSRC = 'syncs'.freeze
144
144
 
145
145
  LATEST_RSRC = 'latest'.freeze
146
146
 
@@ -279,7 +279,7 @@ module Jamf
279
279
  instance_id = valid_id instance_ident, cnx: cnx
280
280
  raise Jamf::NoSuchItemError "No DeviceEnrollment instance matches '#{instance_ident}'" unless instance_id
281
281
 
282
- rsrc = "#{RSRC_VERSION}/#{RSRC_PATH}/#{SYNC_RSRC}/#{instance_id}"
282
+ rsrc = "#{RSRC_VERSION}/#{RSRC_PATH}/#{instance_id}/#{SYNC_RSRC}"
283
283
  rsrc += "/#{LATEST_RSRC}" if latest
284
284
  else
285
285
  rsrc = "#{RSRC_VERSION}/#{RSRC_PATH}/#{SYNC_RSRC}"
@@ -27,6 +27,6 @@
27
27
  module Jamf
28
28
 
29
29
  ### The version of the Jamf module
30
- VERSION = '0.0.1'.freeze
30
+ VERSION = '0.0.3'.freeze
31
31
 
32
32
  end # module
@@ -1358,7 +1358,10 @@ module JSS
1358
1358
  #
1359
1359
  def ppx
1360
1360
  return nil unless creatable? || updatable?
1361
- REXML::Document.new(rest_xml).write $stdout, 2
1361
+
1362
+ formatter = REXML::Formatters::Pretty.new(2)
1363
+ formatter.compact = true
1364
+ formatter.write(REXML::Document.new(rest_xml), $stdout)
1362
1365
  puts
1363
1366
  end
1364
1367
 
@@ -1691,4 +1694,7 @@ require 'jss/api_object/user'
1691
1694
  require 'jss/api_object/webhook'
1692
1695
  require 'jss/api_object/printer'
1693
1696
 
1697
+ ### Other
1698
+ require 'jss/api_object/app_store_country_codes'
1699
+
1694
1700
  JSS::APIObject.define_identifier_list_methods
@@ -62,6 +62,7 @@ module JSS
62
62
  include JSS::Criteriable
63
63
  include JSS::Sitable
64
64
 
65
+
65
66
  # Class Constants
66
67
  #####################################
67
68
 
@@ -85,6 +86,22 @@ module JSS
85
86
  #
86
87
  attr_reader :search_results
87
88
 
89
+ # @return [Array<String>] the fields to be returned with the search results
90
+ #
91
+ # The API delivers these as an array of Hashes,
92
+ # where each hash has only one key, :name => the name of the fields/ExtAttrib
93
+ # to display. It should probably not have the underlying Hashes, and just
94
+ # be an array of names. This class converts it to just an Array of field names
95
+ # (Strings) for internal use.
96
+ #
97
+ # These fields are returned in the @search_results
98
+ # data along with :id, :name, and other unique identifiers
99
+ # for each found item. In that data, their names have colons removed, abd
100
+ # spaces and dashes converted to underscores, and they are
101
+ # symbolized. See attribute result_display_keys
102
+ #
103
+ attr_reader :display_fields
104
+
88
105
  # @return [Array<Symbol>]
89
106
  #
90
107
  # The search result Hash keys for the {#display_fields} of the search
@@ -95,8 +112,9 @@ module JSS
95
112
  # methods are for.
96
113
  #
97
114
  # However, when those names come back as the Hash Keys of the {#search_results}
98
- # they (inconsistently) have spaces and/or dashes converted to underscores, and,
99
- # the JSON module converts the keys to Symbols, so they don't match the {#display_fields}.
115
+ # they (inconsistently) have spaces and/or dashes converted to underscores,
116
+ # and colons are removed. The JSON module then converts the keys to Symbols,
117
+ # so they don't match the {#display_fields}.
100
118
  #
101
119
  # For example, the display field "Last Check-in" might come back as any of these Symbols:
102
120
  # - :"Last Check-in"
@@ -147,13 +165,13 @@ module JSS
147
165
  # make sure each hash of the search results
148
166
  # has a key matching a standard key.
149
167
  #
150
- @search_results.each do |hash|
151
- hash.keys.each do |key|
152
- std_key = key.to_s.gsub(/ |-/, '_').to_sym
153
- next if hash[std_key]
154
- hash[std_key] = hash[key]
155
- end
156
- end
168
+ # @search_results.each do |hash|
169
+ # hash.keys.each do |key|
170
+ # std_key = key.to_s.gsub(':', '').gsub(/ |-/, '_').to_sym
171
+ # next if hash[std_key]
172
+ # hash[std_key] = hash[key]
173
+ # end
174
+ # end
157
175
  end # init
158
176
 
159
177
  # Public Instance Methods
@@ -231,23 +249,6 @@ module JSS
231
249
  end
232
250
  end
233
251
 
234
- # @return [Array<String>] the fields to be returned with the search results
235
- #
236
- # The API delivers these as an array of Hashes,
237
- # where each hash has only one key, :name => the name of the fields/ExtAttrib
238
- # to display. It should probably not have the underlying Hashes, and just
239
- # be an array of names. This class converts it to just an Array of field names
240
- # (Strings) for internal use.
241
- #
242
- # These fields are returned in the @search_results
243
- # data along with :id, :name, and other unique identifiers
244
- # for each found item. In that data, their names have
245
- # spaces and dashes converted to underscores, and they are
246
- # symbolized.
247
- #
248
- #
249
- attr_reader :display_fields
250
-
251
252
  # Set the list of fields to be retrieved with the
252
253
  # search results.
253
254
  #
@@ -0,0 +1,298 @@
1
+ ### Copyright 2020 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
+ # Return the names and codes containing a given string
30
+ # (case insentitive).
31
+ #
32
+ # @param str [String] the string to look for, must be >=2 chars long
33
+ #
34
+ # @return [Hash] the country names => codes that contain the string.
35
+ #
36
+ def self.country_code_match(str)
37
+ raise ArgumentError, 'Match string must be at least 2 characters long' if str.size < 2
38
+
39
+ str = str.to_s.upcase
40
+ APP_STORE_COUNTRY_CODES.select { |n, c| n.upcase.include?(str) || c.include?(str) }
41
+ end
42
+
43
+ # The official names and two-letter codes for countries known to the App Store.
44
+ # These were retrieved from the Jamf Pro API endpoint in August 2020.
45
+ # They should be updated regularly. To do so using the Jamf Module, just
46
+ # replace this Hash with the one you get from:
47
+ # Jamf::AppStoreCountryCodes.codes_by_name
48
+ #
49
+ APP_STORE_COUNTRY_CODES = {
50
+ 'Afghanistan' => 'AF',
51
+ 'Aland Islands' => 'AX',
52
+ 'Albania' => 'AL',
53
+ 'Algeria' => 'DZ',
54
+ 'American Samoa' => 'AS',
55
+ 'Andorra' => 'AD',
56
+ 'Angola' => 'AO',
57
+ 'Anguilla' => 'AI',
58
+ 'Antarctica' => 'AQ',
59
+ 'Antigua and Barbuda' => 'AG',
60
+ 'Argentina' => 'AR',
61
+ 'Armenia' => 'AM',
62
+ 'Aruba' => 'AW',
63
+ 'Australia' => 'AU',
64
+ 'Austria' => 'AT',
65
+ 'Azerbaijan' => 'AZ',
66
+ 'Bahamas' => 'BS',
67
+ 'Bahrain' => 'BH',
68
+ 'Bangladesh' => 'BD',
69
+ 'Barbados' => 'BB',
70
+ 'Belarus' => 'BY',
71
+ 'Belgium' => 'BE',
72
+ 'Belize' => 'BZ',
73
+ 'Benin' => 'BJ',
74
+ 'Bermuda' => 'BM',
75
+ 'Bhutan' => 'BT',
76
+ 'Bolivia, Plurinational State of' => 'BO',
77
+ 'Bosnia and Herzegovina' => 'BA',
78
+ 'Botswana' => 'BW',
79
+ 'Bouvet Island' => 'BV',
80
+ 'Brazil' => 'BR',
81
+ 'British Indian Ocean Territory' => 'IO',
82
+ 'Brunei Darussalam' => 'BN',
83
+ 'Bulgaria' => 'BG',
84
+ 'Burkina Faso' => 'BF',
85
+ 'Burundi' => 'BI',
86
+ 'Cambodia' => 'KH',
87
+ 'Cameroon' => 'CM',
88
+ 'Canada' => 'CA',
89
+ 'Cape Verde' => 'CV',
90
+ 'Cayman Islands' => 'KY',
91
+ 'Central African Republic' => 'CF',
92
+ 'Chad' => 'TD',
93
+ 'Chile' => 'CL',
94
+ 'China' => 'CN',
95
+ 'Christmas Island' => 'CX',
96
+ 'Cocos (Keeling) Islands' => 'CC',
97
+ 'Colombia' => 'CO',
98
+ 'Comoros' => 'KM',
99
+ 'Congo' => 'CG',
100
+ 'Congo, the Democratic Republic of the' => 'CD',
101
+ 'Cook Islands' => 'CK',
102
+ 'Costa Rica' => 'CR',
103
+ "Cote d'Ivoire" => 'CI',
104
+ 'Croatia' => 'HR',
105
+ 'Cuba' => 'CU',
106
+ 'Cyprus' => 'CY',
107
+ 'Czech Republic' => 'CZ',
108
+ 'Denmark' => 'DK',
109
+ 'Djibouti' => 'DJ',
110
+ 'Dominica' => 'DM',
111
+ 'Dominican Republic' => 'DO',
112
+ 'Ecuador' => 'EC',
113
+ 'Egypt' => 'EG',
114
+ 'El Salvador' => 'SV',
115
+ 'Equatorial Guinea' => 'GQ',
116
+ 'Eritrea' => 'ER',
117
+ 'Estonia' => 'EE',
118
+ 'Ethiopia' => 'ET',
119
+ 'Falkland Islands (Malvinas)' => 'FK',
120
+ 'Faroe Islands' => 'FO',
121
+ 'Fiji' => 'FJ',
122
+ 'Finland' => 'FI',
123
+ 'France' => 'FR',
124
+ 'French Guiana' => 'GF',
125
+ 'French Polynesia' => 'PF',
126
+ 'French Southern Territories' => 'TF',
127
+ 'Gabon' => 'GA',
128
+ 'Gambia' => 'GM',
129
+ 'Georgia' => 'GE',
130
+ 'Germany' => 'DE',
131
+ 'Ghana' => 'GH',
132
+ 'Gibraltar' => 'GI',
133
+ 'Greece' => 'GR',
134
+ 'Greenland' => 'GL',
135
+ 'Grenada' => 'GD',
136
+ 'Guadeloupe' => 'GP',
137
+ 'Guam' => 'GU',
138
+ 'Guatemala' => 'GT',
139
+ 'Guernsey' => 'GG',
140
+ 'Guinea' => 'GN',
141
+ 'Guinea-Bissau' => 'GW',
142
+ 'Guyana' => 'GY',
143
+ 'Haiti' => 'HT',
144
+ 'Heard Island and McDonald Islands' => 'HM',
145
+ 'Holy See (Vatican City State)' => 'VA',
146
+ 'Honduras' => 'HN',
147
+ 'Hong Kong' => 'HK',
148
+ 'Hungary' => 'HU',
149
+ 'Iceland' => 'IS',
150
+ 'India' => 'IN',
151
+ 'Indonesia' => 'ID',
152
+ 'Iran, Islamic Republic of' => 'IR',
153
+ 'Iraq' => 'IQ',
154
+ 'Ireland' => 'IE',
155
+ 'Isle of Man' => 'IM',
156
+ 'Israel' => 'IL',
157
+ 'Italy' => 'IT',
158
+ 'Jamaica' => 'JM',
159
+ 'Japan' => 'JP',
160
+ 'Jersey' => 'JE',
161
+ 'Jordan' => 'JO',
162
+ 'Kazakhstan' => 'KZ',
163
+ 'Kenya' => 'KE',
164
+ 'Kiribati' => 'KI',
165
+ "Korea, Democratic People's Republic of" => 'KP',
166
+ 'Korea, Republic of' => 'KR',
167
+ 'Kuwait' => 'KW',
168
+ 'Kyrgyzstan' => 'KG',
169
+ "Lao People's Democratic Republic" => 'LA',
170
+ 'Latvia' => 'LV',
171
+ 'Lebanon' => 'LB',
172
+ 'Lesotho' => 'LS',
173
+ 'Liberia' => 'LR',
174
+ 'Libyan Arab Jamahiriya' => 'LY',
175
+ 'Liechtenstein' => 'LI',
176
+ 'Lithuania' => 'LT',
177
+ 'Luxembourg' => 'LU',
178
+ 'Macao' => 'MO',
179
+ 'Macedonia, the former Yugoslav Republic of' => 'MK',
180
+ 'Madagascar' => 'MG',
181
+ 'Malawi' => 'MW',
182
+ 'Malaysia' => 'MY',
183
+ 'Maldives' => 'MV',
184
+ 'Mali' => 'ML',
185
+ 'Malta' => 'MT',
186
+ 'Marshall Islands' => 'MH',
187
+ 'Martinique' => 'MQ',
188
+ 'Mauritania' => 'MR',
189
+ 'Mauritius' => 'MU',
190
+ 'Mayotte' => 'YT',
191
+ 'Mexico' => 'MX',
192
+ 'Micronesia, Federated States of' => 'FM',
193
+ 'Moldova, Republic of' => 'MD',
194
+ 'Monaco' => 'MC',
195
+ 'Mongolia' => 'MN',
196
+ 'Montenegro' => 'ME',
197
+ 'Montserrat' => 'MS',
198
+ 'Morocco' => 'MA',
199
+ 'Mozambique' => 'MZ',
200
+ 'Myanmar' => 'MM',
201
+ 'Namibia' => 'NA',
202
+ 'Nauru' => 'NR',
203
+ 'Nepal' => 'NP',
204
+ 'Netherlands' => 'NL',
205
+ 'Netherlands Antilles' => 'AN',
206
+ 'New Caledonia' => 'NC',
207
+ 'New Zealand' => 'NZ',
208
+ 'Nicaragua' => 'NI',
209
+ 'Niger' => 'NE',
210
+ 'Nigeria' => 'NG',
211
+ 'Niue' => 'NU',
212
+ 'Norfolk Island' => 'NF',
213
+ 'Northern Mariana Islands' => 'MP',
214
+ 'Norway' => 'NO',
215
+ 'Oman' => 'OM',
216
+ 'Pakistan' => 'PK',
217
+ 'Palau' => 'PW',
218
+ 'Palestinian Territory, Occupied' => 'PS',
219
+ 'Panama' => 'PA',
220
+ 'Papua New Guinea' => 'PG',
221
+ 'Paraguay' => 'PY',
222
+ 'Peru' => 'PE',
223
+ 'Philippines' => 'PH',
224
+ 'Pitcairn' => 'PN',
225
+ 'Poland' => 'PL',
226
+ 'Portugal' => 'PT',
227
+ 'Puerto Rico' => 'PR',
228
+ 'Qatar' => 'QA',
229
+ 'Reunion' => 'RE',
230
+ 'Romania' => 'RO',
231
+ 'Russian Federation' => 'RU',
232
+ 'Rwanda' => 'RW',
233
+ 'Saint Barthelemy' => 'BL',
234
+ 'Saint Helena, Ascension and Tristan da Cunha' => 'SH',
235
+ 'Saint Kitts and Nevis' => 'KN',
236
+ 'Saint Lucia' => 'LC',
237
+ 'Saint Martin (French part)' => 'MF',
238
+ 'Saint Pierre and Miquelon' => 'PM',
239
+ 'Saint Vincent and the Grenadines' => 'VC',
240
+ 'Samoa' => 'WS',
241
+ 'San Marino' => 'SM',
242
+ 'Sao Tome and Principe' => 'ST',
243
+ 'Saudi Arabia' => 'SA',
244
+ 'Senegal' => 'SN',
245
+ 'Serbia' => 'RS',
246
+ 'Seychelles' => 'SC',
247
+ 'Sierra Leone' => 'SL',
248
+ 'Singapore' => 'SG',
249
+ 'Slovakia' => 'SK',
250
+ 'Slovenia' => 'SI',
251
+ 'Solomon Islands' => 'SB',
252
+ 'Somalia' => 'SO',
253
+ 'South Africa' => 'ZA',
254
+ 'South Georgia and the South Sandwich Islands' => 'GS',
255
+ 'Spain' => 'ES',
256
+ 'Sri Lanka' => 'LK',
257
+ 'Sudan' => 'SD',
258
+ 'Suriname' => 'SR',
259
+ 'Svalbard and Jan Mayen' => 'SJ',
260
+ 'Swaziland' => 'SZ',
261
+ 'Sweden' => 'SE',
262
+ 'Switzerland' => 'CH',
263
+ 'Syrian Arab Republic' => 'SY',
264
+ 'Taiwan, Province of China' => 'TW',
265
+ 'Tajikistan' => 'TJ',
266
+ 'Tanzania, United Republic of' => 'TZ',
267
+ 'Thailand' => 'TH',
268
+ 'Timor-Leste' => 'TL',
269
+ 'Togo' => 'TG',
270
+ 'Tokelau' => 'TK',
271
+ 'Tonga' => 'TO',
272
+ 'Trinidad and Tobago' => 'TT',
273
+ 'Tunisia' => 'TN',
274
+ 'Turkey' => 'TR',
275
+ 'Turkmenistan' => 'TM',
276
+ 'Turks and Caicos Islands' => 'TC',
277
+ 'Tuvalu' => 'TV',
278
+ 'Uganda' => 'UG',
279
+ 'Ukraine' => 'UA',
280
+ 'United Arab Emirates' => 'AE',
281
+ 'United Kingdom' => 'GB',
282
+ 'United States' => 'US',
283
+ 'United States Minor Outlying Islands' => 'UM',
284
+ 'Uruguay' => 'UY',
285
+ 'Uzbekistan' => 'UZ',
286
+ 'Vanuatu' => 'VU',
287
+ 'Venezuela, Bolivarian Republic of' => 'VE',
288
+ 'Viet Nam' => 'VN',
289
+ 'Virgin Islands, British' => 'VG',
290
+ 'Virgin Islands, U.S.' => 'VI',
291
+ 'Wallis and Futuna' => 'WF',
292
+ 'Western Sahara' => 'EH',
293
+ 'Yemen' => 'YE',
294
+ 'Zambia' => 'ZM',
295
+ 'Zimbabwe' => 'ZW'
296
+ }.freeze
297
+
298
+ end # JSS