module Mongo::Auth::StringPrep

This namespace contains all behavior related to string preparation (RFC 3454). It's used to implement SCRAM-SHA-256 authentication, which is available in MongoDB server versions 4.0 and up.

@since 2.6.0

Public Instance Methods

prepare(data, mappings, prohibited, options = {}) click to toggle source

Prepare a string given a set of mappings and prohibited character tables.

@example Prepare a string.

StringPrep.prepare("some string",
                   StringPrep::Profiles::SASL::MAPPINGS,
                   StringPrep::Profiles::SASL::PROHIBITED,
                   normalize: true, bidi: true)

@param [ String ] data The string to prepare. @param [ Array ] mappings A list of mappings to apply to the data. @param [ Array ] prohibited A list of prohibited character lists to ensure the data doesn't

contain after mapping and normalizing the data.

@param [ Hash ] options Optional operations to perform during string preparation.

@option options [ Boolean ] :normalize Whether or not to apply Unicode normalization to the

data.

@option options [ Boolean ] :bidi Whether or not to ensure that the data contains valid

bidirectional input.

@raise [ Error::FailedStringPrepValidation ] If stringprep validations fails.

@since 2.6.0

# File lib/mongo/auth/stringprep.rb, line 50
def prepare(data, mappings, prohibited, options = {})
  apply_maps(data, mappings).tap do |mapped|
    normalize!(mapped) if options[:normalize]
    check_prohibited!(mapped, prohibited)
    check_bidi!(mapped) if options[:bidi]
  end
end

Private Instance Methods

apply_maps(data, mappings) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 60
def apply_maps(data, mappings)
  data.each_char.inject('') do |out, c|
    out << mapping(c.ord, mappings)
  end
end
check_bidi!(out) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 66
def check_bidi!(out)
  if out.each_char.any? { |c| table_contains?(Tables::C8, c) }
    raise Mongo::Error::FailedStringPrepValidation.new(Error::FailedStringPrepValidation::INVALID_BIDIRECTIONAL)
  end

  if out.each_char.any? { |c| table_contains?(Tables::D1, c) }
    if out.each_char.any? { |c| table_contains?(Tables::D2, c) }
      raise Mongo::Error::FailedStringPrepValidation.new(Error::FailedStringPrepValidation::INVALID_BIDIRECTIONAL)
    end

    unless table_contains?(Tables::D1, out[0]) && table_contains?(Tables::D1, out[-1])
      raise Mongo::Error::FailedStringPrepValidation.new(Error::FailedStringPrepValidation::INVALID_BIDIRECTIONAL)
    end
  end
end
check_prohibited!(out, prohibited) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 82
def check_prohibited!(out, prohibited)
  out.each_char do |c|
    prohibited.each do |table|
      if table_contains?(table, c)
        raise Error::FailedStringPrepValidation.new(Error::FailedStringPrepValidation::PROHIBITED_CHARACTER)
      end
    end
  end
end
mapping(c, mappings) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 92
def mapping(c, mappings)
  m = mappings.find { |m| m.has_key?(c) }
  mapped = (m && m[c]) || [c]
  mapped.map { |i| i.chr(Encoding::UTF_8) }.join
end
normalize!(out) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 98
def normalize!(out)
  if String.method_defined?(:unicode_normalize!)
    out.unicode_normalize!(:nfkc)
  else
    require 'mongo/auth/stringprep/unicode_normalize/normalize'
    out.replace(UnicodeNormalize.normalize(out, :nfkc))
  end
end
table_contains?(table, c) click to toggle source
# File lib/mongo/auth/stringprep.rb, line 107
def table_contains?(table, c)
  table.any? do |r|
    r.member?(c.ord)
  end
end