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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
# frozen_string_literal: true
# rubocop:todo all
# Copyright (C) 2019-2020 MongoDB Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
module Mongo
module Crypt
# A Context object initialized for explicit encryption
#
# @api private
class ExplicitEncryptionContext < Context
# Create a new ExplicitEncryptionContext object
#
# @param [ Mongo::Crypt::Handle ] mongocrypt a Handle that
# wraps a mongocrypt_t object used to create a new mongocrypt_ctx_t
# @param [ ClientEncryption::IO ] io A instance of the IO class
# that implements driver I/O methods required to run the
# state machine
# @param [ BSON::Document ] doc A document to encrypt
#
# @param [ Hash ] options
# @option options [ BSON::Binary ] :key_id A BSON::Binary object of type
# :uuid representing the UUID of the data key to use for encryption.
# @option options [ String ] :key_alt_name The alternate name of the data key
# that will be used to encrypt the value.
# @option options [ String ] :algorithm The algorithm used to encrypt the
# value. Valid algorithms are "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic",
# "AEAD_AES_256_CBC_HMAC_SHA_512-Random", "Indexed", "Unindexed", "Range".
# @option options [ Integer | nil ] :contention_factor Contention factor
# to be applied if encryption algorithm is set to "Indexed". If not
# provided, it defaults to a value of 0. Contention factor should be set
# only if encryption algorithm is set to "Indexed".
# @option options [ String | nil ] query_type Query type to be applied
# if encryption algorithm is set to "Indexed" or "Range".
# Allowed values are "equality" and "range".
# @option options [ Hash | nil ] :range_opts Specifies index options for
# a Queryable Encryption field supporting "range" queries.
# Allowed options are:
# - :min
# - :max
# - :trim_factor
# - :sparsity
# - :precision
# min, max, trim_factor, sparsity, and precision must match the values set in
# the encryptedFields of the destination collection.
# For double and decimal128, min/max/precision must all be set,
# or all be unset.
#
# @note The Range algorithm is experimental only. It is not intended for
# public use.
#
# @raise [ ArgumentError|Mongo::Error::CryptError ] If invalid options are provided
def initialize(mongocrypt, io, doc, options = {})
super(mongocrypt, io)
set_key_opts(options)
set_algorithm_opts(options)
init(doc)
end
def init(doc)
Binding.ctx_explicit_encrypt_init(self, doc)
end
private
def set_key_opts(options)
if options[:key_id].nil? && options[:key_alt_name].nil?
raise ArgumentError.new(
'The :key_id and :key_alt_name options cannot both be nil. ' +
'Specify a :key_id option or :key_alt_name option (but not both)'
)
end
if options[:key_id] && options[:key_alt_name]
raise ArgumentError.new(
'The :key_id and :key_alt_name options cannot both be present. ' +
'Identify the data key by specifying its id with the :key_id ' +
'option or specifying its alternate name with the :key_alt_name option'
)
end
if options[:key_id]
set_key_id(options[:key_id])
elsif options[:key_alt_name]
set_key_alt_name(options[:key_alt_name])
end
end
def set_key_id(key_id)
unless key_id.is_a?(BSON::Binary) &&
key_id.type == :uuid
raise ArgumentError.new(
"Expected the :key_id option to be a BSON::Binary object with " +
"type :uuid. #{key_id} is an invalid :key_id option"
)
end
Binding.ctx_setopt_key_id(self, key_id.data)
end
def set_key_alt_name(key_alt_name)
unless key_alt_name.is_a?(String)
raise ArgumentError.new(':key_alt_name option must be a String')
end
Binding.ctx_setopt_key_alt_names(self, [key_alt_name])
end
def set_algorithm_opts(options)
Binding.ctx_setopt_algorithm(self, options[:algorithm])
if %w(Indexed Range).include?(options[:algorithm])
if options[:contention_factor]
Binding.ctx_setopt_contention_factor(self, options[:contention_factor])
end
if options[:query_type]
Binding.ctx_setopt_query_type(self, options[:query_type])
end
else
if options[:contention_factor]
raise ArgumentError.new(':contention_factor is allowed only for "Indexed" or "Range" algorithms')
end
if options[:query_type]
raise ArgumentError.new(':query_type is allowed only for "Indexed" or "Range" algorithms')
end
end
if options[:algorithm] == 'Range'
Binding.ctx_setopt_algorithm_range(self, convert_range_opts(options[:range_opts]))
end
end
def convert_range_opts(range_opts)
range_opts.dup.tap do |opts|
if opts[:sparsity] && !opts[:sparsity].is_a?(BSON::Int64)
opts[:sparsity] = BSON::Int64.new(opts[:sparsity])
end
if opts[:trim_factor]
opts[:trimFactor] = opts.delete(:trim_factor)
end
end
end
end
end
end
|