diff options
-rw-r--r-- | jstests/auth/lib/commands_lib.js | 23 | ||||
-rw-r--r-- | jstests/replsets/oplog_note_cmd.js | 21 | ||||
-rw-r--r-- | src/mongo/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/base/error_codes.err | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/action_types.txt | 1 | ||||
-rw-r--r-- | src/mongo/db/auth/role_graph_builtin_roles.cpp | 7 | ||||
-rw-r--r-- | src/mongo/db/commands/oplog_note.cpp | 82 |
7 files changed, 134 insertions, 2 deletions
diff --git a/jstests/auth/lib/commands_lib.js b/jstests/auth/lib/commands_lib.js index 1dca80e1ac2..a5bd6a47b4f 100644 --- a/jstests/auth/lib/commands_lib.js +++ b/jstests/auth/lib/commands_lib.js @@ -308,6 +308,29 @@ var authCommandsLib = { ] }, { + testname: "appendOplogNote", + command: {appendOplogNote: 1, data: {a: 1}}, + skipSharded: true, + testcases: [ + { + runOnDb: adminDbName, + roles: { + backup: 1, + clusterManager: 1, + clusterAdmin: 1, + root: 1, + __system: 1 + }, + privileges: [ + { resource: {cluster: true}, actions: ["appendOplogNote"] } + ], + expectFail: true, // because no replication enabled + }, + { runOnDb: firstDbName, roles: {} }, + { runOnDb: secondDbName, roles: {} } + ] + }, + { testname: "buildInfo", command: {buildInfo: 1}, testcases: [ diff --git a/jstests/replsets/oplog_note_cmd.js b/jstests/replsets/oplog_note_cmd.js new file mode 100644 index 00000000000..2db0c2ae0b1 --- /dev/null +++ b/jstests/replsets/oplog_note_cmd.js @@ -0,0 +1,21 @@ +// Test that the "appendOplogNote" command works properly + +var rs = new ReplSetTest({name: "oplogNoteTest", nodes: 1}); +rs.startSet(); +rs.initiate(); + +var primary = rs.getPrimary(); +var db = primary.getDB('admin'); +db.foo.insert({a:1}); + +// Make sure "optime" field gets updated +var statusBefore = db.runCommand({replSetGetStatus: 1}); +assert.commandWorked(db.runCommand({appendOplogNote: 1, data: {a: 1}})); +var statusAfter = db.runCommand({replSetGetStatus: 1}); +assert.lt(statusBefore.members[0].optime, statusAfter.members[0].optime); + +// Make sure note written successfully +var op = db.getSiblingDB('local').oplog.rs.find().sort({$natural: -1}).limit(1).next(); +assert.eq(1, op.o.a); + +rs.stopSet();
\ No newline at end of file diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 0316d177ddc..fc6519e4a83 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -633,6 +633,7 @@ serverOnlyFiles = [ "db/curop.cpp", "db/commands/group.cpp", "db/commands/index_stats.cpp", "db/commands/mr.cpp", + "db/commands/oplog_note.cpp", "db/commands/pipeline_command.cpp", "db/commands/plan_cache_commands.cpp", "db/commands/rename_collection.cpp", diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index 936d77a33fc..c29612eafa3 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -75,6 +75,7 @@ error_code("InvalidOptions", 72) error_code("InvalidNamespace", 73) error_code("NodeNotFound", 74) error_code("WriteConcernLegacyOK", 75) +error_code("NoReplicationEnabled", 76) # Non-sequential error codes (for compatibility only) error_code("DuplicateKey", 11000) diff --git a/src/mongo/db/auth/action_types.txt b/src/mongo/db/auth/action_types.txt index 7b0f8022f19..fed412281b3 100644 --- a/src/mongo/db/auth/action_types.txt +++ b/src/mongo/db/auth/action_types.txt @@ -6,6 +6,7 @@ # also may change between versions. ["addShard", "anyAction", # Special ActionType that represents *all* actions +"appendOplogNote", "applicationMessage", # Not used for permissions checks, but to id the event in logs. "auditLogRotate", # Not used for permissions checks, but to id the event in logs. "authCheck", # Not used for permissions checks, but to id the authorization-checking event in logs. diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp index 85f8b33f058..d4c3a9060e1 100644 --- a/src/mongo/db/auth/role_graph_builtin_roles.cpp +++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp @@ -218,6 +218,7 @@ namespace { // clusterManager role actions that target the cluster resource clusterManagerRoleClusterActions + << ActionType::appendOplogNote // backup gets this also << ActionType::applicationMessage // hostManager gets this also << ActionType::replSetConfigure << ActionType::replSetGetStatus // clusterMonitor gets this also @@ -464,9 +465,11 @@ namespace { privileges, Privilege(ResourcePattern::forAnyNormalResource(), ActionType::find)); + ActionSet clusterActions; + clusterActions << ActionType::listDatabases + << ActionType::appendOplogNote; Privilege::addPrivilegeToPrivilegeVector( - privileges, - Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases)); + privileges, Privilege(ResourcePattern::forClusterResource(), clusterActions)); Privilege::addPrivilegeToPrivilegeVector( privileges, diff --git a/src/mongo/db/commands/oplog_note.cpp b/src/mongo/db/commands/oplog_note.cpp new file mode 100644 index 00000000000..a1d280b3755 --- /dev/null +++ b/src/mongo/db/commands/oplog_note.cpp @@ -0,0 +1,82 @@ +/** + * Copyright (C) 2013 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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 + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * 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 GNU Affero General 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 <string> + +#include "mongo/bson/util/bson_extract.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_session.h" +#include "mongo/db/auth/resource_pattern.h" +#include "mongo/db/jsobj.h" +#include "mongo/db/commands.h" +#include "mongo/db/repl/oplog.h" +#include "mongo/db/repl/replication_server_status.h" + +namespace mongo { + class AppendOplogNoteCmd : public Command { + public: + AppendOplogNoteCmd() : Command( "appendOplogNote" ) {} + virtual bool slaveOk() const { return false; } + virtual bool adminOnly() const { return true; } + virtual LockType locktype() const { return NONE; } + virtual void help( stringstream &help ) const { + help << "Adds a no-op entry to the oplog"; + } + virtual Status checkAuthForCommand(ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj) { + if (!client->getAuthorizationSession()->isAuthorizedForActionsOnResource( + ResourcePattern::forClusterResource(), ActionType::appendOplogNote)) { + return Status(ErrorCodes::Unauthorized, "Unauthorized"); + } + return Status::OK(); + } + virtual bool run(const string& dbname, + BSONObj& cmdObj, + int, + string& errmsg, + BSONObjBuilder& result, + bool fromRepl) { + if (!replSettings.master) { + return appendCommandStatus(result, Status( + ErrorCodes::NoReplicationEnabled, + "Must have replication set up to run \"appendOplogNote\"")); + } + BSONElement dataElement; + Status status = bsonExtractTypedField(cmdObj, "data", Object, &dataElement); + if (!status.isOK()) { + return appendCommandStatus(result, status); + } + + logOpComment(dataElement.Obj()); + return true; + } + + } appendOplogNoteCmd; + +} // namespace mongo |