diff options
-rw-r--r-- | jstests/auth/auth1.js | 8 | ||||
-rw-r--r-- | jstests/auth/renameSystemCollections.js | 14 | ||||
-rw-r--r-- | jstests/auth/show_log_auth.js | 8 | ||||
-rw-r--r-- | jstests/auth/system_user_privileges.js | 3 | ||||
-rw-r--r-- | jstests/sharding/auth.js | 2 | ||||
-rw-r--r-- | src/mongo/db/commands.cpp | 49 | ||||
-rw-r--r-- | src/mongo/db/commands.h | 20 | ||||
-rw-r--r-- | src/mongo/db/dbcommands.cpp | 29 | ||||
-rw-r--r-- | src/mongo/s/s_only.cpp | 31 |
9 files changed, 96 insertions, 68 deletions
diff --git a/jstests/auth/auth1.js b/jstests/auth/auth1.js index 0a995ea9655..ad5a579feb8 100644 --- a/jstests/auth/auth1.js +++ b/jstests/auth/auth1.js @@ -26,9 +26,11 @@ db.getSisterDB( "admin" ).addUser( "super", "super" ); assert.throws( function() { t.findOne() }, [], "read without login" ); print("make sure we can't run certain commands w/out auth"); -var errmsg = "unauthorized"; -assert.eq(db.runCommand({eval : "function() { return 1; }"}).errmsg, errmsg); -assert.eq(db.adminCommand({getLog : "global"}).errmsg, errmsg); +var codeUnauthorized = 13; +var rslt = db.runCommand({eval : "function() { return 1; }"}); +assert.eq(rslt.code, codeUnauthorized, tojson(rslt)); +var rslt = db.runCommand({getLog : "global"}); +assert.eq(rslt.code, codeUnauthorized, tojson(rslt)); assert(!db.auth("eliot", "eliot2"), "auth succeeded with wrong password"); assert(db.auth("eliot", "eliot"), "auth failed"); diff --git a/jstests/auth/renameSystemCollections.js b/jstests/auth/renameSystemCollections.js index 20296070bb3..96ea309b3d3 100644 --- a/jstests/auth/renameSystemCollections.js +++ b/jstests/auth/renameSystemCollections.js @@ -5,6 +5,8 @@ var adminDB = conn.getDB("admin"); var testDB = conn.getDB("testdb"); var testDB2 = conn.getDB("testdb2"); +var CodeUnauthorized = 13; + testDB.addUser({user:'spencer', pwd:'password', roles:['readWrite']}); @@ -24,13 +26,13 @@ userAdminConn.getDB('admin').addUser({user:'readWriteAdmin', testDB.auth('spencer', 'password'); res = testDB.system.profile.renameCollection("profile"); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); // Test that a readWrite user can't rename system.users to something they can read. var res = testDB.system.users.renameCollection("users"); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); assert.eq(0, testDB.users.count()); @@ -40,7 +42,7 @@ testDB.users.insert({user:'backdoor', roles:'userAdmin'}); res = testDB.users.renameCollection("system.users", true); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); assert.eq(null, userAdminConn.getDB('testdb').system.users.findOne({user:'backdoor'})); @@ -51,7 +53,7 @@ testDB2.users.insert({user:'backdoor', roles:'userAdmin'}); res = testDB2.users.renameCollection("system.users"); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); assert.eq(0, userAdminConn.getDB('testdb2').system.users.count()); @@ -59,7 +61,7 @@ assert.eq(0, userAdminConn.getDB('testdb2').system.users.count()); testDB2.users.drop(); var res = adminDB.runCommand({renameCollection:'testdb.system.users', to:'testdb2.users'}); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); assert.eq(0, testDB2.users.count()); @@ -67,7 +69,7 @@ assert.eq(0, testDB2.users.count()); testDB.users.drop(); var res = userAdminConn.getDB('testdb').system.users.renameCollection("users"); assert.eq(0, res.ok); -assert.eq("unauthorized", res.errmsg); +assert.eq(CodeUnauthorized, res.code); assert.eq(0, testDB.users.count()); diff --git a/jstests/auth/show_log_auth.js b/jstests/auth/show_log_auth.js index bef5180ba9d..e28703aa1f1 100644 --- a/jstests/auth/show_log_auth.js +++ b/jstests/auth/show_log_auth.js @@ -21,8 +21,12 @@ finally { print = oldprint; } -assert(printed[0]=='Error while trying to show logs: unauthorized'); -assert(printed[1]=='Error while trying to show ' + baseName + ' log: unauthorized'); +function assertStartsWith(s, prefix) { + assert.eq(s.substr(0, prefix.length), prefix); +} + +assertStartsWith(printed[0], 'Error while trying to show logs'); +assertStartsWith(printed[1], 'Error while trying to show ' + baseName + ' log'); db.auth( "admin" , "pass" ); db.shutdownServer(); diff --git a/jstests/auth/system_user_privileges.js b/jstests/auth/system_user_privileges.js index 6d397e70999..4bbabc77e2b 100644 --- a/jstests/auth/system_user_privileges.js +++ b/jstests/auth/system_user_privileges.js @@ -22,8 +22,7 @@ // Asserts that on the given "conn", "dbName"."collectionName".count() fails as unauthorized. function assertCountUnauthorized(conn, dbName, collectionName) { - assert.eq(runCountCommand(conn, dbName, collectionName).errmsg, - "unauthorized", + assert.eq(runCountCommand(conn, dbName, collectionName).code, 13, "On " + dbName + "." + collectionName); } diff --git a/jstests/sharding/auth.js b/jstests/sharding/auth.js index 230e2429467..f9bcb881d36 100644 --- a/jstests/sharding/auth.js +++ b/jstests/sharding/auth.js @@ -80,7 +80,7 @@ logout(adminUser); var result = s.getDB("admin").runCommand({addShard : shardName}); printjson(result); -assert.eq(result.errmsg, "unauthorized"); +assert.eq(result.code, 13); login(adminUser); diff --git a/src/mongo/db/commands.cpp b/src/mongo/db/commands.cpp index 493f8793644..3ededf1ec93 100644 --- a/src/mongo/db/commands.cpp +++ b/src/mongo/db/commands.cpp @@ -209,6 +209,14 @@ namespace mongo { return client->getAuthorizationSession()->checkAuthForPrivileges(privileges); } + void Command::appendCommandStatus(BSONObjBuilder& result, const Status& status) { + appendCommandStatus(result, status.isOK(), status.reason()); + BSONObj tmp = result.asTempObj(); + if (!status.isOK() && !tmp.hasField("code")) { + result.append("code", status.code()); + } + } + void Command::logIfSlow( const Timer& timer, const string& msg ) { int ms = timer.millis(); if ( ms > cmdLine.slowMS ) { @@ -216,6 +224,47 @@ namespace mongo { } } + static Status _checkAuthorizationImpl(Command* c, + ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj, + bool fromRepl) { + if ( c->adminOnly() && ! fromRepl && dbname != "admin" ) { + return Status(ErrorCodes::Unauthorized, str::stream() << c->name << + " may only be run against the admin database."); + } + if (AuthorizationManager::isAuthEnabled()) { + Status status = c->checkAuthForCommand(client, dbname, cmdObj); + if (status == ErrorCodes::Unauthorized) { + return Status(ErrorCodes::Unauthorized, + str::stream() << "not authorized on " << dbname << + " to execute command " << cmdObj); + } + if (!status.isOK()) { + return status; + } + } + else if (c->adminOnly() && + c->localHostOnlyIfNoAuth(cmdObj) && + !client->getIsLocalHostConnection()) { + + return Status(ErrorCodes::Unauthorized, str::stream() << c->name << + " must run from localhost when running db without auth"); + } + return Status::OK(); + } + + Status Command::_checkAuthorization(Command* c, + ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj, + bool fromRepl) { + Status status = _checkAuthorizationImpl(c, client, dbname, cmdObj, fromRepl); + if (!status.isOK()) { + log() << status << std::endl; + } + return status; + } } #include "../client/connpool.h" diff --git a/src/mongo/db/commands.h b/src/mongo/db/commands.h index 5f52aa97de7..edaa185e867 100644 --- a/src/mongo/db/commands.h +++ b/src/mongo/db/commands.h @@ -197,9 +197,29 @@ namespace mongo { // Helper for setting errmsg and ok field in command result object. static void appendCommandStatus(BSONObjBuilder& result, bool ok, const std::string& errmsg); + static void appendCommandStatus(BSONObjBuilder& result, const Status& status); // Set by command line. Controls whether or not testing-only commands should be available. static int testCommandsEnabled; + + private: + /** + * Checks to see if the client is authorized to run the given command with the given + * parameters on the given named database. + * + * fromRepl is true if this command is running as part of oplog application, which for + * historic reasons has slightly different authorization semantics. TODO(schwerin): Check + * to see if this oddity can now be eliminated. + * + * Returns Status::OK() if the command is authorized. Most likely returns + * ErrorCodes::Unauthorized otherwise, but any return other than Status::OK implies not + * authorized. + */ + static Status _checkAuthorization(Command* c, + ClientBasic* client, + const std::string& dbname, + const BSONObj& cmdObj, + bool fromRepl); }; bool _runCommands(const char *ns, BSONObj& jsobj, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions); diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp index 3a2a9036f62..8fa0c7d9442 100644 --- a/src/mongo/db/dbcommands.cpp +++ b/src/mongo/db/dbcommands.cpp @@ -2110,35 +2110,12 @@ namespace mongo { std::string dbname = nsToDatabase( cmdns ); scoped_ptr<MaintenanceModeSetter> mmSetter; - if (c->adminOnly() && - c->localHostOnlyIfNoAuth(cmdObj) && - !AuthorizationManager::isAuthEnabled() && - !client.getIsLocalHostConnection()) { - log() << "command denied: " << cmdObj.toString() << endl; - appendCommandStatus(result, - false, - "unauthorized: this command must run from localhost when running " - "db without auth"); + Status status = _checkAuthorization(c, &client, dbname, cmdObj, fromRepl); + if (!status.isOK()) { + appendCommandStatus(result, status); return; } - if ( c->adminOnly() && ! fromRepl && dbname != "admin" ) { - log() << "command denied: " << cmdObj.toString() << endl; - appendCommandStatus(result, false, "access denied; use admin db"); - return; - } - - if (AuthorizationManager::isAuthEnabled()) { - Status status = c->checkAuthForCommand(&client, dbname, cmdObj); - if (!status.isOK()) { - log() << "command denied: " << cmdObj.toString() << endl; - result.append("note", str::stream() << "not authorized for command: " << - c->name << " on database " << dbname); - appendCommandStatus(result, false, status.reason()); - return; - } - } - if ( cmdObj["help"].trueValue() ) { client.curop()->ensureStarted(); stringstream ss; diff --git a/src/mongo/s/s_only.cpp b/src/mongo/s/s_only.cpp index 48824ab7da3..ec71411b468 100644 --- a/src/mongo/s/s_only.cpp +++ b/src/mongo/s/s_only.cpp @@ -123,38 +123,13 @@ namespace mongo { BSONObj& cmdObj, BSONObjBuilder& result, bool fromRepl ) { - verify(c); - std::string dbname = nsToDatabase(ns); - // Access control checks - if (AuthorizationManager::isAuthEnabled()) { - Status status = c->checkAuthForCommand(&client, dbname, cmdObj); - if (!status.isOK()) { - log() << "command denied: " << cmdObj.toString() << endl; - result.append("note", str::stream() << "not authorized for command: " << - c->name << " on database " << dbname); - appendCommandStatus(result, false, status.reason()); - return; - } - } - if (c->adminOnly() && - c->localHostOnlyIfNoAuth(cmdObj) && - !AuthorizationManager::isAuthEnabled() && - !client.getIsLocalHostConnection()) { - log() << "command denied: " << cmdObj.toString() << endl; - appendCommandStatus(result, - false, - "unauthorized: this command must run from localhost when running db " - "without auth"); - return; - } - if (c->adminOnly() && !startsWith(ns, "admin.")) { - log() << "command denied: " << cmdObj.toString() << endl; - appendCommandStatus(result, false, "access denied - use admin db"); + Status status = _checkAuthorization(c, &client, dbname, cmdObj, fromRepl); + if (!status.isOK()) { + appendCommandStatus(result, status); return; } - // End of access control checks if (cmdObj.getBoolField("help")) { stringstream help; |