diff options
Diffstat (limited to 'src/mongo/db/auth')
-rw-r--r-- | src/mongo/db/auth/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_identifier_test.cpp | 124 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_name.cpp | 153 | ||||
-rw-r--r-- | src/mongo/db/auth/auth_name.h | 240 | ||||
-rw-r--r-- | src/mongo/db/auth/role_name.cpp | 84 | ||||
-rw-r--r-- | src/mongo/db/auth/role_name.h | 207 | ||||
-rw-r--r-- | src/mongo/db/auth/user_name.cpp | 158 | ||||
-rw-r--r-- | src/mongo/db/auth/user_name.h | 223 |
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, |