Skip to content

Commit

Permalink
SERVER-32965: Expose per-user SASL mechanism negotiation via isMaster
Browse files Browse the repository at this point in the history
  • Loading branch information
spencerjackson committed Feb 2, 2018
1 parent 481b4de commit b40c004
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 0 deletions.
36 changes: 36 additions & 0 deletions jstests/auth/sasl_mechanism_discovery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Tests that a client may discover a user's supported SASL mechanisms via isMaster.
(function() {
"use strict";

function runTest(conn) {
var db = conn.getDB("admin");
var externalDB = conn.getDB("$external");

// Make users
assert.commandWorked(db.runCommand({createUser: "user", pwd: "pwd", roles: []}));
assert.commandWorked(externalDB.runCommand({createUser: "user", roles: []}));

// Internal users should support SCRAM-SHA-1.
var isMasterResult =
assert.commandWorked(db.runCommand({isMaster: 1, saslSupportedMechs: "admin.user"}));
assert.eq(["SCRAM-SHA-1"], isMasterResult.saslSupportedMechs, tojson(isMasterResult));

// External users should support PLAIN, but not SCRAM-SHA-1.
isMasterResult = assert.commandWorked(
db.runCommand({isMaster: 1, saslSupportedMechs: "$external.user"}));
assert.eq(["PLAIN"], isMasterResult.saslSupportedMechs, tojson(isMasterResult));
}

// Test standalone.
var m = MongoRunner.runMongod({setParameter: "authenticationMechanisms=SCRAM-SHA-1,PLAIN"});
runTest(m);
MongoRunner.stopMongod(m);

// Test mongos.
var st = new ShardingTest({
shards: 0,
other: {mongosOptions: {setParameter: "authenticationMechanisms=PLAIN,SCRAM-SHA-1"}}
});
runTest(st.s0);
st.stop();
})();
1 change: 1 addition & 0 deletions src/mongo/db/auth/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ env.Library(
'role_graph.cpp',
'role_graph_update.cpp',
'role_graph_builtin_roles.cpp',
'sasl_mechanism_advertiser.cpp',
'user.cpp',
'user_document_parser.cpp',
'user_management_commands_parser.cpp',
Expand Down
80 changes: 80 additions & 0 deletions src/mongo/db/auth/sasl_mechanism_advertiser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (C) 2018 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 "mongo/platform/basic.h"

#include "mongo/db/auth/sasl_mechanism_advertiser.h"

#include "mongo/crypto/sha1_block.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/sasl_options.h"
#include "mongo/db/auth/user.h"

namespace mongo {

namespace {
void appendMechanismIfSupported(StringData mechanism, BSONArrayBuilder* builder) {
const auto& globalMechanisms = saslGlobalParams.authenticationMechanisms;
if (std::find(globalMechanisms.begin(), globalMechanisms.end(), mechanism) !=
globalMechanisms.end()) {
(*builder) << mechanism;
}
}
} // namespace


void SASLMechanismAdvertiser::advertise(OperationContext* opCtx,
const BSONObj& cmdObj,
BSONObjBuilder* result) {
BSONElement saslSupportedMechs = cmdObj["saslSupportedMechs"];
if (saslSupportedMechs.type() == BSONType::String) {
AuthorizationManager* authManager = AuthorizationManager::get(opCtx->getServiceContext());

UserName userName = uassertStatusOK(UserName::parse(saslSupportedMechs.String()));

User* userObj;
Status status = authManager->acquireUser(opCtx, userName, &userObj);
uassertStatusOK(status);
auto credentials = userObj->getCredentials();
authManager->releaseUser(userObj);

BSONArrayBuilder mechanismsBuilder;
if (credentials.isExternal) {
for (const StringData& userMechanism : {"GSSAPI", "PLAIN"}) {
appendMechanismIfSupported(userMechanism, &mechanismsBuilder);
}
} else if (credentials.scram<SHA1Block>().isValid()) {
appendMechanismIfSupported("SCRAM-SHA-1", &mechanismsBuilder);
}

result->appendArray("saslSupportedMechs", mechanismsBuilder.arr());
}
}


} // namespace mongo
42 changes: 42 additions & 0 deletions src/mongo/db/auth/sasl_mechanism_advertiser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (C) 2018 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.
*/

#pragma once

#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/operation_context.h"

namespace mongo {

class SASLMechanismAdvertiser {
public:
static void advertise(OperationContext* opCtx, const BSONObj& cmdObj, BSONObjBuilder* result);
};

} // namespace mongo
15 changes: 15 additions & 0 deletions src/mongo/db/auth/user_name.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ UserName::UserName(StringData user, StringData dbname) {
_splitPoint = user.size();
}


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);
}

std::ostream& operator<<(std::ostream& os, const UserName& name) {
return os << name.getFullName();
}
Expand Down
6 changes: 6 additions & 0 deletions src/mongo/db/auth/user_name.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@


#include "mongo/base/disallow_copying.h"
#include "mongo/base/status_with.h"
#include "mongo/base/string_data.h"

namespace mongo {
Expand All @@ -47,6 +48,11 @@ class UserName {
UserName() : _splitPoint(0) {}
UserName(StringData user, StringData dbname);

/**
* Parses a string of the form "db.username" into a UserName object.
*/
static StatusWith<UserName> parse(StringData userNameStr);

/**
* Gets the user part of a UserName.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/mongo/db/repl/replication_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <vector>

#include "mongo/client/connpool.h"
#include "mongo/db/auth/sasl_mechanism_advertiser.h"
#include "mongo/db/client.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/db_raii.h"
Expand Down Expand Up @@ -389,6 +390,8 @@ class CmdIsMaster : public BasicCommand {
.serverNegotiate(cmdObj, &result);
}

SASLMechanismAdvertiser::advertise(opCtx, cmdObj, &result);

return true;
}
} cmdismaster;
Expand Down
3 changes: 3 additions & 0 deletions src/mongo/s/commands/cluster_is_master_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "mongo/platform/basic.h"

#include "mongo/db/auth/sasl_mechanism_advertiser.h"
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
#include "mongo/db/logical_session_id.h"
Expand Down Expand Up @@ -132,6 +133,8 @@ class CmdIsMaster : public BasicCommand {
MessageCompressorManager::forSession(opCtx->getClient()->session())
.serverNegotiate(cmdObj, &result);

SASLMechanismAdvertiser::advertise(opCtx, cmdObj, &result);

return true;
}

Expand Down

0 comments on commit b40c004

Please sign in to comment.