| // 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 |