blob: f35b236782e4704c744787d026ea940adc0ca2af [file]
// 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