summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2021-09-29 18:57:08 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-10-18 19:14:33 +0000
commitc0bb6b8677c895e525c30c738b91c83608dcf04d (patch)
treec76396a87fb015c3e40da5ef84fe68b82bf8ac15 /src/mongo
parent9416d3062590bf24f94bd90a57a97d42d608b242 (diff)
downloadmongo-c0bb6b8677c895e525c30c738b91c83608dcf04d.tar.gz
SERVER-60333 Refactor UserName and RoleName
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/client/internal_auth.cpp11
-rw-r--r--src/mongo/db/auth/SConscript1
-rw-r--r--src/mongo/db/auth/auth_identifier_test.cpp168
-rw-r--r--src/mongo/db/auth/authentication_session.cpp6
-rw-r--r--src/mongo/db/auth/authorization_manager_impl.cpp4
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.cpp2
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp11
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp2
-rw-r--r--src/mongo/db/auth/role_name.cpp15
-rw-r--r--src/mongo/db/auth/role_name.h69
-rw-r--r--src/mongo/db/auth/sasl_mechanism_registry.h2
-rw-r--r--src/mongo/db/auth/security_key.cpp4
-rw-r--r--src/mongo/db/auth/user.cpp2
-rw-r--r--src/mongo/db/auth/user_name.cpp43
-rw-r--r--src/mongo/db/auth/user_name.h88
-rw-r--r--src/mongo/db/commands/user_management_commands.cpp39
-rw-r--r--src/mongo/db/commands/user_management_commands_common.cpp21
-rw-r--r--src/mongo/db/curop.cpp2
-rw-r--r--src/mongo/db/logical_session_id_helpers.cpp8
-rw-r--r--src/mongo/util/net/ssl_manager_openssl.cpp4
20 files changed, 353 insertions, 149 deletions
diff --git a/src/mongo/client/internal_auth.cpp b/src/mongo/client/internal_auth.cpp
index 1effff0a08a..59b8d6da5dc 100644
--- a/src/mongo/client/internal_auth.cpp
+++ b/src/mongo/client/internal_auth.cpp
@@ -104,8 +104,8 @@ BSONObj getInternalAuthParams(size_t idx, StringData mechanism) {
auto password = internalAuthKeys.at(idx);
if (mechanism == kMechanismScramSha1) {
- password = mongo::createPasswordDigest(
- internalSecurity.user->getName().getUser().toString(), password);
+ password =
+ mongo::createPasswordDigest(internalSecurity.user->getName().getUser(), password);
}
return BSON(saslCommandMechanismFieldName
@@ -131,8 +131,11 @@ std::string getInternalAuthDB() {
return getBSONString(internalAuthParams, saslCommandUserDBFieldName);
}
- auto isu = internalSecurity.user;
- return isu ? isu->getName().getDB().toString() : "admin";
+ if (auto isu = internalSecurity.user) {
+ return isu->getName().getDB();
+ }
+
+ return "admin";
}
} // namespace auth
diff --git a/src/mongo/db/auth/SConscript b/src/mongo/db/auth/SConscript
index f83622f3075..c99651c652e 100644
--- a/src/mongo/db/auth/SConscript
+++ b/src/mongo/db/auth/SConscript
@@ -521,6 +521,7 @@ env.CppUnitTest(
source=[
'action_set_test.cpp',
'address_restriction_test.cpp',
+ 'auth_identifier_test.cpp',
'authorization_contract_test.cpp',
'auth_op_observer_test.cpp',
'authorization_manager_test.cpp',
diff --git a/src/mongo/db/auth/auth_identifier_test.cpp b/src/mongo/db/auth/auth_identifier_test.cpp
new file mode 100644
index 00000000000..0598c568b21
--- /dev/null
+++ b/src/mongo/db/auth/auth_identifier_test.cpp
@@ -0,0 +1,168 @@
+/**
+ * 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.
+ */
+
+/**
+ * Unit tests of the UserName and RoleName types.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include <string>
+
+#include "mongo/base/status.h"
+#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/auth/role_name.h"
+#include "mongo/db/auth/user_name.h"
+#include "mongo/unittest/unittest.h"
+
+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();
+ }
+};
+
+template <>
+struct Traits<RoleName> {
+ static constexpr auto kFieldName = "role"_sd;
+ static const std::string& getName(const RoleName& obj) {
+ return obj.getRole();
+ }
+};
+
+template <typename Stream, typename T>
+std::string stream(const T& obj) {
+ Stream sb;
+ sb << obj;
+ return sb.str();
+}
+
+template <typename T, typename Name, typename Db>
+void checkValueAssertions(const T& obj, Name name, Db db) {
+ const bool expectEmpty = StringData(name).empty() && StringData(db).empty();
+ ASSERT_EQ(obj.empty(), expectEmpty);
+
+ ASSERT_EQ(obj.getDB(), db);
+ ASSERT_EQ(Traits<T>::getName(obj), name);
+
+ std::string expectDisplay, expectUnique;
+ if (!expectEmpty) {
+ expectDisplay = str::stream() << name << '@' << db;
+ expectUnique = str::stream() << db << '.' << name;
+ }
+ ASSERT_EQ(obj.getDisplayName(), expectDisplay);
+ ASSERT_EQ(stream<StringBuilder>(obj), expectDisplay);
+ ASSERT_EQ(stream<std::ostringstream>(obj), expectDisplay);
+ ASSERT_EQ(obj.getUnambiguousName(), expectUnique);
+}
+
+template <typename T>
+void doConstructorTest() {
+ checkValueAssertions(T(), "", "");
+
+ checkValueAssertions(T("", ""), "", "");
+ checkValueAssertions(T(std::string(), std::string()), "", "");
+ checkValueAssertions(T(StringData(), StringData()), "", "");
+ checkValueAssertions(T(std::string(), StringData()), "", "");
+
+ checkValueAssertions(T("name1", "db1"), "name1", "db1");
+ checkValueAssertions(T("name1", ""), "name1", "");
+ checkValueAssertions(T("", "db1"), "", "db1");
+}
+
+TEST(AuthName, ConstructorTest) {
+ doConstructorTest<UserName>();
+ doConstructorTest<RoleName>();
+}
+
+template <typename T, typename Name, typename Db>
+void doBSONParseTest(Name name, Db db) {
+ auto obj = BSON(Traits<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);
+ }
+}
+
+template <typename T, typename Name, typename Db>
+void doBSONParseFailure(Name name, Db db) {
+ auto obj = BSON(Traits<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);
+ }
+}
+
+template <typename T>
+void doBSONParseTests() {
+ doBSONParseTest<T>("", "");
+ doBSONParseTest<T>("name", "");
+ doBSONParseTest<T>("", "db");
+ doBSONParseTest<T>("name", "db");
+
+ doBSONParseFailure<T>(123, "db");
+ doBSONParseFailure<T>("name", 123);
+ doBSONParseFailure<T>(OID(), "db");
+}
+
+TEST(AuthName, BSONParseTests) {
+ doBSONParseTests<UserName>();
+ doBSONParseTests<RoleName>();
+}
+
+template <typename T>
+void doStringParseTests() {
+ checkValueAssertions(uassertStatusOK(T::parse("db.name")), "name", "db");
+ checkValueAssertions(uassertStatusOK(T::parse("db.")), "", "db");
+ checkValueAssertions(uassertStatusOK(T::parse(".name")), "name", "");
+ checkValueAssertions(uassertStatusOK(T::parse("db.name.str")), "name.str", "db");
+ checkValueAssertions(uassertStatusOK(T::parse(".")), "", "");
+
+ ASSERT_NOT_OK(T::parse(""));
+}
+
+TEST(AuthName, StringParseTests) {
+ doStringParseTests<UserName>();
+ // RoleName doesn't support parse(StringData)
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/auth/authentication_session.cpp b/src/mongo/db/auth/authentication_session.cpp
index 6de2fe5d5ea..30d6f74fc31 100644
--- a/src/mongo/db/auth/authentication_session.cpp
+++ b/src/mongo/db/auth/authentication_session.cpp
@@ -41,7 +41,7 @@ namespace {
constexpr auto kDiagnosticLogLevel = 3;
Status crossVerifyUserNames(const UserName& oldUser, const UserName& newUser) noexcept {
- if (oldUser.getFullName().empty()) {
+ if (oldUser.empty()) {
return Status::OK();
}
@@ -53,7 +53,7 @@ Status crossVerifyUserNames(const UserName& oldUser, const UserName& newUser) no
return {ErrorCodes::ProtocolError,
str::stream()
<< "Attempt to switch database target during SASL authentication from "
- << oldUser.toString() << " to " << newUser.toString()};
+ << oldUser << " to " << newUser};
}
}
@@ -65,7 +65,7 @@ Status crossVerifyUserNames(const UserName& oldUser, const UserName& newUser) no
if (oldUser.getUser() != newUser.getUser()) {
return {ErrorCodes::ProtocolError,
str::stream() << "Attempt to switch user during SASL authentication from "
- << oldUser.toString() << " to " << newUser.toString()};
+ << oldUser << " to " << newUser};
}
return Status::OK();
diff --git a/src/mongo/db/auth/authorization_manager_impl.cpp b/src/mongo/db/auth/authorization_manager_impl.cpp
index 5ddc068c1d2..3a24e2d8165 100644
--- a/src/mongo/db/auth/authorization_manager_impl.cpp
+++ b/src/mongo/db/auth/authorization_manager_impl.cpp
@@ -532,7 +532,7 @@ StatusWith<UserHandle> AuthorizationManagerImpl::reacquireUser(OperationContext*
auto ret = std::move(swUserHandle.getValue());
if (user->getID() != ret->getID()) {
return {ErrorCodes::UserNotFound,
- str::stream() << "User id from privilege document '" << userName.toString()
+ str::stream() << "User id from privilege document '" << userName
<< "' does not match user id in session."};
}
@@ -629,7 +629,7 @@ void AuthorizationManagerImpl::_pinnedUsersThreadRoutine() noexcept try {
if (status != ErrorCodes::UserNotFound) {
LOGV2_WARNING(20239,
"Unable to fetch pinned user",
- "user"_attr = userName.toString(),
+ "user"_attr = userName,
"error"_attr = status);
} else {
LOGV2_DEBUG(20233, 2, "Pinned user not found", "user"_attr = userName);
diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp
index 5d2eb103f95..1e979d19b2f 100644
--- a/src/mongo/db/auth/authz_manager_external_state.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state.cpp
@@ -57,7 +57,7 @@ Status AuthzManagerExternalState::makeRoleNotFoundStatus(
sb << 's';
}
for (const auto& unknownRole : unknownRoles) {
- sb << delim << ' ' << unknownRole.toString();
+ sb << delim << ' ' << unknownRole;
delim = ',';
}
return {ErrorCodes::RoleNotFound, sb.str()};
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.cpp b/src/mongo/db/auth/authz_manager_external_state_local.cpp
index f5067d6aa33..3b71f8dce4d 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -441,7 +441,7 @@ StatusWith<ResolvedRoleData> AuthzManagerExternalStateLocal::resolveRoles(
if (elem.type() != Array) {
return {ErrorCodes::BadValue,
str::stream()
- << "Invalid 'roles' field in role document '" << role.getFullName()
+ << "Invalid 'roles' field in role document '" << role
<< "', expected an array but found " << typeName(elem.type())};
}
for (const auto& subroleElem : elem.Obj()) {
@@ -461,8 +461,8 @@ StatusWith<ResolvedRoleData> AuthzManagerExternalStateLocal::resolveRoles(
if (processPrivs && (elem = roleDoc["privileges"])) {
if (elem.type() != Array) {
return {ErrorCodes::UnsupportedFormat,
- str::stream() << "Invalid 'privileges' field in role document '"
- << role.getFullName() << "'"};
+ str::stream()
+ << "Invalid 'privileges' field in role document '" << role << "'"};
}
for (const auto& privElem : elem.Obj()) {
auto priv = Privilege::fromBSON(privElem);
@@ -475,7 +475,7 @@ StatusWith<ResolvedRoleData> AuthzManagerExternalStateLocal::resolveRoles(
return {ErrorCodes::UnsupportedFormat,
str::stream()
<< "Invalid 'authenticationRestrictions' field in role document '"
- << role.getFullName() << "'"};
+ << role << "'"};
}
inheritedRestrictions.push_back(
uassertStatusOK(parseAuthenticationRestriction(BSONArray(elem.Obj()))));
@@ -589,8 +589,7 @@ Status AuthzManagerExternalStateLocal::getRolesDescription(
result->push_back(roleBuilder.obj());
} catch (const AssertionException& ex) {
return {ex.code(),
- str::stream() << "Failed fetching role '" << role.getFullName()
- << "': " << ex.reason()};
+ str::stream() << "Failed fetching role '" << role << "': " << ex.reason()};
}
}
diff --git a/src/mongo/db/auth/authz_manager_external_state_s.cpp b/src/mongo/db/auth/authz_manager_external_state_s.cpp
index b3f0483ecc1..1b2c0b7331c 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp
@@ -150,7 +150,7 @@ Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* opC
std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
if (foundUsers.size() == 0) {
return Status(ErrorCodes::UserNotFound,
- "User \"" + userName.toString() + "\" not found");
+ str::stream() << "User \"" << userName << "\" not found");
}
if (foundUsers.size() > 1) {
diff --git a/src/mongo/db/auth/role_name.cpp b/src/mongo/db/auth/role_name.cpp
index d8cf76f48d5..126e0b665b2 100644
--- a/src/mongo/db/auth/role_name.cpp
+++ b/src/mongo/db/auth/role_name.cpp
@@ -39,21 +39,6 @@
namespace mongo {
-RoleName::RoleName(StringData role, StringData dbname) {
- _fullName.resize(role.size() + dbname.size() + 1);
- std::string::iterator iter =
- std::copy(role.rawData(), role.rawData() + role.size(), _fullName.begin());
- *iter = '@';
- ++iter;
- iter = std::copy(dbname.rawData(), dbname.rawData() + dbname.size(), iter);
- dassert(iter == _fullName.end());
- _splitPoint = role.size();
-}
-
-std::ostream& operator<<(std::ostream& os, const RoleName& name) {
- return os << name.getFullName();
-}
-
RoleName RoleName::parseFromBSON(const BSONElement& elem) {
auto obj = elem.embeddedObjectUserCheck();
std::array<BSONElement, 2> fields;
diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h
index 81055b5ac20..9a0d5978541 100644
--- a/src/mongo/db/auth/role_name.h
+++ b/src/mongo/db/auth/role_name.h
@@ -49,8 +49,22 @@ namespace mongo {
*/
class RoleName {
public:
- RoleName() : _splitPoint(0) {}
- RoleName(StringData role, StringData dbname);
+ 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();
+ }
+ }
// Added for IDL support
static RoleName parseFromBSON(const BSONElement& elem);
@@ -62,19 +76,19 @@ public:
/**
* Gets the name of the role excluding the "@dbname" component.
*/
- StringData getRole() const {
- return StringData(_fullName).substr(0, _splitPoint);
+ const std::string& getRole() const {
+ return _role;
}
/**
* Gets the database name part of a role name.
*/
- StringData getDB() const {
- return StringData(_fullName).substr(_splitPoint + 1);
+ const std::string& getDB() const {
+ return _db;
}
bool empty() const {
- return _fullName.empty();
+ return _role.empty() && _db.empty();
}
/**
@@ -82,33 +96,41 @@ public:
*
* Allowed for keys in non-persistent data structures, such as std::map.
*/
- const std::string& getFullName() const {
- return _fullName;
+ std::string getDisplayName() const {
+ if (empty()) {
+ return "";
+ }
+ return str::stream() << _role << '@' << _db;
}
/**
- * Stringifies the object, for logging/debugging.
+ * Gets the full unambiguous unique name of a user as a string, formatted as "db.role"
*/
- const std::string& toString() const {
- return getFullName();
+ std::string getUnambiguousName() const {
+ if (empty()) {
+ return "";
+ }
+ return str::stream() << _db << '.' << _role;
}
template <typename H>
friend H AbslHashValue(H h, const RoleName& rname) {
- return H::combine(std::move(h), rname.getFullName());
+ auto state = H::combine(std::move(h), rname._db);
+ state = H::combine(std::move(state), '.');
+ return H::combine(std::move(state), rname._role);
}
private:
- std::string _fullName; // The full name, stored as a string. "role@db".
- size_t _splitPoint; // The index of the "@" separating the role and db name parts.
+ std::string _role;
+ std::string _db;
};
static inline bool operator==(const RoleName& lhs, const RoleName& rhs) {
- return lhs.getFullName() == rhs.getFullName();
+ return (lhs.getRole() == rhs.getRole()) && (lhs.getDB() == rhs.getDB());
}
static inline bool operator!=(const RoleName& lhs, const RoleName& rhs) {
- return lhs.getFullName() != rhs.getFullName();
+ return (lhs.getRole() != rhs.getRole()) || (lhs.getDB() != rhs.getDB());
}
static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
@@ -118,8 +140,19 @@ static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
return lhs.getDB() < rhs.getDB();
}
-std::ostream& operator<<(std::ostream& os, const RoleName& name);
+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.
diff --git a/src/mongo/db/auth/sasl_mechanism_registry.h b/src/mongo/db/auth/sasl_mechanism_registry.h
index cceff146750..cc6a233c11b 100644
--- a/src/mongo/db/auth/sasl_mechanism_registry.h
+++ b/src/mongo/db/auth/sasl_mechanism_registry.h
@@ -166,7 +166,7 @@ public:
* authentication mechanisms
*/
bool isClusterMember() const {
- return _principalName == internalSecurity.user->getName().getUser().toString() &&
+ return _principalName == internalSecurity.user->getName().getUser() &&
getAuthenticationDatabase() == internalSecurity.user->getName().getDB();
};
diff --git a/src/mongo/db/auth/security_key.cpp b/src/mongo/db/auth/security_key.cpp
index c09075d4f31..f69dfeba5e1 100644
--- a/src/mongo/db/auth/security_key.cpp
+++ b/src/mongo/db/auth/security_key.cpp
@@ -83,8 +83,8 @@ public:
"error"_attr = swSaslPassword.getStatus());
return boost::none;
}
- const auto passwordDigest = mongo::createPasswordDigest(
- internalSecurity.user->getName().getUser().toString(), password);
+ const auto passwordDigest =
+ mongo::createPasswordDigest(internalSecurity.user->getName().getUser(), password);
User::CredentialData credentials;
if (!_copyCredentials(
diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp
index 6ed98aae6f3..ff967a9f8a0 100644
--- a/src/mongo/db/auth/user.cpp
+++ b/src/mongo/db/auth/user.cpp
@@ -47,7 +47,7 @@ namespace mongo {
namespace {
SHA256Block computeDigest(const UserName& name) {
- const auto& fn = name.getFullName();
+ auto fn = name.getDisplayName();
return SHA256Block::computeHash({ConstDataRange(fn.c_str(), fn.size())});
};
diff --git a/src/mongo/db/auth/user_name.cpp b/src/mongo/db/auth/user_name.cpp
index 65aa84cba1e..2a95f374167 100644
--- a/src/mongo/db/auth/user_name.cpp
+++ b/src/mongo/db/auth/user_name.cpp
@@ -38,17 +38,6 @@
namespace mongo {
-UserName::UserName(StringData user, StringData dbname) {
- _fullName.resize(user.size() + dbname.size() + 1);
- std::string::iterator iter =
- std::copy(user.rawData(), user.rawData() + user.size(), _fullName.begin());
- *iter = '@';
- ++iter;
- iter = std::copy(dbname.rawData(), dbname.rawData() + dbname.size(), iter);
- dassert(iter == _fullName.end());
- _splitPoint = user.size();
-}
-
/**
* Don't change the logic of this function as it will break stable API version 1.
*/
@@ -82,18 +71,15 @@ UserName UserName::parseFromVariant(const stdx::variant<std::string, BSONObj>& h
*/
UserName UserName::parseFromBSONObj(const BSONObj& obj) {
std::bitset<2> usedFields;
- const auto kUserNameFieldName = AuthorizationManager::USER_NAME_FIELD_NAME;
- const auto kUserDbFieldName = AuthorizationManager::USER_DB_FIELD_NAME;
- const size_t kUserNameFieldBit = 0;
- const size_t kUserDbFieldBit = 1;
+ 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 contains an unknown field named: '" << fieldName,
- fieldName == kUserNameFieldName || fieldName == kUserDbFieldName);
uassert(ErrorCodes::BadValue,
str::stream() << "username must contain a string field named: " << fieldName,
@@ -114,18 +100,19 @@ UserName UserName::parseFromBSONObj(const BSONObj& obj) {
usedFields.set(kUserDbFieldBit);
userDb = element.valueStringData();
+ } else {
+ uasserted(ErrorCodes::BadValue,
+ str::stream() << "username contains an unknown field named: '" << fieldName);
}
}
- if (!usedFields[kUserNameFieldBit]) {
- uasserted(ErrorCodes::BadValue,
- str::stream() << "username must contain a field named: " << kUserNameFieldName);
- }
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "username must contain a field named: " << kUserNameFieldName,
+ usedFields[kUserNameFieldBit]);
- if (!usedFields[kUserDbFieldBit]) {
- uasserted(ErrorCodes::BadValue,
- str::stream() << "username must contain a field named: " << kUserDbFieldName);
- }
+ uassert(ErrorCodes::BadValue,
+ str::stream() << "username must contain a field named: " << kUserDbFieldName,
+ usedFields[kUserDbFieldBit]);
return UserName(userName, userDb);
}
@@ -168,8 +155,4 @@ BSONObj UserName::toBSON() const {
return ret.obj();
}
-std::ostream& operator<<(std::ostream& os, const UserName& name) {
- return os << name.getFullName();
-}
-
} // namespace mongo
diff --git a/src/mongo/db/auth/user_name.h b/src/mongo/db/auth/user_name.h
index 4654ce4dabb..681d4a9f736 100644
--- a/src/mongo/db/auth/user_name.h
+++ b/src/mongo/db/auth/user_name.h
@@ -50,8 +50,22 @@ namespace mongo {
*/
class UserName {
public:
- UserName() : _splitPoint(0) {}
- UserName(StringData user, StringData dbname);
+ 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();
+ }
+ }
/**
* Parses a string of the form "db.username" into a UserName object.
@@ -83,67 +97,91 @@ public:
/**
* Gets the user part of a UserName.
*/
- StringData getUser() const {
- return StringData(_fullName.data(), _splitPoint);
+ const std::string& getUser() const {
+ return _user;
}
/**
* Gets the database name part of a UserName.
*/
- StringData getDB() const {
- if (_fullName.empty()) {
- return _fullName;
- }
-
- const auto offset = _splitPoint + 1;
- invariant(offset <= _fullName.size());
- return StringData(_fullName.data() + offset, _fullName.size() - offset);
+ const std::string& getDB() const {
+ return _db;
}
/**
* Gets the full unique name of a user as a string, formatted as "user@db".
*/
- const std::string& getFullName() const {
- return _fullName;
+ 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 {
- return str::stream() << getDB() << "." << getUser();
+ if (empty()) {
+ return "";
+ }
+ return str::stream() << _db << "." << _user;
}
/**
- * Stringifies the object, for logging/debugging.
+ * True if the username and dbname have not been set.
*/
- std::string toString() const {
- return getFullName();
+ bool empty() const {
+ return _db.empty() && _user.empty();
}
bool operator==(const UserName& rhs) const {
- return _splitPoint == rhs._splitPoint && getFullName() == rhs.getFullName();
+ return (_user == rhs._user) && (_db == rhs._db);
}
bool operator!=(const UserName& rhs) const {
- return _splitPoint != rhs._splitPoint || getFullName() != rhs.getFullName();
+ return !(*this == rhs);
}
bool operator<(const UserName& rhs) const {
- return getUser() < rhs.getUser() || (getUser() == rhs.getUser() && getDB() < rhs.getDB());
+ return (_user < rhs._user) || ((_user == rhs._user) && (_db < rhs._db));
}
template <typename H>
friend H AbslHashValue(H h, const UserName& userName) {
- return H::combine(std::move(h), userName.getFullName());
+ auto state = H::combine(std::move(h), userName._db);
+ state = H::combine(std::move(state), '.');
+ return H::combine(std::move(state), userName._user);
}
private:
- std::string _fullName; // The full name, stored as a string. "user@db".
- size_t _splitPoint; // The index of the "@" separating the user and db name parts.
+ std::string _user;
+ std::string _db;
};
-std::ostream& operator<<(std::ostream& os, const UserName& name);
+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.
diff --git a/src/mongo/db/commands/user_management_commands.cpp b/src/mongo/db/commands/user_management_commands.cpp
index e5a43d3a7bc..28c1590e8d5 100644
--- a/src/mongo/db/commands/user_management_commands.cpp
+++ b/src/mongo/db/commands/user_management_commands.cpp
@@ -148,7 +148,7 @@ Status checkOkayToGrantRolesToRole(OperationContext* opCtx,
for (const auto& roleToAdd : rolesToAdd) {
if (roleToAdd == role) {
return {ErrorCodes::InvalidRoleModification,
- str::stream() << "Cannot grant role " << role.getFullName() << " to itself."};
+ str::stream() << "Cannot grant role " << role << " to itself."};
}
if (role.getDB() != "admin" && roleToAdd.getDB() != role.getDB()) {
@@ -161,21 +161,20 @@ Status checkOkayToGrantRolesToRole(OperationContext* opCtx,
auto status = authzManager->rolesExist(opCtx, rolesToAdd);
if (!status.isOK()) {
return {status.code(),
- str::stream() << "Cannot grant roles to '" << role.toString()
- << "': " << status.reason()};
+ str::stream() << "Cannot grant roles to '" << role << "': " << status.reason()};
}
auto swData = authzManager->resolveRoles(
opCtx, rolesToAdd, AuthorizationManager::ResolveRoleOption::kRoles);
if (!swData.isOK()) {
return {swData.getStatus().code(),
- str::stream() << "Cannot grant roles to '" << role.toString()
+ str::stream() << "Cannot grant roles to '" << role
<< "': " << swData.getStatus().reason()};
}
if (sequenceContains(swData.getValue().roles.get(), role)) {
return {ErrorCodes::InvalidRoleModification,
- str::stream() << "Granting roles to " << role.getFullName()
+ str::stream() << "Granting roles to " << role
<< " would introduce a cycle in the role graph"};
}
@@ -414,8 +413,7 @@ Status updateRoleDocument(OperationContext* opCtx, const RoleName& role, const B
return status;
}
if (status.code() == ErrorCodes::NoMatchingDocument) {
- return Status(ErrorCodes::RoleNotFound,
- str::stream() << "Role " << role.getFullName() << " not found");
+ return Status(ErrorCodes::RoleNotFound, str::stream() << "Role " << role << " not found");
}
if (status.code() == ErrorCodes::UnknownError) {
return Status(ErrorCodes::RoleModificationFailed, status.reason());
@@ -476,8 +474,7 @@ Status updatePrivilegeDocument(OperationContext* opCtx,
return {ErrorCodes::UserModificationFailed, status.reason()};
}
if (status.code() == ErrorCodes::NoMatchingDocument) {
- return {ErrorCodes::UserNotFound,
- str::stream() << "User " << user.getFullName() << " not found"};
+ return {ErrorCodes::UserNotFound, str::stream() << "User " << user << " not found"};
}
return status;
}
@@ -1228,7 +1225,7 @@ void CmdUMCTyped<DropUserCommand>::Invocation::typedRun(OperationContext* opCtx)
uassertStatusOK(status);
uassert(ErrorCodes::UserNotFound,
- str::stream() << "User '" << userName.getFullName() << "' not found",
+ str::stream() << "User '" << userName << "' not found",
numMatched > 0);
}
@@ -1598,7 +1595,7 @@ void CmdUMCTyped<GrantPrivilegesToRoleCommand>::Invocation::typedRun(OperationCo
!cmd.getPrivileges().empty());
uassert(ErrorCodes::BadValue,
- str::stream() << roleName.getFullName() << " is a built-in role and cannot be modified",
+ str::stream() << roleName << " is a built-in role and cannot be modified",
!auth::isBuiltinRole(roleName));
auto* client = opCtx->getClient();
@@ -1647,7 +1644,7 @@ void CmdUMCTyped<RevokePrivilegesFromRoleCommand>::Invocation::typedRun(Operatio
!cmd.getPrivileges().empty());
uassert(ErrorCodes::BadValue,
- str::stream() << roleName.getFullName() << " is a built-in role and cannot be modified",
+ str::stream() << roleName << " is a built-in role and cannot be modified",
!auth::isBuiltinRole(roleName));
auto* client = opCtx->getClient();
@@ -1701,7 +1698,7 @@ void CmdUMCTyped<GrantRolesToRoleCommand>::Invocation::typedRun(OperationContext
!cmd.getRoles().empty());
uassert(ErrorCodes::BadValue,
- str::stream() << roleName.getFullName() << " is a built-in role and cannot be modified",
+ str::stream() << roleName << " is a built-in role and cannot be modified",
!auth::isBuiltinRole(roleName));
auto rolesToAdd = auth::resolveRoleNames(cmd.getRoles(), dbname);
@@ -1741,7 +1738,7 @@ void CmdUMCTyped<RevokeRolesFromRoleCommand>::Invocation::typedRun(OperationCont
!cmd.getRoles().empty());
uassert(ErrorCodes::BadValue,
- str::stream() << roleName.getFullName() << " is a built-in role and cannot be modified",
+ str::stream() << roleName << " is a built-in role and cannot be modified",
!auth::isBuiltinRole(roleName));
auto rolesToRemove = auth::resolveRoleNames(cmd.getRoles(), dbname);
@@ -1842,7 +1839,7 @@ void CmdUMCTyped<DropRoleCommand>::Invocation::typedRun(OperationContext* opCtx)
RoleName roleName(cmd.getCommandParameter(), dbname);
uassert(ErrorCodes::BadValue,
- str::stream() << roleName.getFullName() << " is a built-in role and cannot be modified",
+ str::stream() << roleName << " is a built-in role and cannot be modified",
!auth::isBuiltinRole(roleName));
auto* client = opCtx->getClient();
@@ -1868,8 +1865,8 @@ void CmdUMCTyped<DropRoleCommand>::Invocation::typedRun(OperationContext* opCtx)
BSON("$pull" << BSON("roles" << roleName.toBSON())));
if (!swCount.isOK()) {
return useDefaultCode(swCount.getStatus(), ErrorCodes::UserModificationFailed)
- .withContext(str::stream() << "Failed to remove role " << roleName.getFullName()
- << " from all users");
+ .withContext(str::stream()
+ << "Failed to remove role " << roleName << " from all users");
}
// Remove this role from all other roles
@@ -1878,15 +1875,15 @@ void CmdUMCTyped<DropRoleCommand>::Invocation::typedRun(OperationContext* opCtx)
BSON("$pull" << BSON("roles" << roleName.toBSON())));
if (!swCount.isOK()) {
return useDefaultCode(swCount.getStatus(), ErrorCodes::RoleModificationFailed)
- .withContext(str::stream() << "Failed to remove role " << roleName.getFullName()
- << " from all users");
+ .withContext(str::stream()
+ << "Failed to remove role " << roleName << " from all users");
}
// Finally, remove the actual role document
swCount = txn.remove(AuthorizationManager::rolesCollectionNamespace, roleName.toBSON());
if (!swCount.isOK()) {
- return swCount.getStatus().withContext(str::stream() << "Failed to remove role "
- << roleName.getFullName());
+ return swCount.getStatus().withContext(str::stream()
+ << "Failed to remove role " << roleName);
}
return Status::OK();
diff --git a/src/mongo/db/commands/user_management_commands_common.cpp b/src/mongo/db/commands/user_management_commands_common.cpp
index 0bc6f32cf49..7e2a861bfb1 100644
--- a/src/mongo/db/commands/user_management_commands_common.cpp
+++ b/src/mongo/db/commands/user_management_commands_common.cpp
@@ -90,8 +90,7 @@ Status checkAuthorizedToGrantRoles(AuthorizationSession* authzSession,
if (!authzSession->isAuthorizedForActionsOnResource(
ResourcePattern::forDatabaseName(roles[i].getDB()), ActionType::grantRole)) {
return Status(ErrorCodes::Unauthorized,
- str::stream()
- << "Not authorized to grant role: " << roles[i].getFullName());
+ str::stream() << "Not authorized to grant role: " << roles[i]);
}
}
@@ -116,8 +115,7 @@ Status checkAuthorizedToRevokeRoles(AuthorizationSession* authzSession,
if (!authzSession->isAuthorizedForActionsOnResource(
ResourcePattern::forDatabaseName(roles[i].getDB()), ActionType::revokeRole)) {
return Status(ErrorCodes::Unauthorized,
- str::stream()
- << "Not authorized to revoke role: " << roles[i].getFullName());
+ str::stream() << "Not authorized to revoke role: " << roles[i]);
}
}
return Status::OK();
@@ -201,16 +199,15 @@ void checkAuthForTypedCommand(Client* client, const UpdateUserCommand& request)
auto* as = AuthorizationSession::get(client);
UserName userName(request.getCommandParameter(), dbname);
- uassert(
- ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to change password of user: " << userName.getFullName(),
- (request.getPwd() == boost::none) || isAuthorizedToChangeOwnPasswordAsUser(as, userName) ||
- as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
- ActionType::changePassword));
+ uassert(ErrorCodes::Unauthorized,
+ str::stream() << "Not authorized to change password of user: " << userName,
+ (request.getPwd() == boost::none) ||
+ isAuthorizedToChangeOwnPasswordAsUser(as, userName) ||
+ as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
+ ActionType::changePassword));
uassert(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to change customData of user: "
- << userName.getFullName(),
+ str::stream() << "Not authorized to change customData of user: " << userName,
(request.getCustomData() == boost::none) ||
isAuthorizedToChangeOwnCustomDataAsUser(as, userName) ||
as->isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(dbname),
diff --git a/src/mongo/db/curop.cpp b/src/mongo/db/curop.cpp
index 64de6da5a53..3043e65a06f 100644
--- a/src/mongo/db/curop.cpp
+++ b/src/mongo/db/curop.cpp
@@ -1113,7 +1113,7 @@ void OpDebug::appendUserInfo(const CurOp& c,
}
allUsers.doneFast();
- builder.append("user", bestUser.getUser().empty() ? "" : bestUser.getFullName());
+ builder.append("user", bestUser.getUser().empty() ? "" : bestUser.getDisplayName());
}
std::function<BSONObj(ProfileFilter::Args)> OpDebug::appendStaged(StringSet requestedFields,
diff --git a/src/mongo/db/logical_session_id_helpers.cpp b/src/mongo/db/logical_session_id_helpers.cpp
index 46f15cb01b6..525ed561db2 100644
--- a/src/mongo/db/logical_session_id_helpers.cpp
+++ b/src/mongo/db/logical_session_id_helpers.cpp
@@ -58,7 +58,7 @@ SHA256Block getLogicalSessionUserDigestForLoggedInUser(const OperationContext* o
uassert(ErrorCodes::BadValue,
"Username too long to use with logical sessions",
- user->getName().getFullName().length() < kMaximumUserNameLengthForLogicalSessions);
+ user->getName().getDisplayNameLength() < kMaximumUserNameLengthForLogicalSessions);
return user->getDigest();
} else {
@@ -71,7 +71,7 @@ SHA256Block getLogicalSessionUserDigestFor(StringData user, StringData db) {
return kNoAuthDigest;
}
const UserName un(user, db);
- const auto& fn = un.getFullName();
+ auto fn = un.getDisplayName();
return SHA256Block::computeHash({ConstDataRange(fn.c_str(), fn.size())});
}
@@ -153,7 +153,7 @@ LogicalSessionRecord makeLogicalSessionRecord(OperationContext* opCtx, Date_t la
invariant(user);
id.setUid(user->getDigest());
- lsr.setUser(StringData(user->getName().toString()));
+ lsr.setUser(StringData(user->getName().getDisplayName()));
} else {
id.setUid(kNoAuthDigest);
}
@@ -191,7 +191,7 @@ LogicalSessionRecord makeLogicalSessionRecord(OperationContext* opCtx,
invariant(user);
if (user->getDigest() == lsid.getUid()) {
- lsr.setUser(StringData(user->getName().toString()));
+ lsr.setUser(StringData(user->getName().getDisplayName()));
}
}
diff --git a/src/mongo/util/net/ssl_manager_openssl.cpp b/src/mongo/util/net/ssl_manager_openssl.cpp
index 7612f08a205..e175d584e59 100644
--- a/src/mongo/util/net/ssl_manager_openssl.cpp
+++ b/src/mongo/util/net/ssl_manager_openssl.cpp
@@ -3088,7 +3088,7 @@ Status _validatePeerRoles(const stdx::unordered_set<RoleName>& embeddedRoles, SS
auto allowedRoles = it->second;
// See TLSCATrustsSetParameter::set() for a description of tlsCATrusts format.
- if (allowedRoles.count(RoleName("", ""))) {
+ if (allowedRoles.count(RoleName())) {
// CA is authorized for all role assignments.
return Status::OK();
}
@@ -3099,7 +3099,7 @@ Status _validatePeerRoles(const stdx::unordered_set<RoleName>& embeddedRoles, SS
!allowedRoles.count(RoleName("", role.getDB()))) {
return {ErrorCodes::BadValue,
str::stream() << "CA: " << sha256.toHexString()
- << " is not authorized to grant role " << role.toString()
+ << " is not authorized to grant role " << role
<< " due to tlsCATrusts parameter"};
}
}