summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-11-12 20:23:10 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-11-19 22:33:54 +0000
commitbf6aa01aac1bb2e51f5d77ef8b710a18e5e03282 (patch)
treef65c237b5fd25f7b11102c29eae84b60e689d40d /src
parent57877f14e139e08d60558f3e70f320e33b7d4079 (diff)
downloadmongo-bf6aa01aac1bb2e51f5d77ef8b710a18e5e03282.tar.gz
SERVER-61455 Unify UserName and RoleName into AuthName
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/auth/SConscript3
-rw-r--r--src/mongo/db/auth/auth_identifier_test.cpp124
-rw-r--r--src/mongo/db/auth/auth_name.cpp153
-rw-r--r--src/mongo/db/auth/auth_name.h240
-rw-r--r--src/mongo/db/auth/role_name.cpp84
-rw-r--r--src/mongo/db/auth/role_name.h207
-rw-r--r--src/mongo/db/auth/user_name.cpp158
-rw-r--r--src/mongo/db/auth/user_name.h223
8 files changed, 509 insertions, 683 deletions
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index 95bdc5c2559..e62251887c6 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -39,11 +39,10 @@ env.Library(
env.Library(
target='auth',
source=[
+ 'auth_name.cpp',
'authorization_manager.cpp',
'authorization_session.cpp',
'auth_decorations.cpp',
- 'user_name.cpp',
- 'role_name.cpp',
'role_name_or_string.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/db/auth/auth_identifier_test.cpp b/src/mongo/db/auth/auth_identifier_test.cpp
index 0598c568b21..bb3b6e2f313 100644
--- a/src/mongo/db/auth/auth_identifier_test.cpp
+++ b/src/mongo/db/auth/auth_identifier_test.cpp
@@ -45,24 +45,13 @@
namespace mongo {
namespace {
-template <typename T>
-struct Traits {};
-
-template <>
-struct Traits<UserName> {
- static constexpr auto kFieldName = "user"_sd;
- static const std::string& getName(const UserName& obj) {
- return obj.getUser();
- }
-};
+const std::string& getName(const UserName& obj) {
+ return obj.getUser();
+}
-template <>
-struct Traits<RoleName> {
- static constexpr auto kFieldName = "role"_sd;
- static const std::string& getName(const RoleName& obj) {
- return obj.getRole();
- }
-};
+const std::string& getName(const RoleName& obj) {
+ return obj.getRole();
+}
template <typename Stream, typename T>
std::string stream(const T& obj) {
@@ -77,7 +66,8 @@ void checkValueAssertions(const T& obj, Name name, Db db) {
ASSERT_EQ(obj.empty(), expectEmpty);
ASSERT_EQ(obj.getDB(), db);
- ASSERT_EQ(Traits<T>::getName(obj), name);
+ ASSERT_EQ(obj.getName(), name);
+ ASSERT_EQ(getName(obj), name);
std::string expectDisplay, expectUnique;
if (!expectEmpty) {
@@ -111,24 +101,18 @@ TEST(AuthName, ConstructorTest) {
template <typename T, typename Name, typename Db>
void doBSONParseTest(Name name, Db db) {
- auto obj = BSON(Traits<T>::kFieldName << name << "db" << db);
+ auto obj = BSON(T::kFieldName << name << "db" << db);
checkValueAssertions(T::parseFromBSON(BSON("" << obj).firstElement()), name, db);
- // RoleName doesn't support parseFromBSONObj()
- if constexpr (std::is_same_v<T, UserName>) {
- checkValueAssertions(T::parseFromBSONObj(obj), name, db);
- }
+ checkValueAssertions(T::parseFromBSONObj(obj), name, db);
}
template <typename T, typename Name, typename Db>
void doBSONParseFailure(Name name, Db db) {
- auto obj = BSON(Traits<T>::kFieldName << name << "db" << db);
+ auto obj = BSON(T::kFieldName << name << "db" << db);
ASSERT_THROWS(T::parseFromBSON(BSON("" << obj).firstElement()), AssertionException);
- // RoleName doesn't support parseFromBSONObj()
- if constexpr (std::is_same_v<T, UserName>) {
- ASSERT_THROWS(T::parseFromBSONObj(obj), AssertionException);
- }
+ ASSERT_THROWS(T::parseFromBSONObj(obj), AssertionException);
}
template <typename T>
@@ -161,7 +145,89 @@ void doStringParseTests() {
TEST(AuthName, StringParseTests) {
doStringParseTests<UserName>();
- // RoleName doesn't support parse(StringData)
+ doStringParseTests<RoleName>();
+}
+
+// Tests explicitly using UserName/RoleName specializations directly with iteration.
+
+TEST(AuthName, UserName) {
+ const std::vector<UserName> userNames = {
+ UserName(std::string("alice"), "db1"_sd),
+ UserName("bob"_sd, std::string("db2")),
+ uassertStatusOK(UserName::parse("db3.claire")),
+ };
+
+ auto it = makeUserNameIteratorForContainer(userNames);
+
+ ASSERT_EQ(it.more(), true);
+ auto first = it.next();
+ ASSERT_EQ(first.getDisplayName(), "alice@db1");
+ ASSERT_EQ(first.getUnambiguousName(), "db1.alice");
+ ASSERT_EQ(first.getUser(), "alice");
+ ASSERT_EQ(first.getDB(), "db1");
+ ASSERT(first == userNames[0]);
+
+ ASSERT_EQ(it.more(), true);
+ auto second = it.next();
+ ASSERT_EQ(second.getDisplayName(), "bob@db2");
+ ASSERT_EQ(second.getUnambiguousName(), "db2.bob");
+ ASSERT_EQ(second.getUser(), "bob");
+ ASSERT_EQ(second.getDB(), "db2");
+ ASSERT(second == userNames[1]);
+
+ ASSERT_EQ(it.more(), true);
+ auto third = it.next();
+ ASSERT_EQ(third.getDisplayName(), "claire@db3");
+ ASSERT_EQ(third.getUnambiguousName(), "db3.claire");
+ ASSERT_EQ(third.getUser(), "claire");
+ ASSERT_EQ(third.getDB(), "db3");
+ ASSERT(third == userNames[2]);
+
+ ASSERT(first != second);
+ ASSERT(second != third);
+ ASSERT(third != first);
+
+ ASSERT_EQ(it.more(), false);
+}
+
+TEST(AuthName, RoleName) {
+ const std::vector<RoleName> roleNames = {
+ RoleName(std::string("alice"), "db1"_sd),
+ RoleName("bob"_sd, std::string("db2")),
+ uassertStatusOK(RoleName::parse("db3.claire")),
+ };
+
+ auto it = makeRoleNameIteratorForContainer(roleNames);
+
+ ASSERT_EQ(it.more(), true);
+ auto first = it.next();
+ ASSERT_EQ(first.getDisplayName(), "alice@db1");
+ ASSERT_EQ(first.getUnambiguousName(), "db1.alice");
+ ASSERT_EQ(first.getRole(), "alice");
+ ASSERT_EQ(first.getDB(), "db1");
+ ASSERT(first == roleNames[0]);
+
+ ASSERT_EQ(it.more(), true);
+ auto second = it.next();
+ ASSERT_EQ(second.getDisplayName(), "bob@db2");
+ ASSERT_EQ(second.getUnambiguousName(), "db2.bob");
+ ASSERT_EQ(second.getRole(), "bob");
+ ASSERT_EQ(second.getDB(), "db2");
+ ASSERT(second == roleNames[1]);
+
+ ASSERT_EQ(it.more(), true);
+ auto third = it.next();
+ ASSERT_EQ(third.getDisplayName(), "claire@db3");
+ ASSERT_EQ(third.getUnambiguousName(), "db3.claire");
+ ASSERT_EQ(third.getRole(), "claire");
+ ASSERT_EQ(third.getDB(), "db3");
+ ASSERT(third == roleNames[2]);
+
+ ASSERT(first != second);
+ ASSERT(second != third);
+ ASSERT(third != first);
+
+ ASSERT_EQ(it.more(), false);
}
} // namespace
diff --git a/src/mongo/db/auth/auth_name.cpp b/src/mongo/db/auth/auth_name.cpp
new file mode 100644
index 00000000000..dc15c685485
--- /dev/null
+++ b/src/mongo/db/auth/auth_name.cpp
@@ -0,0 +1,153 @@
+/**
+ * Copyright (C) 2018-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/auth/auth_name.h"
+
+#include "mongo/db/auth/role_name.h"
+#include "mongo/db/auth/user_name.h"
+
+namespace mongo {
+
+template <typename T>
+StatusWith<T> AuthName<T>::parse(StringData str) {
+ auto split = str.find('.');
+
+ if (split == std::string::npos) {
+ return Status(ErrorCodes::BadValue,
+ str::stream() << T::kName << " must contain a '.' separated database."
+ << T::kFieldName << " pair");
+ }
+
+ return T(str.substr(split + 1), str.substr(0, split));
+}
+
+template <typename T>
+T AuthName<T>::parseFromVariant(const stdx::variant<std::string, BSONObj>& name) {
+ if (stdx::holds_alternative<std::string>(name)) {
+ return uassertStatusOK(parse(stdx::get<std::string>(name)));
+ }
+
+ return parseFromBSONObj(stdx::get<BSONObj>(name));
+}
+
+template <typename T>
+T AuthName<T>::parseFromBSONObj(const BSONObj& obj) {
+ std::bitset<2> usedFields;
+ constexpr size_t kNameFieldBit = 0;
+ constexpr size_t kDbFieldBit = 1;
+ StringData name, db;
+
+ for (const auto& element : obj) {
+ const auto fieldName = element.fieldNameStringData();
+
+ if (fieldName == T::kFieldName) {
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName
+ << " must contain a string field named: " << T::kFieldName,
+ element.type() == String);
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName
+ << " has more than one field named: " << T::kFieldName,
+ !usedFields[kNameFieldBit]);
+
+ usedFields.set(kNameFieldBit);
+ name = element.valueStringData();
+ } else if (fieldName == "db"_sd) {
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName
+ << " must contain a string field named: " << T::kFieldName,
+ element.type() == String);
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName << " has more than one field named: db",
+ !usedFields[kDbFieldBit]);
+
+ usedFields.set(kDbFieldBit);
+ db = element.valueStringData();
+ } else if constexpr (std::is_same_v<UserName, T>) {
+ // Only UserName is strict, RoleName is non-strict.
+ uasserted(ErrorCodes::BadValue,
+ str::stream()
+ << T::kName << " contains an unknown field named: '" << fieldName);
+ }
+ }
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName << " must contain a field named: " << T::kFieldName,
+ usedFields[kNameFieldBit]);
+
+ uassert(ErrorCodes::BadValue,
+ str::stream() << T::kName << " must contain a field named: db",
+ usedFields[kDbFieldBit]);
+
+ return T(name, db);
+}
+
+template <typename T>
+T AuthName<T>::parseFromBSON(const BSONElement& elem) {
+ if (elem.type() == String) {
+ return uassertStatusOK(parse(elem.valueStringData()));
+ } else if (elem.type() == Object) {
+ const auto obj = elem.embeddedObject();
+ return parseFromBSONObj(obj);
+ } else {
+ uasserted(ErrorCodes::BadValue,
+ str::stream() << T::kName << " must be either a string or an object");
+ }
+}
+
+template <typename T>
+void AuthName<T>::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const {
+ BSONObjBuilder builder(bob->subobjStart(fieldName));
+ appendToBSON(&builder);
+}
+
+template <typename T>
+void AuthName<T>::serializeToBSON(BSONArrayBuilder* bob) const {
+ BSONObjBuilder builder(bob->subobjStart());
+ appendToBSON(&builder);
+}
+
+template <typename T>
+void AuthName<T>::appendToBSON(BSONObjBuilder* bob) const {
+ *bob << T::kFieldName << getName() << "db"_sd << getDB();
+}
+
+template <typename T>
+BSONObj AuthName<T>::toBSON() const {
+ BSONObjBuilder bob;
+ appendToBSON(&bob);
+ return bob.obj();
+}
+
+
+// Materialize the types we care about.
+template class AuthName<RoleName>;
+template class AuthName<UserName>;
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/auth_name.h b/src/mongo/db/auth/auth_name.h
new file mode 100644
index 00000000000..f00300fec36
--- /dev/null
+++ b/src/mongo/db/auth/auth_name.h
@@ -0,0 +1,240 @@
+/**
+ * Copyright (C) 2021-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#pragma once
+
+#include <iosfwd>
+#include <memory>
+#include <string>
+
+
+#include "mongo/base/clonable_ptr.h"
+#include "mongo/base/status_with.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonobjbuilder.h"
+#include "mongo/stdx/variant.h"
+
+namespace mongo {
+
+/**
+ * Representation of a generic authentication name such as a UserName or RoleName.
+ *
+ * Consists of a general "name" part, and a "database name" part.
+ */
+template <typename T>
+class AuthName {
+public:
+ AuthName() = default;
+
+ template <typename Name, typename DB>
+ AuthName(Name name, DB db) {
+ if constexpr (std::is_same_v<Name, std::string>) {
+ _name = std::move(name);
+ } else {
+ _name = StringData(name).toString();
+ }
+
+ if constexpr (std::is_same_v<DB, std::string>) {
+ _db = std::move(db);
+ } else {
+ _db = StringData(db).toString();
+ }
+ }
+
+ /**
+ * Parses a string of the form "db.name" into an AuthName object.
+ */
+ static StatusWith<T> parse(StringData str);
+
+ /**
+ * These methods support parsing usernames from IDL
+ */
+ static T parseFromVariant(const stdx::variant<std::string, mongo::BSONObj>& name);
+ static T parseFromBSONObj(const BSONObj& obj);
+ static T parseFromBSON(const BSONElement& elem);
+ void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const;
+ void serializeToBSON(BSONArrayBuilder* bob) const;
+ void appendToBSON(BSONObjBuilder* bob) const;
+ BSONObj toBSON() const;
+
+ /**
+ * Gets the name part of a AuthName.
+ */
+ const std::string& getName() const {
+ return _name;
+ }
+
+ /**
+ * Gets the database name part of an AuthName.
+ */
+ const std::string& getDB() const {
+ return _db;
+ }
+
+ /**
+ * Gets the full unique name of a user as a string, formatted as "name@db".
+ */
+ std::string getDisplayName() const {
+ if (empty()) {
+ return "";
+ }
+ return str::stream() << _name << "@" << _db;
+ }
+
+ /**
+ * Predict name length without actually forming string.
+ */
+ std::size_t getDisplayNameLength() const {
+ if (empty()) {
+ return 0;
+ }
+ return _db.size() + 1 + _name.size();
+ }
+
+ /**
+ * Gets the full unambiguous unique name of a user as a string, formatted as "db.name"
+ */
+ std::string getUnambiguousName() const {
+ if (empty()) {
+ return "";
+ }
+ return str::stream() << _db << "." << _name;
+ }
+
+ /**
+ * True if the username and dbname have not been set.
+ */
+ bool empty() const {
+ return _db.empty() && _name.empty();
+ }
+
+ bool operator==(const AuthName& rhs) const {
+ return (_name == rhs._name) && (_db == rhs._db);
+ }
+
+ bool operator!=(const AuthName& rhs) const {
+ return !(*this == rhs);
+ }
+
+ bool operator<(const AuthName& rhs) const {
+ return (_name < rhs._name) || ((_name == rhs._name) && (_db < rhs._db));
+ }
+
+ template <typename H>
+ friend H AbslHashValue(H h, const AuthName& name) {
+ return H::combine(std::move(h), name._db, '.', name._name);
+ }
+
+private:
+ std::string _name;
+ std::string _db;
+};
+
+template <typename Stream, typename T>
+static inline Stream& operator<<(Stream& os, const AuthName<T>& name) {
+ if (!name.empty()) {
+ os << name.getName() << '@' << name.getDB();
+ }
+ return os;
+}
+
+/**
+ * Iterator over an unspecified container of AuthName objects.
+ */
+template <typename T>
+class AuthNameIterator {
+public:
+ class Impl {
+ public:
+ Impl() = default;
+ virtual ~Impl(){};
+ std::unique_ptr<Impl> clone() const {
+ return std::unique_ptr<Impl>(doClone());
+ }
+ virtual bool more() const = 0;
+ virtual const T& get() const = 0;
+
+ virtual const T& next() = 0;
+
+ private:
+ virtual Impl* doClone() const = 0;
+ };
+
+ AuthNameIterator() = default;
+ explicit AuthNameIterator(std::unique_ptr<Impl> impl) : _impl(std::move(impl)) {}
+
+ bool more() const {
+ return _impl.get() && _impl->more();
+ }
+ const T& get() const {
+ return _impl->get();
+ }
+
+ const T& next() {
+ return _impl->next();
+ }
+
+ const T& operator*() const {
+ return get();
+ }
+ const T* operator->() const {
+ return &get();
+ }
+
+private:
+ clonable_ptr<Impl> _impl;
+};
+
+
+template <typename ContainerIterator, typename T>
+class AuthNameContainerIteratorImpl : public AuthNameIterator<T>::Impl {
+public:
+ AuthNameContainerIteratorImpl(const ContainerIterator& begin, const ContainerIterator& end)
+ : _curr(begin), _end(end) {}
+ ~AuthNameContainerIteratorImpl() override {}
+ bool more() const override {
+ return _curr != _end;
+ }
+ const T& next() override {
+ return *(_curr++);
+ }
+ const T& get() const override {
+ return *_curr;
+ }
+ typename AuthNameIterator<T>::Impl* doClone() const override {
+ return new AuthNameContainerIteratorImpl(*this);
+ }
+
+private:
+ ContainerIterator _curr;
+ ContainerIterator _end;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/role_name.cpp b/src/mongo/db/auth/role_name.cpp
deleted file mode 100644
index 126e0b665b2..00000000000
--- a/src/mongo/db/auth/role_name.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/db/auth/role_name.h"
-
-#include <algorithm>
-#include <iostream>
-#include <string>
-
-#include "mongo/base/string_data.h"
-#include "mongo/db/auth/authorization_manager.h"
-#include "mongo/util/assert_util.h"
-
-namespace mongo {
-
-RoleName RoleName::parseFromBSON(const BSONElement& elem) {
- auto obj = elem.embeddedObjectUserCheck();
- std::array<BSONElement, 2> fields;
- obj.getFields(
- {AuthorizationManager::ROLE_NAME_FIELD_NAME, AuthorizationManager::ROLE_DB_FIELD_NAME},
- &fields);
- const auto& nameField = fields[0];
- uassert(ErrorCodes::BadValue,
- str::stream() << "user name must contain a string field named: "
- << AuthorizationManager::ROLE_NAME_FIELD_NAME,
- nameField.type() == String);
-
- const auto& dbField = fields[1];
- uassert(ErrorCodes::BadValue,
- str::stream() << "role name must contain a string field named: "
- << AuthorizationManager::ROLE_DB_FIELD_NAME,
- dbField.type() == String);
-
- return RoleName(nameField.valueStringData(), dbField.valueStringData());
-}
-
-void RoleName::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const {
- BSONObjBuilder sub(bob->subobjStart(fieldName));
- appendToBSON(&sub);
-}
-
-void RoleName::serializeToBSON(BSONArrayBuilder* bob) const {
- BSONObjBuilder sub(bob->subobjStart());
- appendToBSON(&sub);
-}
-
-void RoleName::appendToBSON(BSONObjBuilder* bob) const {
- bob->append(AuthorizationManager::ROLE_NAME_FIELD_NAME, getRole());
- bob->append(AuthorizationManager::ROLE_DB_FIELD_NAME, getDB());
-}
-
-BSONObj RoleName::toBSON() const {
- BSONObjBuilder bob;
- appendToBSON(&bob);
- return bob.obj();
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h
index 9a0d5978541..5feedf661d7 100644
--- a/src/mongo/db/auth/role_name.h
+++ b/src/mongo/db/auth/role_name.h
@@ -29,16 +29,7 @@
#pragma once
-#include <iosfwd>
-#include <memory>
-#include <string>
-#include <vector>
-
-
-#include "mongo/base/string_data.h"
-#include "mongo/bson/bsonelement.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/util/assert_util.h"
+#include "mongo/db/auth/auth_name.h"
namespace mongo {
@@ -47,203 +38,27 @@ namespace mongo {
*
* Consists of a "role name" part and a "datbase name" part.
*/
-class RoleName {
+class RoleName : public AuthName<RoleName> {
public:
- RoleName() = default;
-
- template <typename Role, typename DB>
- RoleName(Role role, DB db) {
- if constexpr (std::is_same_v<Role, std::string>) {
- _role = std::move(role);
- } else {
- _role = StringData(role).toString();
- }
-
- if constexpr (std::is_same_v<DB, std::string>) {
- _db = std::move(db);
- } else {
- _db = StringData(db).toString();
- }
- }
+ static constexpr auto kName = "RoleName"_sd;
+ static constexpr auto kFieldName = "role"_sd;
- // Added for IDL support
- static RoleName parseFromBSON(const BSONElement& elem);
- void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const;
- void serializeToBSON(BSONArrayBuilder* bob) const;
- void appendToBSON(BSONObjBuilder* sub) const;
- BSONObj toBSON() const;
+ using AuthName::AuthName;
- /**
- * Gets the name of the role excluding the "@dbname" component.
- */
const std::string& getRole() const {
- return _role;
- }
-
- /**
- * Gets the database name part of a role name.
- */
- const std::string& getDB() const {
- return _db;
- }
-
- bool empty() const {
- return _role.empty() && _db.empty();
- }
-
- /**
- * Gets the full name of a role as a string, formatted as "role@db".
- *
- * Allowed for keys in non-persistent data structures, such as std::map.
- */
- std::string getDisplayName() const {
- if (empty()) {
- return "";
- }
- return str::stream() << _role << '@' << _db;
- }
-
- /**
- * Gets the full unambiguous unique name of a user as a string, formatted as "db.role"
- */
- std::string getUnambiguousName() const {
- if (empty()) {
- return "";
- }
- return str::stream() << _db << '.' << _role;
- }
-
- template <typename H>
- friend H AbslHashValue(H h, const RoleName& rname) {
- auto state = H::combine(std::move(h), rname._db);
- state = H::combine(std::move(state), '.');
- return H::combine(std::move(state), rname._role);
+ return getName();
}
-
-private:
- std::string _role;
- std::string _db;
};
-static inline bool operator==(const RoleName& lhs, const RoleName& rhs) {
- return (lhs.getRole() == rhs.getRole()) && (lhs.getDB() == rhs.getDB());
-}
-
-static inline bool operator!=(const RoleName& lhs, const RoleName& rhs) {
- return (lhs.getRole() != rhs.getRole()) || (lhs.getDB() != rhs.getDB());
-}
-
-static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
- if (lhs.getDB() == rhs.getDB()) {
- return lhs.getRole() < rhs.getRole();
- }
- return lhs.getDB() < rhs.getDB();
-}
-
-static inline std::ostream& operator<<(std::ostream& os, const RoleName& role) {
- if (!role.empty()) {
- os << role.getRole() << '@' << role.getDB();
- }
- return os;
-}
-
-static inline StringBuilder& operator<<(StringBuilder& os, const RoleName& role) {
- if (!role.empty()) {
- os << role.getRole() << '@' << role.getDB();
- }
- return os;
-}
-
-/**
- * Iterator over an unspecified container of RoleName objects.
- */
-class RoleNameIterator {
-public:
- class Impl {
- Impl(const Impl&) = delete;
- Impl& operator=(const Impl&) = delete;
-
- public:
- Impl(){};
- virtual ~Impl(){};
- static Impl* clone(Impl* orig) {
- return orig ? orig->doClone() : nullptr;
- }
- virtual bool more() const = 0;
- virtual const RoleName& get() const = 0;
-
- virtual const RoleName& next() = 0;
-
- private:
- virtual Impl* doClone() const = 0;
- };
-
- RoleNameIterator() : _impl(nullptr) {}
- RoleNameIterator(const RoleNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
- explicit RoleNameIterator(Impl* impl) : _impl(impl) {}
-
- RoleNameIterator& operator=(const RoleNameIterator& other) {
- _impl.reset(Impl::clone(other._impl.get()));
- return *this;
- }
-
- bool more() const {
- return _impl.get() && _impl->more();
- }
- const RoleName& get() const {
- return _impl->get();
- }
-
- const RoleName& next() {
- return _impl->next();
- }
-
- const RoleName& operator*() const {
- return get();
- }
- const RoleName* operator->() const {
- return &get();
- }
-
-private:
- std::unique_ptr<Impl> _impl;
-};
-
-} // namespace mongo
-
-namespace mongo {
-
-template <typename ContainerIterator>
-class RoleNameContainerIteratorImpl : public RoleNameIterator::Impl {
- RoleNameContainerIteratorImpl(const RoleNameContainerIteratorImpl&) = delete;
- RoleNameContainerIteratorImpl& operator=(const RoleNameContainerIteratorImpl&) = delete;
-
-public:
- RoleNameContainerIteratorImpl(const ContainerIterator& begin, const ContainerIterator& end)
- : _curr(begin), _end(end) {}
- virtual ~RoleNameContainerIteratorImpl() {}
- virtual bool more() const {
- return _curr != _end;
- }
- virtual const RoleName& next() {
- return *(_curr++);
- }
- virtual const RoleName& get() const {
- return *_curr;
- }
- virtual RoleNameIterator::Impl* doClone() const {
- return new RoleNameContainerIteratorImpl(_curr, _end);
- }
-
-private:
- ContainerIterator _curr;
- ContainerIterator _end;
-};
+using RoleNameIterator = AuthNameIterator<RoleName>;
+template <typename Container>
+using RoleNameContainerIteratorImpl = AuthNameContainerIteratorImpl<Container, RoleName>;
template <typename ContainerIterator>
RoleNameIterator makeRoleNameIterator(const ContainerIterator& begin,
const ContainerIterator& end) {
- return RoleNameIterator(new RoleNameContainerIteratorImpl<ContainerIterator>(begin, end));
+ return RoleNameIterator(
+ std::make_unique<RoleNameContainerIteratorImpl<ContainerIterator>>(begin, end));
}
template <typename Container>
diff --git a/src/mongo/db/auth/user_name.cpp b/src/mongo/db/auth/user_name.cpp
deleted file mode 100644
index 2a95f374167..00000000000
--- a/src/mongo/db/auth/user_name.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include "mongo/db/auth/user_name.h"
-
-#include <algorithm>
-#include <iostream>
-#include <string>
-
-#include "mongo/db/auth/authorization_manager.h"
-#include "mongo/util/assert_util.h"
-
-namespace mongo {
-
-/**
- * Don't change the logic of this function as it will break stable API version 1.
- */
-StatusWith<UserName> UserName::parse(StringData userNameStr) {
- size_t splitPoint = userNameStr.find('.');
-
- if (splitPoint == std::string::npos) {
- return Status(ErrorCodes::BadValue,
- "username must contain a '.' separated database.user pair");
- }
-
- StringData userDBPortion = userNameStr.substr(0, splitPoint);
- StringData userNamePortion = userNameStr.substr(splitPoint + 1);
-
- return UserName(userNamePortion, userDBPortion);
-}
-
-/**
- * Don't change the logic of this function as it will break stable API version 1.
- */
-UserName UserName::parseFromVariant(const stdx::variant<std::string, BSONObj>& helloUserName) {
- if (stdx::holds_alternative<std::string>(helloUserName)) {
- return uassertStatusOK(parse(stdx::get<std::string>(helloUserName)));
- }
-
- return parseFromBSONObj(stdx::get<BSONObj>(helloUserName));
-}
-
-/**
- * Don't change the logic of this function as it will break stable API version 1.
- */
-UserName UserName::parseFromBSONObj(const BSONObj& obj) {
- std::bitset<2> usedFields;
- constexpr auto kUserNameFieldName = AuthorizationManager::USER_NAME_FIELD_NAME;
- constexpr auto kUserDbFieldName = AuthorizationManager::USER_DB_FIELD_NAME;
- constexpr size_t kUserNameFieldBit = 0;
- constexpr size_t kUserDbFieldBit = 1;
- StringData userName, userDb;
-
- for (const auto& element : obj) {
- const auto fieldName = element.fieldNameStringData();
-
-
- uassert(ErrorCodes::BadValue,
- str::stream() << "username must contain a string field named: " << fieldName,
- element.type() == String);
-
- if (fieldName == kUserNameFieldName) {
- uassert(ErrorCodes::BadValue,
- str::stream() << "username has more than one field named: "
- << kUserNameFieldName,
- !usedFields[kUserNameFieldBit]);
-
- usedFields.set(kUserNameFieldBit);
- userName = element.valueStringData();
- } else if (fieldName == kUserDbFieldName) {
- uassert(ErrorCodes::BadValue,
- str::stream() << "username has more than one field named: " << kUserDbFieldName,
- !usedFields[kUserDbFieldBit]);
-
- usedFields.set(kUserDbFieldBit);
- userDb = element.valueStringData();
- } else {
- uasserted(ErrorCodes::BadValue,
- str::stream() << "username contains an unknown field named: '" << fieldName);
- }
- }
-
- uassert(ErrorCodes::BadValue,
- str::stream() << "username must contain a field named: " << kUserNameFieldName,
- usedFields[kUserNameFieldBit]);
-
- uassert(ErrorCodes::BadValue,
- str::stream() << "username must contain a field named: " << kUserDbFieldName,
- usedFields[kUserDbFieldBit]);
-
- return UserName(userName, userDb);
-}
-
-UserName UserName::parseFromBSON(const BSONElement& elem) {
- if (elem.type() == String) {
- return uassertStatusOK(UserName::parse(elem.valueStringData()));
- } else if (elem.type() == Object) {
- const auto obj = elem.embeddedObject();
- return parseFromBSONObj(obj);
- } else {
- uasserted(ErrorCodes::BadValue, "username must be either a string or an object");
- }
-}
-
-void UserName::serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const {
- BSONObjBuilder sub(bob->subobjStart(fieldName));
- appendToBSON(&sub);
-}
-
-void UserName::serializeToBSON(BSONArrayBuilder* bab) const {
- BSONObjBuilder sub(bab->subobjStart());
- appendToBSON(&sub);
-}
-
-/**
- * Don't change the logic of this function as it will break stable API version 1.
- */
-void UserName::appendToBSON(BSONObjBuilder* bob) const {
- *bob << AuthorizationManager::USER_NAME_FIELD_NAME << getUser()
- << AuthorizationManager::USER_DB_FIELD_NAME << getDB();
-}
-
-/**
- * Don't change the logic of this function as it will break stable API version 1.
- */
-BSONObj UserName::toBSON() const {
- BSONObjBuilder ret;
- appendToBSON(&ret);
- return ret.obj();
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/auth/user_name.h b/src/mongo/db/auth/user_name.h
index 681d4a9f736..f9aa420284d 100644
--- a/src/mongo/db/auth/user_name.h
+++ b/src/mongo/db/auth/user_name.h
@@ -29,230 +29,25 @@
#pragma once
-#include <iosfwd>
-#include <memory>
-#include <string>
-
-
-#include "mongo/base/clonable_ptr.h"
-#include "mongo/base/status_with.h"
-#include "mongo/base/string_data.h"
-#include "mongo/bson/bsonelement.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/stdx/variant.h"
+#include "mongo/db/auth/auth_name.h"
namespace mongo {
-/**
- * Representation of a name of a principal (authenticatable user) in a MongoDB system.
- *
- * Consists of a "user name" part, and a "database name" part.
- */
-class UserName {
+class UserName : public AuthName<UserName> {
public:
- UserName() = default;
-
- template <typename User, typename DB>
- UserName(User user, DB db) {
- if constexpr (std::is_same_v<User, std::string>) {
- _user = std::move(user);
- } else {
- _user = StringData(user).toString();
- }
-
- if constexpr (std::is_same_v<DB, std::string>) {
- _db = std::move(db);
- } else {
- _db = StringData(db).toString();
- }
- }
+ static constexpr auto kName = "UserName"_sd;
+ static constexpr auto kFieldName = "user"_sd;
- /**
- * Parses a string of the form "db.username" into a UserName object.
- * Don't change the logic of this function as it will break stable API version 1.
- */
- static StatusWith<UserName> parse(StringData userNameStr);
+ using AuthName::AuthName;
- /**
- * These methods support parsing usernames from IDL
- * Don't change the logic of this function as it will break stable API version 1.
- */
- static UserName parseFromVariant(
- const stdx::variant<std::string, mongo::BSONObj>& helloUserName);
- static UserName parseFromBSONObj(const BSONObj& obj);
- static UserName parseFromBSON(const BSONElement& elem);
- void serializeToBSON(StringData fieldName, BSONObjBuilder* bob) const;
- void serializeToBSON(BSONArrayBuilder* bob) const;
-
- /**
- * Don't change the logic of this function as it will break stable API version 1.
- */
- void appendToBSON(BSONObjBuilder* bob) const;
-
- /**
- * Don't change the logic of this function as it will break stable API version 1.
- */
- BSONObj toBSON() const;
-
- /**
- * Gets the user part of a UserName.
- */
const std::string& getUser() const {
- return _user;
- }
-
- /**
- * Gets the database name part of a UserName.
- */
- const std::string& getDB() const {
- return _db;
- }
-
- /**
- * Gets the full unique name of a user as a string, formatted as "user@db".
- */
- std::string getDisplayName() const {
- if (empty()) {
- return "";
- }
- return str::stream() << _user << "@" << _db;
- }
-
- /**
- * Predict name length without actually forming string.
- */
- std::size_t getDisplayNameLength() const {
- if (empty()) {
- return 0;
- }
- return _db.size() + 1 + _user.size();
- }
-
- /**
- * Gets the full unambiguous unique name of a user as a string, formatted as "db.user"
- */
- std::string getUnambiguousName() const {
- if (empty()) {
- return "";
- }
- return str::stream() << _db << "." << _user;
- }
-
- /**
- * True if the username and dbname have not been set.
- */
- bool empty() const {
- return _db.empty() && _user.empty();
- }
-
- bool operator==(const UserName& rhs) const {
- return (_user == rhs._user) && (_db == rhs._db);
- }
-
- bool operator!=(const UserName& rhs) const {
- return !(*this == rhs);
- }
-
- bool operator<(const UserName& rhs) const {
- return (_user < rhs._user) || ((_user == rhs._user) && (_db < rhs._db));
- }
-
- template <typename H>
- friend H AbslHashValue(H h, const UserName& userName) {
- auto state = H::combine(std::move(h), userName._db);
- state = H::combine(std::move(state), '.');
- return H::combine(std::move(state), userName._user);
+ return getName();
}
-
-private:
- std::string _user;
- std::string _db;
};
-static inline std::ostream& operator<<(std::ostream& os, const UserName& user) {
- if (!user.empty()) {
- os << user.getUser() << '@' << user.getDB();
- }
- return os;
-}
-
-static inline StringBuilder& operator<<(StringBuilder& os, const UserName& user) {
- if (!user.empty()) {
- os << user.getUser() << '@' << user.getDB();
- }
- return os;
-}
-
-/**
- * Iterator over an unspecified container of UserName objects.
- */
-class UserNameIterator {
-public:
- class Impl {
- public:
- Impl() = default;
- virtual ~Impl(){};
- std::unique_ptr<Impl> clone() const {
- return std::unique_ptr<Impl>(doClone());
- }
- virtual bool more() const = 0;
- virtual const UserName& get() const = 0;
-
- virtual const UserName& next() = 0;
-
- private:
- virtual Impl* doClone() const = 0;
- };
-
- UserNameIterator() = default;
- explicit UserNameIterator(std::unique_ptr<Impl> impl) : _impl(std::move(impl)) {}
-
- bool more() const {
- return _impl.get() && _impl->more();
- }
- const UserName& get() const {
- return _impl->get();
- }
-
- const UserName& next() {
- return _impl->next();
- }
-
- const UserName& operator*() const {
- return get();
- }
- const UserName* operator->() const {
- return &get();
- }
-
-private:
- clonable_ptr<Impl> _impl;
-};
-
-
-template <typename ContainerIterator>
-class UserNameContainerIteratorImpl : public UserNameIterator::Impl {
-public:
- UserNameContainerIteratorImpl(const ContainerIterator& begin, const ContainerIterator& end)
- : _curr(begin), _end(end) {}
- ~UserNameContainerIteratorImpl() override {}
- bool more() const override {
- return _curr != _end;
- }
- const UserName& next() override {
- return *(_curr++);
- }
- const UserName& get() const override {
- return *_curr;
- }
- UserNameIterator::Impl* doClone() const override {
- return new UserNameContainerIteratorImpl(*this);
- }
-
-private:
- ContainerIterator _curr;
- ContainerIterator _end;
-};
+using UserNameIterator = AuthNameIterator<UserName>;
+template <typename Container>
+using UserNameContainerIteratorImpl = AuthNameContainerIteratorImpl<Container, UserName>;
template <typename ContainerIterator>
UserNameIterator makeUserNameIterator(const ContainerIterator& begin,