parent
4860738db7
commit
4ca878799b
30 changed files with 964 additions and 519 deletions
@ -0,0 +1,339 @@ |
|||||||
|
// Copyright 2021 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include "src/core/lib/security/authorization/matchers.h" |
||||||
|
|
||||||
|
#include "absl/memory/memory.h" |
||||||
|
#include "absl/strings/str_cat.h" |
||||||
|
#include "absl/strings/str_format.h" |
||||||
|
#include "absl/strings/str_join.h" |
||||||
|
#include "absl/strings/str_split.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
//
|
||||||
|
// StringMatcher
|
||||||
|
//
|
||||||
|
|
||||||
|
absl::StatusOr<StringMatcher> StringMatcher::Create(Type type, |
||||||
|
const std::string& matcher, |
||||||
|
bool case_sensitive) { |
||||||
|
if (type == Type::SAFE_REGEX) { |
||||||
|
RE2::Options options; |
||||||
|
options.set_case_sensitive(case_sensitive); |
||||||
|
auto regex_matcher = absl::make_unique<RE2>(matcher, options); |
||||||
|
if (!regex_matcher->ok()) { |
||||||
|
return absl::InvalidArgumentError( |
||||||
|
"Invalid regex string specified in matcher."); |
||||||
|
} |
||||||
|
return StringMatcher(std::move(regex_matcher), case_sensitive); |
||||||
|
} else { |
||||||
|
return StringMatcher(type, matcher, case_sensitive); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StringMatcher::StringMatcher(Type type, const std::string& matcher, |
||||||
|
bool case_sensitive) |
||||||
|
: type_(type), string_matcher_(matcher), case_sensitive_(case_sensitive) {} |
||||||
|
|
||||||
|
StringMatcher::StringMatcher(std::unique_ptr<RE2> regex_matcher, |
||||||
|
bool case_sensitive) |
||||||
|
: type_(Type::SAFE_REGEX), |
||||||
|
regex_matcher_(std::move(regex_matcher)), |
||||||
|
case_sensitive_(case_sensitive) {} |
||||||
|
|
||||||
|
StringMatcher::StringMatcher(const StringMatcher& other) |
||||||
|
: type_(other.type_), case_sensitive_(other.case_sensitive_) { |
||||||
|
if (type_ == Type::SAFE_REGEX) { |
||||||
|
RE2::Options options; |
||||||
|
options.set_case_sensitive(other.case_sensitive_); |
||||||
|
regex_matcher_ = |
||||||
|
absl::make_unique<RE2>(other.regex_matcher_->pattern(), options); |
||||||
|
} else { |
||||||
|
string_matcher_ = other.string_matcher_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StringMatcher& StringMatcher::operator=(const StringMatcher& other) { |
||||||
|
type_ = other.type_; |
||||||
|
if (type_ == Type::SAFE_REGEX) { |
||||||
|
RE2::Options options; |
||||||
|
options.set_case_sensitive(other.case_sensitive_); |
||||||
|
regex_matcher_ = |
||||||
|
absl::make_unique<RE2>(other.regex_matcher_->pattern(), options); |
||||||
|
} else { |
||||||
|
string_matcher_ = other.string_matcher_; |
||||||
|
} |
||||||
|
case_sensitive_ = other.case_sensitive_; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
StringMatcher::StringMatcher(StringMatcher&& other) noexcept |
||||||
|
: type_(other.type_), case_sensitive_(other.case_sensitive_) { |
||||||
|
if (type_ == Type::SAFE_REGEX) { |
||||||
|
regex_matcher_ = std::move(other.regex_matcher_); |
||||||
|
} else { |
||||||
|
string_matcher_ = std::move(other.string_matcher_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StringMatcher& StringMatcher::operator=(StringMatcher&& other) noexcept { |
||||||
|
type_ = other.type_; |
||||||
|
if (type_ == Type::SAFE_REGEX) { |
||||||
|
regex_matcher_ = std::move(other.regex_matcher_); |
||||||
|
} else { |
||||||
|
string_matcher_ = std::move(other.string_matcher_); |
||||||
|
} |
||||||
|
case_sensitive_ = other.case_sensitive_; |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
bool StringMatcher::operator==(const StringMatcher& other) const { |
||||||
|
if (type_ != other.type_ || case_sensitive_ != other.case_sensitive_) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (type_ == Type::SAFE_REGEX) { |
||||||
|
return regex_matcher_->pattern() == other.regex_matcher_->pattern(); |
||||||
|
} else { |
||||||
|
return string_matcher_ == other.string_matcher_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool StringMatcher::Match(absl::string_view value) const { |
||||||
|
switch (type_) { |
||||||
|
case Type::EXACT: |
||||||
|
return case_sensitive_ ? value == string_matcher_ |
||||||
|
: absl::EqualsIgnoreCase(value, string_matcher_); |
||||||
|
case StringMatcher::Type::PREFIX: |
||||||
|
return case_sensitive_ |
||||||
|
? absl::StartsWith(value, string_matcher_) |
||||||
|
: absl::StartsWithIgnoreCase(value, string_matcher_); |
||||||
|
case StringMatcher::Type::SUFFIX: |
||||||
|
return case_sensitive_ ? absl::EndsWith(value, string_matcher_) |
||||||
|
: absl::EndsWithIgnoreCase(value, string_matcher_); |
||||||
|
case StringMatcher::Type::CONTAINS: |
||||||
|
return case_sensitive_ |
||||||
|
? absl::StrContains(value, string_matcher_) |
||||||
|
: absl::StrContains(absl::AsciiStrToLower(value), |
||||||
|
absl::AsciiStrToLower(string_matcher_)); |
||||||
|
case StringMatcher::Type::SAFE_REGEX: |
||||||
|
return RE2::FullMatch(std::string(value), *regex_matcher_); |
||||||
|
default: |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::string StringMatcher::ToString() const { |
||||||
|
switch (type_) { |
||||||
|
case Type::EXACT: |
||||||
|
return absl::StrFormat("StringMatcher{exact=%s%s}", string_matcher_, |
||||||
|
case_sensitive_ ? "" : ", case_sensitive=false"); |
||||||
|
case Type::PREFIX: |
||||||
|
return absl::StrFormat("StringMatcher{prefix=%s%s}", string_matcher_, |
||||||
|
case_sensitive_ ? "" : ", case_sensitive=false"); |
||||||
|
case Type::SUFFIX: |
||||||
|
return absl::StrFormat("StringMatcher{suffix=%s%s}", string_matcher_, |
||||||
|
case_sensitive_ ? "" : ", case_sensitive=false"); |
||||||
|
case Type::CONTAINS: |
||||||
|
return absl::StrFormat("StringMatcher{contains=%s%s}", string_matcher_, |
||||||
|
case_sensitive_ ? "" : ", case_sensitive=false"); |
||||||
|
case Type::SAFE_REGEX: |
||||||
|
return absl::StrFormat("StringMatcher{safe_regex=%s%s}", |
||||||
|
regex_matcher_->pattern(), |
||||||
|
case_sensitive_ ? "" : ", case_sensitive=false"); |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
// HeaderMatcher
|
||||||
|
//
|
||||||
|
|
||||||
|
absl::StatusOr<HeaderMatcher> HeaderMatcher::Create( |
||||||
|
const std::string& name, Type type, const std::string& matcher, |
||||||
|
int64_t range_start, int64_t range_end, bool present_match, |
||||||
|
bool invert_match) { |
||||||
|
if (static_cast<int>(type) < 5) { |
||||||
|
// Only for EXACT, PREFIX, SUFFIX, SAFE_REGEX and CONTAINS.
|
||||||
|
absl::StatusOr<StringMatcher> string_matcher = |
||||||
|
StringMatcher::Create(static_cast<StringMatcher::Type>(type), matcher, |
||||||
|
/*case_sensitive=*/true); |
||||||
|
if (!string_matcher.ok()) { |
||||||
|
return string_matcher.status(); |
||||||
|
} |
||||||
|
return HeaderMatcher(name, type, std::move(string_matcher.value()), |
||||||
|
invert_match); |
||||||
|
} else if (type == Type::RANGE) { |
||||||
|
if (range_start > range_end) { |
||||||
|
return absl::InvalidArgumentError( |
||||||
|
"Invalid range specifier specified: end cannot be smaller than " |
||||||
|
"start."); |
||||||
|
} |
||||||
|
return HeaderMatcher(name, range_start, range_end, invert_match); |
||||||
|
} else { |
||||||
|
return HeaderMatcher(name, present_match, invert_match); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
HeaderMatcher::HeaderMatcher(const std::string& name, Type type, |
||||||
|
StringMatcher string_matcher, bool invert_match) |
||||||
|
: name_(name), |
||||||
|
type_(type), |
||||||
|
matcher_(std::move(string_matcher)), |
||||||
|
invert_match_(invert_match) {} |
||||||
|
|
||||||
|
HeaderMatcher::HeaderMatcher(const std::string& name, int64_t range_start, |
||||||
|
int64_t range_end, bool invert_match) |
||||||
|
: name_(name), |
||||||
|
type_(Type::RANGE), |
||||||
|
range_start_(range_start), |
||||||
|
range_end_(range_end), |
||||||
|
invert_match_(invert_match) {} |
||||||
|
|
||||||
|
HeaderMatcher::HeaderMatcher(const std::string& name, bool present_match, |
||||||
|
bool invert_match) |
||||||
|
: name_(name), |
||||||
|
type_(Type::PRESENT), |
||||||
|
present_match_(present_match), |
||||||
|
invert_match_(invert_match) {} |
||||||
|
|
||||||
|
HeaderMatcher::HeaderMatcher(const HeaderMatcher& other) |
||||||
|
: name_(other.name_), |
||||||
|
type_(other.type_), |
||||||
|
invert_match_(other.invert_match_) { |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
range_start_ = other.range_start_; |
||||||
|
range_end_ = other.range_end_; |
||||||
|
break; |
||||||
|
case Type::PRESENT: |
||||||
|
present_match_ = other.present_match_; |
||||||
|
break; |
||||||
|
default: |
||||||
|
matcher_ = other.matcher_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
HeaderMatcher& HeaderMatcher::operator=(const HeaderMatcher& other) { |
||||||
|
name_ = other.name_; |
||||||
|
type_ = other.type_; |
||||||
|
invert_match_ = other.invert_match_; |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
range_start_ = other.range_start_; |
||||||
|
range_end_ = other.range_end_; |
||||||
|
break; |
||||||
|
case Type::PRESENT: |
||||||
|
present_match_ = other.present_match_; |
||||||
|
break; |
||||||
|
default: |
||||||
|
matcher_ = other.matcher_; |
||||||
|
} |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
HeaderMatcher::HeaderMatcher(HeaderMatcher&& other) noexcept |
||||||
|
: name_(std::move(other.name_)), |
||||||
|
type_(other.type_), |
||||||
|
invert_match_(other.invert_match_) { |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
range_start_ = other.range_start_; |
||||||
|
range_end_ = other.range_end_; |
||||||
|
break; |
||||||
|
case Type::PRESENT: |
||||||
|
present_match_ = other.present_match_; |
||||||
|
break; |
||||||
|
default: |
||||||
|
matcher_ = std::move(other.matcher_); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
HeaderMatcher& HeaderMatcher::operator=(HeaderMatcher&& other) noexcept { |
||||||
|
name_ = std::move(other.name_); |
||||||
|
type_ = other.type_; |
||||||
|
invert_match_ = other.invert_match_; |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
range_start_ = other.range_start_; |
||||||
|
range_end_ = other.range_end_; |
||||||
|
break; |
||||||
|
case Type::PRESENT: |
||||||
|
present_match_ = other.present_match_; |
||||||
|
break; |
||||||
|
default: |
||||||
|
matcher_ = std::move(other.matcher_); |
||||||
|
} |
||||||
|
return *this; |
||||||
|
} |
||||||
|
|
||||||
|
bool HeaderMatcher::operator==(const HeaderMatcher& other) const { |
||||||
|
if (name_ != other.name_) return false; |
||||||
|
if (type_ != other.type_) return false; |
||||||
|
if (invert_match_ != other.invert_match_) return false; |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
return range_start_ == other.range_start_ && |
||||||
|
range_end_ == other.range_end_; |
||||||
|
case Type::PRESENT: |
||||||
|
return present_match_ == other.present_match_; |
||||||
|
default: |
||||||
|
return matcher_ == other.matcher_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool HeaderMatcher::Match( |
||||||
|
const absl::optional<absl::string_view>& value) const { |
||||||
|
bool match; |
||||||
|
if (type_ == Type::PRESENT) { |
||||||
|
match = value.has_value() == present_match_; |
||||||
|
} else if (!value.has_value()) { |
||||||
|
// All other types fail to match if field is not present.
|
||||||
|
match = false; |
||||||
|
} else if (type_ == Type::RANGE) { |
||||||
|
int64_t int_value; |
||||||
|
match = absl::SimpleAtoi(value.value(), &int_value) && |
||||||
|
int_value >= range_start_ && int_value < range_end_; |
||||||
|
} else { |
||||||
|
match = matcher_.Match(value.value()); |
||||||
|
} |
||||||
|
return match != invert_match_; |
||||||
|
} |
||||||
|
|
||||||
|
std::string HeaderMatcher::ToString() const { |
||||||
|
switch (type_) { |
||||||
|
case Type::RANGE: |
||||||
|
return absl::StrFormat("HeaderMatcher{%s %srange=[%d, %d]}", name_, |
||||||
|
invert_match_ ? "not " : "", range_start_, |
||||||
|
range_end_); |
||||||
|
case Type::PRESENT: |
||||||
|
return absl::StrFormat("HeaderMatcher{%s %spresent=%s}", name_, |
||||||
|
invert_match_ ? "not " : "", |
||||||
|
present_match_ ? "true" : "false"); |
||||||
|
case Type::EXACT: |
||||||
|
case Type::PREFIX: |
||||||
|
case Type::SUFFIX: |
||||||
|
case Type::SAFE_REGEX: |
||||||
|
case Type::CONTAINS: |
||||||
|
return absl::StrFormat("HeaderMatcher{%s %s%s}", name_, |
||||||
|
invert_match_ ? "not " : "", matcher_.ToString()); |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
@ -0,0 +1,158 @@ |
|||||||
|
// Copyright 2021 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H |
||||||
|
#define GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H |
||||||
|
|
||||||
|
#include <grpc/support/port_platform.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "absl/status/statusor.h" |
||||||
|
#include "absl/strings/string_view.h" |
||||||
|
#include "absl/types/optional.h" |
||||||
|
|
||||||
|
#include "re2/re2.h" |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
class StringMatcher { |
||||||
|
public: |
||||||
|
enum class Type { |
||||||
|
EXACT, // value stored in string_matcher_ field
|
||||||
|
PREFIX, // value stored in string_matcher_ field
|
||||||
|
SUFFIX, // value stored in string_matcher_ field
|
||||||
|
SAFE_REGEX, // pattern stored in regex_matcher_ field
|
||||||
|
CONTAINS, // value stored in string_matcher_ field
|
||||||
|
}; |
||||||
|
|
||||||
|
// Creates StringMatcher instance. Returns error status on failure.
|
||||||
|
static absl::StatusOr<StringMatcher> Create(Type type, |
||||||
|
const std::string& matcher, |
||||||
|
bool case_sensitive = true); |
||||||
|
|
||||||
|
StringMatcher() = default; |
||||||
|
StringMatcher(const StringMatcher& other); |
||||||
|
StringMatcher& operator=(const StringMatcher& other); |
||||||
|
StringMatcher(StringMatcher&& other) noexcept; |
||||||
|
StringMatcher& operator=(StringMatcher&& other) noexcept; |
||||||
|
bool operator==(const StringMatcher& other) const; |
||||||
|
|
||||||
|
bool Match(absl::string_view value) const; |
||||||
|
|
||||||
|
std::string ToString() const; |
||||||
|
|
||||||
|
Type type() const { return type_; } |
||||||
|
|
||||||
|
// Valid for EXACT, PREFIX, SUFFIX and CONTAINS
|
||||||
|
const std::string& string_matcher() const { return string_matcher_; } |
||||||
|
|
||||||
|
// Valid for SAFE_REGEX
|
||||||
|
RE2* regex_matcher() const { return regex_matcher_.get(); } |
||||||
|
|
||||||
|
bool case_sensitive() const { return case_sensitive_; } |
||||||
|
|
||||||
|
private: |
||||||
|
StringMatcher(Type type, const std::string& matcher, bool case_sensitive); |
||||||
|
StringMatcher(std::unique_ptr<RE2> regex_matcher, bool case_sensitive); |
||||||
|
|
||||||
|
Type type_ = Type::EXACT; |
||||||
|
std::string string_matcher_; |
||||||
|
std::unique_ptr<RE2> regex_matcher_; |
||||||
|
bool case_sensitive_ = true; |
||||||
|
}; |
||||||
|
|
||||||
|
class HeaderMatcher { |
||||||
|
public: |
||||||
|
enum class Type { |
||||||
|
EXACT, // value stored in StringMatcher field
|
||||||
|
PREFIX, // value stored in StringMatcher field
|
||||||
|
SUFFIX, // value stored in StringMatcher field
|
||||||
|
SAFE_REGEX, // value stored in StringMatcher field
|
||||||
|
CONTAINS, // value stored in StringMatcher field
|
||||||
|
RANGE, // uses range_start and range_end fields
|
||||||
|
PRESENT, // uses present_match field
|
||||||
|
}; |
||||||
|
|
||||||
|
// Make sure that the first five HeaderMatcher::Type enum values match up to
|
||||||
|
// the corresponding StringMatcher::Type enum values, so that it's safe to
|
||||||
|
// convert by casting when delegating to StringMatcher.
|
||||||
|
static_assert(static_cast<StringMatcher::Type>(Type::EXACT) == |
||||||
|
StringMatcher::Type::EXACT, |
||||||
|
""); |
||||||
|
static_assert(static_cast<StringMatcher::Type>(Type::PREFIX) == |
||||||
|
StringMatcher::Type::PREFIX, |
||||||
|
""); |
||||||
|
static_assert(static_cast<StringMatcher::Type>(Type::SUFFIX) == |
||||||
|
StringMatcher::Type::SUFFIX, |
||||||
|
""); |
||||||
|
static_assert(static_cast<StringMatcher::Type>(Type::SAFE_REGEX) == |
||||||
|
StringMatcher::Type::SAFE_REGEX, |
||||||
|
""); |
||||||
|
static_assert(static_cast<StringMatcher::Type>(Type::CONTAINS) == |
||||||
|
StringMatcher::Type::CONTAINS, |
||||||
|
""); |
||||||
|
|
||||||
|
// Creates HeaderMatcher instance. Returns error status on failure.
|
||||||
|
static absl::StatusOr<HeaderMatcher> Create( |
||||||
|
const std::string& name, Type type, const std::string& matcher, |
||||||
|
int64_t range_start = 0, int64_t range_end = 0, |
||||||
|
bool present_match = false, bool invert_match = false); |
||||||
|
|
||||||
|
HeaderMatcher() = default; |
||||||
|
HeaderMatcher(const HeaderMatcher& other); |
||||||
|
HeaderMatcher& operator=(const HeaderMatcher& other); |
||||||
|
HeaderMatcher(HeaderMatcher&& other) noexcept; |
||||||
|
HeaderMatcher& operator=(HeaderMatcher&& other) noexcept; |
||||||
|
bool operator==(const HeaderMatcher& other) const; |
||||||
|
|
||||||
|
const std::string& name() const { return name_; } |
||||||
|
|
||||||
|
Type type() const { return type_; } |
||||||
|
|
||||||
|
// Valid for EXACT, PREFIX, SUFFIX and CONTAINS
|
||||||
|
const std::string& string_matcher() const { |
||||||
|
return matcher_.string_matcher(); |
||||||
|
} |
||||||
|
|
||||||
|
// Valid for SAFE_REGEX
|
||||||
|
RE2* regex_matcher() const { return matcher_.regex_matcher(); } |
||||||
|
|
||||||
|
bool Match(const absl::optional<absl::string_view>& value) const; |
||||||
|
|
||||||
|
std::string ToString() const; |
||||||
|
|
||||||
|
private: |
||||||
|
// For StringMatcher.
|
||||||
|
HeaderMatcher(const std::string& name, Type type, StringMatcher matcher, |
||||||
|
bool invert_match); |
||||||
|
// For RangeMatcher.
|
||||||
|
HeaderMatcher(const std::string& name, int64_t range_start, int64_t range_end, |
||||||
|
bool invert_match); |
||||||
|
// For PresentMatcher.
|
||||||
|
HeaderMatcher(const std::string& name, bool present_match, bool invert_match); |
||||||
|
|
||||||
|
std::string name_; |
||||||
|
Type type_ = Type::EXACT; |
||||||
|
StringMatcher matcher_; |
||||||
|
int64_t range_start_; |
||||||
|
int64_t range_end_; |
||||||
|
bool present_match_; |
||||||
|
bool invert_match_ = false; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
#endif /* GRPC_CORE_LIB_SECURITY_AUTHORIZATION_MATCHERS_H */ |
@ -0,0 +1,218 @@ |
|||||||
|
// Copyright 2021 gRPC authors.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "src/core/lib/security/authorization/matchers.h" |
||||||
|
|
||||||
|
#include <gtest/gtest.h> |
||||||
|
|
||||||
|
namespace grpc_core { |
||||||
|
|
||||||
|
TEST(StringMatcherTest, ExactMatchCaseSensitive) { |
||||||
|
auto string_matcher = |
||||||
|
StringMatcher::Create(StringMatcher::Type::EXACT, |
||||||
|
/*matcher=*/"exact", /*case_sensitive=*/true); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("exact")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Exact")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("exacz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, ExactMatchCaseInsensitive) { |
||||||
|
auto string_matcher = |
||||||
|
StringMatcher::Create(StringMatcher::Type::EXACT, |
||||||
|
/*matcher=*/"exact", /*case_sensitive=*/false); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("Exact")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Exacz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, PrefixMatchCaseSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::PREFIX, |
||||||
|
/*matcher=*/"prefix", |
||||||
|
/*case_sensitive=*/true); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("prefix-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("xx-prefix-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Prefix-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("pre-test")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, PrefixMatchCaseInsensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::PREFIX, |
||||||
|
/*matcher=*/"prefix", |
||||||
|
/*case_sensitive=*/false); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("PREfix-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("xx-PREfix-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("PRE-test")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, SuffixMatchCaseSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SUFFIX, |
||||||
|
/*matcher=*/"suffix", |
||||||
|
/*case_sensitive=*/true); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("test-suffix")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-Suffix")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-suffix-xx")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-suffiz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, SuffixMatchCaseInSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SUFFIX, |
||||||
|
/*matcher=*/"suffix", |
||||||
|
/*case_sensitive=*/false); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("Test-SUFFIX")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Test-SUFFIX-xx")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Test-SUFFIZ")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, InvalidRegex) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX, |
||||||
|
/*matcher=*/"a[b-a]", |
||||||
|
/*case_sensitive=*/true); |
||||||
|
EXPECT_FALSE(string_matcher.ok()); |
||||||
|
EXPECT_EQ(string_matcher.status().code(), absl::StatusCode::kInvalidArgument); |
||||||
|
EXPECT_EQ(string_matcher.status().message(), |
||||||
|
"Invalid regex string specified in matcher."); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, SafeRegexMatchCaseSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX, |
||||||
|
/*matcher=*/"regex.*", |
||||||
|
/*case_sensitive=*/true); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("regex-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("xx-regex-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Regex-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-regex")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, SafeRegexMatchCaseInSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::SAFE_REGEX, |
||||||
|
/*matcher=*/"regex.*", |
||||||
|
/*case_sensitive=*/false); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("regex-test")); |
||||||
|
EXPECT_TRUE(string_matcher->Match("Regex-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("xx-Regex-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-regex")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, ContainsMatchCaseSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::CONTAINS, |
||||||
|
/*matcher=*/"contains", |
||||||
|
/*case_sensitive=*/true); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("test-contains")); |
||||||
|
EXPECT_TRUE(string_matcher->Match("test-contains-test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-Contains")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("test-containz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(StringMatcherTest, ContainsMatchCaseInSensitive) { |
||||||
|
auto string_matcher = StringMatcher::Create(StringMatcher::Type::CONTAINS, |
||||||
|
/*matcher=*/"contains", |
||||||
|
/*case_sensitive=*/false); |
||||||
|
ASSERT_TRUE(string_matcher.ok()); |
||||||
|
EXPECT_TRUE(string_matcher->Match("Test-Contains")); |
||||||
|
EXPECT_TRUE(string_matcher->Match("Test-Contains-Test")); |
||||||
|
EXPECT_FALSE(string_matcher->Match("Test-Containz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, StringMatcher) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::EXACT, |
||||||
|
/*matcher=*/"exact"); |
||||||
|
ASSERT_TRUE(header_matcher.ok()); |
||||||
|
EXPECT_TRUE(header_matcher->Match("exact")); |
||||||
|
EXPECT_FALSE(header_matcher->Match("Exact")); |
||||||
|
EXPECT_FALSE(header_matcher->Match("exacz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, StringMatcherWithInvertMatch) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::EXACT, |
||||||
|
/*matcher=*/"exact", |
||||||
|
/*range_start=*/0, /*range_end=*/0, |
||||||
|
/*present_match=*/false, /*invert_match=*/true); |
||||||
|
ASSERT_TRUE(header_matcher.ok()); |
||||||
|
EXPECT_FALSE(header_matcher->Match("exact")); |
||||||
|
EXPECT_TRUE(header_matcher->Match("Exact")); |
||||||
|
EXPECT_TRUE(header_matcher->Match("exacz")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, InvalidRegex) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::SAFE_REGEX, |
||||||
|
/*matcher=*/"a[b-a]", |
||||||
|
/*range_start=*/0, /*range_end=*/0, |
||||||
|
/*present_match=*/false, /*invert_match=*/true); |
||||||
|
EXPECT_FALSE(header_matcher.ok()); |
||||||
|
EXPECT_EQ(header_matcher.status().code(), absl::StatusCode::kInvalidArgument); |
||||||
|
EXPECT_EQ(header_matcher.status().message(), |
||||||
|
"Invalid regex string specified in matcher."); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, RangeMatcherValidRange) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::RANGE, |
||||||
|
/*matcher=*/"", /*range_start=*/10, |
||||||
|
/*range_end*/ 20); |
||||||
|
ASSERT_TRUE(header_matcher.ok()); |
||||||
|
EXPECT_TRUE(header_matcher->Match("16")); |
||||||
|
EXPECT_TRUE(header_matcher->Match("10")); |
||||||
|
EXPECT_FALSE(header_matcher->Match("3")); |
||||||
|
EXPECT_FALSE(header_matcher->Match("20")); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, RangeMatcherInvalidRange) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::RANGE, |
||||||
|
/*matcher=*/"", /*range_start=*/20, |
||||||
|
/*range_end*/ 10); |
||||||
|
EXPECT_FALSE(header_matcher.ok()); |
||||||
|
EXPECT_EQ(header_matcher.status().code(), absl::StatusCode::kInvalidArgument); |
||||||
|
EXPECT_EQ( |
||||||
|
header_matcher.status().message(), |
||||||
|
"Invalid range specifier specified: end cannot be smaller than start."); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, PresentMatcherTrue) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::PRESENT, |
||||||
|
/*matcher=*/"", /*range_start=*/0, |
||||||
|
/*range_end=*/0, /*present_match=*/true); |
||||||
|
ASSERT_TRUE(header_matcher.ok()); |
||||||
|
EXPECT_TRUE(header_matcher->Match("any_value")); |
||||||
|
EXPECT_FALSE(header_matcher->Match(absl::nullopt)); |
||||||
|
} |
||||||
|
|
||||||
|
TEST(HeaderMatcherTest, PresentMatcherFalse) { |
||||||
|
auto header_matcher = |
||||||
|
HeaderMatcher::Create(/*name=*/"key", HeaderMatcher::Type::PRESENT, |
||||||
|
/*matcher=*/"", /*range_start=*/0, |
||||||
|
/*range_end=*/0, /*present_match=*/false); |
||||||
|
ASSERT_TRUE(header_matcher.ok()); |
||||||
|
EXPECT_FALSE(header_matcher->Match("any_value")); |
||||||
|
EXPECT_TRUE(header_matcher->Match(absl::nullopt)); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace grpc_core
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
::testing::InitGoogleTest(&argc, argv); |
||||||
|
return RUN_ALL_TESTS(); |
||||||
|
} |
Loading…
Reference in new issue