| // Copyright 2026 Google LLC |
| // |
| // 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 |
| // |
| // https://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 "utils/distinguished_name.h" |
| |
| #include "absl/status/status.h" |
| #include "absl/status/status_matchers.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| |
| namespace { |
| |
| using ::absl_testing::IsOkAndHolds; |
| using ::absl_testing::StatusIs; |
| using ::credentio::ParseDistinguishedName; |
| using ::testing::IsEmpty; |
| using ::testing::Pair; |
| using ::testing::UnorderedElementsAre; |
| |
| TEST(ParseDn, EmptyString) { |
| EXPECT_THAT(ParseDistinguishedName(""), IsOkAndHolds(IsEmpty())); |
| } |
| |
| TEST(ParseDn, SingleValue) { |
| EXPECT_THAT(ParseDistinguishedName("CN=Foo"), |
| IsOkAndHolds(UnorderedElementsAre(Pair("CN", "Foo")))); |
| } |
| |
| TEST(ParseDn, MultipleValues) { |
| EXPECT_THAT(ParseDistinguishedName("CN=Foo,O=Gizmotron,OU=Widgets Division"), |
| IsOkAndHolds(UnorderedElementsAre( |
| Pair("CN", "Foo"), Pair("O", "Gizmotron"), |
| Pair("OU", "Widgets Division")))); |
| } |
| |
| TEST(ParseDn, BackslashEscapeSequences) { |
| EXPECT_THAT(ParseDistinguishedName( |
| R"(CN=Foo\2fBar\2FBaz,O=P\=NP?,OU=\57idgets Division)"), |
| IsOkAndHolds(UnorderedElementsAre( |
| Pair("CN", R"(Foo/Bar/Baz)"), Pair("O", R"(P=NP?)"), |
| Pair("OU", R"(Widgets Division)")))); |
| } |
| |
| TEST(ParseDn, TrailingBackslash) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo\)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, InvalidBackslashHex) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=F\9j)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, UnrecognizedBackslashEscape) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Fo\o)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, MissingEquals) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo,OU)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, DuplicateAttribute) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo,CN=Bar)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, DuplicateAttributeCaseInsensitive) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo,cn=Bar)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, QuotedString) { |
| EXPECT_THAT( |
| ParseDistinguishedName( |
| R"(CN="This &= That, Inc.",O="A,B,C,\58\2c\59\2c\5A\+")"), |
| IsOkAndHolds(UnorderedElementsAre(Pair("CN", R"(This &= That, Inc.)"), |
| Pair("O", R"(A,B,C,X,Y,Z+)")))); |
| } |
| |
| TEST(ParseDn, MissingCloseQuote) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN="Foo)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, OidAttribute) { |
| EXPECT_THAT(ParseDistinguishedName("CN=Foo,1.2.345=Bar"), |
| IsOkAndHolds(UnorderedElementsAre(Pair("CN", "Foo"), |
| Pair("1.2.345", "Bar")))); |
| } |
| |
| TEST(ParseDn, InvalidAttributeName) { |
| EXPECT_THAT(ParseDistinguishedName(".CN=Foo"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, HexString) { |
| EXPECT_THAT(ParseDistinguishedName("CN=#466f6F"), |
| IsOkAndHolds(UnorderedElementsAre(Pair("CN", "Foo")))); |
| } |
| |
| TEST(ParseDn, HexStringOddLength) { |
| EXPECT_THAT(ParseDistinguishedName("CN=#466f6"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, EmptyAttributeValues) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=,OU="",O=#)"), |
| IsOkAndHolds(UnorderedElementsAre(Pair("CN", ""), Pair("OU", ""), |
| Pair("O", "")))); |
| } |
| |
| TEST(ParseDn, InvalidSeparator) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo>O=Bar)"), |
| StatusIs(absl::StatusCode::kInvalidArgument)); |
| } |
| |
| TEST(ParseDn, MultiValueUnsupported) { |
| EXPECT_THAT(ParseDistinguishedName(R"(CN=Foo+O=Bar)"), |
| StatusIs(absl::StatusCode::kUnimplemented)); |
| } |
| |
| } // namespace |