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