lib/jamf/validate.rb in ruby-jss-1.6.4 vs lib/jamf/validate.rb in ruby-jss-2.0.0a10
- old
+ new
@@ -1,6 +1,6 @@
-# Copyright 2020 Pixar
+# Copyright 2022 Pixar
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
@@ -20,244 +20,205 @@
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
#
-# The Module
module Jamf
- # A collection of methods for validating values. Mostly for
- # ensuring the validity of data being set as attributes of APIObject
- # subclass instances.
+ # A collection of methods for validating values.
#
# Some of these methods can take multiple input types, such as a String
# or an Array. All of them will either raise an exception
# if the value isn't valid, or will return a standardized form of the input
# (e.g. an Array, even if given a String)
#
module Validate
+ # Raise an invalid data error
+ def self.raise_invalid_data_error(msg)
+ raise Jamf::InvalidDataError, msg.strip
+ end
+
+ extend Jamf::OAPIValidate
+
# The regular expression that matches a valid MAC address.
MAC_ADDR_RE = /^[a-f0-9]{2}(:[a-f0-9]{2}){5}$/i.freeze
+ # The Regexp that matches a valid IPv4 address
+ IPV4_ADDR_RE = /^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)(\.|$)){4}/.freeze
+
+ # the regular expression that matches a valid UDID/UUID
+ UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.freeze
+
# Validate the format and content of a MAC address
#
# @param val[String] The value to validate
#
# @param msg[String] A custom error message when the value is invalid
#
# @return [String] The valid value
#
- def self.mac_address(val, msg = nil)
- msg ||= "Not a valid MAC address: '#{val}'"
- raise Jamf::InvalidDataError, msg unless val =~ MAC_ADDR_RE
+ def self.mac_address(val, msg: nil)
+ return val if val =~ MAC_ADDR_RE
- val
+ raise_invalid_data_error(msg || "Not a valid MAC address: '#{val}'")
end
# Validate the format and content of an IPv4 address
#
# @param val[String] The value to validate
#
# @param msg[String] A custom error message when the value is invalid
#
# @return [String] The valid value
#
- def self.ip_address(val, msg = nil)
- msg ||= "Not a valid IPv4 address: '#{val}'"
- ok = true
- parts = val.strip.split '.'
- ok = false unless parts.size == 4
- parts.each { |p| ok = false unless p.j_integer? && p.to_i < 256 && p.to_i >= 0 }
- raise Jamf::InvalidDataError, msg unless ok
+ def self.ip_address(val, msg: nil)
+ val = val.strip
+ return val if val =~ IPV4_ADDR_RE
- val
+ raise_invalid_data_error(msg || "Not a valid IPv4 address: '#{val}'")
end
- # Does a given JSONObject class have a given JSON attribute?
+ # Validate that a value doesn't already exist for a given identifier of a
+ # given class
#
- # @param klass [<JSONObject] A class descended from JSONObject
- #
- # @param attr_name [Symbol] The attribute to validate
- #
- # @return [Symbol] The valid attribute
- #
- def self.json_attribute_name(klass, attr_name)
- raise "#{klass} is not a descendent of JSONObject" unless klass < Jamf::JSONObject
-
- raise Jamf::NoSuchItemError, "No attribute #{attr_name} for class #{klass}" unless klass::OBJECT_MODEL.key? attrib
-
- attr_name
- end
-
- # Does a value exist in a given enum array?
- #
- # @param klass [<JSONObject] A class descended from JSONObject
- #
- # @param attr_name [Symbol] The attribute to validate
- #
- # @return [Symbol] The valid attribute
- #
- def self.in_enum(val, enum)
- raise Jamf::InvalidDataError, "Value must be one of: #{enum.join ', '}" unless enum.include? val
-
- val
- end
-
- # Validate that a value doesn't already exist for a given identifier of
- # a given CollectionResource class
- #
# e.g. when klass = Jamf::Computer, identifier = :name, and val = 'foo'
- # will raise an error when a computer named 'foo' already exists
+ # will raise an error when a computer named 'foo' exists
#
# Otherwise returns val.
#
- # @param val[Object] The value to check for uniqueness
+ # @param klass[Jamf::APIObject] A subclass of Jamf::APIObject, e.g. Jamf::Computer
#
- # @param klass[Jamf::CollectionResource] A descendent of Jamf::CollectionResource, e.g. Jamf::Computer
+ # @param identifier[Symbol] One of the keys of an Item of the class's #all Array
#
- # @param identifier[Symbol] One of the values of klass.identifiers
+ # @param val[Object] The value to check for uniqueness
#
# @param msg[String] A custom error message when the value is invalid
#
- # @param cnx[Jamf::Connection] The api connection to use for validation
+ # @param cnx [Jamf::Connection] The api connection to use for validation
#
# @return [Object] the validated unique value
#
- def self.doesnt_exist(val, klass, identifier, msg = nil, cnx: Jamf.cnx)
- msg ||= "A #{klass} already exists with #{identifier} '#{val}'"
+ def self.doesnt_already_exist(klass, identifier, val, msg: nil, api: Jamf.cnx)
+ return val unless klass.all(:refresh, cnx: cnx).map { |i| i[identifier] }.include? val
- raise Jamf::InvalidDataError, "No identifier '#{identifier}' for #{klass}" unless klass.identifiers.include? identifier
+ key = klass.real_lookup_key identifier
- return val unless klass.send("all_#{identifier}s", :refresh, cnx: cnx).include? val
+ # use map_all_ids_to cuz it works with any identifer, even non-existing
+ existing_values = klass.map_all_ids_to(key, cnx: cnx).values
+ matches = existing_values.select { |existing_val| existing_val.casecmp? val }
+ return val if matches.empty?
- raise Jamf::AlreadyExistsError, msg
+ raise_invalid_data_error(msg || "A #{klass} already exists with #{identifier} '#{val}'")
end
- TRUE_FALSE = [true, false].freeze
-
- # Confirm that the given value is a boolean value, accepting
- # strings and symbols and returning real booleans as needed
- # Accepts: true, false, 'true', 'false', 'yes', 'no', 't','f', 'y', or 'n'
- # as strings or symbols, case insensitive
+ # validate that the given value is a non-empty string
#
- # @param val [Boolean,String,Symbol] The value to validate
+ # @param val [Object] the thing to validate
#
# @param msg[String] A custom error message when the value is invalid
#
- # @return [Boolean] the valid boolean
+ # @return [String] the valid non-empty string
#
- def self.boolean(val, msg = 'Value must be true or false, or equivalent string or symbol')
- return val if TRUE_FALSE.include? val
- return true if val.to_s =~ /^(t(rue)?|y(es)?)$/i
- return false if val.to_s =~ /^(f(alse)?|no?)$/i
+ def self.non_empty_string(val, attr_name: nil, msg: nil)
+ return val if val.is_a?(String) && !val.empty?
- raise Jamf::InvalidDataError, msg
+ raise_invalid_data_error(msg || "#{attr_name} value must be a non-empty String")
end
# Confirm that a value provided is an integer or a string version
# of an integer, and return the string version
#
# The JPAPI specs say that all IDs are integers in strings
- # tho, the endpoints are still implementing that in different versions.
+ # tho, some endpoints are still using actual integers.
#
# @param val[Object] the value to validate
#
# @param msg[String] A custom error message when the value is invalid
#
# @return [String] the valid integer-in-a-string
#
- def self.j_id(val, msg = 'Value must be an Integer or an Integer in a String, e.g. "42"')
+ def self.j_id(val, attr_name: nil, msg: nil)
case val
when Integer
return val.to_s
when String
return val if val.j_integer?
end
- raise Jamf::InvalidDataError, msg
+ raise_invalid_data_error(msg || "#{attr_name} value must be an Integer or an Integer in a String, e.g. \"42\"")
end
- # Confirm that a value is an Integer or a String representation of an
- # Integer. Return the integer, or raise an error
+ # validate that the given value is a valid uuid string
#
- # @param val[Object] the value to validate
+ # @param val [Object] the thing to validate
#
# @param msg[String] A custom error message when the value is invalid
#
- # @return [Integer] the valid integer
+ # @return [String] the valid uuid string
#
- def self.integer(val, msg = 'Value must be an Integer')
- val = val.to_i if val.is_a?(String) && val.j_integer?
- raise Jamf::InvalidDataError, msg unless val.is_a? Integer
+ def self.uuid(val, msg: nil)
+ return val if val.is_a?(String) && val =~ UUID_RE
- val
+ raise_invalid_data_error(msg || 'value must be valid uuid')
end
- # Confirm that a value is a Float or a String representation of a Float.
- # Return the Float, or raise an error
+ # validate that the given value is an integer in the Jamf::IBeacon::MAJOR_MINOR_RANGE
#
- # @param val[Object] the value to validate
+ # @param val [Object] the thing to validate
#
# @param msg[String] A custom error message when the value is invalid
#
- # @return [Float] the valid float
+ # @return [String] the valid integer
#
- def self.float(val, msg = 'Value must be a Floating Point number')
- val = val.to_f if val.is_a?(String) && val.j_float?
- raise Jamf::InvalidDataError, msg unless val.is_a? Float
+ def self.ibeacon_major_minor(val, msg: nil)
+ val = val.to_i if val.is_a?(String) && val.jss_integer?
+ ok = val.is_a? Integer
+ ok = Jamf::IBeacon::MAJOR_MINOR_RANGE.include? val if ok
+ return val if ok
- val
+ raise_invalid_data_error(msg || "value must be an integer in the range #{Jamf::IBeacon::MAJOR_MINOR_RANGE}")
end
- # Confirm that a value is a string, symbol, or nil,
- # all of which will be returned as a string
+ # validate a country name or code from Jamf::APP_STORE_COUNTRY_CODES
+ # returning the validated code, or raising an error
#
- # @param val[Object] the value to validate
+ # @param country[String] The country name or code
#
# @param msg[String] A custom error message when the value is invalid
#
- # @return [String] the valid String
+ # @return [String] the valid two-letter country code
#
- def self.string(val, msg = 'Value must be a String')
- return Jamf::BLANK if val.nil?
+ def self.app_store_country_code(country, msg: nil)
+ country = country.to_s.upcase
+ return country if Jamf::APP_STORE_COUNTRY_CODES.value? country
- val = val.to_s if val.is_a? Symbol
- raise Jamf::InvalidDataError, msg unless val.is_a? String
+ Jamf::APP_STORE_COUNTRY_CODES.each do |name, code|
+ return code if name.upcase == country
+ end
- val
+ raise_invalid_data_error(msg || "Unknown country name or code '#{country}'. See Jamf::APP_STORE_COUNTRY_CODES or JSS.country_code_match(str)")
end
- # validate that the given value is a non-empty string
- # Symbols are accepted and returned as strings
+ # validate an email address - must match the RegEx /^\S+@\S+\.\S+$/
+ # i.e.:
+ # 1 or more non-whitespace chars, followed by
+ # an @ character, followed by
+ # 1 or more non-whitespace chars, followed by
+ # a dot, followed by
+ # 1 or more non-whitespace chars
#
- # @param val [Object] the thing to validate
+ # @param email[String] The email address
#
# @param msg[String] A custom error message when the value is invalid
#
- # @return [String] the valid non-empty string
+ # @return [String] the validly formatted email address
#
- def self.non_empty_string(val, msg = 'value must be a non-empty String')
- val = val.to_s if val.is_a? Symbol
- raise Jamf::InvalidDataError, msg unless val.is_a?(String) && !val.empty?
+ def self.email_address(email, msg: nil)
+ email = email.to_s
+ return email if email =~ /^\S+@\S+\.\S+$/
- val
+ raise_invalid_data_error(msg || "'#{email}' is not formatted as a valid email address")
end
- SCRIPT_SHEBANG = '#!'.freeze
-
- # validate that the given value is a string that starts with #!
- #
- # @param val [Object] the thing to validate
- #
- # @param msg[String] A custom error message when the value is invalid
- #
- # @return [String] the validated string
- #
- def self.script_contents(val, msg = "value must be a String starting with '#!'")
- raise Jamf::InvalidDataError, msg unless val.is_a?(String) && val.start_with?(SCRIPT_SHEBANG)
-
- val
- end
-
end # module validate
-end # module JSS
+end # module Jamf