summaryrefslogtreecommitdiff
path: root/src/mongo/db/auth/role_graph_update.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/auth/role_graph_update.cpp')
-rw-r--r--src/mongo/db/auth/role_graph_update.cpp458
1 files changed, 223 insertions, 235 deletions
diff --git a/src/mongo/db/auth/role_graph_update.cpp b/src/mongo/db/auth/role_graph_update.cpp
index 71e9370ec61..62df4a33a4b 100644
--- a/src/mongo/db/auth/role_graph_update.cpp
+++ b/src/mongo/db/auth/role_graph_update.cpp
@@ -40,278 +40,266 @@ namespace mongo {
namespace {
- /**
- * Structure representing information parsed out of a role document.
- */
- struct RoleInfo {
- RoleName name;
- std::vector<RoleName> roles;
- PrivilegeVector privileges;
- };
+/**
+ * Structure representing information parsed out of a role document.
+ */
+struct RoleInfo {
+ RoleName name;
+ std::vector<RoleName> roles;
+ PrivilegeVector privileges;
+};
- /**
- * Parses the role name out of a BSON document.
- */
- Status parseRoleNameFromDocument(const BSONObj& doc, RoleName* name) {
- BSONElement nameElement;
- BSONElement sourceElement;
- Status status = bsonExtractTypedField(
- doc, AuthorizationManager::ROLE_NAME_FIELD_NAME, String, &nameElement);
- if (!status.isOK())
- return status;
- status = bsonExtractTypedField(
- doc, AuthorizationManager::ROLE_DB_FIELD_NAME, String, &sourceElement);
- if (!status.isOK())
- return status;
- *name = RoleName(nameElement.valueStringData(), sourceElement.valueStringData());
+/**
+ * Parses the role name out of a BSON document.
+ */
+Status parseRoleNameFromDocument(const BSONObj& doc, RoleName* name) {
+ BSONElement nameElement;
+ BSONElement sourceElement;
+ Status status = bsonExtractTypedField(
+ doc, AuthorizationManager::ROLE_NAME_FIELD_NAME, String, &nameElement);
+ if (!status.isOK())
return status;
- }
+ status = bsonExtractTypedField(
+ doc, AuthorizationManager::ROLE_DB_FIELD_NAME, String, &sourceElement);
+ if (!status.isOK())
+ return status;
+ *name = RoleName(nameElement.valueStringData(), sourceElement.valueStringData());
+ return status;
+}
- /**
- * Checks whether the given "roleName" corresponds with the given _id field.
- * In admin.system.roles, documents with role name "role@db" must have _id
- * "db.role".
- *
- * Returns Status::OK if the two values are compatible.
- */
- Status checkIdMatchesRoleName(const BSONElement& idElement, const RoleName& roleName) {
- if (idElement.type() != String) {
- return Status(ErrorCodes::TypeMismatch,
- "Role document _id fields must be strings.");
- }
- StringData idField = idElement.valueStringData();
- size_t firstDot = idField.find('.');
- if (firstDot == std::string::npos ||
- idField.substr(0, firstDot) != roleName.getDB() ||
- idField.substr(firstDot + 1) != roleName.getRole()) {
- return Status(ErrorCodes::FailedToParse, mongoutils::str::stream() <<
- "Role document _id fields must be encoded as the string "
- "dbname.rolename. Found " << idField << " for " <<
- roleName.getFullName());
- }
- return Status::OK();
+/**
+ * Checks whether the given "roleName" corresponds with the given _id field.
+ * In admin.system.roles, documents with role name "role@db" must have _id
+ * "db.role".
+ *
+ * Returns Status::OK if the two values are compatible.
+ */
+Status checkIdMatchesRoleName(const BSONElement& idElement, const RoleName& roleName) {
+ if (idElement.type() != String) {
+ return Status(ErrorCodes::TypeMismatch, "Role document _id fields must be strings.");
+ }
+ StringData idField = idElement.valueStringData();
+ size_t firstDot = idField.find('.');
+ if (firstDot == std::string::npos || idField.substr(0, firstDot) != roleName.getDB() ||
+ idField.substr(firstDot + 1) != roleName.getRole()) {
+ return Status(ErrorCodes::FailedToParse,
+ mongoutils::str::stream()
+ << "Role document _id fields must be encoded as the string "
+ "dbname.rolename. Found " << idField << " for "
+ << roleName.getFullName());
}
+ return Status::OK();
+}
- /**
- * Parses "idElement" to extract the role name, according to the "dbname.role" convention
- * used for admin.system.roles documents.
- */
- Status getRoleNameFromIdField(const BSONElement& idElement, RoleName* roleName) {
- if (idElement.type() != String) {
- return Status(ErrorCodes::TypeMismatch,
- "Role document _id fields must be strings.");
- }
- StringData idField = idElement.valueStringData();
- size_t dotPos = idField.find('.');
- if (dotPos == std::string::npos) {
- return Status(ErrorCodes::BadValue,
- "Role document _id fields must have the form dbname.rolename");
- }
- *roleName = RoleName(idField.substr(dotPos + 1), idField.substr(0, dotPos));
- return Status::OK();
+/**
+ * Parses "idElement" to extract the role name, according to the "dbname.role" convention
+ * used for admin.system.roles documents.
+ */
+Status getRoleNameFromIdField(const BSONElement& idElement, RoleName* roleName) {
+ if (idElement.type() != String) {
+ return Status(ErrorCodes::TypeMismatch, "Role document _id fields must be strings.");
}
+ StringData idField = idElement.valueStringData();
+ size_t dotPos = idField.find('.');
+ if (dotPos == std::string::npos) {
+ return Status(ErrorCodes::BadValue,
+ "Role document _id fields must have the form dbname.rolename");
+ }
+ *roleName = RoleName(idField.substr(dotPos + 1), idField.substr(0, dotPos));
+ return Status::OK();
+}
- /**
- * Parses information about a role from a BSON document.
- */
- Status parseRoleFromDocument(const BSONObj& doc, RoleInfo* role) {
- BSONElement rolesElement;
- Status status = parseRoleNameFromDocument(doc, &role->name);
- if (!status.isOK())
- return status;
- status = checkIdMatchesRoleName(doc["_id"], role->name);
- if (!status.isOK())
- return status;
- status = bsonExtractTypedField(doc, "roles", Array, &rolesElement);
- if (!status.isOK())
- return status;
- BSONForEach(singleRoleElement, rolesElement.Obj()) {
- if (singleRoleElement.type() != Object) {
- return Status(ErrorCodes::TypeMismatch,
- "Elements of roles array must be objects.");
- }
- RoleName possessedRoleName;
- status = parseRoleNameFromDocument(singleRoleElement.Obj(), &possessedRoleName);
- if (!status.isOK())
- return status;
- role->roles.push_back(possessedRoleName);
+/**
+ * Parses information about a role from a BSON document.
+ */
+Status parseRoleFromDocument(const BSONObj& doc, RoleInfo* role) {
+ BSONElement rolesElement;
+ Status status = parseRoleNameFromDocument(doc, &role->name);
+ if (!status.isOK())
+ return status;
+ status = checkIdMatchesRoleName(doc["_id"], role->name);
+ if (!status.isOK())
+ return status;
+ status = bsonExtractTypedField(doc, "roles", Array, &rolesElement);
+ if (!status.isOK())
+ return status;
+ BSONForEach(singleRoleElement, rolesElement.Obj()) {
+ if (singleRoleElement.type() != Object) {
+ return Status(ErrorCodes::TypeMismatch, "Elements of roles array must be objects.");
}
-
- BSONElement privilegesElement;
- status = bsonExtractTypedField(doc, "privileges", Array, &privilegesElement);
+ RoleName possessedRoleName;
+ status = parseRoleNameFromDocument(singleRoleElement.Obj(), &possessedRoleName);
if (!status.isOK())
return status;
- status = auth::parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()),
- &role->privileges);
- return status;
+ role->roles.push_back(possessedRoleName);
}
- /**
- * Updates roleGraph for an insert-type oplog operation on admin.system.roles.
- */
- Status handleOplogInsert(RoleGraph* roleGraph, const BSONObj& insertedObj) {
- RoleInfo role;
- Status status = parseRoleFromDocument(insertedObj, &role);
- if (!status.isOK())
- return status;
- status = roleGraph->replaceRole(role.name, role.roles, role.privileges);
+ BSONElement privilegesElement;
+ status = bsonExtractTypedField(doc, "privileges", Array, &privilegesElement);
+ if (!status.isOK())
return status;
- }
+ status =
+ auth::parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()), &role->privileges);
+ return status;
+}
- /**
- * Updates roleGraph for an update-type oplog operation on admin.system.roles.
- *
- * Treats all updates as upserts.
- */
- Status handleOplogUpdate(RoleGraph* roleGraph,
- const BSONObj& updatePattern,
- const BSONObj& queryPattern) {
- RoleName roleToUpdate;
- Status status = getRoleNameFromIdField(queryPattern["_id"], &roleToUpdate);
- if (!status.isOK())
- return status;
+/**
+ * Updates roleGraph for an insert-type oplog operation on admin.system.roles.
+ */
+Status handleOplogInsert(RoleGraph* roleGraph, const BSONObj& insertedObj) {
+ RoleInfo role;
+ Status status = parseRoleFromDocument(insertedObj, &role);
+ if (!status.isOK())
+ return status;
+ status = roleGraph->replaceRole(role.name, role.roles, role.privileges);
+ return status;
+}
- UpdateDriver::Options updateOptions;
- UpdateDriver driver(updateOptions);
- status = driver.parse(updatePattern);
- if (!status.isOK())
- return status;
+/**
+ * Updates roleGraph for an update-type oplog operation on admin.system.roles.
+ *
+ * Treats all updates as upserts.
+ */
+Status handleOplogUpdate(RoleGraph* roleGraph,
+ const BSONObj& updatePattern,
+ const BSONObj& queryPattern) {
+ RoleName roleToUpdate;
+ Status status = getRoleNameFromIdField(queryPattern["_id"], &roleToUpdate);
+ if (!status.isOK())
+ return status;
- mutablebson::Document roleDocument;
- status = AuthorizationManager::getBSONForRole(
- roleGraph, roleToUpdate, roleDocument.root());
- if (status == ErrorCodes::RoleNotFound) {
- // The query pattern will only contain _id, no other immutable fields are present
- status = driver.populateDocumentWithQueryFields(queryPattern, NULL, roleDocument);
- }
- if (!status.isOK())
- return status;
+ UpdateDriver::Options updateOptions;
+ UpdateDriver driver(updateOptions);
+ status = driver.parse(updatePattern);
+ if (!status.isOK())
+ return status;
- status = driver.update(StringData(), &roleDocument);
- if (!status.isOK())
- return status;
+ mutablebson::Document roleDocument;
+ status = AuthorizationManager::getBSONForRole(roleGraph, roleToUpdate, roleDocument.root());
+ if (status == ErrorCodes::RoleNotFound) {
+ // The query pattern will only contain _id, no other immutable fields are present
+ status = driver.populateDocumentWithQueryFields(queryPattern, NULL, roleDocument);
+ }
+ if (!status.isOK())
+ return status;
- // Now use the updated document to totally replace the role in the graph!
- RoleInfo role;
- status = parseRoleFromDocument(roleDocument.getObject(), &role);
- if (!status.isOK())
- return status;
- status = roleGraph->replaceRole(role.name, role.roles, role.privileges);
+ status = driver.update(StringData(), &roleDocument);
+ if (!status.isOK())
+ return status;
+ // Now use the updated document to totally replace the role in the graph!
+ RoleInfo role;
+ status = parseRoleFromDocument(roleDocument.getObject(), &role);
+ if (!status.isOK())
return status;
- }
+ status = roleGraph->replaceRole(role.name, role.roles, role.privileges);
- /**
- * Updates roleGraph for a delete-type oplog operation on admin.system.roles.
- */
- Status handleOplogDelete(
- RoleGraph* roleGraph,
- const BSONObj& deletePattern) {
+ return status;
+}
- RoleName roleToDelete;
- Status status = getRoleNameFromIdField(deletePattern["_id"], &roleToDelete);
- if (!status.isOK())
- return status;
- status = roleGraph->deleteRole(roleToDelete);
- if (ErrorCodes::RoleNotFound == status) {
- // Double-delete can happen in oplog application.
- status = Status::OK();
- }
+/**
+ * Updates roleGraph for a delete-type oplog operation on admin.system.roles.
+ */
+Status handleOplogDelete(RoleGraph* roleGraph, const BSONObj& deletePattern) {
+ RoleName roleToDelete;
+ Status status = getRoleNameFromIdField(deletePattern["_id"], &roleToDelete);
+ if (!status.isOK())
return status;
+ status = roleGraph->deleteRole(roleToDelete);
+ if (ErrorCodes::RoleNotFound == status) {
+ // Double-delete can happen in oplog application.
+ status = Status::OK();
}
+ return status;
+}
- /**
- * Updates roleGraph for command-type oplog operations on the admin database.
- */
- Status handleOplogCommand(RoleGraph* roleGraph, const BSONObj& cmdObj) {
- const NamespaceString& rolesCollectionNamespace =
- AuthorizationManager::rolesCollectionNamespace;
- const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
- if (cmdName == "applyOps") {
- // Operations applied by applyOps will be passed into RoleGraph::handleOplog() by the
- // implementation of applyOps itself.
- return Status::OK();
- }
- if (cmdName == "create") {
- return Status::OK();
- }
- if (cmdName == "drop") {
- if (cmdObj.firstElement().str() == rolesCollectionNamespace.coll()) {
- *roleGraph = RoleGraph();
- }
- return Status::OK();
- }
- if (cmdName == "dropDatabase") {
+/**
+ * Updates roleGraph for command-type oplog operations on the admin database.
+ */
+Status handleOplogCommand(RoleGraph* roleGraph, const BSONObj& cmdObj) {
+ const NamespaceString& rolesCollectionNamespace =
+ AuthorizationManager::rolesCollectionNamespace;
+ const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
+ if (cmdName == "applyOps") {
+ // Operations applied by applyOps will be passed into RoleGraph::handleOplog() by the
+ // implementation of applyOps itself.
+ return Status::OK();
+ }
+ if (cmdName == "create") {
+ return Status::OK();
+ }
+ if (cmdName == "drop") {
+ if (cmdObj.firstElement().str() == rolesCollectionNamespace.coll()) {
*roleGraph = RoleGraph();
- return Status::OK();
}
- if (cmdName == "renameCollection") {
- if (cmdObj.firstElement().str() == rolesCollectionNamespace.ns()) {
- *roleGraph = RoleGraph();
- return Status::OK();
- }
- if (cmdObj["to"].str() == rolesCollectionNamespace.ns()) {
- *roleGraph = RoleGraph();
- return Status(ErrorCodes::OplogOperationUnsupported,
- "Renaming into admin.system.roles produces inconsistent state; "
- "must resynchronize role graph.");
- }
- return Status::OK();
- }
- if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
+ return Status::OK();
+ }
+ if (cmdName == "dropDatabase") {
+ *roleGraph = RoleGraph();
+ return Status::OK();
+ }
+ if (cmdName == "renameCollection") {
+ if (cmdObj.firstElement().str() == rolesCollectionNamespace.ns()) {
+ *roleGraph = RoleGraph();
return Status::OK();
}
- if ((cmdName == "collMod" || cmdName == "emptycapped") &&
- cmdObj.firstElement().str() != rolesCollectionNamespace.coll()) {
-
- // We don't care about these if they're not on the roles collection.
- return Status::OK();
+ if (cmdObj["to"].str() == rolesCollectionNamespace.ns()) {
+ *roleGraph = RoleGraph();
+ return Status(ErrorCodes::OplogOperationUnsupported,
+ "Renaming into admin.system.roles produces inconsistent state; "
+ "must resynchronize role graph.");
}
- // No other commands expected. Warn.
- return Status(ErrorCodes::OplogOperationUnsupported, "Unsupported oplog operation");
+ return Status::OK();
}
+ if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
+ return Status::OK();
+ }
+ if ((cmdName == "collMod" || cmdName == "emptycapped") &&
+ cmdObj.firstElement().str() != rolesCollectionNamespace.coll()) {
+ // We don't care about these if they're not on the roles collection.
+ return Status::OK();
+ }
+ // No other commands expected. Warn.
+ return Status(ErrorCodes::OplogOperationUnsupported, "Unsupported oplog operation");
+}
} // namespace
- Status RoleGraph::addRoleFromDocument(const BSONObj& doc) {
- RoleInfo role;
- Status status = parseRoleFromDocument(doc, &role);
- if (!status.isOK())
- return status;
- status = replaceRole(role.name, role.roles, role.privileges);
+Status RoleGraph::addRoleFromDocument(const BSONObj& doc) {
+ RoleInfo role;
+ Status status = parseRoleFromDocument(doc, &role);
+ if (!status.isOK())
return status;
- }
+ status = replaceRole(role.name, role.roles, role.privileges);
+ return status;
+}
- Status RoleGraph::handleLogOp(
- const char* op,
- const NamespaceString& ns,
- const BSONObj& o,
- const BSONObj* o2) {
-
- if (op == StringData("db", StringData::LiteralTag()))
- return Status::OK();
- if (op[0] == '\0' || op[1] != '\0') {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "Unrecognized \"op\" field value \"" <<
- op << '"');
- }
+Status RoleGraph::handleLogOp(const char* op,
+ const NamespaceString& ns,
+ const BSONObj& o,
+ const BSONObj* o2) {
+ if (op == StringData("db", StringData::LiteralTag()))
+ return Status::OK();
+ if (op[0] == '\0' || op[1] != '\0') {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "Unrecognized \"op\" field value \"" << op
+ << '"');
+ }
- if (ns.db() != AuthorizationManager::rolesCollectionNamespace.db())
- return Status::OK();
+ if (ns.db() != AuthorizationManager::rolesCollectionNamespace.db())
+ return Status::OK();
- if (ns.isCommand()) {
- if (*op == 'c') {
- return handleOplogCommand(this, o);
- }
- else {
- return Status(ErrorCodes::BadValue,
- "Non-command oplog entry on admin.$cmd namespace");
- }
+ if (ns.isCommand()) {
+ if (*op == 'c') {
+ return handleOplogCommand(this, o);
+ } else {
+ return Status(ErrorCodes::BadValue, "Non-command oplog entry on admin.$cmd namespace");
}
+ }
- if (ns.coll() != AuthorizationManager::rolesCollectionNamespace.coll())
- return Status::OK();
+ if (ns.coll() != AuthorizationManager::rolesCollectionNamespace.coll())
+ return Status::OK();
- switch (*op) {
+ switch (*op) {
case 'i':
return handleOplogInsert(this, o);
case 'u':
@@ -329,9 +317,9 @@ namespace {
"Namespace admin.system.roles is not a valid target for commands");
default:
return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "Unrecognized \"op\" field value \"" <<
- op << '"');
- }
+ mongoutils::str::stream() << "Unrecognized \"op\" field value \"" << op
+ << '"');
}
+}
} // namespace mongo