summaryrefslogtreecommitdiff
path: root/src/mongo/db/auth
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/auth')
-rw-r--r--src/mongo/db/auth/action_set.cpp175
-rw-r--r--src/mongo/db/auth/action_set.h96
-rw-r--r--src/mongo/db/auth/action_set_test.cpp238
-rw-r--r--src/mongo/db/auth/auth_decorations.cpp170
-rw-r--r--src/mongo/db/auth/auth_index_d.cpp124
-rw-r--r--src/mongo/db/auth/auth_index_d.h26
-rw-r--r--src/mongo/db/auth/authentication_session.h65
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp1123
-rw-r--r--src/mongo/db/auth/authorization_manager.h720
-rw-r--r--src/mongo/db/auth/authorization_manager_global.cpp75
-rw-r--r--src/mongo/db/auth/authorization_manager_global.h14
-rw-r--r--src/mongo/db/auth/authorization_manager_mock_init.cpp22
-rw-r--r--src/mongo/db/auth/authorization_manager_test.cpp337
-rw-r--r--src/mongo/db/auth/authorization_session.cpp899
-rw-r--r--src/mongo/db/auth/authorization_session.h441
-rw-r--r--src/mongo/db/auth/authorization_session_test.cpp849
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.cpp6
-rw-r--r--src/mongo/db/auth/authz_manager_external_state.h194
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.cpp72
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_d.h42
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.cpp695
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_local.h194
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.cpp457
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_mock.h151
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.cpp260
-rw-r--r--src/mongo/db/auth/authz_manager_external_state_s.h49
-rw-r--r--src/mongo/db/auth/authz_session_external_state.cpp12
-rw-r--r--src/mongo/db/auth/authz_session_external_state.h97
-rw-r--r--src/mongo/db/auth/authz_session_external_state_d.cpp39
-rw-r--r--src/mongo/db/auth/authz_session_external_state_d.h26
-rw-r--r--src/mongo/db/auth/authz_session_external_state_mock.h64
-rw-r--r--src/mongo/db/auth/authz_session_external_state_s.cpp15
-rw-r--r--src/mongo/db/auth/authz_session_external_state_s.h22
-rw-r--r--src/mongo/db/auth/authz_session_external_state_server_common.cpp75
-rw-r--r--src/mongo/db/auth/authz_session_external_state_server_common.h42
-rw-r--r--src/mongo/db/auth/impersonation_session.cpp43
-rw-r--r--src/mongo/db/auth/impersonation_session.h30
-rw-r--r--src/mongo/db/auth/internal_user_auth.cpp116
-rw-r--r--src/mongo/db/auth/internal_user_auth.h64
-rw-r--r--src/mongo/db/auth/mongo_authentication_session.cpp8
-rw-r--r--src/mongo/db/auth/mongo_authentication_session.h41
-rw-r--r--src/mongo/db/auth/native_sasl_authentication_session.cpp186
-rw-r--r--src/mongo/db/auth/native_sasl_authentication_session.h42
-rw-r--r--src/mongo/db/auth/privilege.cpp69
-rw-r--r--src/mongo/db/auth/privilege.h67
-rw-r--r--src/mongo/db/auth/privilege_parser.cpp705
-rw-r--r--src/mongo/db/auth/privilege_parser.h304
-rw-r--r--src/mongo/db/auth/privilege_parser_test.cpp341
-rw-r--r--src/mongo/db/auth/resource_pattern.cpp12
-rw-r--r--src/mongo/db/auth/resource_pattern.h331
-rw-r--r--src/mongo/db/auth/role_graph.cpp930
-rw-r--r--src/mongo/db/auth/role_graph.h528
-rw-r--r--src/mongo/db/auth/role_graph_builtin_roles.cpp1390
-rw-r--r--src/mongo/db/auth/role_graph_test.cpp1343
-rw-r--r--src/mongo/db/auth/role_graph_update.cpp458
-rw-r--r--src/mongo/db/auth/role_name.cpp27
-rw-r--r--src/mongo/db/auth/role_name.h248
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.cpp76
-rw-r--r--src/mongo/db/auth/sasl_authentication_session.h252
-rw-r--r--src/mongo/db/auth/sasl_commands.cpp559
-rw-r--r--src/mongo/db/auth/sasl_options.cpp294
-rw-r--r--src/mongo/db/auth/sasl_options.h33
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.cpp113
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.h31
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp505
-rw-r--r--src/mongo/db/auth/sasl_scramsha1_server_conversation.h67
-rw-r--r--src/mongo/db/auth/sasl_server_conversation.cpp12
-rw-r--r--src/mongo/db/auth/sasl_server_conversation.h87
-rw-r--r--src/mongo/db/auth/sasl_test_crutch.cpp2
-rw-r--r--src/mongo/db/auth/security_key.cpp156
-rw-r--r--src/mongo/db/auth/security_key.h18
-rw-r--r--src/mongo/db/auth/user.cpp228
-rw-r--r--src/mongo/db/auth/user.h369
-rw-r--r--src/mongo/db/auth/user_cache_invalidator_job.cpp215
-rw-r--r--src/mongo/db/auth/user_cache_invalidator_job.h44
-rw-r--r--src/mongo/db/auth/user_document_parser.cpp787
-rw-r--r--src/mongo/db/auth/user_document_parser.h58
-rw-r--r--src/mongo/db/auth/user_document_parser_test.cpp776
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.cpp1032
-rw-r--r--src/mongo/db/auth/user_management_commands_parser.h369
-rw-r--r--src/mongo/db/auth/user_name.cpp27
-rw-r--r--src/mongo/db/auth/user_name.h219
-rw-r--r--src/mongo/db/auth/user_name_hash.h11
-rw-r--r--src/mongo/db/auth/user_set.cpp155
-rw-r--r--src/mongo/db/auth/user_set.h161
-rw-r--r--src/mongo/db/auth/user_set_test.cpp144
86 files changed, 10905 insertions, 11064 deletions
diff --git a/src/mongo/db/auth/action_set.cpp b/src/mongo/db/auth/action_set.cpp
index 85bcc079814..6f89222679b 100644
--- a/src/mongo/db/auth/action_set.cpp
+++ b/src/mongo/db/auth/action_set.cpp
@@ -41,110 +41,109 @@
namespace mongo {
- void ActionSet::addAction(const ActionType& action) {
- if (action == ActionType::anyAction) {
- addAllActions();
- return;
- }
- _actions.set(action.getIdentifier(), true);
+void ActionSet::addAction(const ActionType& action) {
+ if (action == ActionType::anyAction) {
+ addAllActions();
+ return;
}
+ _actions.set(action.getIdentifier(), true);
+}
- void ActionSet::addAllActionsFromSet(const ActionSet& actions) {
- if (actions.contains(ActionType::anyAction)) {
- addAllActions();
- return;
- }
- _actions |= actions._actions;
+void ActionSet::addAllActionsFromSet(const ActionSet& actions) {
+ if (actions.contains(ActionType::anyAction)) {
+ addAllActions();
+ return;
}
+ _actions |= actions._actions;
+}
- void ActionSet::addAllActions() {
- _actions = ~std::bitset<ActionType::NUM_ACTION_TYPES>();
- }
+void ActionSet::addAllActions() {
+ _actions = ~std::bitset<ActionType::NUM_ACTION_TYPES>();
+}
+
+void ActionSet::removeAction(const ActionType& action) {
+ _actions.set(action.getIdentifier(), false);
+ _actions.set(ActionType::anyAction.getIdentifier(), false);
+}
- void ActionSet::removeAction(const ActionType& action) {
- _actions.set(action.getIdentifier(), false);
+void ActionSet::removeAllActionsFromSet(const ActionSet& other) {
+ _actions &= ~other._actions;
+ if (!other.empty()) {
_actions.set(ActionType::anyAction.getIdentifier(), false);
}
-
- void ActionSet::removeAllActionsFromSet(const ActionSet& other) {
- _actions &= ~other._actions;
- if (!other.empty()) {
- _actions.set(ActionType::anyAction.getIdentifier(), false);
+}
+
+void ActionSet::removeAllActions() {
+ _actions = std::bitset<ActionType::NUM_ACTION_TYPES>();
+}
+
+bool ActionSet::contains(const ActionType& action) const {
+ return _actions[action.getIdentifier()];
+}
+
+bool ActionSet::isSupersetOf(const ActionSet& other) const {
+ return (_actions & other._actions) == other._actions;
+}
+
+Status ActionSet::parseActionSetFromString(const std::string& actionsString, ActionSet* result) {
+ std::vector<std::string> actionsList;
+ splitStringDelim(actionsString, &actionsList, ',');
+ return parseActionSetFromStringVector(actionsList, result);
+}
+
+Status ActionSet::parseActionSetFromStringVector(const std::vector<std::string>& actionsVector,
+ ActionSet* result) {
+ ActionSet actions;
+ for (size_t i = 0; i < actionsVector.size(); i++) {
+ ActionType action;
+ Status status = ActionType::parseActionFromString(actionsVector[i], &action);
+ if (status != Status::OK()) {
+ ActionSet empty;
+ *result = empty;
+ return status;
}
+ if (action == ActionType::anyAction) {
+ actions.addAllActions();
+ break;
+ }
+ actions.addAction(action);
}
+ *result = actions;
+ return Status::OK();
+}
- void ActionSet::removeAllActions() {
- _actions = std::bitset<ActionType::NUM_ACTION_TYPES>();
- }
-
- bool ActionSet::contains(const ActionType& action) const {
- return _actions[action.getIdentifier()];
- }
-
- bool ActionSet::isSupersetOf(const ActionSet& other) const {
- return (_actions & other._actions) == other._actions;
- }
-
- Status ActionSet::parseActionSetFromString(const std::string& actionsString,
- ActionSet* result) {
- std::vector<std::string> actionsList;
- splitStringDelim(actionsString, &actionsList, ',');
- return parseActionSetFromStringVector(actionsList, result);
+std::string ActionSet::toString() const {
+ if (contains(ActionType::anyAction)) {
+ return ActionType::anyAction.toString();
}
-
- Status ActionSet::parseActionSetFromStringVector(const std::vector<std::string>& actionsVector,
- ActionSet* result) {
- ActionSet actions;
- for (size_t i = 0; i < actionsVector.size(); i++) {
- ActionType action;
- Status status = ActionType::parseActionFromString(actionsVector[i], &action);
- if (status != Status::OK()) {
- ActionSet empty;
- *result = empty;
- return status;
+ StringBuilder str;
+ bool addedOne = false;
+ for (int i = 0; i < ActionType::actionTypeEndValue; i++) {
+ ActionType action(i);
+ if (contains(action)) {
+ if (addedOne) {
+ str << ",";
}
- if (action == ActionType::anyAction) {
- actions.addAllActions();
- break;
- }
- actions.addAction(action);
+ str << ActionType::actionToString(action);
+ addedOne = true;
}
- *result = actions;
- return Status::OK();
}
+ return str.str();
+}
- std::string ActionSet::toString() const {
- if (contains(ActionType::anyAction)) {
- return ActionType::anyAction.toString();
- }
- StringBuilder str;
- bool addedOne = false;
- for (int i = 0; i < ActionType::actionTypeEndValue; i++) {
- ActionType action(i);
- if (contains(action)) {
- if (addedOne) {
- str << ",";
- }
- str << ActionType::actionToString(action);
- addedOne = true;
- }
- }
- return str.str();
+std::vector<std::string> ActionSet::getActionsAsStrings() const {
+ std::vector<std::string> result;
+ if (contains(ActionType::anyAction)) {
+ result.push_back(ActionType::anyAction.toString());
+ return result;
}
-
- std::vector<std::string> ActionSet::getActionsAsStrings() const {
- std::vector<std::string> result;
- if (contains(ActionType::anyAction)) {
- result.push_back(ActionType::anyAction.toString());
- return result;
+ for (int i = 0; i < ActionType::actionTypeEndValue; i++) {
+ ActionType action(i);
+ if (contains(action)) {
+ result.push_back(ActionType::actionToString(action));
}
- for (int i = 0; i < ActionType::actionTypeEndValue; i++) {
- ActionType action(i);
- if (contains(action)) {
- result.push_back(ActionType::actionToString(action));
- }
- }
- return result;
}
+ return result;
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/action_set.h b/src/mongo/db/auth/action_set.h
index 4a5c6ef45d0..339a68f019c 100644
--- a/src/mongo/db/auth/action_set.h
+++ b/src/mongo/db/auth/action_set.h
@@ -35,60 +35,62 @@
namespace mongo {
- /*
- * An ActionSet is a bitmask of ActionTypes that represents a set of actions.
- * These are the actions that a Privilege can grant a user to perform on a resource.
- * If the special ActionType::anyAction is granted to this set, it automatically sets all bits
- * in the bitmask, indicating that it contains all possible actions.
- */
- class ActionSet {
- public:
-
- ActionSet() : _actions(0) {}
-
- void addAction(const ActionType& action);
- void addAllActionsFromSet(const ActionSet& actionSet);
- void addAllActions();
-
- // Removes action from the set. Also removes the "anyAction" action, if present.
- // Note: removing the "anyAction" action does *not* remove all other actions.
- void removeAction(const ActionType& action);
- void removeAllActionsFromSet(const ActionSet& actionSet);
- void removeAllActions();
-
- bool empty() const { return _actions.none(); }
-
- bool equals(const ActionSet& other) const { return this->_actions == other._actions; }
+/*
+ * An ActionSet is a bitmask of ActionTypes that represents a set of actions.
+ * These are the actions that a Privilege can grant a user to perform on a resource.
+ * If the special ActionType::anyAction is granted to this set, it automatically sets all bits
+ * in the bitmask, indicating that it contains all possible actions.
+ */
+class ActionSet {
+public:
+ ActionSet() : _actions(0) {}
+
+ void addAction(const ActionType& action);
+ void addAllActionsFromSet(const ActionSet& actionSet);
+ void addAllActions();
+
+ // Removes action from the set. Also removes the "anyAction" action, if present.
+ // Note: removing the "anyAction" action does *not* remove all other actions.
+ void removeAction(const ActionType& action);
+ void removeAllActionsFromSet(const ActionSet& actionSet);
+ void removeAllActions();
+
+ bool empty() const {
+ return _actions.none();
+ }
- bool contains(const ActionType& action) const;
+ bool equals(const ActionSet& other) const {
+ return this->_actions == other._actions;
+ }
- // Returns true only if this ActionSet contains all the actions present in the 'other'
- // ActionSet.
- bool isSupersetOf(const ActionSet& other) const;
+ bool contains(const ActionType& action) const;
- // Returns the std::string representation of this ActionSet
- std::string toString() const;
+ // Returns true only if this ActionSet contains all the actions present in the 'other'
+ // ActionSet.
+ bool isSupersetOf(const ActionSet& other) const;
- // Returns a vector of strings representing the actions in the ActionSet.
- std::vector<std::string> getActionsAsStrings() const;
+ // Returns the std::string representation of this ActionSet
+ std::string toString() const;
- // Takes a comma-separated std::string of action type std::string representations and returns
- // an int bitmask of the actions.
- static Status parseActionSetFromString(const std::string& actionsString, ActionSet* result);
+ // Returns a vector of strings representing the actions in the ActionSet.
+ std::vector<std::string> getActionsAsStrings() const;
- // Takes a vector of action type std::string representations and returns an ActionSet of the
- // actions.
- static Status parseActionSetFromStringVector(const std::vector<std::string>& actionsVector,
- ActionSet* result);
+ // Takes a comma-separated std::string of action type std::string representations and returns
+ // an int bitmask of the actions.
+ static Status parseActionSetFromString(const std::string& actionsString, ActionSet* result);
- private:
+ // Takes a vector of action type std::string representations and returns an ActionSet of the
+ // actions.
+ static Status parseActionSetFromStringVector(const std::vector<std::string>& actionsVector,
+ ActionSet* result);
- // bitmask of actions this privilege grants
- std::bitset<ActionType::NUM_ACTION_TYPES> _actions;
- };
+private:
+ // bitmask of actions this privilege grants
+ std::bitset<ActionType::NUM_ACTION_TYPES> _actions;
+};
- static inline bool operator==(const ActionSet& lhs, const ActionSet& rhs) {
- return lhs.equals(rhs);
- }
+static inline bool operator==(const ActionSet& lhs, const ActionSet& rhs) {
+ return lhs.equals(rhs);
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/action_set_test.cpp b/src/mongo/db/auth/action_set_test.cpp
index efe9f6f2761..9689a656549 100644
--- a/src/mongo/db/auth/action_set_test.cpp
+++ b/src/mongo/db/auth/action_set_test.cpp
@@ -36,125 +36,125 @@
namespace mongo {
namespace {
- TEST(ActionSetTest, ParseActionSetFromString) {
- ActionSet result;
- ASSERT_OK(ActionSet::parseActionSetFromString("find,insert,update,remove", &result));
- ASSERT_TRUE(result.contains(ActionType::find));
- ASSERT_TRUE(result.contains(ActionType::insert));
- ASSERT_TRUE(result.contains(ActionType::update));
- ASSERT_TRUE(result.contains(ActionType::remove));
-
- // Order of the strings doesn't matter
- ASSERT_OK(ActionSet::parseActionSetFromString("update,find,remove,insert", &result));
- ASSERT_TRUE(result.contains(ActionType::find));
- ASSERT_TRUE(result.contains(ActionType::insert));
- ASSERT_TRUE(result.contains(ActionType::update));
- ASSERT_TRUE(result.contains(ActionType::remove));
-
- ASSERT_OK(ActionSet::parseActionSetFromString("find", &result));
-
- ASSERT_TRUE(result.contains(ActionType::find));
- ASSERT_FALSE(result.contains(ActionType::insert));
- ASSERT_FALSE(result.contains(ActionType::update));
- ASSERT_FALSE(result.contains(ActionType::remove));
-
- ASSERT_OK(ActionSet::parseActionSetFromString("", &result));
-
- ASSERT_FALSE(result.contains(ActionType::find));
- ASSERT_FALSE(result.contains(ActionType::insert));
- ASSERT_FALSE(result.contains(ActionType::update));
- ASSERT_FALSE(result.contains(ActionType::remove));
-
- ASSERT_EQUALS(ErrorCodes::FailedToParse,
- ActionSet::parseActionSetFromString("INVALID INPUT", &result).code());
- }
-
- TEST(ActionSetTest, ToString) {
- ActionSet actionSet;
-
- ASSERT_EQUALS("", actionSet.toString());
- actionSet.addAction(ActionType::find);
- ASSERT_EQUALS("find", actionSet.toString());
- actionSet.addAction(ActionType::insert);
- ASSERT_EQUALS("find,insert", actionSet.toString());
- actionSet.addAction(ActionType::update);
- ASSERT_EQUALS("find,insert,update", actionSet.toString());
- actionSet.addAction(ActionType::remove);
- ASSERT_EQUALS("find,insert,remove,update", actionSet.toString());
-
- // Now make sure adding actions in a different order doesn't change anything.
- ActionSet actionSet2;
- ASSERT_EQUALS("", actionSet2.toString());
- actionSet2.addAction(ActionType::insert);
- ASSERT_EQUALS("insert", actionSet2.toString());
- actionSet2.addAction(ActionType::remove);
- ASSERT_EQUALS("insert,remove", actionSet2.toString());
- actionSet2.addAction(ActionType::find);
- ASSERT_EQUALS("find,insert,remove", actionSet2.toString());
- actionSet2.addAction(ActionType::update);
- ASSERT_EQUALS("find,insert,remove,update", actionSet2.toString());
- }
-
- TEST(ActionSetTest, IsSupersetOf) {
- ActionSet set1, set2, set3;
- ASSERT_OK(ActionSet::parseActionSetFromString("find,update,insert", &set1));
- ASSERT_OK(ActionSet::parseActionSetFromString("find,update,remove", &set2));
- ASSERT_OK(ActionSet::parseActionSetFromString("find,update", &set3));
-
- ASSERT_FALSE(set1.isSupersetOf(set2));
- ASSERT_TRUE(set1.isSupersetOf(set3));
-
- ASSERT_FALSE(set2.isSupersetOf(set1));
- ASSERT_TRUE(set2.isSupersetOf(set3));
-
- ASSERT_FALSE(set3.isSupersetOf(set1));
- ASSERT_FALSE(set3.isSupersetOf(set2));
- }
-
- TEST(ActionSetTest, anyAction) {
- ActionSet set;
-
- ASSERT_OK(ActionSet::parseActionSetFromString("anyAction", &set));
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_TRUE(set.contains(ActionType::anyAction));
-
- set.removeAllActions();
- set.addAllActions();
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_TRUE(set.contains(ActionType::anyAction));
-
- set.removeAllActions();
- set.addAction(ActionType::anyAction);
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_TRUE(set.contains(ActionType::anyAction));
-
- set.removeAction(ActionType::find);
- ASSERT_FALSE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_FALSE(set.contains(ActionType::anyAction));
-
- set.addAction(ActionType::find);
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_FALSE(set.contains(ActionType::anyAction));
-
- set.addAction(ActionType::anyAction);
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_TRUE(set.contains(ActionType::anyAction));
-
- ASSERT_EQUALS("anyAction", set.toString());
-
- set.removeAction(ActionType::anyAction);
- ASSERT_TRUE(set.contains(ActionType::find));
- ASSERT_TRUE(set.contains(ActionType::insert));
- ASSERT_FALSE(set.contains(ActionType::anyAction));
-
- ASSERT_NOT_EQUALS("anyAction", set.toString());
- }
+TEST(ActionSetTest, ParseActionSetFromString) {
+ ActionSet result;
+ ASSERT_OK(ActionSet::parseActionSetFromString("find,insert,update,remove", &result));
+ ASSERT_TRUE(result.contains(ActionType::find));
+ ASSERT_TRUE(result.contains(ActionType::insert));
+ ASSERT_TRUE(result.contains(ActionType::update));
+ ASSERT_TRUE(result.contains(ActionType::remove));
+
+ // Order of the strings doesn't matter
+ ASSERT_OK(ActionSet::parseActionSetFromString("update,find,remove,insert", &result));
+ ASSERT_TRUE(result.contains(ActionType::find));
+ ASSERT_TRUE(result.contains(ActionType::insert));
+ ASSERT_TRUE(result.contains(ActionType::update));
+ ASSERT_TRUE(result.contains(ActionType::remove));
+
+ ASSERT_OK(ActionSet::parseActionSetFromString("find", &result));
+
+ ASSERT_TRUE(result.contains(ActionType::find));
+ ASSERT_FALSE(result.contains(ActionType::insert));
+ ASSERT_FALSE(result.contains(ActionType::update));
+ ASSERT_FALSE(result.contains(ActionType::remove));
+
+ ASSERT_OK(ActionSet::parseActionSetFromString("", &result));
+
+ ASSERT_FALSE(result.contains(ActionType::find));
+ ASSERT_FALSE(result.contains(ActionType::insert));
+ ASSERT_FALSE(result.contains(ActionType::update));
+ ASSERT_FALSE(result.contains(ActionType::remove));
+
+ ASSERT_EQUALS(ErrorCodes::FailedToParse,
+ ActionSet::parseActionSetFromString("INVALID INPUT", &result).code());
+}
+
+TEST(ActionSetTest, ToString) {
+ ActionSet actionSet;
+
+ ASSERT_EQUALS("", actionSet.toString());
+ actionSet.addAction(ActionType::find);
+ ASSERT_EQUALS("find", actionSet.toString());
+ actionSet.addAction(ActionType::insert);
+ ASSERT_EQUALS("find,insert", actionSet.toString());
+ actionSet.addAction(ActionType::update);
+ ASSERT_EQUALS("find,insert,update", actionSet.toString());
+ actionSet.addAction(ActionType::remove);
+ ASSERT_EQUALS("find,insert,remove,update", actionSet.toString());
+
+ // Now make sure adding actions in a different order doesn't change anything.
+ ActionSet actionSet2;
+ ASSERT_EQUALS("", actionSet2.toString());
+ actionSet2.addAction(ActionType::insert);
+ ASSERT_EQUALS("insert", actionSet2.toString());
+ actionSet2.addAction(ActionType::remove);
+ ASSERT_EQUALS("insert,remove", actionSet2.toString());
+ actionSet2.addAction(ActionType::find);
+ ASSERT_EQUALS("find,insert,remove", actionSet2.toString());
+ actionSet2.addAction(ActionType::update);
+ ASSERT_EQUALS("find,insert,remove,update", actionSet2.toString());
+}
+
+TEST(ActionSetTest, IsSupersetOf) {
+ ActionSet set1, set2, set3;
+ ASSERT_OK(ActionSet::parseActionSetFromString("find,update,insert", &set1));
+ ASSERT_OK(ActionSet::parseActionSetFromString("find,update,remove", &set2));
+ ASSERT_OK(ActionSet::parseActionSetFromString("find,update", &set3));
+
+ ASSERT_FALSE(set1.isSupersetOf(set2));
+ ASSERT_TRUE(set1.isSupersetOf(set3));
+
+ ASSERT_FALSE(set2.isSupersetOf(set1));
+ ASSERT_TRUE(set2.isSupersetOf(set3));
+
+ ASSERT_FALSE(set3.isSupersetOf(set1));
+ ASSERT_FALSE(set3.isSupersetOf(set2));
+}
+
+TEST(ActionSetTest, anyAction) {
+ ActionSet set;
+
+ ASSERT_OK(ActionSet::parseActionSetFromString("anyAction", &set));
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_TRUE(set.contains(ActionType::anyAction));
+
+ set.removeAllActions();
+ set.addAllActions();
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_TRUE(set.contains(ActionType::anyAction));
+
+ set.removeAllActions();
+ set.addAction(ActionType::anyAction);
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_TRUE(set.contains(ActionType::anyAction));
+
+ set.removeAction(ActionType::find);
+ ASSERT_FALSE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_FALSE(set.contains(ActionType::anyAction));
+
+ set.addAction(ActionType::find);
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_FALSE(set.contains(ActionType::anyAction));
+
+ set.addAction(ActionType::anyAction);
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_TRUE(set.contains(ActionType::anyAction));
+
+ ASSERT_EQUALS("anyAction", set.toString());
+
+ set.removeAction(ActionType::anyAction);
+ ASSERT_TRUE(set.contains(ActionType::find));
+ ASSERT_TRUE(set.contains(ActionType::insert));
+ ASSERT_FALSE(set.contains(ActionType::anyAction));
+
+ ASSERT_NOT_EQUALS("anyAction", set.toString());
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/auth_decorations.cpp b/src/mongo/db/auth/auth_decorations.cpp
index cbd4ca3083a..bc93e494ff2 100644
--- a/src/mongo/db/auth/auth_decorations.cpp
+++ b/src/mongo/db/auth/auth_decorations.cpp
@@ -45,99 +45,93 @@
namespace mongo {
namespace {
- MONGO_INITIALIZER_WITH_PREREQUISITES(CreateAuthorizationManager,
- ("SetupInternalSecurityUser",
- "OIDGeneration",
- "SetGlobalEnvironment",
- "CreateAuthorizationExternalStateFactory",
- "EndStartupOptionStorage"))
- (InitializerContext* context) {
- auto authzManager = stdx::make_unique<AuthorizationManager>(
- AuthzManagerExternalState::create());
- authzManager->setAuthEnabled(serverGlobalParams.isAuthEnabled);
- AuthorizationManager::set(getGlobalServiceContext(), std::move(authzManager));
- return Status::OK();
+MONGO_INITIALIZER_WITH_PREREQUISITES(CreateAuthorizationManager,
+ ("SetupInternalSecurityUser",
+ "OIDGeneration",
+ "SetGlobalEnvironment",
+ "CreateAuthorizationExternalStateFactory",
+ "EndStartupOptionStorage"))
+(InitializerContext* context) {
+ auto authzManager =
+ stdx::make_unique<AuthorizationManager>(AuthzManagerExternalState::create());
+ authzManager->setAuthEnabled(serverGlobalParams.isAuthEnabled);
+ AuthorizationManager::set(getGlobalServiceContext(), std::move(authzManager));
+ return Status::OK();
+}
+
+const auto getAuthenticationSession =
+ ClientBasic::declareDecoration<std::unique_ptr<AuthenticationSession>>();
+
+const auto getAuthorizationManager =
+ ServiceContext::declareDecoration<std::unique_ptr<AuthorizationManager>>();
+
+const auto getAuthorizationSession =
+ ClientBasic::declareDecoration<std::unique_ptr<AuthorizationSession>>();
+
+class AuthzClientObserver final : public ServiceContext::ClientObserver {
+public:
+ void onCreateClient(Client* client) override {
+ auto service = client->getServiceContext();
+ AuthorizationSession::set(client,
+ AuthorizationManager::get(service)->makeAuthorizationSession());
}
- const auto getAuthenticationSession =
- ClientBasic::declareDecoration<std::unique_ptr<AuthenticationSession>>();
+ void onDestroyClient(Client* client) override {}
- const auto getAuthorizationManager =
- ServiceContext::declareDecoration<std::unique_ptr<AuthorizationManager>>();
-
- const auto getAuthorizationSession =
- ClientBasic::declareDecoration<std::unique_ptr<AuthorizationSession>>();
-
- class AuthzClientObserver final : public ServiceContext::ClientObserver {
- public:
- void onCreateClient(Client* client) override {
- auto service = client->getServiceContext();
- AuthorizationSession::set(
- client,
- AuthorizationManager::get(service)->makeAuthorizationSession());
- }
-
- void onDestroyClient(Client* client) override {}
-
- void onCreateOperationContext(OperationContext* opCtx) override {}
- void onDestroyOperationContext(OperationContext* opCtx) override {}
- };
+ void onCreateOperationContext(OperationContext* opCtx) override {}
+ void onDestroyOperationContext(OperationContext* opCtx) override {}
+};
} // namespace
- void AuthenticationSession::set(
- ClientBasic* client,
- std::unique_ptr<AuthenticationSession> newSession) {
- getAuthenticationSession(client) = std::move(newSession);
- }
-
- void AuthenticationSession::swap(
- ClientBasic* client,
- std::unique_ptr<AuthenticationSession>& other) {
- using std::swap;
- swap(getAuthenticationSession(client), other);
- }
-
- AuthorizationManager* AuthorizationManager::get(ServiceContext* service) {
- return getAuthorizationManager(service).get();
- }
-
- AuthorizationManager* AuthorizationManager::get(ServiceContext& service) {
- return getAuthorizationManager(service).get();
- }
-
- void AuthorizationManager::set(ServiceContext* service,
- std::unique_ptr<AuthorizationManager> authzManager) {
- auto& manager = getAuthorizationManager(service);
- invariant(authzManager);
- invariant(!manager);
- manager = std::move(authzManager);
- service->registerClientObserver(stdx::make_unique<AuthzClientObserver>());
- }
-
- AuthorizationSession* AuthorizationSession::get(ClientBasic* client) {
- return get(*client);
- }
-
- AuthorizationSession* AuthorizationSession::get(ClientBasic& client) {
- AuthorizationSession* retval = getAuthorizationSession(client).get();
- massert(16481,
- "No AuthorizationManager has been set up for this connection",
- retval);
- return retval;
- }
-
- bool AuthorizationSession::exists(ClientBasic* client) {
- return getAuthorizationSession(client).get();
- }
-
- void AuthorizationSession::set(
- ClientBasic* client,
- std::unique_ptr<AuthorizationSession> authorizationSession) {
- auto& authzSession = getAuthorizationSession(client);
- invariant(authorizationSession);
- invariant(!authzSession);
- authzSession = std::move(authorizationSession);
- }
+void AuthenticationSession::set(ClientBasic* client,
+ std::unique_ptr<AuthenticationSession> newSession) {
+ getAuthenticationSession(client) = std::move(newSession);
+}
+
+void AuthenticationSession::swap(ClientBasic* client,
+ std::unique_ptr<AuthenticationSession>& other) {
+ using std::swap;
+ swap(getAuthenticationSession(client), other);
+}
+
+AuthorizationManager* AuthorizationManager::get(ServiceContext* service) {
+ return getAuthorizationManager(service).get();
+}
+
+AuthorizationManager* AuthorizationManager::get(ServiceContext& service) {
+ return getAuthorizationManager(service).get();
+}
+
+void AuthorizationManager::set(ServiceContext* service,
+ std::unique_ptr<AuthorizationManager> authzManager) {
+ auto& manager = getAuthorizationManager(service);
+ invariant(authzManager);
+ invariant(!manager);
+ manager = std::move(authzManager);
+ service->registerClientObserver(stdx::make_unique<AuthzClientObserver>());
+}
+
+AuthorizationSession* AuthorizationSession::get(ClientBasic* client) {
+ return get(*client);
+}
+
+AuthorizationSession* AuthorizationSession::get(ClientBasic& client) {
+ AuthorizationSession* retval = getAuthorizationSession(client).get();
+ massert(16481, "No AuthorizationManager has been set up for this connection", retval);
+ return retval;
+}
+
+bool AuthorizationSession::exists(ClientBasic* client) {
+ return getAuthorizationSession(client).get();
+}
+
+void AuthorizationSession::set(ClientBasic* client,
+ std::unique_ptr<AuthorizationSession> authorizationSession) {
+ auto& authzSession = getAuthorizationSession(client);
+ invariant(authorizationSession);
+ invariant(!authzSession);
+ authzSession = std::move(authorizationSession);
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/auth_index_d.cpp b/src/mongo/db/auth/auth_index_d.cpp
index 8cbd093c2c7..546df4c1da9 100644
--- a/src/mongo/db/auth/auth_index_d.cpp
+++ b/src/mongo/db/auth/auth_index_d.cpp
@@ -46,85 +46,79 @@
namespace mongo {
- using std::endl;
+using std::endl;
namespace authindex {
namespace {
- BSONObj v1SystemUsersKeyPattern;
- BSONObj v3SystemUsersKeyPattern;
- BSONObj v3SystemRolesKeyPattern;
- std::string v3SystemUsersIndexName;
- std::string v3SystemRolesIndexName;
-
- MONGO_INITIALIZER(AuthIndexKeyPatterns)(InitializerContext*) {
- v1SystemUsersKeyPattern = BSON("user" << 1 << "userSource" << 1);
- v3SystemUsersKeyPattern = BSON(AuthorizationManager::USER_NAME_FIELD_NAME << 1 <<
- AuthorizationManager::USER_DB_FIELD_NAME << 1);
- v3SystemRolesKeyPattern = BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME << 1 <<
- AuthorizationManager::ROLE_DB_FIELD_NAME << 1);
- v3SystemUsersIndexName = std::string(
- str::stream() <<
- AuthorizationManager::USER_NAME_FIELD_NAME << "_1_" <<
- AuthorizationManager::USER_DB_FIELD_NAME << "_1");
- v3SystemRolesIndexName = std::string(
- str::stream() <<
- AuthorizationManager::ROLE_NAME_FIELD_NAME << "_1_" <<
- AuthorizationManager::ROLE_DB_FIELD_NAME << "_1");
-
- return Status::OK();
- }
+BSONObj v1SystemUsersKeyPattern;
+BSONObj v3SystemUsersKeyPattern;
+BSONObj v3SystemRolesKeyPattern;
+std::string v3SystemUsersIndexName;
+std::string v3SystemRolesIndexName;
+
+MONGO_INITIALIZER(AuthIndexKeyPatterns)(InitializerContext*) {
+ v1SystemUsersKeyPattern = BSON("user" << 1 << "userSource" << 1);
+ v3SystemUsersKeyPattern = BSON(AuthorizationManager::USER_NAME_FIELD_NAME
+ << 1 << AuthorizationManager::USER_DB_FIELD_NAME << 1);
+ v3SystemRolesKeyPattern = BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME
+ << 1 << AuthorizationManager::ROLE_DB_FIELD_NAME << 1);
+ v3SystemUsersIndexName =
+ std::string(str::stream() << AuthorizationManager::USER_NAME_FIELD_NAME << "_1_"
+ << AuthorizationManager::USER_DB_FIELD_NAME << "_1");
+ v3SystemRolesIndexName =
+ std::string(str::stream() << AuthorizationManager::ROLE_NAME_FIELD_NAME << "_1_"
+ << AuthorizationManager::ROLE_DB_FIELD_NAME << "_1");
+
+ return Status::OK();
+}
} // namespace
- Status verifySystemIndexes(OperationContext* txn) {
- const NamespaceString systemUsers = AuthorizationManager::usersCollectionNamespace;
+Status verifySystemIndexes(OperationContext* txn) {
+ const NamespaceString systemUsers = AuthorizationManager::usersCollectionNamespace;
- // Make sure the old unique index from v2.4 on system.users doesn't exist.
- ScopedTransaction scopedXact(txn, MODE_IX);
- AutoGetDb autoDb(txn, systemUsers.db(), MODE_X);
- if (!autoDb.getDb()) {
- return Status::OK();
- }
-
- Collection* collection = autoDb.getDb()->getCollection(NamespaceString(systemUsers));
- if (!collection) {
- return Status::OK();
- }
+ // Make sure the old unique index from v2.4 on system.users doesn't exist.
+ ScopedTransaction scopedXact(txn, MODE_IX);
+ AutoGetDb autoDb(txn, systemUsers.db(), MODE_X);
+ if (!autoDb.getDb()) {
+ return Status::OK();
+ }
- IndexCatalog* indexCatalog = collection->getIndexCatalog();
- IndexDescriptor* oldIndex = NULL;
+ Collection* collection = autoDb.getDb()->getCollection(NamespaceString(systemUsers));
+ if (!collection) {
+ return Status::OK();
+ }
- if (indexCatalog &&
- (oldIndex = indexCatalog->findIndexByKeyPattern(txn, v1SystemUsersKeyPattern))) {
- return Status(ErrorCodes::AuthSchemaIncompatible,
- "Old 2.4 style user index identified. "
- "The authentication schema needs to be updated by "
- "running authSchemaUpgrade on a 2.6 server.");
- }
+ IndexCatalog* indexCatalog = collection->getIndexCatalog();
+ IndexDescriptor* oldIndex = NULL;
- return Status::OK();
+ if (indexCatalog &&
+ (oldIndex = indexCatalog->findIndexByKeyPattern(txn, v1SystemUsersKeyPattern))) {
+ return Status(ErrorCodes::AuthSchemaIncompatible,
+ "Old 2.4 style user index identified. "
+ "The authentication schema needs to be updated by "
+ "running authSchemaUpgrade on a 2.6 server.");
}
- void createSystemIndexes(OperationContext* txn, Collection* collection) {
- invariant( collection );
- const NamespaceString& ns = collection->ns();
- if (ns == AuthorizationManager::usersCollectionNamespace) {
- collection->getIndexCatalog()->createIndexOnEmptyCollection(
- txn,
- BSON("name" << v3SystemUsersIndexName
- << "ns" << collection->ns().ns()
- << "key" << v3SystemUsersKeyPattern
- << "unique" << true));
- } else if (ns == AuthorizationManager::rolesCollectionNamespace) {
- collection->getIndexCatalog()->createIndexOnEmptyCollection(
- txn,
- BSON("name" << v3SystemRolesIndexName
- << "ns" << collection->ns().ns()
- << "key" << v3SystemRolesKeyPattern
- << "unique" << true));
- }
+ return Status::OK();
+}
+
+void createSystemIndexes(OperationContext* txn, Collection* collection) {
+ invariant(collection);
+ const NamespaceString& ns = collection->ns();
+ if (ns == AuthorizationManager::usersCollectionNamespace) {
+ collection->getIndexCatalog()->createIndexOnEmptyCollection(
+ txn,
+ BSON("name" << v3SystemUsersIndexName << "ns" << collection->ns().ns() << "key"
+ << v3SystemUsersKeyPattern << "unique" << true));
+ } else if (ns == AuthorizationManager::rolesCollectionNamespace) {
+ collection->getIndexCatalog()->createIndexOnEmptyCollection(
+ txn,
+ BSON("name" << v3SystemRolesIndexName << "ns" << collection->ns().ns() << "key"
+ << v3SystemRolesKeyPattern << "unique" << true));
}
+}
} // namespace authindex
} // namespace mongo
diff --git a/src/mongo/db/auth/auth_index_d.h b/src/mongo/db/auth/auth_index_d.h
index b643e7c8a11..9b85e02e000 100644
--- a/src/mongo/db/auth/auth_index_d.h
+++ b/src/mongo/db/auth/auth_index_d.h
@@ -32,22 +32,22 @@
namespace mongo {
- class Collection;
- class OperationContext;
+class Collection;
+class OperationContext;
namespace authindex {
- /**
- * Creates the appropriate indexes on _new_ system collections supporting authentication and
- * authorization.
- */
- void createSystemIndexes(OperationContext* txn, Collection* collection);
-
- /**
- * Verifies that only the appropriate indexes to support authentication and authorization
- * are present in the admin database
- */
- Status verifySystemIndexes(OperationContext* txn);
+/**
+ * Creates the appropriate indexes on _new_ system collections supporting authentication and
+ * authorization.
+ */
+void createSystemIndexes(OperationContext* txn, Collection* collection);
+
+/**
+ * Verifies that only the appropriate indexes to support authentication and authorization
+ * are present in the admin database
+ */
+Status verifySystemIndexes(OperationContext* txn);
} // namespace authindex
} // namespace mongo
diff --git a/src/mongo/db/auth/authentication_session.h b/src/mongo/db/auth/authentication_session.h
index 8870abefc76..1c3b34b16ee 100644
--- a/src/mongo/db/auth/authentication_session.h
+++ b/src/mongo/db/auth/authentication_session.h
@@ -33,44 +33,47 @@
namespace mongo {
- class ClientBasic;
+class ClientBasic;
+
+/**
+ * Abstract type representing an ongoing authentication session.
+ *
+ * An example subclass is MongoAuthenticationSession.
+ */
+class AuthenticationSession {
+ MONGO_DISALLOW_COPYING(AuthenticationSession);
+
+public:
+ enum SessionType {
+ SESSION_TYPE_MONGO, // The mongo-specific challenge-response authentication mechanism.
+ SESSION_TYPE_SASL // SASL authentication mechanism.
+ };
/**
- * Abstract type representing an ongoing authentication session.
- *
- * An example subclass is MongoAuthenticationSession.
+ * Sets the authentication session for the given "client" to "newSession".
*/
- class AuthenticationSession {
- MONGO_DISALLOW_COPYING(AuthenticationSession);
- public:
- enum SessionType {
- SESSION_TYPE_MONGO, // The mongo-specific challenge-response authentication mechanism.
- SESSION_TYPE_SASL // SASL authentication mechanism.
- };
-
- /**
- * Sets the authentication session for the given "client" to "newSession".
- */
- static void set(ClientBasic* client, std::unique_ptr<AuthenticationSession> newSession);
+ static void set(ClientBasic* client, std::unique_ptr<AuthenticationSession> newSession);
- /**
- * Swaps "client"'s current authentication session with "other".
- */
- static void swap(ClientBasic* client, std::unique_ptr<AuthenticationSession>& other);
+ /**
+ * Swaps "client"'s current authentication session with "other".
+ */
+ static void swap(ClientBasic* client, std::unique_ptr<AuthenticationSession>& other);
- virtual ~AuthenticationSession() = default;
+ virtual ~AuthenticationSession() = default;
- /**
- * Return an identifer of the type of session, so that a caller can safely cast it and
- * extract the type-specific data stored within.
- */
- SessionType getType() const { return _sessionType; }
+ /**
+ * Return an identifer of the type of session, so that a caller can safely cast it and
+ * extract the type-specific data stored within.
+ */
+ SessionType getType() const {
+ return _sessionType;
+ }
- protected:
- explicit AuthenticationSession(SessionType sessionType) : _sessionType(sessionType) {}
+protected:
+ explicit AuthenticationSession(SessionType sessionType) : _sessionType(sessionType) {}
- private:
- const SessionType _sessionType;
- };
+private:
+ const SessionType _sessionType;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index 016d8da33c6..30123e78177 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -64,428 +64,418 @@
namespace mongo {
- using std::endl;
- using std::string;
- using std::vector;
-
- AuthInfo internalSecurity;
-
- MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser, MONGO_NO_PREREQUISITES)(
- InitializerContext* context) {
-
- User* user = new User(UserName("__system", "local"));
-
- user->incrementRefCount(); // Pin this user so the ref count never drops below 1.
- ActionSet allActions;
- allActions.addAllActions();
- PrivilegeVector privileges;
- RoleGraph::generateUniversalPrivileges(&privileges);
- user->addPrivileges(privileges);
- internalSecurity.user = user;
-
- return Status::OK();
- }
-
- const std::string AuthorizationManager::USER_NAME_FIELD_NAME = "user";
- const std::string AuthorizationManager::USER_DB_FIELD_NAME = "db";
- const std::string AuthorizationManager::ROLE_NAME_FIELD_NAME = "role";
- const std::string AuthorizationManager::ROLE_DB_FIELD_NAME = "db";
- const std::string AuthorizationManager::PASSWORD_FIELD_NAME = "pwd";
- const std::string AuthorizationManager::V1_USER_NAME_FIELD_NAME = "user";
- const std::string AuthorizationManager::V1_USER_SOURCE_FIELD_NAME = "userSource";
-
- const NamespaceString AuthorizationManager::adminCommandNamespace("admin.$cmd");
- const NamespaceString AuthorizationManager::rolesCollectionNamespace("admin.system.roles");
- const NamespaceString AuthorizationManager::usersAltCollectionNamespace(
- "admin.system.new_users");
- const NamespaceString AuthorizationManager::usersBackupCollectionNamespace(
- "admin.system.backup_users");
- const NamespaceString AuthorizationManager::usersCollectionNamespace("admin.system.users");
- const NamespaceString AuthorizationManager::versionCollectionNamespace("admin.system.version");
- const NamespaceString AuthorizationManager::defaultTempUsersCollectionNamespace(
- "admin.tempusers");
- const NamespaceString AuthorizationManager::defaultTempRolesCollectionNamespace(
- "admin.temproles");
-
- const BSONObj AuthorizationManager::versionDocumentQuery = BSON("_id" << "authSchema");
-
- const std::string AuthorizationManager::schemaVersionFieldName = "currentVersion";
+using std::endl;
+using std::string;
+using std::vector;
+
+AuthInfo internalSecurity;
+
+MONGO_INITIALIZER_WITH_PREREQUISITES(SetupInternalSecurityUser,
+ MONGO_NO_PREREQUISITES)(InitializerContext* context) {
+ User* user = new User(UserName("__system", "local"));
+
+ user->incrementRefCount(); // Pin this user so the ref count never drops below 1.
+ ActionSet allActions;
+ allActions.addAllActions();
+ PrivilegeVector privileges;
+ RoleGraph::generateUniversalPrivileges(&privileges);
+ user->addPrivileges(privileges);
+ internalSecurity.user = user;
+
+ return Status::OK();
+}
+
+const std::string AuthorizationManager::USER_NAME_FIELD_NAME = "user";
+const std::string AuthorizationManager::USER_DB_FIELD_NAME = "db";
+const std::string AuthorizationManager::ROLE_NAME_FIELD_NAME = "role";
+const std::string AuthorizationManager::ROLE_DB_FIELD_NAME = "db";
+const std::string AuthorizationManager::PASSWORD_FIELD_NAME = "pwd";
+const std::string AuthorizationManager::V1_USER_NAME_FIELD_NAME = "user";
+const std::string AuthorizationManager::V1_USER_SOURCE_FIELD_NAME = "userSource";
+
+const NamespaceString AuthorizationManager::adminCommandNamespace("admin.$cmd");
+const NamespaceString AuthorizationManager::rolesCollectionNamespace("admin.system.roles");
+const NamespaceString AuthorizationManager::usersAltCollectionNamespace("admin.system.new_users");
+const NamespaceString AuthorizationManager::usersBackupCollectionNamespace(
+ "admin.system.backup_users");
+const NamespaceString AuthorizationManager::usersCollectionNamespace("admin.system.users");
+const NamespaceString AuthorizationManager::versionCollectionNamespace("admin.system.version");
+const NamespaceString AuthorizationManager::defaultTempUsersCollectionNamespace("admin.tempusers");
+const NamespaceString AuthorizationManager::defaultTempRolesCollectionNamespace("admin.temproles");
+
+const BSONObj AuthorizationManager::versionDocumentQuery = BSON("_id"
+ << "authSchema");
+
+const std::string AuthorizationManager::schemaVersionFieldName = "currentVersion";
#ifndef _MSC_EXTENSIONS
- const int AuthorizationManager::schemaVersion24;
- const int AuthorizationManager::schemaVersion26Upgrade;
- const int AuthorizationManager::schemaVersion26Final;
- const int AuthorizationManager::schemaVersion28SCRAM;
+const int AuthorizationManager::schemaVersion24;
+const int AuthorizationManager::schemaVersion26Upgrade;
+const int AuthorizationManager::schemaVersion26Final;
+const int AuthorizationManager::schemaVersion28SCRAM;
#endif
+/**
+ * Guard object for synchronizing accesses to data cached in AuthorizationManager instances.
+ * This guard allows one thread to access the cache at a time, and provides an exception-safe
+ * mechanism for a thread to release the cache mutex while performing network or disk operations
+ * while allowing other readers to proceed.
+ *
+ * There are two ways to use this guard. One may simply instantiate the guard like a
+ * std::lock_guard, and perform reads or writes of the cache.
+ *
+ * Alternatively, one may instantiate the guard, examine the cache, and then enter into an
+ * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then
+ * calling beginFetchPhase(). At this point, other threads may acquire the guard in the simple
+ * manner and do reads, but other threads may not enter into a fetch phase. During the fetch
+ * phase, the thread should perform required network or disk activity to determine what update
+ * it will make to the cache. Then, it should call endFetchPhase(), to reacquire the user cache
+ * mutex. At that point, the thread can make its modifications to the cache and let the guard
+ * go out of scope.
+ *
+ * All updates by guards using a fetch-phase are totally ordered with respect to one another,
+ * and all guards using no fetch phase are totally ordered with respect to one another, but
+ * there is not a total ordering among all guard objects.
+ *
+ * The cached data has an associated counter, called the cache generation. If the cache
+ * generation changes while a guard is in fetch phase, the fetched data should not be stored
+ * into the cache, because some invalidation event occurred during the fetch phase.
+ *
+ * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase
+ * operations are allowed to acquire database locks themselves, so entering fetch while holding
+ * a database lock may lead to deadlock.
+ */
+class AuthorizationManager::CacheGuard {
+ MONGO_DISALLOW_COPYING(CacheGuard);
+
+public:
+ enum FetchSynchronization { fetchSynchronizationAutomatic, fetchSynchronizationManual };
+
/**
- * Guard object for synchronizing accesses to data cached in AuthorizationManager instances.
- * This guard allows one thread to access the cache at a time, and provides an exception-safe
- * mechanism for a thread to release the cache mutex while performing network or disk operations
- * while allowing other readers to proceed.
- *
- * There are two ways to use this guard. One may simply instantiate the guard like a
- * std::lock_guard, and perform reads or writes of the cache.
- *
- * Alternatively, one may instantiate the guard, examine the cache, and then enter into an
- * update mode by first wait()ing until otherUpdateInFetchPhase() is false, and then
- * calling beginFetchPhase(). At this point, other threads may acquire the guard in the simple
- * manner and do reads, but other threads may not enter into a fetch phase. During the fetch
- * phase, the thread should perform required network or disk activity to determine what update
- * it will make to the cache. Then, it should call endFetchPhase(), to reacquire the user cache
- * mutex. At that point, the thread can make its modifications to the cache and let the guard
- * go out of scope.
- *
- * All updates by guards using a fetch-phase are totally ordered with respect to one another,
- * and all guards using no fetch phase are totally ordered with respect to one another, but
- * there is not a total ordering among all guard objects.
- *
- * The cached data has an associated counter, called the cache generation. If the cache
- * generation changes while a guard is in fetch phase, the fetched data should not be stored
- * into the cache, because some invalidation event occurred during the fetch phase.
- *
- * NOTE: It is not safe to enter fetch phase while holding a database lock. Fetch phase
- * operations are allowed to acquire database locks themselves, so entering fetch while holding
- * a database lock may lead to deadlock.
+ * Constructs a cache guard, locking the mutex that synchronizes user cache accesses.
*/
- class AuthorizationManager::CacheGuard {
- MONGO_DISALLOW_COPYING(CacheGuard);
- public:
- enum FetchSynchronization {
- fetchSynchronizationAutomatic,
- fetchSynchronizationManual
- };
-
- /**
- * Constructs a cache guard, locking the mutex that synchronizes user cache accesses.
- */
- CacheGuard(AuthorizationManager* authzManager,
- const FetchSynchronization sync = fetchSynchronizationAutomatic) :
- _isThisGuardInFetchPhase(false),
- _authzManager(authzManager),
- _lock(authzManager->_cacheMutex) {
-
- if (fetchSynchronizationAutomatic == sync) {
- synchronizeWithFetchPhase();
- }
- }
-
- /**
- * Releases the mutex that synchronizes user cache access, if held, and notifies
- * any threads waiting for their own opportunity to update the user cache.
- */
- ~CacheGuard() {
- if (!_lock.owns_lock()) {
- _lock.lock();
- }
- if (_isThisGuardInFetchPhase) {
- fassert(17190, _authzManager->_isFetchPhaseBusy);
- _authzManager->_isFetchPhaseBusy = false;
- _authzManager->_fetchPhaseIsReady.notify_all();
- }
- }
-
- /**
- * Returns true of the authzManager reports that it is in fetch phase.
- */
- bool otherUpdateInFetchPhase() { return _authzManager->_isFetchPhaseBusy; }
-
- /**
- * Waits on the _authzManager->_fetchPhaseIsReady condition.
- */
- void wait() {
- fassert(17222, !_isThisGuardInFetchPhase);
- _authzManager->_fetchPhaseIsReady.wait(_lock);
- }
-
- /**
- * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current
- * cache generation.
- */
- void beginFetchPhase() {
- fassert(17191, !_authzManager->_isFetchPhaseBusy);
- _isThisGuardInFetchPhase = true;
- _authzManager->_isFetchPhaseBusy = true;
- _startGeneration = _authzManager->_cacheGeneration;
- _lock.unlock();
+ CacheGuard(AuthorizationManager* authzManager,
+ const FetchSynchronization sync = fetchSynchronizationAutomatic)
+ : _isThisGuardInFetchPhase(false),
+ _authzManager(authzManager),
+ _lock(authzManager->_cacheMutex) {
+ if (fetchSynchronizationAutomatic == sync) {
+ synchronizeWithFetchPhase();
}
+ }
- /**
- * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex.
- */
- void endFetchPhase() {
+ /**
+ * Releases the mutex that synchronizes user cache access, if held, and notifies
+ * any threads waiting for their own opportunity to update the user cache.
+ */
+ ~CacheGuard() {
+ if (!_lock.owns_lock()) {
_lock.lock();
- // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until
- // ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters
- // before you're ready to release the mutex, because they'll just go to sleep on the
- // mutex. Second, in order to meaningfully check the preconditions of
- // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now
- // has been exited." That state is _isThisGuardInFetchPhase == true and
- // _lock.owns_lock() == true.
}
-
- /**
- * Returns true if _authzManager->_cacheGeneration remained the same while this guard was
- * in fetch phase. Behavior is undefined if this guard never entered fetch phase.
- *
- * If this returns true, do not update the cached data with this
- */
- bool isSameCacheGeneration() const {
- fassert(17223, _isThisGuardInFetchPhase);
- fassert(17231, _lock.owns_lock());
- return _startGeneration == _authzManager->_cacheGeneration;
- }
-
- private:
- void synchronizeWithFetchPhase() {
- while (otherUpdateInFetchPhase())
- wait();
- fassert(17192, !_authzManager->_isFetchPhaseBusy);
- _isThisGuardInFetchPhase = true;
- _authzManager->_isFetchPhaseBusy = true;
- }
-
- OID _startGeneration;
- bool _isThisGuardInFetchPhase;
- AuthorizationManager* _authzManager;
- stdx::unique_lock<stdx::mutex> _lock;
- };
-
- AuthorizationManager::AuthorizationManager(
- std::unique_ptr<AuthzManagerExternalState> externalState) :
- _authEnabled(false),
- _privilegeDocsExist(false),
- _externalState(std::move(externalState)),
- _version(schemaVersionInvalid),
- _isFetchPhaseBusy(false) {
- _updateCacheGeneration_inlock();
- }
-
- AuthorizationManager::~AuthorizationManager() {
- for (unordered_map<UserName, User*>::iterator it = _userCache.begin();
- it != _userCache.end(); ++it) {
- fassert(17265, it->second != internalSecurity.user);
- delete it->second ;
+ if (_isThisGuardInFetchPhase) {
+ fassert(17190, _authzManager->_isFetchPhaseBusy);
+ _authzManager->_isFetchPhaseBusy = false;
+ _authzManager->_fetchPhaseIsReady.notify_all();
}
}
- std::unique_ptr<AuthorizationSession> AuthorizationManager::makeAuthorizationSession() {
- return stdx::make_unique<AuthorizationSession>(
- _externalState->makeAuthzSessionExternalState(this));
- }
-
- Status AuthorizationManager::getAuthorizationVersion(OperationContext* txn, int* version) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- int newVersion = _version;
- if (schemaVersionInvalid == newVersion) {
- while (guard.otherUpdateInFetchPhase())
- guard.wait();
- guard.beginFetchPhase();
- Status status = _externalState->getStoredAuthorizationVersion(txn, &newVersion);
- guard.endFetchPhase();
- if (!status.isOK()) {
- warning() << "Problem fetching the stored schema version of authorization data: "
- << status;
- *version = schemaVersionInvalid;
- return status;
- }
-
- if (guard.isSameCacheGeneration()) {
- _version = newVersion;
- }
- }
- *version = newVersion;
- return Status::OK();
+ /**
+ * Returns true of the authzManager reports that it is in fetch phase.
+ */
+ bool otherUpdateInFetchPhase() {
+ return _authzManager->_isFetchPhaseBusy;
}
- OID AuthorizationManager::getCacheGeneration() {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- return _cacheGeneration;
+ /**
+ * Waits on the _authzManager->_fetchPhaseIsReady condition.
+ */
+ void wait() {
+ fassert(17222, !_isThisGuardInFetchPhase);
+ _authzManager->_fetchPhaseIsReady.wait(_lock);
}
- void AuthorizationManager::setAuthEnabled(bool enabled) {
- _authEnabled = enabled;
+ /**
+ * Enters fetch phase, releasing the _authzManager->_cacheMutex after recording the current
+ * cache generation.
+ */
+ void beginFetchPhase() {
+ fassert(17191, !_authzManager->_isFetchPhaseBusy);
+ _isThisGuardInFetchPhase = true;
+ _authzManager->_isFetchPhaseBusy = true;
+ _startGeneration = _authzManager->_cacheGeneration;
+ _lock.unlock();
}
- bool AuthorizationManager::isAuthEnabled() const {
- return _authEnabled;
+ /**
+ * Exits the fetch phase, reacquiring the _authzManager->_cacheMutex.
+ */
+ void endFetchPhase() {
+ _lock.lock();
+ // We do not clear _authzManager->_isFetchPhaseBusy or notify waiters until
+ // ~CacheGuard(), for two reasons. First, there's no value to notifying the waiters
+ // before you're ready to release the mutex, because they'll just go to sleep on the
+ // mutex. Second, in order to meaningfully check the preconditions of
+ // isSameCacheGeneration(), we need a state that means "fetch phase was entered and now
+ // has been exited." That state is _isThisGuardInFetchPhase == true and
+ // _lock.owns_lock() == true.
}
- bool AuthorizationManager::hasAnyPrivilegeDocuments(OperationContext* txn) {
- stdx::unique_lock<stdx::mutex> lk(_privilegeDocsExistMutex);
- if (_privilegeDocsExist) {
- // If we know that a user exists, don't re-check.
- return true;
- }
-
- lk.unlock();
- bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(txn);
- lk.lock();
-
- if (privDocsExist) {
- _privilegeDocsExist = true;
- }
-
- return _privilegeDocsExist;
+ /**
+ * Returns true if _authzManager->_cacheGeneration remained the same while this guard was
+ * in fetch phase. Behavior is undefined if this guard never entered fetch phase.
+ *
+ * If this returns true, do not update the cached data with this
+ */
+ bool isSameCacheGeneration() const {
+ fassert(17223, _isThisGuardInFetchPhase);
+ fassert(17231, _lock.owns_lock());
+ return _startGeneration == _authzManager->_cacheGeneration;
}
- Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges,
- mutablebson::Element resultArray) {
- for (PrivilegeVector::const_iterator it = privileges.begin();
- it != privileges.end(); ++it) {
- std::string errmsg;
- ParsedPrivilege privilege;
- if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) {
- return Status(ErrorCodes::BadValue, errmsg);
- }
- resultArray.appendObject("privileges", privilege.toBSON());
- }
- return Status::OK();
+private:
+ void synchronizeWithFetchPhase() {
+ while (otherUpdateInFetchPhase())
+ wait();
+ fassert(17192, !_authzManager->_isFetchPhaseBusy);
+ _isThisGuardInFetchPhase = true;
+ _authzManager->_isFetchPhaseBusy = true;
}
- Status AuthorizationManager::getBSONForRole(RoleGraph* graph,
- const RoleName& roleName,
- mutablebson::Element result) {
- if (!graph->roleExists(roleName)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << roleName.getFullName() <<
- "does not name an existing role");
- }
- std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole();
- result.appendString("_id", id);
- result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole());
- result.appendString(ROLE_DB_FIELD_NAME, roleName.getDB());
-
- // Build privileges array
- mutablebson::Element privilegesArrayElement =
- result.getDocument().makeElementArray("privileges");
- result.pushBack(privilegesArrayElement);
- const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName);
- Status status = getBSONForPrivileges(privileges, privilegesArrayElement);
+ OID _startGeneration;
+ bool _isThisGuardInFetchPhase;
+ AuthorizationManager* _authzManager;
+ stdx::unique_lock<stdx::mutex> _lock;
+};
+
+AuthorizationManager::AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState)
+ : _authEnabled(false),
+ _privilegeDocsExist(false),
+ _externalState(std::move(externalState)),
+ _version(schemaVersionInvalid),
+ _isFetchPhaseBusy(false) {
+ _updateCacheGeneration_inlock();
+}
+
+AuthorizationManager::~AuthorizationManager() {
+ for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); it != _userCache.end();
+ ++it) {
+ fassert(17265, it->second != internalSecurity.user);
+ delete it->second;
+ }
+}
+
+std::unique_ptr<AuthorizationSession> AuthorizationManager::makeAuthorizationSession() {
+ return stdx::make_unique<AuthorizationSession>(
+ _externalState->makeAuthzSessionExternalState(this));
+}
+
+Status AuthorizationManager::getAuthorizationVersion(OperationContext* txn, int* version) {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ int newVersion = _version;
+ if (schemaVersionInvalid == newVersion) {
+ while (guard.otherUpdateInFetchPhase())
+ guard.wait();
+ guard.beginFetchPhase();
+ Status status = _externalState->getStoredAuthorizationVersion(txn, &newVersion);
+ guard.endFetchPhase();
if (!status.isOK()) {
+ warning() << "Problem fetching the stored schema version of authorization data: "
+ << status;
+ *version = schemaVersionInvalid;
return status;
}
- // Build roles array
- mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles");
- result.pushBack(rolesArrayElement);
- for (RoleNameIterator roles = graph->getDirectSubordinates(roleName);
- roles.more();
- roles.next()) {
-
- const RoleName& subRole = roles.get();
- mutablebson::Element roleObj = result.getDocument().makeElementObject("");
- roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole());
- roleObj.appendString(ROLE_DB_FIELD_NAME, subRole.getDB());
- rolesArrayElement.pushBack(roleObj);
+ if (guard.isSameCacheGeneration()) {
+ _version = newVersion;
}
+ }
+ *version = newVersion;
+ return Status::OK();
+}
+
+OID AuthorizationManager::getCacheGeneration() {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ return _cacheGeneration;
+}
+
+void AuthorizationManager::setAuthEnabled(bool enabled) {
+ _authEnabled = enabled;
+}
+
+bool AuthorizationManager::isAuthEnabled() const {
+ return _authEnabled;
+}
+
+bool AuthorizationManager::hasAnyPrivilegeDocuments(OperationContext* txn) {
+ stdx::unique_lock<stdx::mutex> lk(_privilegeDocsExistMutex);
+ if (_privilegeDocsExist) {
+ // If we know that a user exists, don't re-check.
+ return true;
+ }
- return Status::OK();
+ lk.unlock();
+ bool privDocsExist = _externalState->hasAnyPrivilegeDocuments(txn);
+ lk.lock();
+
+ if (privDocsExist) {
+ _privilegeDocsExist = true;
}
- Status AuthorizationManager::_initializeUserFromPrivilegeDocument(
- User* user, const BSONObj& privDoc) {
- V2UserDocumentParser parser;
- std::string userName = parser.extractUserNameFromUserDocument(privDoc);
- if (userName != user->getName().getUser()) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "User name from privilege document \""
- << userName
- << "\" doesn't match name of provided User \""
- << user->getName().getUser()
- << "\"",
- 0);
- }
+ return _privilegeDocsExist;
+}
- Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc);
- if (!status.isOK()) {
- return status;
- }
- status = parser.initializeUserRolesFromUserDocument(privDoc, user);
- if (!status.isOK()) {
- return status;
- }
- status = parser.initializeUserIndirectRolesFromUserDocument(privDoc, user);
- if (!status.isOK()) {
- return status;
+Status AuthorizationManager::getBSONForPrivileges(const PrivilegeVector& privileges,
+ mutablebson::Element resultArray) {
+ for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) {
+ std::string errmsg;
+ ParsedPrivilege privilege;
+ if (!ParsedPrivilege::privilegeToParsedPrivilege(*it, &privilege, &errmsg)) {
+ return Status(ErrorCodes::BadValue, errmsg);
}
- status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user);
- if (!status.isOK()) {
- return status;
- }
-
- return Status::OK();
+ resultArray.appendObject("privileges", privilege.toBSON());
+ }
+ return Status::OK();
+}
+
+Status AuthorizationManager::getBSONForRole(RoleGraph* graph,
+ const RoleName& roleName,
+ mutablebson::Element result) {
+ if (!graph->roleExists(roleName)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << roleName.getFullName()
+ << "does not name an existing role");
+ }
+ std::string id = mongoutils::str::stream() << roleName.getDB() << "." << roleName.getRole();
+ result.appendString("_id", id);
+ result.appendString(ROLE_NAME_FIELD_NAME, roleName.getRole());
+ result.appendString(ROLE_DB_FIELD_NAME, roleName.getDB());
+
+ // Build privileges array
+ mutablebson::Element privilegesArrayElement =
+ result.getDocument().makeElementArray("privileges");
+ result.pushBack(privilegesArrayElement);
+ const PrivilegeVector& privileges = graph->getDirectPrivileges(roleName);
+ Status status = getBSONForPrivileges(privileges, privilegesArrayElement);
+ if (!status.isOK()) {
+ return status;
}
- Status AuthorizationManager::getUserDescription(OperationContext* txn,
- const UserName& userName,
- BSONObj* result) {
- return _externalState->getUserDescription(txn, userName, result);
+ // Build roles array
+ mutablebson::Element rolesArrayElement = result.getDocument().makeElementArray("roles");
+ result.pushBack(rolesArrayElement);
+ for (RoleNameIterator roles = graph->getDirectSubordinates(roleName); roles.more();
+ roles.next()) {
+ const RoleName& subRole = roles.get();
+ mutablebson::Element roleObj = result.getDocument().makeElementObject("");
+ roleObj.appendString(ROLE_NAME_FIELD_NAME, subRole.getRole());
+ roleObj.appendString(ROLE_DB_FIELD_NAME, subRole.getDB());
+ rolesArrayElement.pushBack(roleObj);
}
- Status AuthorizationManager::getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result) {
- return _externalState->getRoleDescription(roleName, showPrivileges, result);
+ return Status::OK();
+}
+
+Status AuthorizationManager::_initializeUserFromPrivilegeDocument(User* user,
+ const BSONObj& privDoc) {
+ V2UserDocumentParser parser;
+ std::string userName = parser.extractUserNameFromUserDocument(privDoc);
+ if (userName != user->getName().getUser()) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "User name from privilege document \""
+ << userName
+ << "\" doesn't match name of provided User \""
+ << user->getName().getUser() << "\"",
+ 0);
}
- Status AuthorizationManager::getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- vector<BSONObj>* result) {
- return _externalState->getRoleDescriptionsForDB(dbname,
- showPrivileges,
- showBuiltinRoles,
- result);
+ Status status = parser.initializeUserCredentialsFromUserDocument(user, privDoc);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = parser.initializeUserRolesFromUserDocument(privDoc, user);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = parser.initializeUserIndirectRolesFromUserDocument(privDoc, user);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = parser.initializeUserPrivilegesFromUserDocument(privDoc, user);
+ if (!status.isOK()) {
+ return status;
}
- Status AuthorizationManager::acquireUser(
- OperationContext* txn, const UserName& userName, User** acquiredUser) {
- if (userName == internalSecurity.user->getName()) {
- *acquiredUser = internalSecurity.user;
- return Status::OK();
- }
+ return Status::OK();
+}
+
+Status AuthorizationManager::getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result) {
+ return _externalState->getUserDescription(txn, userName, result);
+}
+
+Status AuthorizationManager::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
+ return _externalState->getRoleDescription(roleName, showPrivileges, result);
+}
+
+Status AuthorizationManager::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) {
+ return _externalState->getRoleDescriptionsForDB(
+ dbname, showPrivileges, showBuiltinRoles, result);
+}
+
+Status AuthorizationManager::acquireUser(OperationContext* txn,
+ const UserName& userName,
+ User** acquiredUser) {
+ if (userName == internalSecurity.user->getName()) {
+ *acquiredUser = internalSecurity.user;
+ return Status::OK();
+ }
- unordered_map<UserName, User*>::iterator it;
+ unordered_map<UserName, User*>::iterator it;
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- while ((_userCache.end() == (it = _userCache.find(userName))) &&
- guard.otherUpdateInFetchPhase()) {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ while ((_userCache.end() == (it = _userCache.find(userName))) &&
+ guard.otherUpdateInFetchPhase()) {
+ guard.wait();
+ }
- guard.wait();
- }
+ if (it != _userCache.end()) {
+ fassert(16914, it->second);
+ fassert(17003, it->second->isValid());
+ fassert(17008, it->second->getRefCount() > 0);
+ it->second->incrementRefCount();
+ *acquiredUser = it->second;
+ return Status::OK();
+ }
- if (it != _userCache.end()) {
- fassert(16914, it->second);
- fassert(17003, it->second->isValid());
- fassert(17008, it->second->getRefCount() > 0);
- it->second->incrementRefCount();
- *acquiredUser = it->second;
- return Status::OK();
- }
+ std::unique_ptr<User> user;
- std::unique_ptr<User> user;
+ int authzVersion = _version;
+ guard.beginFetchPhase();
- int authzVersion = _version;
- guard.beginFetchPhase();
+ // Number of times to retry a user document that fetches due to transient
+ // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly
+ // after schema upgrades.
+ static const int maxAcquireRetries = 2;
+ Status status = Status::OK();
+ for (int i = 0; i < maxAcquireRetries; ++i) {
+ if (authzVersion == schemaVersionInvalid) {
+ Status status = _externalState->getStoredAuthorizationVersion(txn, &authzVersion);
+ if (!status.isOK())
+ return status;
+ }
- // Number of times to retry a user document that fetches due to transient
- // AuthSchemaIncompatible errors. These errors should only ever occur during and shortly
- // after schema upgrades.
- static const int maxAcquireRetries = 2;
- Status status = Status::OK();
- for (int i = 0; i < maxAcquireRetries; ++i) {
- if (authzVersion == schemaVersionInvalid) {
- Status status = _externalState->getStoredAuthorizationVersion(txn, &authzVersion);
- if (!status.isOK())
- return status;
- }
-
- switch (authzVersion) {
+ switch (authzVersion) {
default:
- status = Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Illegal value for authorization data schema version, " <<
- authzVersion);
+ status = Status(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Illegal value for authorization data schema version, "
+ << authzVersion);
break;
case schemaVersion28SCRAM:
case schemaVersion26Final:
@@ -493,182 +483,174 @@ namespace mongo {
status = _fetchUserV2(txn, userName, &user);
break;
case schemaVersion24:
- status = Status(ErrorCodes::AuthSchemaIncompatible, mongoutils::str::stream() <<
- "Authorization data schema version " << schemaVersion24 <<
- " not supported after MongoDB version 2.6.");
- break;
- }
- if (status.isOK())
+ status = Status(ErrorCodes::AuthSchemaIncompatible,
+ mongoutils::str::stream()
+ << "Authorization data schema version " << schemaVersion24
+ << " not supported after MongoDB version 2.6.");
break;
- if (status != ErrorCodes::AuthSchemaIncompatible)
- return status;
-
- authzVersion = schemaVersionInvalid;
}
- if (!status.isOK())
+ if (status.isOK())
+ break;
+ if (status != ErrorCodes::AuthSchemaIncompatible)
return status;
- guard.endFetchPhase();
-
- user->incrementRefCount();
- // NOTE: It is not safe to throw an exception from here to the end of the method.
- if (guard.isSameCacheGeneration()) {
- _userCache.insert(std::make_pair(userName, user.get()));
- if (_version == schemaVersionInvalid)
- _version = authzVersion;
- }
- else {
- // If the cache generation changed while this thread was in fetch mode, the data
- // associated with the user may now be invalid, so we must mark it as such. The caller
- // may still opt to use the information for a short while, but not indefinitely.
- user->invalidate();
- }
- *acquiredUser = user.release();
-
- return Status::OK();
+ authzVersion = schemaVersionInvalid;
+ }
+ if (!status.isOK())
+ return status;
+
+ guard.endFetchPhase();
+
+ user->incrementRefCount();
+ // NOTE: It is not safe to throw an exception from here to the end of the method.
+ if (guard.isSameCacheGeneration()) {
+ _userCache.insert(std::make_pair(userName, user.get()));
+ if (_version == schemaVersionInvalid)
+ _version = authzVersion;
+ } else {
+ // If the cache generation changed while this thread was in fetch mode, the data
+ // associated with the user may now be invalid, so we must mark it as such. The caller
+ // may still opt to use the information for a short while, but not indefinitely.
+ user->invalidate();
+ }
+ *acquiredUser = user.release();
+
+ return Status::OK();
+}
+
+Status AuthorizationManager::_fetchUserV2(OperationContext* txn,
+ const UserName& userName,
+ std::unique_ptr<User>* acquiredUser) {
+ BSONObj userObj;
+ Status status = getUserDescription(txn, userName, &userObj);
+ if (!status.isOK()) {
+ return status;
}
- Status AuthorizationManager::_fetchUserV2(OperationContext* txn,
- const UserName& userName,
- std::unique_ptr<User>* acquiredUser) {
- BSONObj userObj;
- Status status = getUserDescription(txn, userName, &userObj);
- if (!status.isOK()) {
- return status;
- }
-
- // Put the new user into an unique_ptr temporarily in case there's an error while
- // initializing the user.
- std::unique_ptr<User> user(new User(userName));
+ // Put the new user into an unique_ptr temporarily in case there's an error while
+ // initializing the user.
+ std::unique_ptr<User> user(new User(userName));
- status = _initializeUserFromPrivilegeDocument(user.get(), userObj);
- if (!status.isOK()) {
- return status;
- }
- acquiredUser->reset(user.release());
- return Status::OK();
+ status = _initializeUserFromPrivilegeDocument(user.get(), userObj);
+ if (!status.isOK()) {
+ return status;
}
+ acquiredUser->reset(user.release());
+ return Status::OK();
+}
- void AuthorizationManager::releaseUser(User* user) {
- if (user == internalSecurity.user) {
- return;
- }
+void AuthorizationManager::releaseUser(User* user) {
+ if (user == internalSecurity.user) {
+ return;
+ }
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- user->decrementRefCount();
- if (user->getRefCount() == 0) {
- // If it's been invalidated then it's not in the _userCache anymore.
- if (user->isValid()) {
- MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName());
- dassert(erased);
- }
- delete user;
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ user->decrementRefCount();
+ if (user->getRefCount() == 0) {
+ // If it's been invalidated then it's not in the _userCache anymore.
+ if (user->isValid()) {
+ MONGO_COMPILER_VARIABLE_UNUSED bool erased = _userCache.erase(user->getName());
+ dassert(erased);
}
+ delete user;
+ }
+}
+
+void AuthorizationManager::invalidateUserByName(const UserName& userName) {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ _updateCacheGeneration_inlock();
+ unordered_map<UserName, User*>::iterator it = _userCache.find(userName);
+ if (it == _userCache.end()) {
+ return;
}
- void AuthorizationManager::invalidateUserByName(const UserName& userName) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _updateCacheGeneration_inlock();
- unordered_map<UserName, User*>::iterator it = _userCache.find(userName);
- if (it == _userCache.end()) {
- return;
- }
+ User* user = it->second;
+ _userCache.erase(it);
+ user->invalidate();
+}
+void AuthorizationManager::invalidateUsersFromDB(const std::string& dbname) {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ _updateCacheGeneration_inlock();
+ unordered_map<UserName, User*>::iterator it = _userCache.begin();
+ while (it != _userCache.end()) {
User* user = it->second;
- _userCache.erase(it);
- user->invalidate();
- }
-
- void AuthorizationManager::invalidateUsersFromDB(const std::string& dbname) {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _updateCacheGeneration_inlock();
- unordered_map<UserName, User*>::iterator it = _userCache.begin();
- while (it != _userCache.end()) {
- User* user = it->second;
- if (user->getName().getDB() == dbname) {
- _userCache.erase(it++);
- user->invalidate();
- } else {
- ++it;
- }
+ if (user->getName().getDB() == dbname) {
+ _userCache.erase(it++);
+ user->invalidate();
+ } else {
+ ++it;
}
}
-
- void AuthorizationManager::invalidateUserCache() {
- CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
- _invalidateUserCache_inlock();
+}
+
+void AuthorizationManager::invalidateUserCache() {
+ CacheGuard guard(this, CacheGuard::fetchSynchronizationManual);
+ _invalidateUserCache_inlock();
+}
+
+void AuthorizationManager::_invalidateUserCache_inlock() {
+ _updateCacheGeneration_inlock();
+ for (unordered_map<UserName, User*>::iterator it = _userCache.begin(); it != _userCache.end();
+ ++it) {
+ fassert(17266, it->second != internalSecurity.user);
+ it->second->invalidate();
}
+ _userCache.clear();
- void AuthorizationManager::_invalidateUserCache_inlock() {
- _updateCacheGeneration_inlock();
- for (unordered_map<UserName, User*>::iterator it = _userCache.begin();
- it != _userCache.end(); ++it) {
- fassert(17266, it->second != internalSecurity.user);
- it->second->invalidate();
- }
- _userCache.clear();
+ // Reread the schema version before acquiring the next user.
+ _version = schemaVersionInvalid;
+}
- // Reread the schema version before acquiring the next user.
- _version = schemaVersionInvalid;
- }
+Status AuthorizationManager::initialize(OperationContext* txn) {
+ invalidateUserCache();
+ Status status = _externalState->initialize(txn);
+ if (!status.isOK())
+ return status;
- Status AuthorizationManager::initialize(OperationContext* txn) {
- invalidateUserCache();
- Status status = _externalState->initialize(txn);
- if (!status.isOK())
- return status;
-
- return Status::OK();
- }
+ return Status::OK();
+}
namespace {
- bool isAuthzNamespace(StringData ns) {
- return (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
- ns == AuthorizationManager::usersCollectionNamespace.ns() ||
- ns == AuthorizationManager::versionCollectionNamespace.ns());
- }
-
- bool isAuthzCollection(StringData coll) {
- return (coll == AuthorizationManager::rolesCollectionNamespace.coll() ||
- coll == AuthorizationManager::usersCollectionNamespace.coll() ||
- coll == AuthorizationManager::versionCollectionNamespace.coll());
- }
-
- bool loggedCommandOperatesOnAuthzData(const char* ns, const BSONObj& cmdObj) {
- if (ns != AuthorizationManager::adminCommandNamespace.ns())
- return false;
- const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
- if (cmdName == "drop") {
- return isAuthzCollection(cmdObj.firstElement().valueStringData());
- }
- else if (cmdName == "dropDatabase") {
- return true;
- }
- else if (cmdName == "renameCollection") {
- return isAuthzCollection(cmdObj.firstElement().str()) ||
- isAuthzCollection(cmdObj["to"].str());
- }
- else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
- return false;
- }
- else if (cmdName == "create") {
- return false;
- }
- else {
- return true;
- }
+bool isAuthzNamespace(StringData ns) {
+ return (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
+ ns == AuthorizationManager::usersCollectionNamespace.ns() ||
+ ns == AuthorizationManager::versionCollectionNamespace.ns());
+}
+
+bool isAuthzCollection(StringData coll) {
+ return (coll == AuthorizationManager::rolesCollectionNamespace.coll() ||
+ coll == AuthorizationManager::usersCollectionNamespace.coll() ||
+ coll == AuthorizationManager::versionCollectionNamespace.coll());
+}
+
+bool loggedCommandOperatesOnAuthzData(const char* ns, const BSONObj& cmdObj) {
+ if (ns != AuthorizationManager::adminCommandNamespace.ns())
+ return false;
+ const StringData cmdName(cmdObj.firstElement().fieldNameStringData());
+ if (cmdName == "drop") {
+ return isAuthzCollection(cmdObj.firstElement().valueStringData());
+ } else if (cmdName == "dropDatabase") {
+ return true;
+ } else if (cmdName == "renameCollection") {
+ return isAuthzCollection(cmdObj.firstElement().str()) ||
+ isAuthzCollection(cmdObj["to"].str());
+ } else if (cmdName == "dropIndexes" || cmdName == "deleteIndexes") {
+ return false;
+ } else if (cmdName == "create") {
+ return false;
+ } else {
+ return true;
}
+}
- bool appliesToAuthzData(
- const char* op,
- const char* ns,
- const BSONObj& o) {
-
- switch (*op) {
+bool appliesToAuthzData(const char* op, const char* ns, const BSONObj& o) {
+ switch (*op) {
case 'i':
case 'u':
case 'd':
- if (op[1] != '\0') return false; // "db" op type
+ if (op[1] != '\0')
+ return false; // "db" op type
return isAuthzNamespace(ns);
case 'c':
return loggedCommandOperatesOnAuthzData(ns, o);
@@ -677,71 +659,66 @@ namespace {
return false;
default:
return true;
- }
}
-
- // Updates to users in the oplog are done by matching on the _id, which will always have the
- // form "<dbname>.<username>". This function extracts the UserName from that string.
- StatusWith<UserName> extractUserNameFromIdString(StringData idstr) {
- size_t splitPoint = idstr.find('.');
- if (splitPoint == string::npos) {
- return StatusWith<UserName>(
- ErrorCodes::FailedToParse,
- mongoutils::str::stream() << "_id entries for user documents must be of "
- "the form <dbname>.<username>. Found: " << idstr);
- }
- return StatusWith<UserName>(UserName(idstr.substr(splitPoint + 1),
- idstr.substr(0, splitPoint)));
+}
+
+// Updates to users in the oplog are done by matching on the _id, which will always have the
+// form "<dbname>.<username>". This function extracts the UserName from that string.
+StatusWith<UserName> extractUserNameFromIdString(StringData idstr) {
+ size_t splitPoint = idstr.find('.');
+ if (splitPoint == string::npos) {
+ return StatusWith<UserName>(ErrorCodes::FailedToParse,
+ mongoutils::str::stream()
+ << "_id entries for user documents must be of "
+ "the form <dbname>.<username>. Found: " << idstr);
}
+ return StatusWith<UserName>(
+ UserName(idstr.substr(splitPoint + 1), idstr.substr(0, splitPoint)));
+}
} // namespace
- void AuthorizationManager::_updateCacheGeneration_inlock() {
- _cacheGeneration = OID::gen();
+void AuthorizationManager::_updateCacheGeneration_inlock() {
+ _cacheGeneration = OID::gen();
+}
+
+void AuthorizationManager::_invalidateRelevantCacheData(const char* op,
+ const char* ns,
+ const BSONObj& o,
+ const BSONObj* o2) {
+ if (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
+ ns == AuthorizationManager::versionCollectionNamespace.ns()) {
+ invalidateUserCache();
+ return;
}
- void AuthorizationManager::_invalidateRelevantCacheData(const char* op,
- const char* ns,
- const BSONObj& o,
- const BSONObj* o2) {
- if (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
- ns == AuthorizationManager::versionCollectionNamespace.ns()) {
- invalidateUserCache();
- return;
- }
+ if (*op == 'i' || *op == 'd' || *op == 'u') {
+ // If you got into this function isAuthzNamespace() must have returned true, and we've
+ // already checked that it's not the roles or version collection.
+ invariant(ns == AuthorizationManager::usersCollectionNamespace.ns());
- if (*op == 'i' || *op == 'd' || *op == 'u') {
- // If you got into this function isAuthzNamespace() must have returned true, and we've
- // already checked that it's not the roles or version collection.
- invariant(ns == AuthorizationManager::usersCollectionNamespace.ns());
-
- StatusWith<UserName> userName = (*op == 'u') ?
- extractUserNameFromIdString((*o2)["_id"].str()) :
- extractUserNameFromIdString(o["_id"].str());
-
- if (!userName.isOK()) {
- warning() << "Invalidating user cache based on user being updated failed, will "
- "invalidate the entire cache instead: " << userName.getStatus() << endl;
- invalidateUserCache();
- return;
- }
- invalidateUserByName(userName.getValue());
- } else {
+ StatusWith<UserName> userName = (*op == 'u')
+ ? extractUserNameFromIdString((*o2)["_id"].str())
+ : extractUserNameFromIdString(o["_id"].str());
+
+ if (!userName.isOK()) {
+ warning() << "Invalidating user cache based on user being updated failed, will "
+ "invalidate the entire cache instead: " << userName.getStatus() << endl;
invalidateUserCache();
+ return;
}
+ invalidateUserByName(userName.getValue());
+ } else {
+ invalidateUserCache();
}
+}
- void AuthorizationManager::logOp(
- OperationContext* txn,
- const char* op,
- const char* ns,
- const BSONObj& o,
- BSONObj* o2) {
-
- _externalState->logOp(txn, op, ns, o, o2);
- if (appliesToAuthzData(op, ns, o)) {
- _invalidateRelevantCacheData(op, ns, o, o2);
- }
+void AuthorizationManager::logOp(
+ OperationContext* txn, const char* op, const char* ns, const BSONObj& o, BSONObj* o2) {
+ _externalState->logOp(txn, op, ns, o, o2);
+ if (appliesToAuthzData(op, ns, o)) {
+ _invalidateRelevantCacheData(op, ns, o, o2);
}
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index 8f7afa9f3fc..9c7fdbaf9d0 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -50,363 +50,363 @@
namespace mongo {
- class AuthorizationSession;
- class AuthzManagerExternalState;
- class OperationContext;
- class ServiceContext;
- class UserDocumentParser;
-
- /**
- * Internal secret key info.
- */
- struct AuthInfo {
- User* user;
- };
- extern AuthInfo internalSecurity; // set at startup and not changed after initialization.
-
- /**
- * Contains server/cluster-wide information about Authorization.
- */
- class AuthorizationManager {
- MONGO_DISALLOW_COPYING(AuthorizationManager);
- public:
- static AuthorizationManager* get(ServiceContext* service);
- static AuthorizationManager* get(ServiceContext& service);
- static void set(ServiceContext* service,
- std::unique_ptr<AuthorizationManager> authzManager);
-
- // The newly constructed AuthorizationManager takes ownership of "externalState"
- explicit AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState);
-
- ~AuthorizationManager();
-
- static const std::string USER_NAME_FIELD_NAME;
- static const std::string USER_DB_FIELD_NAME;
- static const std::string ROLE_NAME_FIELD_NAME;
- static const std::string ROLE_DB_FIELD_NAME;
- static const std::string PASSWORD_FIELD_NAME;
- static const std::string V1_USER_NAME_FIELD_NAME;
- static const std::string V1_USER_SOURCE_FIELD_NAME;
-
- static const NamespaceString adminCommandNamespace;
- static const NamespaceString rolesCollectionNamespace;
- static const NamespaceString usersAltCollectionNamespace;
- static const NamespaceString usersBackupCollectionNamespace;
- static const NamespaceString usersCollectionNamespace;
- static const NamespaceString versionCollectionNamespace;
- static const NamespaceString defaultTempUsersCollectionNamespace; // for mongorestore
- static const NamespaceString defaultTempRolesCollectionNamespace; // for mongorestore
-
- /**
- * Query to match the auth schema version document in the versionCollectionNamespace.
- */
- static const BSONObj versionDocumentQuery;
-
- /**
- * Name of the field in the auth schema version document containing the current schema
- * version.
- */
- static const std::string schemaVersionFieldName;
-
- /**
- * Value used to represent that the schema version is not cached or invalid.
- */
- static const int schemaVersionInvalid = 0;
-
- /**
- * Auth schema version for MongoDB v2.4 and prior.
- */
- static const int schemaVersion24 = 1;
-
- /**
- * Auth schema version for MongoDB v2.6 during the upgrade process. Same as
- * schemaVersion26Final, except that user documents are found in admin.new.users, and user
- * management commands are disabled.
- */
- static const int schemaVersion26Upgrade = 2;
-
- /**
- * Auth schema version for MongoDB 2.6 and 3.0 MONGODB-CR/SCRAM mixed auth mode.
- * Users are stored in admin.system.users, roles in admin.system.roles.
- */
- static const int schemaVersion26Final = 3;
-
- /**
- * Auth schema version for MongoDB 3.0 SCRAM only mode.
- * Users are stored in admin.system.users, roles in admin.system.roles.
- * MONGODB-CR credentials have been replaced with SCRAM credentials in the user documents.
- */
- static const int schemaVersion28SCRAM = 5;
-
- // TODO: Make the following functions no longer static.
-
- /**
- * Takes a vector of privileges and fills the output param "resultArray" with a BSON array
- * representation of the privileges.
- */
- static Status getBSONForPrivileges(const PrivilegeVector& privileges,
- mutablebson::Element resultArray);
-
- /**
- * Takes a role name and a role graph and fills the output param "result" with a BSON
- * representation of the role object.
- * This function does no locking - it is up to the caller to synchronize access to the
- * role graph.
- * Note: The passed in RoleGraph can't be marked const because some of its accessors can
- * actually modify it internally (to set up built-in roles).
- */
- static Status getBSONForRole(/*const*/ RoleGraph* graph,
- const RoleName& roleName,
- mutablebson::Element result);
-
- /**
- * Returns a new AuthorizationSession for use with this AuthorizationManager.
- */
- std::unique_ptr<AuthorizationSession> makeAuthorizationSession();
-
- /**
- * Sets whether or not access control enforcement is enabled for this manager.
- */
- void setAuthEnabled(bool enabled);
-
- /**
- * Returns true if access control is enabled for this manager .
- */
- bool isAuthEnabled() const;
-
- /**
- * Returns via the output parameter "version" the version number of the authorization
- * system. Returns Status::OK() if it was able to successfully fetch the current
- * authorization version. If it has problems fetching the most up to date version it
- * returns a non-OK status. When returning a non-OK status, *version will be set to
- * schemaVersionInvalid (0).
- */
- Status getAuthorizationVersion(OperationContext* txn, int* version);
-
- /**
- * Returns the user cache generation identifier.
- */
- OID getCacheGeneration();
-
- /**
- * Returns true if there exists at least one privilege document in the system.
- * Used by the AuthorizationSession to determine whether localhost connections should be
- * granted special access to bootstrap the system.
- * NOTE: If this method ever returns true, the result is cached in _privilegeDocsExist,
- * meaning that once this method returns true it will continue to return true for the
- * lifetime of this process, even if all users are subsequently dropped from the system.
- */
- bool hasAnyPrivilegeDocuments(OperationContext* txn);
-
- // Checks to see if "doc" is a valid privilege document, assuming it is stored in the
- // "system.users" collection of database "dbname".
- //
- // Returns Status::OK() if the document is good, or Status(ErrorCodes::BadValue), otherwise.
- Status checkValidPrivilegeDocument(StringData dbname, const BSONObj& doc);
-
- // Given a database name and a readOnly flag return an ActionSet describing all the actions
- // that an old-style user with those attributes should be given.
- ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const;
-
- /**
- * Writes into "result" a document describing the named user and returns Status::OK(). The
- * description includes the user credentials and customData, if present, the user's role
- * membership and delegation information, a full list of the user's privileges, and a full
- * list of the user's roles, including those roles held implicitly through other roles
- * (indirect roles). In the event that some of this information is inconsistent, the
- * document will contain a "warnings" array, with std::string messages describing
- * inconsistencies.
- *
- * If the user does not exist, returns ErrorCodes::UserNotFound.
- */
- Status getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result);
-
- /**
- * Writes into "result" a document describing the named role and returns Status::OK(). The
- * description includes the roles in which the named role has membership and a full list of
- * the roles of which the named role is a member, including those roles memberships held
- * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
- * description documents will also include a full list of the role's privileges.
- * In the event that some of this information is inconsistent, the document will contain a
- * "warnings" array, with std::string messages describing inconsistencies.
- *
- * If the role does not exist, returns ErrorCodes::RoleNotFound.
- */
- Status getRoleDescription(const RoleName& roleName, bool showPrivileges, BSONObj* result);
-
- /**
- * Writes into "result" documents describing the roles that are defined on the given
- * database. Each role description document includes the other roles in which the role has
- * membership and a full list of the roles of which the named role is a member,
- * including those roles memberships held implicitly through other roles (indirect roles).
- * If showPrivileges is true, then the description documents will also include a full list
- * of the role's privileges. If showBuiltinRoles is true, then the result array will
- * contain description documents for all the builtin roles for the given database, if it
- * is false the result will just include user defined roles.
- * In the event that some of the information in a given role description is inconsistent,
- * the document will contain a "warnings" array, with std::string messages describing
- * inconsistencies.
- */
- Status getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- std::vector<BSONObj>* result);
-
- /**
- * Returns the User object for the given userName in the out parameter "acquiredUser".
- * If the user cache already has a user object for this user, it increments the refcount
- * on that object and gives out a pointer to it. If no user object for this user name
- * exists yet in the cache, reads the user's privilege document from disk, builds up
- * a User object, sets the refcount to 1, and gives that out. The returned user may
- * be invalid by the time the caller gets access to it.
- * The AuthorizationManager retains ownership of the returned User object.
- * On non-OK Status return values, acquiredUser will not be modified.
- */
- Status acquireUser(OperationContext* txn, const UserName& userName, User** acquiredUser);
-
- /**
- * Decrements the refcount of the given User object. If the refcount has gone to zero,
- * deletes the User. Caller must stop using its pointer to "user" after calling this.
- */
- void releaseUser(User* user);
-
- /**
- * Marks the given user as invalid and removes it from the user cache.
- */
- void invalidateUserByName(const UserName& user);
-
- /**
- * Invalidates all users who's source is "dbname" and removes them from the user cache.
- */
- void invalidateUsersFromDB(const std::string& dbname);
-
- /**
- * Initializes the authorization manager. Depending on what version the authorization
- * system is at, this may involve building up the user cache and/or the roles graph.
- * Call this function at startup and after resynchronizing a slave/secondary.
- */
- Status initialize(OperationContext* txn);
-
- /**
- * Invalidates all of the contents of the user cache.
- */
- void invalidateUserCache();
-
- /**
- * Parses privDoc and fully initializes the user object (credentials, roles, and privileges)
- * with the information extracted from the privilege document.
- * This should never be called from outside the AuthorizationManager - the only reason it's
- * public instead of private is so it can be unit tested.
- */
- Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc);
-
- /**
- * Hook called by replication code to let the AuthorizationManager observe changes
- * to relevant collections.
- */
- void logOp(OperationContext* txn,
- const char* opstr,
- const char* ns,
- const BSONObj& obj,
- BSONObj* patt);
-
- private:
- /**
- * Type used to guard accesses and updates to the user cache.
- */
- class CacheGuard;
- friend class AuthorizationManager::CacheGuard;
-
- /**
- * Invalidates all User objects in the cache and removes them from the cache.
- * Should only be called when already holding _cacheMutex.
- */
- void _invalidateUserCache_inlock();
-
- /**
- * Given the objects describing an oplog entry that affects authorization data, invalidates
- * the portion of the user cache that is affected by that operation. Should only be called
- * with oplog entries that have been pre-verified to actually affect authorization data.
- */
- void _invalidateRelevantCacheData(const char* op,
- const char* ns,
- const BSONObj& o,
- const BSONObj* o2);
-
- /**
- * Updates _cacheGeneration to a new OID
- */
- void _updateCacheGeneration_inlock();
-
- /**
- * Fetches user information from a v2-schema user document for the named user,
- * and stores a pointer to a new user object into *acquiredUser on success.
- */
- Status _fetchUserV2(OperationContext* txn,
- const UserName& userName,
- std::unique_ptr<User>* acquiredUser);
-
- /**
- * True if access control enforcement is enabled in this AuthorizationManager.
- *
- * Defaults to false. Changes to its value are not synchronized, so it should only be set
- * at initalization-time.
- */
- bool _authEnabled;
-
- /**
- * A cache of whether there are any users set up for the cluster.
- */
- bool _privilegeDocsExist;
-
- // Protects _privilegeDocsExist
- mutable stdx::mutex _privilegeDocsExistMutex;
-
- std::unique_ptr<AuthzManagerExternalState> _externalState;
-
- /**
- * Cached value of the authorization schema version.
- *
- * May be set by acquireUser() and getAuthorizationVersion(). Invalidated by
- * invalidateUserCache().
- *
- * Reads and writes guarded by CacheGuard.
- */
- int _version;
-
- /**
- * Caches User objects with information about user privileges, to avoid the need to
- * go to disk to read user privilege documents whenever possible. Every User object
- * has a reference count - the AuthorizationManager must not delete a User object in the
- * cache unless its reference count is zero.
- */
- unordered_map<UserName, User*> _userCache;
-
- /**
- * Current generation of cached data. Updated every time part of the cache gets
- * invalidated. Protected by CacheGuard.
- */
- OID _cacheGeneration;
-
- /**
- * True if there is an update to the _userCache in progress, and that update is currently in
- * the "fetch phase", during which it does not hold the _cacheMutex.
- *
- * Manipulated via CacheGuard.
- */
- bool _isFetchPhaseBusy;
-
- /**
- * Protects _userCache, _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated
- * via CacheGuard.
- */
- stdx::mutex _cacheMutex;
-
- /**
- * Condition used to signal that it is OK for another CacheGuard to enter a fetch phase.
- * Manipulated via CacheGuard.
- */
- stdx::condition_variable _fetchPhaseIsReady;
- };
-
-} // namespace mongo
+class AuthorizationSession;
+class AuthzManagerExternalState;
+class OperationContext;
+class ServiceContext;
+class UserDocumentParser;
+
+/**
+ * Internal secret key info.
+ */
+struct AuthInfo {
+ User* user;
+};
+extern AuthInfo internalSecurity; // set at startup and not changed after initialization.
+
+/**
+ * Contains server/cluster-wide information about Authorization.
+ */
+class AuthorizationManager {
+ MONGO_DISALLOW_COPYING(AuthorizationManager);
+
+public:
+ static AuthorizationManager* get(ServiceContext* service);
+ static AuthorizationManager* get(ServiceContext& service);
+ static void set(ServiceContext* service, std::unique_ptr<AuthorizationManager> authzManager);
+
+ // The newly constructed AuthorizationManager takes ownership of "externalState"
+ explicit AuthorizationManager(std::unique_ptr<AuthzManagerExternalState> externalState);
+
+ ~AuthorizationManager();
+
+ static const std::string USER_NAME_FIELD_NAME;
+ static const std::string USER_DB_FIELD_NAME;
+ static const std::string ROLE_NAME_FIELD_NAME;
+ static const std::string ROLE_DB_FIELD_NAME;
+ static const std::string PASSWORD_FIELD_NAME;
+ static const std::string V1_USER_NAME_FIELD_NAME;
+ static const std::string V1_USER_SOURCE_FIELD_NAME;
+
+ static const NamespaceString adminCommandNamespace;
+ static const NamespaceString rolesCollectionNamespace;
+ static const NamespaceString usersAltCollectionNamespace;
+ static const NamespaceString usersBackupCollectionNamespace;
+ static const NamespaceString usersCollectionNamespace;
+ static const NamespaceString versionCollectionNamespace;
+ static const NamespaceString defaultTempUsersCollectionNamespace; // for mongorestore
+ static const NamespaceString defaultTempRolesCollectionNamespace; // for mongorestore
+
+ /**
+ * Query to match the auth schema version document in the versionCollectionNamespace.
+ */
+ static const BSONObj versionDocumentQuery;
+
+ /**
+ * Name of the field in the auth schema version document containing the current schema
+ * version.
+ */
+ static const std::string schemaVersionFieldName;
+
+ /**
+ * Value used to represent that the schema version is not cached or invalid.
+ */
+ static const int schemaVersionInvalid = 0;
+
+ /**
+ * Auth schema version for MongoDB v2.4 and prior.
+ */
+ static const int schemaVersion24 = 1;
+
+ /**
+ * Auth schema version for MongoDB v2.6 during the upgrade process. Same as
+ * schemaVersion26Final, except that user documents are found in admin.new.users, and user
+ * management commands are disabled.
+ */
+ static const int schemaVersion26Upgrade = 2;
+
+ /**
+ * Auth schema version for MongoDB 2.6 and 3.0 MONGODB-CR/SCRAM mixed auth mode.
+ * Users are stored in admin.system.users, roles in admin.system.roles.
+ */
+ static const int schemaVersion26Final = 3;
+
+ /**
+ * Auth schema version for MongoDB 3.0 SCRAM only mode.
+ * Users are stored in admin.system.users, roles in admin.system.roles.
+ * MONGODB-CR credentials have been replaced with SCRAM credentials in the user documents.
+ */
+ static const int schemaVersion28SCRAM = 5;
+
+ // TODO: Make the following functions no longer static.
+
+ /**
+ * Takes a vector of privileges and fills the output param "resultArray" with a BSON array
+ * representation of the privileges.
+ */
+ static Status getBSONForPrivileges(const PrivilegeVector& privileges,
+ mutablebson::Element resultArray);
+
+ /**
+ * Takes a role name and a role graph and fills the output param "result" with a BSON
+ * representation of the role object.
+ * This function does no locking - it is up to the caller to synchronize access to the
+ * role graph.
+ * Note: The passed in RoleGraph can't be marked const because some of its accessors can
+ * actually modify it internally (to set up built-in roles).
+ */
+ static Status getBSONForRole(/*const*/ RoleGraph* graph,
+ const RoleName& roleName,
+ mutablebson::Element result);
+
+ /**
+ * Returns a new AuthorizationSession for use with this AuthorizationManager.
+ */
+ std::unique_ptr<AuthorizationSession> makeAuthorizationSession();
+
+ /**
+ * Sets whether or not access control enforcement is enabled for this manager.
+ */
+ void setAuthEnabled(bool enabled);
+
+ /**
+ * Returns true if access control is enabled for this manager .
+ */
+ bool isAuthEnabled() const;
+
+ /**
+ * Returns via the output parameter "version" the version number of the authorization
+ * system. Returns Status::OK() if it was able to successfully fetch the current
+ * authorization version. If it has problems fetching the most up to date version it
+ * returns a non-OK status. When returning a non-OK status, *version will be set to
+ * schemaVersionInvalid (0).
+ */
+ Status getAuthorizationVersion(OperationContext* txn, int* version);
+
+ /**
+ * Returns the user cache generation identifier.
+ */
+ OID getCacheGeneration();
+
+ /**
+ * Returns true if there exists at least one privilege document in the system.
+ * Used by the AuthorizationSession to determine whether localhost connections should be
+ * granted special access to bootstrap the system.
+ * NOTE: If this method ever returns true, the result is cached in _privilegeDocsExist,
+ * meaning that once this method returns true it will continue to return true for the
+ * lifetime of this process, even if all users are subsequently dropped from the system.
+ */
+ bool hasAnyPrivilegeDocuments(OperationContext* txn);
+
+ // Checks to see if "doc" is a valid privilege document, assuming it is stored in the
+ // "system.users" collection of database "dbname".
+ //
+ // Returns Status::OK() if the document is good, or Status(ErrorCodes::BadValue), otherwise.
+ Status checkValidPrivilegeDocument(StringData dbname, const BSONObj& doc);
+
+ // Given a database name and a readOnly flag return an ActionSet describing all the actions
+ // that an old-style user with those attributes should be given.
+ ActionSet getActionsForOldStyleUser(const std::string& dbname, bool readOnly) const;
+
+ /**
+ * Writes into "result" a document describing the named user and returns Status::OK(). The
+ * description includes the user credentials and customData, if present, the user's role
+ * membership and delegation information, a full list of the user's privileges, and a full
+ * list of the user's roles, including those roles held implicitly through other roles
+ * (indirect roles). In the event that some of this information is inconsistent, the
+ * document will contain a "warnings" array, with std::string messages describing
+ * inconsistencies.
+ *
+ * If the user does not exist, returns ErrorCodes::UserNotFound.
+ */
+ Status getUserDescription(OperationContext* txn, const UserName& userName, BSONObj* result);
+
+ /**
+ * Writes into "result" a document describing the named role and returns Status::OK(). The
+ * description includes the roles in which the named role has membership and a full list of
+ * the roles of which the named role is a member, including those roles memberships held
+ * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
+ * description documents will also include a full list of the role's privileges.
+ * In the event that some of this information is inconsistent, the document will contain a
+ * "warnings" array, with std::string messages describing inconsistencies.
+ *
+ * If the role does not exist, returns ErrorCodes::RoleNotFound.
+ */
+ Status getRoleDescription(const RoleName& roleName, bool showPrivileges, BSONObj* result);
+
+ /**
+ * Writes into "result" documents describing the roles that are defined on the given
+ * database. Each role description document includes the other roles in which the role has
+ * membership and a full list of the roles of which the named role is a member,
+ * including those roles memberships held implicitly through other roles (indirect roles).
+ * If showPrivileges is true, then the description documents will also include a full list
+ * of the role's privileges. If showBuiltinRoles is true, then the result array will
+ * contain description documents for all the builtin roles for the given database, if it
+ * is false the result will just include user defined roles.
+ * In the event that some of the information in a given role description is inconsistent,
+ * the document will contain a "warnings" array, with std::string messages describing
+ * inconsistencies.
+ */
+ Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ std::vector<BSONObj>* result);
+
+ /**
+ * Returns the User object for the given userName in the out parameter "acquiredUser".
+ * If the user cache already has a user object for this user, it increments the refcount
+ * on that object and gives out a pointer to it. If no user object for this user name
+ * exists yet in the cache, reads the user's privilege document from disk, builds up
+ * a User object, sets the refcount to 1, and gives that out. The returned user may
+ * be invalid by the time the caller gets access to it.
+ * The AuthorizationManager retains ownership of the returned User object.
+ * On non-OK Status return values, acquiredUser will not be modified.
+ */
+ Status acquireUser(OperationContext* txn, const UserName& userName, User** acquiredUser);
+
+ /**
+ * Decrements the refcount of the given User object. If the refcount has gone to zero,
+ * deletes the User. Caller must stop using its pointer to "user" after calling this.
+ */
+ void releaseUser(User* user);
+
+ /**
+ * Marks the given user as invalid and removes it from the user cache.
+ */
+ void invalidateUserByName(const UserName& user);
+
+ /**
+ * Invalidates all users who's source is "dbname" and removes them from the user cache.
+ */
+ void invalidateUsersFromDB(const std::string& dbname);
+
+ /**
+ * Initializes the authorization manager. Depending on what version the authorization
+ * system is at, this may involve building up the user cache and/or the roles graph.
+ * Call this function at startup and after resynchronizing a slave/secondary.
+ */
+ Status initialize(OperationContext* txn);
+
+ /**
+ * Invalidates all of the contents of the user cache.
+ */
+ void invalidateUserCache();
+
+ /**
+ * Parses privDoc and fully initializes the user object (credentials, roles, and privileges)
+ * with the information extracted from the privilege document.
+ * This should never be called from outside the AuthorizationManager - the only reason it's
+ * public instead of private is so it can be unit tested.
+ */
+ Status _initializeUserFromPrivilegeDocument(User* user, const BSONObj& privDoc);
+
+ /**
+ * Hook called by replication code to let the AuthorizationManager observe changes
+ * to relevant collections.
+ */
+ void logOp(OperationContext* txn,
+ const char* opstr,
+ const char* ns,
+ const BSONObj& obj,
+ BSONObj* patt);
+
+private:
+ /**
+ * Type used to guard accesses and updates to the user cache.
+ */
+ class CacheGuard;
+ friend class AuthorizationManager::CacheGuard;
+
+ /**
+ * Invalidates all User objects in the cache and removes them from the cache.
+ * Should only be called when already holding _cacheMutex.
+ */
+ void _invalidateUserCache_inlock();
+
+ /**
+ * Given the objects describing an oplog entry that affects authorization data, invalidates
+ * the portion of the user cache that is affected by that operation. Should only be called
+ * with oplog entries that have been pre-verified to actually affect authorization data.
+ */
+ void _invalidateRelevantCacheData(const char* op,
+ const char* ns,
+ const BSONObj& o,
+ const BSONObj* o2);
+
+ /**
+ * Updates _cacheGeneration to a new OID
+ */
+ void _updateCacheGeneration_inlock();
+
+ /**
+ * Fetches user information from a v2-schema user document for the named user,
+ * and stores a pointer to a new user object into *acquiredUser on success.
+ */
+ Status _fetchUserV2(OperationContext* txn,
+ const UserName& userName,
+ std::unique_ptr<User>* acquiredUser);
+
+ /**
+ * True if access control enforcement is enabled in this AuthorizationManager.
+ *
+ * Defaults to false. Changes to its value are not synchronized, so it should only be set
+ * at initalization-time.
+ */
+ bool _authEnabled;
+
+ /**
+ * A cache of whether there are any users set up for the cluster.
+ */
+ bool _privilegeDocsExist;
+
+ // Protects _privilegeDocsExist
+ mutable stdx::mutex _privilegeDocsExistMutex;
+
+ std::unique_ptr<AuthzManagerExternalState> _externalState;
+
+ /**
+ * Cached value of the authorization schema version.
+ *
+ * May be set by acquireUser() and getAuthorizationVersion(). Invalidated by
+ * invalidateUserCache().
+ *
+ * Reads and writes guarded by CacheGuard.
+ */
+ int _version;
+
+ /**
+ * Caches User objects with information about user privileges, to avoid the need to
+ * go to disk to read user privilege documents whenever possible. Every User object
+ * has a reference count - the AuthorizationManager must not delete a User object in the
+ * cache unless its reference count is zero.
+ */
+ unordered_map<UserName, User*> _userCache;
+
+ /**
+ * Current generation of cached data. Updated every time part of the cache gets
+ * invalidated. Protected by CacheGuard.
+ */
+ OID _cacheGeneration;
+
+ /**
+ * True if there is an update to the _userCache in progress, and that update is currently in
+ * the "fetch phase", during which it does not hold the _cacheMutex.
+ *
+ * Manipulated via CacheGuard.
+ */
+ bool _isFetchPhaseBusy;
+
+ /**
+ * Protects _userCache, _cacheGeneration, _version and _isFetchPhaseBusy. Manipulated
+ * via CacheGuard.
+ */
+ stdx::mutex _cacheMutex;
+
+ /**
+ * Condition used to signal that it is OK for another CacheGuard to enter a fetch phase.
+ * Manipulated via CacheGuard.
+ */
+ stdx::condition_variable _fetchPhaseIsReady;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_global.cpp b/src/mongo/db/auth/authorization_manager_global.cpp
index cc5ee6513f5..2fc20deef25 100644
--- a/src/mongo/db/auth/authorization_manager_global.cpp
+++ b/src/mongo/db/auth/authorization_manager_global.cpp
@@ -38,51 +38,50 @@
namespace mongo {
namespace {
- class AuthzVersionParameter : public ServerParameter {
- MONGO_DISALLOW_COPYING(AuthzVersionParameter);
- public:
- AuthzVersionParameter(ServerParameterSet* sps, const std::string& name);
- virtual void append(OperationContext* txn, BSONObjBuilder& b, const std::string& name);
- virtual Status set(const BSONElement& newValueElement);
- virtual Status setFromString(const std::string& str);
- };
+class AuthzVersionParameter : public ServerParameter {
+ MONGO_DISALLOW_COPYING(AuthzVersionParameter);
- MONGO_INITIALIZER_GENERAL(AuthzSchemaParameter,
- MONGO_NO_PREREQUISITES,
- ("BeginStartupOptionParsing"))(InitializerContext*) {
- new AuthzVersionParameter(ServerParameterSet::getGlobal(),
- authSchemaVersionServerParameter);
- return Status::OK();
- }
+public:
+ AuthzVersionParameter(ServerParameterSet* sps, const std::string& name);
+ virtual void append(OperationContext* txn, BSONObjBuilder& b, const std::string& name);
+ virtual Status set(const BSONElement& newValueElement);
+ virtual Status setFromString(const std::string& str);
+};
- AuthzVersionParameter::AuthzVersionParameter(ServerParameterSet* sps, const std::string& name) :
- ServerParameter(sps, name, false, false) {}
+MONGO_INITIALIZER_GENERAL(AuthzSchemaParameter,
+ MONGO_NO_PREREQUISITES,
+ ("BeginStartupOptionParsing"))(InitializerContext*) {
+ new AuthzVersionParameter(ServerParameterSet::getGlobal(), authSchemaVersionServerParameter);
+ return Status::OK();
+}
- void AuthzVersionParameter::append(
- OperationContext* txn, BSONObjBuilder& b, const std::string& name) {
- int authzVersion;
- uassertStatusOK(
- getGlobalAuthorizationManager()->getAuthorizationVersion(txn, &authzVersion));
- b.append(name, authzVersion);
- }
+AuthzVersionParameter::AuthzVersionParameter(ServerParameterSet* sps, const std::string& name)
+ : ServerParameter(sps, name, false, false) {}
- Status AuthzVersionParameter::set(const BSONElement& newValueElement) {
- return Status(ErrorCodes::InternalError, "set called on unsettable server parameter");
- }
+void AuthzVersionParameter::append(OperationContext* txn,
+ BSONObjBuilder& b,
+ const std::string& name) {
+ int authzVersion;
+ uassertStatusOK(getGlobalAuthorizationManager()->getAuthorizationVersion(txn, &authzVersion));
+ b.append(name, authzVersion);
+}
- Status AuthzVersionParameter::setFromString(const std::string& newValueString) {
- return Status(ErrorCodes::InternalError, "set called on unsettable server parameter");
- }
+Status AuthzVersionParameter::set(const BSONElement& newValueElement) {
+ return Status(ErrorCodes::InternalError, "set called on unsettable server parameter");
+}
+
+Status AuthzVersionParameter::setFromString(const std::string& newValueString) {
+ return Status(ErrorCodes::InternalError, "set called on unsettable server parameter");
+}
} // namespace
- const std::string authSchemaVersionServerParameter = "authSchemaVersion";
+const std::string authSchemaVersionServerParameter = "authSchemaVersion";
- AuthorizationManager* getGlobalAuthorizationManager() {
- AuthorizationManager* globalAuthManager = AuthorizationManager::get(
- getGlobalServiceContext());
- fassert(16842, globalAuthManager != nullptr);
- return globalAuthManager;
- }
+AuthorizationManager* getGlobalAuthorizationManager() {
+ AuthorizationManager* globalAuthManager = AuthorizationManager::get(getGlobalServiceContext());
+ fassert(16842, globalAuthManager != nullptr);
+ return globalAuthManager;
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_global.h b/src/mongo/db/auth/authorization_manager_global.h
index b0ef39f0069..3e6e936e520 100644
--- a/src/mongo/db/auth/authorization_manager_global.h
+++ b/src/mongo/db/auth/authorization_manager_global.h
@@ -32,12 +32,12 @@
namespace mongo {
- /**
- * Name of the server parameter used to report the auth schema version (via getParameter).
- */
- extern const std::string authSchemaVersionServerParameter;
+/**
+ * Name of the server parameter used to report the auth schema version (via getParameter).
+ */
+extern const std::string authSchemaVersionServerParameter;
- // Gets the singleton AuthorizationManager object for this server process.
- AuthorizationManager* getGlobalAuthorizationManager();
+// Gets the singleton AuthorizationManager object for this server process.
+AuthorizationManager* getGlobalAuthorizationManager();
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_manager_mock_init.cpp b/src/mongo/db/auth/authorization_manager_mock_init.cpp
index eadf9711604..890ee2c0a2b 100644
--- a/src/mongo/db/auth/authorization_manager_mock_init.cpp
+++ b/src/mongo/db/auth/authorization_manager_mock_init.cpp
@@ -41,18 +41,18 @@
namespace mongo {
namespace {
- std::unique_ptr<AuthzManagerExternalState> createAuthzManagerExternalStateMock() {
- return stdx::make_unique<AuthzManagerExternalStateMock>();
- }
+std::unique_ptr<AuthzManagerExternalState> createAuthzManagerExternalStateMock() {
+ return stdx::make_unique<AuthzManagerExternalStateMock>();
+}
- MONGO_INITIALIZER(CreateAuthorizationExternalStateFactory) (InitializerContext* context) {
- AuthzManagerExternalState::create = &createAuthzManagerExternalStateMock;
- return Status::OK();
- }
+MONGO_INITIALIZER(CreateAuthorizationExternalStateFactory)(InitializerContext* context) {
+ AuthzManagerExternalState::create = &createAuthzManagerExternalStateMock;
+ return Status::OK();
+}
- MONGO_INITIALIZER(SetGlobalEnvironment)(InitializerContext* context) {
- setGlobalServiceContext(stdx::make_unique<ServiceContextNoop>());
- return Status::OK();
- }
+MONGO_INITIALIZER(SetGlobalEnvironment)(InitializerContext* context) {
+ setGlobalServiceContext(stdx::make_unique<ServiceContextNoop>());
+ return Status::OK();
+}
}
}
diff --git a/src/mongo/db/auth/authorization_manager_test.cpp b/src/mongo/db/auth/authorization_manager_test.cpp
index 65a2f84243c..9114e8268c0 100644
--- a/src/mongo/db/auth/authorization_manager_test.cpp
+++ b/src/mongo/db/auth/authorization_manager_test.cpp
@@ -49,172 +49,185 @@
namespace mongo {
namespace {
- using std::vector;
-
- TEST(RoleParsingTest, BuildRoleBSON) {
- RoleGraph graph;
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
- ActionSet actions;
- actions.addAction(ActionType::find);
- actions.addAction(ActionType::insert);
-
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleC));
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.addRoleToRole(roleB, roleC));
-
- ASSERT_OK(graph.addPrivilegeToRole(
- roleA, Privilege(ResourcePattern::forAnyNormalResource(), actions)));
- ASSERT_OK(graph.addPrivilegeToRole(
- roleB, Privilege(ResourcePattern::forExactNamespace(NamespaceString("dbB.foo")),
- actions)));
- ASSERT_OK(graph.addPrivilegeToRole(
- roleC, Privilege(ResourcePattern::forClusterResource(), actions)));
- ASSERT_OK(graph.recomputePrivilegeData());
-
-
- // Role A
- mutablebson::Document doc;
- ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleA, doc.root()));
- BSONObj roleDoc = doc.getObject();
-
- ASSERT_EQUALS("dbA.roleA", roleDoc["_id"].String());
- ASSERT_EQUALS("roleA", roleDoc["role"].String());
- ASSERT_EQUALS("dbA", roleDoc["db"].String());
-
- vector<BSONElement> privs = roleDoc["privileges"].Array();
- ASSERT_EQUALS(1U, privs.size());
- ASSERT_EQUALS("", privs[0].Obj()["resource"].Obj()["db"].String());
- ASSERT_EQUALS("", privs[0].Obj()["resource"].Obj()["collection"].String());
- ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].eoo());
- vector<BSONElement> actionElements = privs[0].Obj()["actions"].Array();
- ASSERT_EQUALS(2U, actionElements.size());
- ASSERT_EQUALS("find", actionElements[0].String());
- ASSERT_EQUALS("insert", actionElements[1].String());
-
- vector<BSONElement> roles = roleDoc["roles"].Array();
- ASSERT_EQUALS(2U, roles.size());
- ASSERT_EQUALS("roleC", roles[0].Obj()["role"].String());
- ASSERT_EQUALS("dbC", roles[0].Obj()["db"].String());
- ASSERT_EQUALS("roleB", roles[1].Obj()["role"].String());
- ASSERT_EQUALS("dbB", roles[1].Obj()["db"].String());
-
- // Role B
- doc.reset();
- ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleB, doc.root()));
- roleDoc = doc.getObject();
-
- ASSERT_EQUALS("dbB.roleB", roleDoc["_id"].String());
- ASSERT_EQUALS("roleB", roleDoc["role"].String());
- ASSERT_EQUALS("dbB", roleDoc["db"].String());
-
- privs = roleDoc["privileges"].Array();
- ASSERT_EQUALS(1U, privs.size());
- ASSERT_EQUALS("dbB", privs[0].Obj()["resource"].Obj()["db"].String());
- ASSERT_EQUALS("foo", privs[0].Obj()["resource"].Obj()["collection"].String());
- ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].eoo());
- actionElements = privs[0].Obj()["actions"].Array();
- ASSERT_EQUALS(2U, actionElements.size());
- ASSERT_EQUALS("find", actionElements[0].String());
- ASSERT_EQUALS("insert", actionElements[1].String());
-
- roles = roleDoc["roles"].Array();
- ASSERT_EQUALS(1U, roles.size());
- ASSERT_EQUALS("roleC", roles[0].Obj()["role"].String());
- ASSERT_EQUALS("dbC", roles[0].Obj()["db"].String());
-
- // Role C
- doc.reset();
- ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleC, doc.root()));
- roleDoc = doc.getObject();
-
- ASSERT_EQUALS("dbC.roleC", roleDoc["_id"].String());
- ASSERT_EQUALS("roleC", roleDoc["role"].String());
- ASSERT_EQUALS("dbC", roleDoc["db"].String());
-
- privs = roleDoc["privileges"].Array();
- ASSERT_EQUALS(1U, privs.size());
- ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].Bool());
- ASSERT(privs[0].Obj()["resource"].Obj()["db"].eoo());
- ASSERT(privs[0].Obj()["resource"].Obj()["collection"].eoo());
- actionElements = privs[0].Obj()["actions"].Array();
- ASSERT_EQUALS(2U, actionElements.size());
- ASSERT_EQUALS("find", actionElements[0].String());
- ASSERT_EQUALS("insert", actionElements[1].String());
-
- roles = roleDoc["roles"].Array();
- ASSERT_EQUALS(0U, roles.size());
+using std::vector;
+
+TEST(RoleParsingTest, BuildRoleBSON) {
+ RoleGraph graph;
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+ ActionSet actions;
+ actions.addAction(ActionType::find);
+ actions.addAction(ActionType::insert);
+
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleC));
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.addRoleToRole(roleB, roleC));
+
+ ASSERT_OK(graph.addPrivilegeToRole(
+ roleA, Privilege(ResourcePattern::forAnyNormalResource(), actions)));
+ ASSERT_OK(graph.addPrivilegeToRole(
+ roleB, Privilege(ResourcePattern::forExactNamespace(NamespaceString("dbB.foo")), actions)));
+ ASSERT_OK(
+ graph.addPrivilegeToRole(roleC, Privilege(ResourcePattern::forClusterResource(), actions)));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+
+ // Role A
+ mutablebson::Document doc;
+ ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleA, doc.root()));
+ BSONObj roleDoc = doc.getObject();
+
+ ASSERT_EQUALS("dbA.roleA", roleDoc["_id"].String());
+ ASSERT_EQUALS("roleA", roleDoc["role"].String());
+ ASSERT_EQUALS("dbA", roleDoc["db"].String());
+
+ vector<BSONElement> privs = roleDoc["privileges"].Array();
+ ASSERT_EQUALS(1U, privs.size());
+ ASSERT_EQUALS("", privs[0].Obj()["resource"].Obj()["db"].String());
+ ASSERT_EQUALS("", privs[0].Obj()["resource"].Obj()["collection"].String());
+ ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].eoo());
+ vector<BSONElement> actionElements = privs[0].Obj()["actions"].Array();
+ ASSERT_EQUALS(2U, actionElements.size());
+ ASSERT_EQUALS("find", actionElements[0].String());
+ ASSERT_EQUALS("insert", actionElements[1].String());
+
+ vector<BSONElement> roles = roleDoc["roles"].Array();
+ ASSERT_EQUALS(2U, roles.size());
+ ASSERT_EQUALS("roleC", roles[0].Obj()["role"].String());
+ ASSERT_EQUALS("dbC", roles[0].Obj()["db"].String());
+ ASSERT_EQUALS("roleB", roles[1].Obj()["role"].String());
+ ASSERT_EQUALS("dbB", roles[1].Obj()["db"].String());
+
+ // Role B
+ doc.reset();
+ ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleB, doc.root()));
+ roleDoc = doc.getObject();
+
+ ASSERT_EQUALS("dbB.roleB", roleDoc["_id"].String());
+ ASSERT_EQUALS("roleB", roleDoc["role"].String());
+ ASSERT_EQUALS("dbB", roleDoc["db"].String());
+
+ privs = roleDoc["privileges"].Array();
+ ASSERT_EQUALS(1U, privs.size());
+ ASSERT_EQUALS("dbB", privs[0].Obj()["resource"].Obj()["db"].String());
+ ASSERT_EQUALS("foo", privs[0].Obj()["resource"].Obj()["collection"].String());
+ ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].eoo());
+ actionElements = privs[0].Obj()["actions"].Array();
+ ASSERT_EQUALS(2U, actionElements.size());
+ ASSERT_EQUALS("find", actionElements[0].String());
+ ASSERT_EQUALS("insert", actionElements[1].String());
+
+ roles = roleDoc["roles"].Array();
+ ASSERT_EQUALS(1U, roles.size());
+ ASSERT_EQUALS("roleC", roles[0].Obj()["role"].String());
+ ASSERT_EQUALS("dbC", roles[0].Obj()["db"].String());
+
+ // Role C
+ doc.reset();
+ ASSERT_OK(AuthorizationManager::getBSONForRole(&graph, roleC, doc.root()));
+ roleDoc = doc.getObject();
+
+ ASSERT_EQUALS("dbC.roleC", roleDoc["_id"].String());
+ ASSERT_EQUALS("roleC", roleDoc["role"].String());
+ ASSERT_EQUALS("dbC", roleDoc["db"].String());
+
+ privs = roleDoc["privileges"].Array();
+ ASSERT_EQUALS(1U, privs.size());
+ ASSERT(privs[0].Obj()["resource"].Obj()["cluster"].Bool());
+ ASSERT(privs[0].Obj()["resource"].Obj()["db"].eoo());
+ ASSERT(privs[0].Obj()["resource"].Obj()["collection"].eoo());
+ actionElements = privs[0].Obj()["actions"].Array();
+ ASSERT_EQUALS(2U, actionElements.size());
+ ASSERT_EQUALS("find", actionElements[0].String());
+ ASSERT_EQUALS("insert", actionElements[1].String());
+
+ roles = roleDoc["roles"].Array();
+ ASSERT_EQUALS(0U, roles.size());
+}
+
+class AuthorizationManagerTest : public ::mongo::unittest::Test {
+public:
+ virtual ~AuthorizationManagerTest() {
+ if (authzManager)
+ authzManager->invalidateUserCache();
}
- class AuthorizationManagerTest : public ::mongo::unittest::Test {
- public:
- virtual ~AuthorizationManagerTest() {
- if (authzManager)
- authzManager->invalidateUserCache();
- }
-
- void setUp() {
- auto localExternalState = stdx::make_unique<AuthzManagerExternalStateMock>();
- externalState = localExternalState.get();
- externalState->setAuthzVersion(AuthorizationManager::schemaVersion26Final);
- authzManager = stdx::make_unique<AuthorizationManager>(std::move(localExternalState));
- externalState->setAuthorizationManager(authzManager.get());
- authzManager->setAuthEnabled(true);
- }
-
- std::unique_ptr<AuthorizationManager> authzManager;
- AuthzManagerExternalStateMock* externalState;
- };
-
- TEST_F(AuthorizationManagerTest, testAcquireV2User) {
+ void setUp() {
+ auto localExternalState = stdx::make_unique<AuthzManagerExternalStateMock>();
+ externalState = localExternalState.get();
externalState->setAuthzVersion(AuthorizationManager::schemaVersion26Final);
-
- OperationContextNoop txn;
-
- ASSERT_OK(externalState->insertPrivilegeDocument(
- &txn,
- BSON("_id" << "admin.v2read" <<
- "user" << "v2read" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "password") <<
- "roles" << BSON_ARRAY(BSON("role" << "read" << "db" << "test"))),
- BSONObj()));
- ASSERT_OK(externalState->insertPrivilegeDocument(
- &txn,
- BSON("_id" << "admin.v2cluster" <<
- "user" << "v2cluster" <<
- "db" << "admin" <<
- "credentials" << BSON("MONGODB-CR" << "password") <<
- "roles" << BSON_ARRAY(BSON("role" << "clusterAdmin" << "db" << "admin"))),
- BSONObj()));
-
- User* v2read;
- ASSERT_OK(authzManager->acquireUser(&txn, UserName("v2read", "test"), &v2read));
- ASSERT_EQUALS(UserName("v2read", "test"), v2read->getName());
- ASSERT(v2read->isValid());
- ASSERT_EQUALS(1U, v2read->getRefCount());
- RoleNameIterator roles = v2read->getRoles();
- ASSERT_EQUALS(RoleName("read", "test"), roles.next());
- ASSERT_FALSE(roles.more());
- // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
- authzManager->releaseUser(v2read);
-
- User* v2cluster;
- ASSERT_OK(authzManager->acquireUser(&txn, UserName("v2cluster", "admin"), &v2cluster));
- ASSERT_EQUALS(UserName("v2cluster", "admin"), v2cluster->getName());
- ASSERT(v2cluster->isValid());
- ASSERT_EQUALS(1U, v2cluster->getRefCount());
- RoleNameIterator clusterRoles = v2cluster->getRoles();
- ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), clusterRoles.next());
- ASSERT_FALSE(clusterRoles.more());
- // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
- authzManager->releaseUser(v2cluster);
+ authzManager = stdx::make_unique<AuthorizationManager>(std::move(localExternalState));
+ externalState->setAuthorizationManager(authzManager.get());
+ authzManager->setAuthEnabled(true);
}
+ std::unique_ptr<AuthorizationManager> authzManager;
+ AuthzManagerExternalStateMock* externalState;
+};
+
+TEST_F(AuthorizationManagerTest, testAcquireV2User) {
+ externalState->setAuthzVersion(AuthorizationManager::schemaVersion26Final);
+
+ OperationContextNoop txn;
+
+ ASSERT_OK(
+ externalState->insertPrivilegeDocument(&txn,
+ BSON("_id"
+ << "admin.v2read"
+ << "user"
+ << "v2read"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "password")
+ << "roles" << BSON_ARRAY(BSON("role"
+ << "read"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(
+ externalState->insertPrivilegeDocument(&txn,
+ BSON("_id"
+ << "admin.v2cluster"
+ << "user"
+ << "v2cluster"
+ << "db"
+ << "admin"
+ << "credentials" << BSON("MONGODB-CR"
+ << "password")
+ << "roles" << BSON_ARRAY(BSON("role"
+ << "clusterAdmin"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+
+ User* v2read;
+ ASSERT_OK(authzManager->acquireUser(&txn, UserName("v2read", "test"), &v2read));
+ ASSERT_EQUALS(UserName("v2read", "test"), v2read->getName());
+ ASSERT(v2read->isValid());
+ ASSERT_EQUALS(1U, v2read->getRefCount());
+ RoleNameIterator roles = v2read->getRoles();
+ ASSERT_EQUALS(RoleName("read", "test"), roles.next());
+ ASSERT_FALSE(roles.more());
+ // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
+ authzManager->releaseUser(v2read);
+
+ User* v2cluster;
+ ASSERT_OK(authzManager->acquireUser(&txn, UserName("v2cluster", "admin"), &v2cluster));
+ ASSERT_EQUALS(UserName("v2cluster", "admin"), v2cluster->getName());
+ ASSERT(v2cluster->isValid());
+ ASSERT_EQUALS(1U, v2cluster->getRefCount());
+ RoleNameIterator clusterRoles = v2cluster->getRoles();
+ ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), clusterRoles.next());
+ ASSERT_FALSE(clusterRoles.more());
+ // Make sure user's refCount is 0 at the end of the test to avoid an assertion failure
+ authzManager->releaseUser(v2cluster);
+}
+
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session.cpp b/src/mongo/db/auth/authorization_session.cpp
index bcc66f45d17..22bd1b1f191 100644
--- a/src/mongo/db/auth/authorization_session.cpp
+++ b/src/mongo/db/auth/authorization_session.cpp
@@ -51,461 +51,437 @@
namespace mongo {
- using std::vector;
+using std::vector;
namespace {
- const std::string ADMIN_DBNAME = "admin";
+const std::string ADMIN_DBNAME = "admin";
} // namespace
- AuthorizationSession::AuthorizationSession(
- std::unique_ptr<AuthzSessionExternalState> externalState)
- : _externalState(std::move(externalState)),
- _impersonationFlag(false) {}
-
- AuthorizationSession::~AuthorizationSession() {
- for (UserSet::iterator it = _authenticatedUsers.begin();
- it != _authenticatedUsers.end(); ++it) {
- getAuthorizationManager().releaseUser(*it);
+AuthorizationSession::AuthorizationSession(std::unique_ptr<AuthzSessionExternalState> externalState)
+ : _externalState(std::move(externalState)), _impersonationFlag(false) {}
+
+AuthorizationSession::~AuthorizationSession() {
+ for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
+ ++it) {
+ getAuthorizationManager().releaseUser(*it);
+ }
+}
+
+AuthorizationManager& AuthorizationSession::getAuthorizationManager() {
+ return _externalState->getAuthorizationManager();
+}
+
+void AuthorizationSession::startRequest(OperationContext* txn) {
+ _externalState->startRequest(txn);
+ _refreshUserInfoAsNeeded(txn);
+}
+
+Status AuthorizationSession::addAndAuthorizeUser(OperationContext* txn, const UserName& userName) {
+ User* user;
+ Status status = getAuthorizationManager().acquireUser(txn, userName, &user);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ // Calling add() on the UserSet may return a user that was replaced because it was from the
+ // same database.
+ User* replacedUser = _authenticatedUsers.add(user);
+ if (replacedUser) {
+ getAuthorizationManager().releaseUser(replacedUser);
+ }
+
+ // If there are any users and roles in the impersonation data, clear it out.
+ clearImpersonatedUserData();
+
+ _buildAuthenticatedRolesVector();
+ return Status::OK();
+}
+
+User* AuthorizationSession::lookupUser(const UserName& name) {
+ return _authenticatedUsers.lookup(name);
+}
+
+void AuthorizationSession::logoutDatabase(const std::string& dbname) {
+ User* removedUser = _authenticatedUsers.removeByDBName(dbname);
+ if (removedUser) {
+ getAuthorizationManager().releaseUser(removedUser);
+ }
+ clearImpersonatedUserData();
+ _buildAuthenticatedRolesVector();
+}
+
+UserNameIterator AuthorizationSession::getAuthenticatedUserNames() {
+ return _authenticatedUsers.getNames();
+}
+
+RoleNameIterator AuthorizationSession::getAuthenticatedRoleNames() {
+ return makeRoleNameIterator(_authenticatedRoleNames.begin(), _authenticatedRoleNames.end());
+}
+
+std::string AuthorizationSession::getAuthenticatedUserNamesToken() {
+ std::string ret;
+ for (UserNameIterator nameIter = getAuthenticatedUserNames(); nameIter.more();
+ nameIter.next()) {
+ ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them.
+ ret += nameIter->getFullName();
+ }
+
+ return ret;
+}
+
+void AuthorizationSession::grantInternalAuthorization() {
+ _authenticatedUsers.add(internalSecurity.user);
+ _buildAuthenticatedRolesVector();
+}
+
+PrivilegeVector AuthorizationSession::getDefaultPrivileges() {
+ PrivilegeVector defaultPrivileges;
+
+ // If localhost exception is active (and no users exist),
+ // return a vector of the minimum privileges required to bootstrap
+ // a system and add the first user.
+ if (_externalState->shouldAllowLocalhost()) {
+ ResourcePattern adminDBResource = ResourcePattern::forDatabaseName(ADMIN_DBNAME);
+ ActionSet setupAdminUserActionSet;
+ setupAdminUserActionSet.addAction(ActionType::createUser);
+ setupAdminUserActionSet.addAction(ActionType::grantRole);
+ Privilege setupAdminUserPrivilege = Privilege(adminDBResource, setupAdminUserActionSet);
+
+ ResourcePattern externalDBResource = ResourcePattern::forDatabaseName("$external");
+ Privilege setupExternalUserPrivilege =
+ Privilege(externalDBResource, ActionType::createUser);
+
+ ActionSet setupServerConfigActionSet;
+
+ // If this server is an arbiter, add specific privileges meant to circumvent
+ // the behavior of an arbiter in an authenticated replset. See SERVER-5479.
+ if (_externalState->serverIsArbiter()) {
+ setupServerConfigActionSet.addAction(ActionType::getCmdLineOpts);
+ setupServerConfigActionSet.addAction(ActionType::getParameter);
+ setupServerConfigActionSet.addAction(ActionType::serverStatus);
+ setupServerConfigActionSet.addAction(ActionType::shutdown);
}
- }
-
- AuthorizationManager& AuthorizationSession::getAuthorizationManager() {
- return _externalState->getAuthorizationManager();
- }
- void AuthorizationSession::startRequest(OperationContext* txn) {
- _externalState->startRequest(txn);
- _refreshUserInfoAsNeeded(txn);
- }
-
- Status AuthorizationSession::addAndAuthorizeUser(
- OperationContext* txn, const UserName& userName) {
- User* user;
- Status status = getAuthorizationManager().acquireUser(txn, userName, &user);
- if (!status.isOK()) {
- return status;
- }
+ setupServerConfigActionSet.addAction(ActionType::addShard);
+ setupServerConfigActionSet.addAction(ActionType::replSetConfigure);
+ setupServerConfigActionSet.addAction(ActionType::replSetGetStatus);
+ Privilege setupServerConfigPrivilege =
+ Privilege(ResourcePattern::forClusterResource(), setupServerConfigActionSet);
- // Calling add() on the UserSet may return a user that was replaced because it was from the
- // same database.
- User* replacedUser = _authenticatedUsers.add(user);
- if (replacedUser) {
- getAuthorizationManager().releaseUser(replacedUser);
- }
-
- // If there are any users and roles in the impersonation data, clear it out.
- clearImpersonatedUserData();
-
- _buildAuthenticatedRolesVector();
- return Status::OK();
- }
-
- User* AuthorizationSession::lookupUser(const UserName& name) {
- return _authenticatedUsers.lookup(name);
- }
-
- void AuthorizationSession::logoutDatabase(const std::string& dbname) {
- User* removedUser = _authenticatedUsers.removeByDBName(dbname);
- if (removedUser) {
- getAuthorizationManager().releaseUser(removedUser);
- }
- clearImpersonatedUserData();
- _buildAuthenticatedRolesVector();
- }
-
- UserNameIterator AuthorizationSession::getAuthenticatedUserNames() {
- return _authenticatedUsers.getNames();
- }
-
- RoleNameIterator AuthorizationSession::getAuthenticatedRoleNames() {
- return makeRoleNameIterator(_authenticatedRoleNames.begin(),
- _authenticatedRoleNames.end());
+ Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupAdminUserPrivilege);
+ Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupExternalUserPrivilege);
+ Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupServerConfigPrivilege);
+ return defaultPrivileges;
}
- std::string AuthorizationSession::getAuthenticatedUserNamesToken() {
- std::string ret;
- for (UserNameIterator nameIter = getAuthenticatedUserNames();
- nameIter.more();
- nameIter.next()) {
- ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them.
- ret += nameIter->getFullName();
- }
+ return defaultPrivileges;
+}
- return ret;
+Status AuthorizationSession::checkAuthForQuery(const NamespaceString& ns, const BSONObj& query) {
+ if (MONGO_unlikely(ns.isCommand())) {
+ return Status(ErrorCodes::InternalError,
+ str::stream() << "Checking query auth on command namespace " << ns.ns());
}
-
- void AuthorizationSession::grantInternalAuthorization() {
- _authenticatedUsers.add(internalSecurity.user);
- _buildAuthenticatedRolesVector();
+ if (!isAuthorizedForActionsOnNamespace(ns, ActionType::find)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for query on " << ns.ns());
}
+ return Status::OK();
+}
- PrivilegeVector AuthorizationSession::getDefaultPrivileges() {
- PrivilegeVector defaultPrivileges;
-
- // If localhost exception is active (and no users exist),
- // return a vector of the minimum privileges required to bootstrap
- // a system and add the first user.
- if (_externalState->shouldAllowLocalhost()) {
- ResourcePattern adminDBResource = ResourcePattern::forDatabaseName(ADMIN_DBNAME);
- ActionSet setupAdminUserActionSet;
- setupAdminUserActionSet.addAction(ActionType::createUser);
- setupAdminUserActionSet.addAction(ActionType::grantRole);
- Privilege setupAdminUserPrivilege =
- Privilege(adminDBResource, setupAdminUserActionSet);
-
- ResourcePattern externalDBResource = ResourcePattern::forDatabaseName("$external");
- Privilege setupExternalUserPrivilege =
- Privilege(externalDBResource, ActionType::createUser);
-
- ActionSet setupServerConfigActionSet;
-
- // If this server is an arbiter, add specific privileges meant to circumvent
- // the behavior of an arbiter in an authenticated replset. See SERVER-5479.
- if (_externalState->serverIsArbiter()) {
- setupServerConfigActionSet.addAction(ActionType::getCmdLineOpts);
- setupServerConfigActionSet.addAction(ActionType::getParameter);
- setupServerConfigActionSet.addAction(ActionType::serverStatus);
- setupServerConfigActionSet.addAction(ActionType::shutdown);
- }
-
- setupServerConfigActionSet.addAction(ActionType::addShard);
- setupServerConfigActionSet.addAction(ActionType::replSetConfigure);
- setupServerConfigActionSet.addAction(ActionType::replSetGetStatus);
- Privilege setupServerConfigPrivilege =
- Privilege(ResourcePattern::forClusterResource(), setupServerConfigActionSet);
-
- Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges, setupAdminUserPrivilege);
- Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges,
- setupExternalUserPrivilege);
- Privilege::addPrivilegeToPrivilegeVector(&defaultPrivileges,
- setupServerConfigPrivilege);
- return defaultPrivileges;
+Status AuthorizationSession::checkAuthForGetMore(const NamespaceString& ns, long long cursorID) {
+ // "ns" can be in one of three formats: "listCollections" format, "listIndexes" format, and
+ // normal format.
+ if (ns.isListCollectionsGetMore()) {
+ // "ns" is of the form "<db>.$cmd.listCollections". Check if we can perform the
+ // listCollections action on the database resource for "<db>".
+ if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()),
+ ActionType::listCollections)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for listCollections getMore on "
+ << ns.ns());
}
-
- return defaultPrivileges;
- }
-
- Status AuthorizationSession::checkAuthForQuery(const NamespaceString& ns,
- const BSONObj& query) {
- if (MONGO_unlikely(ns.isCommand())) {
- return Status(ErrorCodes::InternalError, str::stream() <<
- "Checking query auth on command namespace " << ns.ns());
+ } else if (ns.isListIndexesGetMore()) {
+ // "ns" is of the form "<db>.$cmd.listIndexes.<coll>". Check if we can perform the
+ // listIndexes action on the "<db>.<coll>" namespace.
+ NamespaceString targetNS = ns.getTargetNSForListIndexesGetMore();
+ if (!isAuthorizedForActionsOnNamespace(targetNS, ActionType::listIndexes)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for listIndexes getMore on " << ns.ns());
}
+ } else {
+ // "ns" is a regular namespace string. Check if we can perform the find action on it.
if (!isAuthorizedForActionsOnNamespace(ns, ActionType::find)) {
return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for query on " << ns.ns());
+ str::stream() << "not authorized for getMore on " << ns.ns());
}
- return Status::OK();
}
+ return Status::OK();
+}
- Status AuthorizationSession::checkAuthForGetMore(const NamespaceString& ns,
- long long cursorID) {
- // "ns" can be in one of three formats: "listCollections" format, "listIndexes" format, and
- // normal format.
- if (ns.isListCollectionsGetMore()) {
- // "ns" is of the form "<db>.$cmd.listCollections". Check if we can perform the
- // listCollections action on the database resource for "<db>".
- if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()),
- ActionType::listCollections)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for listCollections getMore on "
- << ns.ns());
- }
+Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns,
+ const BSONObj& document) {
+ if (ns.coll() == StringData("system.indexes", StringData::LiteralTag())) {
+ BSONElement nsElement = document["ns"];
+ if (nsElement.type() != String) {
+ return Status(ErrorCodes::Unauthorized,
+ "Cannot authorize inserting into "
+ "system.indexes documents without a string-typed \"ns\" field.");
}
- else if (ns.isListIndexesGetMore()) {
- // "ns" is of the form "<db>.$cmd.listIndexes.<coll>". Check if we can perform the
- // listIndexes action on the "<db>.<coll>" namespace.
- NamespaceString targetNS = ns.getTargetNSForListIndexesGetMore();
- if (!isAuthorizedForActionsOnNamespace(targetNS, ActionType::listIndexes)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for listIndexes getMore on "
- << ns.ns());
- }
+ NamespaceString indexNS(nsElement.str());
+ if (!isAuthorizedForActionsOnNamespace(indexNS, ActionType::createIndex)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized to create index on " << indexNS.ns());
}
- else {
- // "ns" is a regular namespace string. Check if we can perform the find action on it.
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::find)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for getMore on " << ns.ns());
- }
+ } else {
+ if (!isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for insert on " << ns.ns());
}
- return Status::OK();
}
- Status AuthorizationSession::checkAuthForInsert(const NamespaceString& ns,
- const BSONObj& document) {
- if (ns.coll() == StringData("system.indexes", StringData::LiteralTag())) {
- BSONElement nsElement = document["ns"];
- if (nsElement.type() != String) {
- return Status(ErrorCodes::Unauthorized, "Cannot authorize inserting into "
- "system.indexes documents without a string-typed \"ns\" field.");
- }
- NamespaceString indexNS(nsElement.str());
- if (!isAuthorizedForActionsOnNamespace(indexNS, ActionType::createIndex)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to create index on " <<
- indexNS.ns());
- }
- } else {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::insert)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for insert on " << ns.ns());
- }
- }
-
- return Status::OK();
- }
+ return Status::OK();
+}
- Status AuthorizationSession::checkAuthForUpdate(const NamespaceString& ns,
- const BSONObj& query,
- const BSONObj& update,
- bool upsert) {
- if (!upsert) {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::update)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for update on " << ns.ns());
- }
+Status AuthorizationSession::checkAuthForUpdate(const NamespaceString& ns,
+ const BSONObj& query,
+ const BSONObj& update,
+ bool upsert) {
+ if (!upsert) {
+ if (!isAuthorizedForActionsOnNamespace(ns, ActionType::update)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for update on " << ns.ns());
}
- else {
- ActionSet required;
- required.addAction(ActionType::update);
- required.addAction(ActionType::insert);
- if (!isAuthorizedForActionsOnNamespace(ns, required)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized for upsert on " << ns.ns());
- }
+ } else {
+ ActionSet required;
+ required.addAction(ActionType::update);
+ required.addAction(ActionType::insert);
+ if (!isAuthorizedForActionsOnNamespace(ns, required)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized for upsert on " << ns.ns());
}
- return Status::OK();
}
+ return Status::OK();
+}
- Status AuthorizationSession::checkAuthForDelete(const NamespaceString& ns,
- const BSONObj& query) {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::remove)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to remove from " << ns.ns());
- }
- return Status::OK();
+Status AuthorizationSession::checkAuthForDelete(const NamespaceString& ns, const BSONObj& query) {
+ if (!isAuthorizedForActionsOnNamespace(ns, ActionType::remove)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized to remove from " << ns.ns());
}
+ return Status::OK();
+}
- Status AuthorizationSession::checkAuthForKillCursors(const NamespaceString& ns,
- long long cursorID) {
- // See implementation comments in checkAuthForGetMore(). This method looks very similar.
- if (ns.isListCollectionsGetMore()) {
- if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()),
- ActionType::killCursors)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to kill listCollections cursor on "
- << ns.ns());
- }
+Status AuthorizationSession::checkAuthForKillCursors(const NamespaceString& ns,
+ long long cursorID) {
+ // See implementation comments in checkAuthForGetMore(). This method looks very similar.
+ if (ns.isListCollectionsGetMore()) {
+ if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(ns.db()),
+ ActionType::killCursors)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized to kill listCollections cursor on "
+ << ns.ns());
}
- else if (ns.isListIndexesGetMore()) {
- NamespaceString targetNS = ns.getTargetNSForListIndexesGetMore();
- if (!isAuthorizedForActionsOnNamespace(targetNS, ActionType::killCursors)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to kill listIndexes cursor on "
- << ns.ns());
- }
+ } else if (ns.isListIndexesGetMore()) {
+ NamespaceString targetNS = ns.getTargetNSForListIndexesGetMore();
+ if (!isAuthorizedForActionsOnNamespace(targetNS, ActionType::killCursors)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized to kill listIndexes cursor on "
+ << ns.ns());
}
- else {
- if (!isAuthorizedForActionsOnNamespace(ns, ActionType::killCursors)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "not authorized to kill cursor on " << ns.ns());
- }
+ } else {
+ if (!isAuthorizedForActionsOnNamespace(ns, ActionType::killCursors)) {
+ return Status(ErrorCodes::Unauthorized,
+ str::stream() << "not authorized to kill cursor on " << ns.ns());
}
- return Status::OK();
}
+ return Status::OK();
+}
- Status AuthorizationSession::checkAuthorizedToGrantPrivilege(const Privilege& privilege) {
- const ResourcePattern& resource = privilege.getResourcePattern();
- if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
- if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(resource.databaseToMatch()),
- ActionType::grantRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to grant privileges on the "
- << resource.databaseToMatch() << "database");
- }
- } else if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName("admin"),
- ActionType::grantRole)) {
+Status AuthorizationSession::checkAuthorizedToGrantPrivilege(const Privilege& privilege) {
+ const ResourcePattern& resource = privilege.getResourcePattern();
+ if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
+ if (!isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(resource.databaseToMatch()),
+ ActionType::grantRole)) {
return Status(ErrorCodes::Unauthorized,
- "To grant privileges affecting multiple databases or the cluster,"
- " must be authorized to grant roles from the admin database");
+ str::stream() << "Not authorized to grant privileges on the "
+ << resource.databaseToMatch() << "database");
}
- return Status::OK();
- }
-
-
- Status AuthorizationSession::checkAuthorizedToRevokePrivilege(const Privilege& privilege) {
- const ResourcePattern& resource = privilege.getResourcePattern();
- if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
- if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(resource.databaseToMatch()),
- ActionType::revokeRole)) {
- return Status(ErrorCodes::Unauthorized,
- str::stream() << "Not authorized to revoke privileges on the "
- << resource.databaseToMatch() << "database");
- }
- } else if (!isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName("admin"),
- ActionType::revokeRole)) {
+ } else if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName("admin"),
+ ActionType::grantRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ "To grant privileges affecting multiple databases or the cluster,"
+ " must be authorized to grant roles from the admin database");
+ }
+ return Status::OK();
+}
+
+
+Status AuthorizationSession::checkAuthorizedToRevokePrivilege(const Privilege& privilege) {
+ const ResourcePattern& resource = privilege.getResourcePattern();
+ if (resource.isDatabasePattern() || resource.isExactNamespacePattern()) {
+ if (!isAuthorizedForActionsOnResource(
+ ResourcePattern::forDatabaseName(resource.databaseToMatch()),
+ ActionType::revokeRole)) {
return Status(ErrorCodes::Unauthorized,
- "To revoke privileges affecting multiple databases or the cluster,"
- " must be authorized to revoke roles from the admin database");
+ str::stream() << "Not authorized to revoke privileges on the "
+ << resource.databaseToMatch() << "database");
}
- return Status::OK();
- }
-
- bool AuthorizationSession::isAuthorizedToGrantRole(const RoleName& role) {
- return isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(role.getDB()),
- ActionType::grantRole);
- }
-
- bool AuthorizationSession::isAuthorizedToRevokeRole(const RoleName& role) {
- return isAuthorizedForActionsOnResource(
- ResourcePattern::forDatabaseName(role.getDB()),
- ActionType::revokeRole);
- }
-
- bool AuthorizationSession::isAuthorizedForPrivilege(const Privilege& privilege) {
- if (_externalState->shouldIgnoreAuthChecks())
- return true;
-
- return _isAuthorizedForPrivilege(privilege);
- }
-
- bool AuthorizationSession::isAuthorizedForPrivileges(const vector<Privilege>& privileges) {
- if (_externalState->shouldIgnoreAuthChecks())
- return true;
+ } else if (!isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName("admin"),
+ ActionType::revokeRole)) {
+ return Status(ErrorCodes::Unauthorized,
+ "To revoke privileges affecting multiple databases or the cluster,"
+ " must be authorized to revoke roles from the admin database");
+ }
+ return Status::OK();
+}
+
+bool AuthorizationSession::isAuthorizedToGrantRole(const RoleName& role) {
+ return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()),
+ ActionType::grantRole);
+}
+
+bool AuthorizationSession::isAuthorizedToRevokeRole(const RoleName& role) {
+ return isAuthorizedForActionsOnResource(ResourcePattern::forDatabaseName(role.getDB()),
+ ActionType::revokeRole);
+}
+
+bool AuthorizationSession::isAuthorizedForPrivilege(const Privilege& privilege) {
+ if (_externalState->shouldIgnoreAuthChecks())
+ return true;
- for (size_t i = 0; i < privileges.size(); ++i) {
- if (!_isAuthorizedForPrivilege(privileges[i]))
- return false;
- }
+ return _isAuthorizedForPrivilege(privilege);
+}
+bool AuthorizationSession::isAuthorizedForPrivileges(const vector<Privilege>& privileges) {
+ if (_externalState->shouldIgnoreAuthChecks())
return true;
- }
- bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource,
- ActionType action) {
- return isAuthorizedForPrivilege(Privilege(resource, action));
+ for (size_t i = 0; i < privileges.size(); ++i) {
+ if (!_isAuthorizedForPrivilege(privileges[i]))
+ return false;
}
- bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource,
- const ActionSet& actions) {
- return isAuthorizedForPrivilege(Privilege(resource, actions));
- }
+ return true;
+}
- bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
- ActionType action) {
- return isAuthorizedForPrivilege(
- Privilege(ResourcePattern::forExactNamespace(ns), action));
- }
+bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource,
+ ActionType action) {
+ return isAuthorizedForPrivilege(Privilege(resource, action));
+}
- bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
- const ActionSet& actions) {
- return isAuthorizedForPrivilege(
- Privilege(ResourcePattern::forExactNamespace(ns), actions));
- }
+bool AuthorizationSession::isAuthorizedForActionsOnResource(const ResourcePattern& resource,
+ const ActionSet& actions) {
+ return isAuthorizedForPrivilege(Privilege(resource, actions));
+}
- static const int resourceSearchListCapacity = 5;
- /**
- * Builds from "target" an exhaustive list of all ResourcePatterns that match "target".
- *
- * Stores the resulting list into resourceSearchList, and returns the length.
- *
- * The seach lists are as follows, depending on the type of "target":
- *
- * target is ResourcePattern::forAnyResource():
- * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forAnyResource() }
- * target is the ResourcePattern::forClusterResource():
- * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forClusterResource() }
- * target is a database, db:
- * searchList = { ResourcePattern::forAnyResource(),
- * ResourcePattern::forAnyNormalResource(),
- * db }
- * target is a non-system collection, db.coll:
- * searchList = { ResourcePattern::forAnyResource(),
- * ResourcePattern::forAnyNormalResource(),
- * db,
- * coll,
- * db.coll }
- * target is a system collection, db.system.coll:
- * searchList = { ResourcePattern::forAnyResource(),
- * system.coll,
- * db.system.coll }
- */
- static int buildResourceSearchList(
- const ResourcePattern& target,
- ResourcePattern resourceSearchList[resourceSearchListCapacity]) {
-
- int size = 0;
- resourceSearchList[size++] = ResourcePattern::forAnyResource();
- if (target.isExactNamespacePattern()) {
- if (!target.ns().isSystem()) {
- resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
- resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db());
- }
- resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll());
- }
- else if (target.isDatabasePattern()) {
- resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
- }
- resourceSearchList[size++] = target;
- dassert(size <= resourceSearchListCapacity);
- return size;
- }
+bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
+ ActionType action) {
+ return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), action));
+}
- bool AuthorizationSession::isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType) {
- User* user = lookupUser(userName);
- if (!user) {
- return false;
- }
- ResourcePattern resourceSearchList[resourceSearchListCapacity];
- const int resourceSearchListLength =
- buildResourceSearchList(ResourcePattern::forDatabaseName(userName.getDB()),
- resourceSearchList);
+bool AuthorizationSession::isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
+ const ActionSet& actions) {
+ return isAuthorizedForPrivilege(Privilege(ResourcePattern::forExactNamespace(ns), actions));
+}
- ActionSet actions;
- for (int i = 0; i < resourceSearchListLength; ++i) {
- actions.addAllActionsFromSet(user->getActionsForResource(resourceSearchList[i]));
+static const int resourceSearchListCapacity = 5;
+/**
+ * Builds from "target" an exhaustive list of all ResourcePatterns that match "target".
+ *
+ * Stores the resulting list into resourceSearchList, and returns the length.
+ *
+ * The seach lists are as follows, depending on the type of "target":
+ *
+ * target is ResourcePattern::forAnyResource():
+ * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forAnyResource() }
+ * target is the ResourcePattern::forClusterResource():
+ * searchList = { ResourcePattern::forAnyResource(), ResourcePattern::forClusterResource() }
+ * target is a database, db:
+ * searchList = { ResourcePattern::forAnyResource(),
+ * ResourcePattern::forAnyNormalResource(),
+ * db }
+ * target is a non-system collection, db.coll:
+ * searchList = { ResourcePattern::forAnyResource(),
+ * ResourcePattern::forAnyNormalResource(),
+ * db,
+ * coll,
+ * db.coll }
+ * target is a system collection, db.system.coll:
+ * searchList = { ResourcePattern::forAnyResource(),
+ * system.coll,
+ * db.system.coll }
+ */
+static int buildResourceSearchList(const ResourcePattern& target,
+ ResourcePattern resourceSearchList[resourceSearchListCapacity]) {
+ int size = 0;
+ resourceSearchList[size++] = ResourcePattern::forAnyResource();
+ if (target.isExactNamespacePattern()) {
+ if (!target.ns().isSystem()) {
+ resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
+ resourceSearchList[size++] = ResourcePattern::forDatabaseName(target.ns().db());
}
- return actions.contains(actionType);
+ resourceSearchList[size++] = ResourcePattern::forCollectionName(target.ns().coll());
+ } else if (target.isDatabasePattern()) {
+ resourceSearchList[size++] = ResourcePattern::forAnyNormalResource();
+ }
+ resourceSearchList[size++] = target;
+ dassert(size <= resourceSearchListCapacity);
+ return size;
+}
+
+bool AuthorizationSession::isAuthorizedToChangeAsUser(const UserName& userName,
+ ActionType actionType) {
+ User* user = lookupUser(userName);
+ if (!user) {
+ return false;
}
+ ResourcePattern resourceSearchList[resourceSearchListCapacity];
+ const int resourceSearchListLength = buildResourceSearchList(
+ ResourcePattern::forDatabaseName(userName.getDB()), resourceSearchList);
- bool AuthorizationSession::isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) {
- return AuthorizationSession::isAuthorizedToChangeAsUser(userName, ActionType::changeOwnPassword);
+ ActionSet actions;
+ for (int i = 0; i < resourceSearchListLength; ++i) {
+ actions.addAllActionsFromSet(user->getActionsForResource(resourceSearchList[i]));
}
+ return actions.contains(actionType);
+}
- bool AuthorizationSession::isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) {
- return AuthorizationSession::isAuthorizedToChangeAsUser(userName, ActionType::changeOwnCustomData);
- }
+bool AuthorizationSession::isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName) {
+ return AuthorizationSession::isAuthorizedToChangeAsUser(userName,
+ ActionType::changeOwnPassword);
+}
- bool AuthorizationSession::isAuthenticatedAsUserWithRole(const RoleName& roleName) {
- for (UserSet::iterator it = _authenticatedUsers.begin();
- it != _authenticatedUsers.end(); ++it) {
- if ((*it)->hasRole(roleName)) {
- return true;
- }
+bool AuthorizationSession::isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName) {
+ return AuthorizationSession::isAuthorizedToChangeAsUser(userName,
+ ActionType::changeOwnCustomData);
+}
+
+bool AuthorizationSession::isAuthenticatedAsUserWithRole(const RoleName& roleName) {
+ for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
+ ++it) {
+ if ((*it)->hasRole(roleName)) {
+ return true;
}
- return false;
}
+ return false;
+}
- void AuthorizationSession::_refreshUserInfoAsNeeded(OperationContext* txn) {
- AuthorizationManager& authMan = getAuthorizationManager();
- UserSet::iterator it = _authenticatedUsers.begin();
- while (it != _authenticatedUsers.end()) {
- User* user = *it;
+void AuthorizationSession::_refreshUserInfoAsNeeded(OperationContext* txn) {
+ AuthorizationManager& authMan = getAuthorizationManager();
+ UserSet::iterator it = _authenticatedUsers.begin();
+ while (it != _authenticatedUsers.end()) {
+ User* user = *it;
- if (!user->isValid()) {
- // Make a good faith effort to acquire an up-to-date user object, since the one
- // we've cached is marked "out-of-date."
- UserName name = user->getName();
- User* updatedUser;
+ if (!user->isValid()) {
+ // Make a good faith effort to acquire an up-to-date user object, since the one
+ // we've cached is marked "out-of-date."
+ UserName name = user->getName();
+ User* updatedUser;
- Status status = authMan.acquireUser(txn, name, &updatedUser);
- switch (status.code()) {
+ Status status = authMan.acquireUser(txn, name, &updatedUser);
+ switch (status.code()) {
case ErrorCodes::OK: {
// Success! Replace the old User object with the updated one.
fassert(17067, _authenticatedUsers.replaceAt(it, updatedUser) == user);
@@ -517,103 +493,98 @@ namespace {
// User does not exist anymore; remove it from _authenticatedUsers.
fassert(17068, _authenticatedUsers.removeAt(it) == user);
authMan.releaseUser(user);
- log() << "Removed deleted user " << name <<
- " from session cache of user information.";
+ log() << "Removed deleted user " << name
+ << " from session cache of user information.";
continue; // No need to advance "it" in this case.
}
default:
// Unrecognized error; assume that it's transient, and continue working with the
// out-of-date privilege data.
- warning() << "Could not fetch updated user privilege information for " <<
- name << "; continuing to use old information. Reason is " << status;
+ warning() << "Could not fetch updated user privilege information for " << name
+ << "; continuing to use old information. Reason is " << status;
break;
- }
}
- ++it;
}
- _buildAuthenticatedRolesVector();
- }
-
- void AuthorizationSession::_buildAuthenticatedRolesVector() {
- _authenticatedRoleNames.clear();
- for (UserSet::iterator it = _authenticatedUsers.begin();
- it != _authenticatedUsers.end();
- ++it) {
- RoleNameIterator roles = (*it)->getIndirectRoles();
- while (roles.more()) {
- RoleName roleName = roles.next();
- _authenticatedRoleNames.push_back(RoleName(roleName.getRole(),
- roleName.getDB()));
- }
+ ++it;
+ }
+ _buildAuthenticatedRolesVector();
+}
+
+void AuthorizationSession::_buildAuthenticatedRolesVector() {
+ _authenticatedRoleNames.clear();
+ for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
+ ++it) {
+ RoleNameIterator roles = (*it)->getIndirectRoles();
+ while (roles.more()) {
+ RoleName roleName = roles.next();
+ _authenticatedRoleNames.push_back(RoleName(roleName.getRole(), roleName.getDB()));
}
}
+}
- bool AuthorizationSession::_isAuthorizedForPrivilege(const Privilege& privilege) {
- const ResourcePattern& target(privilege.getResourcePattern());
-
- ResourcePattern resourceSearchList[resourceSearchListCapacity];
- const int resourceSearchListLength = buildResourceSearchList(target, resourceSearchList);
+bool AuthorizationSession::_isAuthorizedForPrivilege(const Privilege& privilege) {
+ const ResourcePattern& target(privilege.getResourcePattern());
- ActionSet unmetRequirements = privilege.getActions();
+ ResourcePattern resourceSearchList[resourceSearchListCapacity];
+ const int resourceSearchListLength = buildResourceSearchList(target, resourceSearchList);
- PrivilegeVector defaultPrivileges = getDefaultPrivileges();
- for (PrivilegeVector::iterator it = defaultPrivileges.begin();
- it != defaultPrivileges.end(); ++it) {
+ ActionSet unmetRequirements = privilege.getActions();
- for (int i = 0; i < resourceSearchListLength; ++i) {
- if (!(it->getResourcePattern() == resourceSearchList[i]))
- continue;
+ PrivilegeVector defaultPrivileges = getDefaultPrivileges();
+ for (PrivilegeVector::iterator it = defaultPrivileges.begin(); it != defaultPrivileges.end();
+ ++it) {
+ for (int i = 0; i < resourceSearchListLength; ++i) {
+ if (!(it->getResourcePattern() == resourceSearchList[i]))
+ continue;
- ActionSet userActions = it->getActions();
- unmetRequirements.removeAllActionsFromSet(userActions);
+ ActionSet userActions = it->getActions();
+ unmetRequirements.removeAllActionsFromSet(userActions);
- if (unmetRequirements.empty())
- return true;
- }
+ if (unmetRequirements.empty())
+ return true;
}
+ }
- for (UserSet::iterator it = _authenticatedUsers.begin();
- it != _authenticatedUsers.end(); ++it) {
- User* user = *it;
- for (int i = 0; i < resourceSearchListLength; ++i) {
- ActionSet userActions = user->getActionsForResource(resourceSearchList[i]);
- unmetRequirements.removeAllActionsFromSet(userActions);
+ for (UserSet::iterator it = _authenticatedUsers.begin(); it != _authenticatedUsers.end();
+ ++it) {
+ User* user = *it;
+ for (int i = 0; i < resourceSearchListLength; ++i) {
+ ActionSet userActions = user->getActionsForResource(resourceSearchList[i]);
+ unmetRequirements.removeAllActionsFromSet(userActions);
- if (unmetRequirements.empty())
- return true;
- }
+ if (unmetRequirements.empty())
+ return true;
}
-
- return false;
}
- void AuthorizationSession::setImpersonatedUserData(std::vector<UserName> usernames,
- std::vector<RoleName> roles) {
- _impersonatedUserNames = usernames;
- _impersonatedRoleNames = roles;
- _impersonationFlag = true;
- }
+ return false;
+}
- UserNameIterator AuthorizationSession::getImpersonatedUserNames() {
- return makeUserNameIterator(_impersonatedUserNames.begin(),
- _impersonatedUserNames.end());
- }
+void AuthorizationSession::setImpersonatedUserData(std::vector<UserName> usernames,
+ std::vector<RoleName> roles) {
+ _impersonatedUserNames = usernames;
+ _impersonatedRoleNames = roles;
+ _impersonationFlag = true;
+}
- RoleNameIterator AuthorizationSession::getImpersonatedRoleNames() {
- return makeRoleNameIterator(_impersonatedRoleNames.begin(),
- _impersonatedRoleNames.end());
- }
+UserNameIterator AuthorizationSession::getImpersonatedUserNames() {
+ return makeUserNameIterator(_impersonatedUserNames.begin(), _impersonatedUserNames.end());
+}
- // Clear the vectors of impersonated usernames and roles.
- void AuthorizationSession::clearImpersonatedUserData() {
- _impersonatedUserNames.clear();
- _impersonatedRoleNames.clear();
- _impersonationFlag = false;
- }
+RoleNameIterator AuthorizationSession::getImpersonatedRoleNames() {
+ return makeRoleNameIterator(_impersonatedRoleNames.begin(), _impersonatedRoleNames.end());
+}
+// Clear the vectors of impersonated usernames and roles.
+void AuthorizationSession::clearImpersonatedUserData() {
+ _impersonatedUserNames.clear();
+ _impersonatedRoleNames.clear();
+ _impersonationFlag = false;
+}
- bool AuthorizationSession::isImpersonating() const {
- return _impersonationFlag;
- }
-} // namespace mongo
+bool AuthorizationSession::isImpersonating() const {
+ return _impersonationFlag;
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session.h b/src/mongo/db/auth/authorization_session.h
index 0fe4f1c46cb..d6fe06e11fa 100644
--- a/src/mongo/db/auth/authorization_session.h
+++ b/src/mongo/db/auth/authorization_session.h
@@ -44,234 +44,233 @@
#include "mongo/db/namespace_string.h"
namespace mongo {
- class ClientBasic;
+class ClientBasic;
+/**
+ * Contains all the authorization logic for a single client connection. It contains a set of
+ * the users which have been authenticated, as well as a set of privileges that have been
+ * granted to those users to perform various actions.
+ *
+ * An AuthorizationSession object is present within every mongo::ClientBasic object.
+ *
+ * Users in the _authenticatedUsers cache may get marked as invalid by the AuthorizationManager,
+ * for instance if their privileges are changed by a user or role modification command. At the
+ * beginning of every user-initiated operation startRequest() gets called which updates
+ * the cached information about any users who have been marked as invalid. This guarantees that
+ * every operation looks at one consistent view of each user for every auth check required over
+ * the lifetime of the operation.
+ */
+class AuthorizationSession {
+ MONGO_DISALLOW_COPYING(AuthorizationSession);
+
+public:
/**
- * Contains all the authorization logic for a single client connection. It contains a set of
- * the users which have been authenticated, as well as a set of privileges that have been
- * granted to those users to perform various actions.
+ * Gets the AuthorizationSession associated with the given "client", or nullptr.
*
- * An AuthorizationSession object is present within every mongo::ClientBasic object.
+ * The "client" object continues to own the returned AuthorizationSession.
+ */
+ static AuthorizationSession* get(ClientBasic* client);
+
+ /**
+ * Gets the AuthorizationSession associated with the given "client", or nullptr.
+ *
+ * The "client" object continues to own the returned AuthorizationSession.
+ */
+ static AuthorizationSession* get(ClientBasic& client);
+
+ /**
+ * Returns false if AuthorizationSession::get(client) would return nullptr.
+ */
+ static bool exists(ClientBasic* client);
+
+ /**
+ * Sets the AuthorizationSession associated with "client" to "session".
*
- * Users in the _authenticatedUsers cache may get marked as invalid by the AuthorizationManager,
- * for instance if their privileges are changed by a user or role modification command. At the
- * beginning of every user-initiated operation startRequest() gets called which updates
- * the cached information about any users who have been marked as invalid. This guarantees that
- * every operation looks at one consistent view of each user for every auth check required over
- * the lifetime of the operation.
+ * "session" must not be NULL, and it is only legal to call this function once
+ * on each instance of "client".
*/
- class AuthorizationSession {
- MONGO_DISALLOW_COPYING(AuthorizationSession);
- public:
- /**
- * Gets the AuthorizationSession associated with the given "client", or nullptr.
- *
- * The "client" object continues to own the returned AuthorizationSession.
- */
- static AuthorizationSession* get(ClientBasic* client);
-
- /**
- * Gets the AuthorizationSession associated with the given "client", or nullptr.
- *
- * The "client" object continues to own the returned AuthorizationSession.
- */
- static AuthorizationSession* get(ClientBasic& client);
-
- /**
- * Returns false if AuthorizationSession::get(client) would return nullptr.
- */
- static bool exists(ClientBasic* client);
-
- /**
- * Sets the AuthorizationSession associated with "client" to "session".
- *
- * "session" must not be NULL, and it is only legal to call this function once
- * on each instance of "client".
- */
- static void set(ClientBasic* client, std::unique_ptr<AuthorizationSession> session);
-
- // Takes ownership of the externalState.
- explicit AuthorizationSession(std::unique_ptr<AuthzSessionExternalState> externalState);
- ~AuthorizationSession();
-
- AuthorizationManager& getAuthorizationManager();
-
- // Should be called at the beginning of every new request. This performs the checks
- // necessary to determine if localhost connections should be given full access.
- // TODO: try to eliminate the need for this call.
- void startRequest(OperationContext* txn);
-
- /**
- * Adds the User identified by "UserName" to the authorization session, acquiring privileges
- * for it in the process.
- */
- Status addAndAuthorizeUser(OperationContext* txn, const UserName& userName);
-
- // Returns the authenticated user with the given name. Returns NULL
- // if no such user is found.
- // The user remains in the _authenticatedUsers set for this AuthorizationSession,
- // and ownership of the user stays with the AuthorizationManager
- User* lookupUser(const UserName& name);
-
- // Gets an iterator over the names of all authenticated users stored in this manager.
- UserNameIterator getAuthenticatedUserNames();
-
- // Gets an iterator over the roles of all authenticated users stored in this manager.
- RoleNameIterator getAuthenticatedRoleNames();
-
- // Returns a std::string representing all logged-in users on the current session.
- // WARNING: this std::string will contain NUL bytes so don't call c_str()!
- std::string getAuthenticatedUserNamesToken();
-
- // Removes any authenticated principals whose authorization credentials came from the given
- // database, and revokes any privileges that were granted via that principal.
- void logoutDatabase(const std::string& dbname);
-
- // Adds the internalSecurity user to the set of authenticated users.
- // Used to grant internal threads full access.
- void grantInternalAuthorization();
-
- // Generates a vector of default privileges that are granted to any user,
- // regardless of which roles that user does or does not possess.
- // If localhost exception is active, the permissions include the ability to create
- // the first user and the ability to run the commands needed to bootstrap the system
- // into a state where the first user can be created.
- PrivilegeVector getDefaultPrivileges();
-
- // Checks if this connection has the privileges necessary to perform the given query on the
- // given namespace.
- Status checkAuthForQuery(const NamespaceString& ns, const BSONObj& query);
-
- // Checks if this connection has the privileges necessary to perform a getMore operation on
- // the identified cursor, supposing that cursor is associated with the supplied namespace
- // identifier.
- Status checkAuthForGetMore(const NamespaceString& ns, long long cursorID);
-
- // Checks if this connection has the privileges necessary to perform the given update on the
- // given namespace.
- Status checkAuthForUpdate(const NamespaceString& ns,
- const BSONObj& query,
- const BSONObj& update,
- bool upsert);
-
- // Checks if this connection has the privileges necessary to insert the given document
- // to the given namespace. Correctly interprets inserts to system.indexes and performs
- // the proper auth checks for index building.
- Status checkAuthForInsert(const NamespaceString& ns, const BSONObj& document);
-
- // Checks if this connection has the privileges necessary to perform a delete on the given
- // namespace.
- Status checkAuthForDelete(const NamespaceString& ns, const BSONObj& query);
-
- // Checks if this connection has the privileges necessary to perform a killCursor on
- // the identified cursor, supposing that cursor is associated with the supplied namespace
- // identifier.
- Status checkAuthForKillCursors(const NamespaceString& ns, long long cursorID);
-
- // Checks if this connection has the privileges necessary to grant the given privilege
- // to a role.
- Status checkAuthorizedToGrantPrivilege(const Privilege& privilege);
-
- // Checks if this connection has the privileges necessary to revoke the given privilege
- // from a role.
- Status checkAuthorizedToRevokePrivilege(const Privilege& privilege);
-
- // Utility function for isAuthorizedForActionsOnResource(
- // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole)
- bool isAuthorizedToGrantRole(const RoleName& role);
-
- // Utility function for isAuthorizedForActionsOnResource(
- // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole)
- bool isAuthorizedToRevokeRole(const RoleName& role);
-
- // Utility function for isAuthorizedToChangeOwnPasswordAsUser and isAuthorizedToChangeOwnCustomDataAsUser
- bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType);
-
- // Returns true if the current session is authenticated as the given user and that user
- // is allowed to change his/her own password
- bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName);
-
- // Returns true if the current session is authenticated as the given user and that user
- // is allowed to change his/her own customData.
- bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName);
-
- // Returns true if any of the authenticated users on this session have the given role.
- // NOTE: this does not refresh any of the users even if they are marked as invalid.
- bool isAuthenticatedAsUserWithRole(const RoleName& roleName);
-
- // Returns true if this session is authorized for the given Privilege.
- //
- // Contains all the authorization logic including handling things like the localhost
- // exception.
- bool isAuthorizedForPrivilege(const Privilege& privilege);
-
- // Like isAuthorizedForPrivilege, above, except returns true if the session is authorized
- // for all of the listed privileges.
- bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges);
-
- // Utility function for isAuthorizedForPrivilege(Privilege(resource, action)).
- bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, ActionType action);
-
- // Utility function for isAuthorizedForPrivilege(Privilege(resource, actions)).
- bool isAuthorizedForActionsOnResource(const ResourcePattern& resource,
- const ActionSet& actions);
-
- // Utility function for
- // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), action).
- bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, ActionType action);
-
- // Utility function for
- // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), actions).
- bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
- const ActionSet& actions);
-
- // Replaces the data for users that a system user is impersonating with new data.
- // The auditing system adds these users and their roles to each audit record in the log.
- void setImpersonatedUserData(std::vector<UserName> usernames, std::vector<RoleName> roles);
-
- // Gets an iterator over the names of all users that the system user is impersonating.
- UserNameIterator getImpersonatedUserNames();
-
- // Gets an iterator over the roles of all users that the system user is impersonating.
- RoleNameIterator getImpersonatedRoleNames();
-
- // Clears the data for impersonated users.
- void clearImpersonatedUserData();
-
- // Tells whether impersonation is active or not. This state is set when
- // setImpersonatedUserData is called and cleared when clearImpersonatedUserData is
- // called.
- bool isImpersonating() const;
-
- private:
-
- // If any users authenticated on this session are marked as invalid this updates them with
- // up-to-date information. May require a read lock on the "admin" db to read the user data.
- void _refreshUserInfoAsNeeded(OperationContext* txn);
-
- // Builds a vector of all roles held by users who are authenticated on this connection. The
- // vector is stored in _authenticatedRoleNames. This function is called when users are
- // logged in or logged out, as well as when the user cache is determined to be out of date.
- void _buildAuthenticatedRolesVector();
+ static void set(ClientBasic* client, std::unique_ptr<AuthorizationSession> session);
- // Checks if this connection is authorized for the given Privilege, ignoring whether or not
- // we should even be doing authorization checks in general. Note: this may acquire a read
- // lock on the admin database (to update out-of-date user privilege information).
- bool _isAuthorizedForPrivilege(const Privilege& privilege);
+ // Takes ownership of the externalState.
+ explicit AuthorizationSession(std::unique_ptr<AuthzSessionExternalState> externalState);
+ ~AuthorizationSession();
- std::unique_ptr<AuthzSessionExternalState> _externalState;
+ AuthorizationManager& getAuthorizationManager();
- // All Users who have been authenticated on this connection.
- UserSet _authenticatedUsers;
- // The roles of the authenticated users. This vector is generated when the authenticated
- // users set is changed.
- std::vector<RoleName> _authenticatedRoleNames;
+ // Should be called at the beginning of every new request. This performs the checks
+ // necessary to determine if localhost connections should be given full access.
+ // TODO: try to eliminate the need for this call.
+ void startRequest(OperationContext* txn);
+
+ /**
+ * Adds the User identified by "UserName" to the authorization session, acquiring privileges
+ * for it in the process.
+ */
+ Status addAndAuthorizeUser(OperationContext* txn, const UserName& userName);
+
+ // Returns the authenticated user with the given name. Returns NULL
+ // if no such user is found.
+ // The user remains in the _authenticatedUsers set for this AuthorizationSession,
+ // and ownership of the user stays with the AuthorizationManager
+ User* lookupUser(const UserName& name);
+
+ // Gets an iterator over the names of all authenticated users stored in this manager.
+ UserNameIterator getAuthenticatedUserNames();
+
+ // Gets an iterator over the roles of all authenticated users stored in this manager.
+ RoleNameIterator getAuthenticatedRoleNames();
+
+ // Returns a std::string representing all logged-in users on the current session.
+ // WARNING: this std::string will contain NUL bytes so don't call c_str()!
+ std::string getAuthenticatedUserNamesToken();
+
+ // Removes any authenticated principals whose authorization credentials came from the given
+ // database, and revokes any privileges that were granted via that principal.
+ void logoutDatabase(const std::string& dbname);
+
+ // Adds the internalSecurity user to the set of authenticated users.
+ // Used to grant internal threads full access.
+ void grantInternalAuthorization();
+
+ // Generates a vector of default privileges that are granted to any user,
+ // regardless of which roles that user does or does not possess.
+ // If localhost exception is active, the permissions include the ability to create
+ // the first user and the ability to run the commands needed to bootstrap the system
+ // into a state where the first user can be created.
+ PrivilegeVector getDefaultPrivileges();
+
+ // Checks if this connection has the privileges necessary to perform the given query on the
+ // given namespace.
+ Status checkAuthForQuery(const NamespaceString& ns, const BSONObj& query);
+
+ // Checks if this connection has the privileges necessary to perform a getMore operation on
+ // the identified cursor, supposing that cursor is associated with the supplied namespace
+ // identifier.
+ Status checkAuthForGetMore(const NamespaceString& ns, long long cursorID);
+
+ // Checks if this connection has the privileges necessary to perform the given update on the
+ // given namespace.
+ Status checkAuthForUpdate(const NamespaceString& ns,
+ const BSONObj& query,
+ const BSONObj& update,
+ bool upsert);
+
+ // Checks if this connection has the privileges necessary to insert the given document
+ // to the given namespace. Correctly interprets inserts to system.indexes and performs
+ // the proper auth checks for index building.
+ Status checkAuthForInsert(const NamespaceString& ns, const BSONObj& document);
+
+ // Checks if this connection has the privileges necessary to perform a delete on the given
+ // namespace.
+ Status checkAuthForDelete(const NamespaceString& ns, const BSONObj& query);
+
+ // Checks if this connection has the privileges necessary to perform a killCursor on
+ // the identified cursor, supposing that cursor is associated with the supplied namespace
+ // identifier.
+ Status checkAuthForKillCursors(const NamespaceString& ns, long long cursorID);
+
+ // Checks if this connection has the privileges necessary to grant the given privilege
+ // to a role.
+ Status checkAuthorizedToGrantPrivilege(const Privilege& privilege);
+
+ // Checks if this connection has the privileges necessary to revoke the given privilege
+ // from a role.
+ Status checkAuthorizedToRevokePrivilege(const Privilege& privilege);
+
+ // Utility function for isAuthorizedForActionsOnResource(
+ // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole)
+ bool isAuthorizedToGrantRole(const RoleName& role);
+
+ // Utility function for isAuthorizedForActionsOnResource(
+ // ResourcePattern::forDatabaseName(role.getDB()), ActionType::grantAnyRole)
+ bool isAuthorizedToRevokeRole(const RoleName& role);
+
+ // Utility function for isAuthorizedToChangeOwnPasswordAsUser and isAuthorizedToChangeOwnCustomDataAsUser
+ bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType);
+
+ // Returns true if the current session is authenticated as the given user and that user
+ // is allowed to change his/her own password
+ bool isAuthorizedToChangeOwnPasswordAsUser(const UserName& userName);
+
+ // Returns true if the current session is authenticated as the given user and that user
+ // is allowed to change his/her own customData.
+ bool isAuthorizedToChangeOwnCustomDataAsUser(const UserName& userName);
+
+ // Returns true if any of the authenticated users on this session have the given role.
+ // NOTE: this does not refresh any of the users even if they are marked as invalid.
+ bool isAuthenticatedAsUserWithRole(const RoleName& roleName);
+
+ // Returns true if this session is authorized for the given Privilege.
+ //
+ // Contains all the authorization logic including handling things like the localhost
+ // exception.
+ bool isAuthorizedForPrivilege(const Privilege& privilege);
+
+ // Like isAuthorizedForPrivilege, above, except returns true if the session is authorized
+ // for all of the listed privileges.
+ bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges);
+
+ // Utility function for isAuthorizedForPrivilege(Privilege(resource, action)).
+ bool isAuthorizedForActionsOnResource(const ResourcePattern& resource, ActionType action);
+
+ // Utility function for isAuthorizedForPrivilege(Privilege(resource, actions)).
+ bool isAuthorizedForActionsOnResource(const ResourcePattern& resource,
+ const ActionSet& actions);
+
+ // Utility function for
+ // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), action).
+ bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, ActionType action);
+
+ // Utility function for
+ // isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), actions).
+ bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns, const ActionSet& actions);
+
+ // Replaces the data for users that a system user is impersonating with new data.
+ // The auditing system adds these users and their roles to each audit record in the log.
+ void setImpersonatedUserData(std::vector<UserName> usernames, std::vector<RoleName> roles);
+
+ // Gets an iterator over the names of all users that the system user is impersonating.
+ UserNameIterator getImpersonatedUserNames();
+
+ // Gets an iterator over the roles of all users that the system user is impersonating.
+ RoleNameIterator getImpersonatedRoleNames();
+
+ // Clears the data for impersonated users.
+ void clearImpersonatedUserData();
+
+ // Tells whether impersonation is active or not. This state is set when
+ // setImpersonatedUserData is called and cleared when clearImpersonatedUserData is
+ // called.
+ bool isImpersonating() const;
+
+private:
+ // If any users authenticated on this session are marked as invalid this updates them with
+ // up-to-date information. May require a read lock on the "admin" db to read the user data.
+ void _refreshUserInfoAsNeeded(OperationContext* txn);
+
+ // Builds a vector of all roles held by users who are authenticated on this connection. The
+ // vector is stored in _authenticatedRoleNames. This function is called when users are
+ // logged in or logged out, as well as when the user cache is determined to be out of date.
+ void _buildAuthenticatedRolesVector();
+
+ // Checks if this connection is authorized for the given Privilege, ignoring whether or not
+ // we should even be doing authorization checks in general. Note: this may acquire a read
+ // lock on the admin database (to update out-of-date user privilege information).
+ bool _isAuthorizedForPrivilege(const Privilege& privilege);
+
+ std::unique_ptr<AuthzSessionExternalState> _externalState;
- // A vector of impersonated UserNames and a vector of those users' RoleNames.
- // These are used in the auditing system. They are not used for authz checks.
- std::vector<UserName> _impersonatedUserNames;
- std::vector<RoleName> _impersonatedRoleNames;
- bool _impersonationFlag;
- };
+ // All Users who have been authenticated on this connection.
+ UserSet _authenticatedUsers;
+ // The roles of the authenticated users. This vector is generated when the authenticated
+ // users set is changed.
+ std::vector<RoleName> _authenticatedRoleNames;
+
+ // A vector of impersonated UserNames and a vector of those users' RoleNames.
+ // These are used in the auditing system. They are not used for authz checks.
+ std::vector<UserName> _impersonatedUserNames;
+ std::vector<RoleName> _impersonatedRoleNames;
+ bool _impersonationFlag;
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authorization_session_test.cpp b/src/mongo/db/auth/authorization_session_test.cpp
index 5ce06edb4a7..0f5b4936e9d 100644
--- a/src/mongo/db/auth/authorization_session_test.cpp
+++ b/src/mongo/db/auth/authorization_session_test.cpp
@@ -46,409 +46,464 @@
namespace mongo {
namespace {
- class FailureCapableAuthzManagerExternalStateMock :
- public AuthzManagerExternalStateMock {
- public:
- FailureCapableAuthzManagerExternalStateMock() : _findsShouldFail(false) {}
- virtual ~FailureCapableAuthzManagerExternalStateMock() {}
-
- void setFindsShouldFail(bool enable) { _findsShouldFail = enable; }
-
- virtual Status findOne(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result) {
- if (_findsShouldFail &&
- collectionName == AuthorizationManager::usersCollectionNamespace) {
-
- return Status(ErrorCodes::UnknownError,
- "findOne on admin.system.users set to fail in mock.");
- }
- return AuthzManagerExternalStateMock::findOne(txn, collectionName, query, result);
- }
-
- private:
- bool _findsShouldFail;
- };
-
- class AuthorizationSessionTest : public ::mongo::unittest::Test {
- public:
- FailureCapableAuthzManagerExternalStateMock* managerState;
- OperationContextNoop _txn;
- AuthzSessionExternalStateMock* sessionState;
- std::unique_ptr<AuthorizationManager> authzManager;
- std::unique_ptr<AuthorizationSession> authzSession;
-
- void setUp() {
- auto localManagerState = stdx::make_unique<FailureCapableAuthzManagerExternalStateMock>();
- managerState = localManagerState.get();
- managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final);
- authzManager = stdx::make_unique<AuthorizationManager>(std::move(localManagerState));
- auto localSessionState = stdx::make_unique<AuthzSessionExternalStateMock>(authzManager.get());
- sessionState = localSessionState.get();
- authzSession = stdx::make_unique<AuthorizationSession>(std::move(localSessionState));
- authzManager->setAuthEnabled(true);
- }
- };
-
- const ResourcePattern testDBResource(ResourcePattern::forDatabaseName("test"));
- const ResourcePattern otherDBResource(ResourcePattern::forDatabaseName("other"));
- const ResourcePattern adminDBResource(ResourcePattern::forDatabaseName("admin"));
- const ResourcePattern testFooCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("test.foo")));
- const ResourcePattern otherFooCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("other.foo")));
- const ResourcePattern thirdFooCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("third.foo")));
- const ResourcePattern adminFooCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("admin.foo")));
- const ResourcePattern testUsersCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("test.system.users")));
- const ResourcePattern otherUsersCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("other.system.users")));
- const ResourcePattern thirdUsersCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("third.system.users")));
- const ResourcePattern testIndexesCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("test.system.indexes")));
- const ResourcePattern otherIndexesCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("other.system.indexes")));
- const ResourcePattern thirdIndexesCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("third.system.indexes")));
- const ResourcePattern testProfileCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("test.system.profile")));
- const ResourcePattern otherProfileCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("other.system.profile")));
- const ResourcePattern thirdProfileCollResource(
- ResourcePattern::forExactNamespace(NamespaceString("third.system.profile")));
-
- TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) {
- // Check that disabling auth checks works
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- sessionState->setReturnValueForShouldIgnoreAuthChecks(true);
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- sessionState->setReturnValueForShouldIgnoreAuthChecks(false);
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- // Check that you can't authorize a user that doesn't exist.
- ASSERT_EQUALS(ErrorCodes::UserNotFound,
- authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
-
- // Add a user with readWrite and dbAdmin on the test DB
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWrite" <<
- "db" << "test") <<
- BSON("role" << "dbAdmin" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
-
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testDBResource, ActionType::dbStats));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::insert));
-
- // Add an admin user with readWriteAnyDatabase
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "admin" <<
- "db" << "admin" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWriteAnyDatabase" <<
- "db" << "admin"))),
- BSONObj()));
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("admin", "admin")));
-
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- ResourcePattern::forExactNamespace(
- NamespaceString("anydb.somecollection")),
- ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherDBResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::collMod));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- authzSession->logoutDatabase("test");
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::collMod));
-
- authzSession->logoutDatabase("admin");
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::collMod));
- }
+class FailureCapableAuthzManagerExternalStateMock : public AuthzManagerExternalStateMock {
+public:
+ FailureCapableAuthzManagerExternalStateMock() : _findsShouldFail(false) {}
+ virtual ~FailureCapableAuthzManagerExternalStateMock() {}
- TEST_F(AuthorizationSessionTest, DuplicateRolesOK) {
- // Add a user with doubled-up readWrite and single dbAdmin on the test DB
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWrite" <<
- "db" << "test") <<
- BSON("role" << "dbAdmin" <<
- "db" << "test") <<
- BSON("role" << "readWrite" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
-
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testDBResource, ActionType::dbStats));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherFooCollResource, ActionType::insert));
+ void setFindsShouldFail(bool enable) {
+ _findsShouldFail = enable;
}
- TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) {
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "rw" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWrite" <<
- "db" << "test") <<
- BSON("role" << "dbAdmin" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "useradmin" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "userAdmin" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "rwany" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWriteAnyDatabase" <<
- "db" << "admin") <<
- BSON("role" << "dbAdminAnyDatabase" <<
- "db" << "admin"))),
- BSONObj()));
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "useradminany" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "userAdminAnyDatabase" <<
- "db" << "admin"))),
- BSONObj()));
-
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("rwany", "test")));
-
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testIndexesCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testProfileCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherIndexesCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherProfileCollResource, ActionType::find));
-
- // Logging in as useradminany@test implicitly logs out rwany@test.
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("useradminany", "test")));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::insert));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testIndexesCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testProfileCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherIndexesCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherProfileCollResource, ActionType::find));
-
- // Logging in as rw@test implicitly logs out useradminany@test.
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("rw", "test")));
-
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testIndexesCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testProfileCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherIndexesCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherProfileCollResource, ActionType::find));
-
-
- // Logging in as useradmin@test implicitly logs out rw@test.
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("useradmin", "test")));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherUsersCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testIndexesCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testProfileCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherIndexesCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- otherProfileCollResource, ActionType::find));
- }
-
- TEST_F(AuthorizationSessionTest, InvalidateUser) {
- // Add a readWrite user
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWrite" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
-
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- User* user = authzSession->lookupUser(UserName("spencer", "test"));
- ASSERT(user->isValid());
-
- // Change the user to be read-only
- int ignored;
- managerState->remove(
- &_txn,
- AuthorizationManager::usersCollectionNamespace,
- BSONObj(),
- BSONObj(),
- &ignored);
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "read" <<
- "db" << "test"))),
- BSONObj()));
-
- // Make sure that invalidating the user causes the session to reload its privileges.
- authzManager->invalidateUserByName(user->getName());
- authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- user = authzSession->lookupUser(UserName("spencer", "test"));
- ASSERT(user->isValid());
-
- // Delete the user.
- managerState->remove(
- &_txn,
- AuthorizationManager::usersCollectionNamespace,
- BSONObj(),
- BSONObj(),
- &ignored);
- // Make sure that invalidating the user causes the session to reload its privileges.
- authzManager->invalidateUserByName(user->getName());
- authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
- ASSERT_FALSE(authzSession->lookupUser(UserName("spencer", "test")));
+ virtual Status findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result) {
+ if (_findsShouldFail && collectionName == AuthorizationManager::usersCollectionNamespace) {
+ return Status(ErrorCodes::UnknownError,
+ "findOne on admin.system.users set to fail in mock.");
+ }
+ return AuthzManagerExternalStateMock::findOne(txn, collectionName, query, result);
}
- TEST_F(AuthorizationSessionTest, UseOldUserInfoInFaceOfConnectivityProblems) {
- // Add a readWrite user
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "readWrite" <<
- "db" << "test"))),
- BSONObj()));
- ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
-
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- User* user = authzSession->lookupUser(UserName("spencer", "test"));
- ASSERT(user->isValid());
-
- // Change the user to be read-only
- int ignored;
- managerState->setFindsShouldFail(true);
- managerState->remove(
- &_txn,
- AuthorizationManager::usersCollectionNamespace,
- BSONObj(),
- BSONObj(),
- &ignored);
- ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "read" <<
- "db" << "test"))),
- BSONObj()));
-
- // Even though the user's privileges have been reduced, since we've configured user
- // document lookup to fail, the authz session should continue to use its known out-of-date
- // privilege data.
- authzManager->invalidateUserByName(user->getName());
- authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
-
- // Once we configure document lookup to succeed again, authorization checks should
- // observe the new values.
- managerState->setFindsShouldFail(false);
- authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
- ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::find));
- ASSERT_FALSE(authzSession->isAuthorizedForActionsOnResource(
- testFooCollResource, ActionType::insert));
+private:
+ bool _findsShouldFail;
+};
+
+class AuthorizationSessionTest : public ::mongo::unittest::Test {
+public:
+ FailureCapableAuthzManagerExternalStateMock* managerState;
+ OperationContextNoop _txn;
+ AuthzSessionExternalStateMock* sessionState;
+ std::unique_ptr<AuthorizationManager> authzManager;
+ std::unique_ptr<AuthorizationSession> authzSession;
+
+ void setUp() {
+ auto localManagerState = stdx::make_unique<FailureCapableAuthzManagerExternalStateMock>();
+ managerState = localManagerState.get();
+ managerState->setAuthzVersion(AuthorizationManager::schemaVersion26Final);
+ authzManager = stdx::make_unique<AuthorizationManager>(std::move(localManagerState));
+ auto localSessionState =
+ stdx::make_unique<AuthzSessionExternalStateMock>(authzManager.get());
+ sessionState = localSessionState.get();
+ authzSession = stdx::make_unique<AuthorizationSession>(std::move(localSessionState));
+ authzManager->setAuthEnabled(true);
}
+};
+
+const ResourcePattern testDBResource(ResourcePattern::forDatabaseName("test"));
+const ResourcePattern otherDBResource(ResourcePattern::forDatabaseName("other"));
+const ResourcePattern adminDBResource(ResourcePattern::forDatabaseName("admin"));
+const ResourcePattern testFooCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("test.foo")));
+const ResourcePattern otherFooCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("other.foo")));
+const ResourcePattern thirdFooCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("third.foo")));
+const ResourcePattern adminFooCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("admin.foo")));
+const ResourcePattern testUsersCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("test.system.users")));
+const ResourcePattern otherUsersCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("other.system.users")));
+const ResourcePattern thirdUsersCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("third.system.users")));
+const ResourcePattern testIndexesCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("test.system.indexes")));
+const ResourcePattern otherIndexesCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("other.system.indexes")));
+const ResourcePattern thirdIndexesCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("third.system.indexes")));
+const ResourcePattern testProfileCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("test.system.profile")));
+const ResourcePattern otherProfileCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("other.system.profile")));
+const ResourcePattern thirdProfileCollResource(
+ ResourcePattern::forExactNamespace(NamespaceString("third.system.profile")));
+
+TEST_F(AuthorizationSessionTest, AddUserAndCheckAuthorization) {
+ // Check that disabling auth checks works
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ sessionState->setReturnValueForShouldIgnoreAuthChecks(true);
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ sessionState->setReturnValueForShouldIgnoreAuthChecks(false);
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ // Check that you can't authorize a user that doesn't exist.
+ ASSERT_EQUALS(ErrorCodes::UserNotFound,
+ authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
+
+ // Add a user with readWrite and dbAdmin on the test DB
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test")
+ << BSON("role"
+ << "dbAdmin"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
+
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testDBResource, ActionType::dbStats));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::insert));
+
+ // Add an admin user with readWriteAnyDatabase
+ ASSERT_OK(
+ managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "admin"
+ << "db"
+ << "admin"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWriteAnyDatabase"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("admin", "admin")));
+
+ ASSERT_TRUE(authzSession->isAuthorizedForActionsOnResource(
+ ResourcePattern::forExactNamespace(NamespaceString("anydb.somecollection")),
+ ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherDBResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::collMod));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ authzSession->logoutDatabase("test");
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::collMod));
+
+ authzSession->logoutDatabase("admin");
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::collMod));
+}
+
+TEST_F(AuthorizationSessionTest, DuplicateRolesOK) {
+ // Add a user with doubled-up readWrite and single dbAdmin on the test DB
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test")
+ << BSON("role"
+ << "dbAdmin"
+ << "db"
+ << "test")
+ << BSON("role"
+ << "readWrite"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
+
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testDBResource, ActionType::dbStats));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherFooCollResource, ActionType::insert));
+}
+
+TEST_F(AuthorizationSessionTest, SystemCollectionsAccessControl) {
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "rw"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test")
+ << BSON("role"
+ << "dbAdmin"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "useradmin"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "userAdmin"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(
+ managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "rwany"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWriteAnyDatabase"
+ << "db"
+ << "admin")
+ << BSON("role"
+ << "dbAdminAnyDatabase"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+ ASSERT_OK(
+ managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "useradminany"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "userAdminAnyDatabase"
+ << "db"
+ << "admin"))),
+ BSONObj()));
+
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("rwany", "test")));
+
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testIndexesCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testProfileCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherIndexesCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find));
+
+ // Logging in as useradminany@test implicitly logs out rwany@test.
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("useradminany", "test")));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::insert));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testIndexesCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testProfileCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherIndexesCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find));
+
+ // Logging in as rw@test implicitly logs out useradminany@test.
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("rw", "test")));
+
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testIndexesCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testProfileCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherIndexesCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find));
+
+
+ // Logging in as useradmin@test implicitly logs out rw@test.
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("useradmin", "test")));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::insert));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherUsersCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testIndexesCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testProfileCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherIndexesCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(otherProfileCollResource, ActionType::find));
+}
+
+TEST_F(AuthorizationSessionTest, InvalidateUser) {
+ // Add a readWrite user
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
+
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ User* user = authzSession->lookupUser(UserName("spencer", "test"));
+ ASSERT(user->isValid());
+
+ // Change the user to be read-only
+ int ignored;
+ managerState->remove(
+ &_txn, AuthorizationManager::usersCollectionNamespace, BSONObj(), BSONObj(), &ignored);
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "read"
+ << "db"
+ << "test"))),
+ BSONObj()));
+
+ // Make sure that invalidating the user causes the session to reload its privileges.
+ authzManager->invalidateUserByName(user->getName());
+ authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ user = authzSession->lookupUser(UserName("spencer", "test"));
+ ASSERT(user->isValid());
+
+ // Delete the user.
+ managerState->remove(
+ &_txn, AuthorizationManager::usersCollectionNamespace, BSONObj(), BSONObj(), &ignored);
+ // Make sure that invalidating the user causes the session to reload its privileges.
+ authzManager->invalidateUserByName(user->getName());
+ authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+ ASSERT_FALSE(authzSession->lookupUser(UserName("spencer", "test")));
+}
+
+TEST_F(AuthorizationSessionTest, UseOldUserInfoInFaceOfConnectivityProblems) {
+ // Add a readWrite user
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "readWrite"
+ << "db"
+ << "test"))),
+ BSONObj()));
+ ASSERT_OK(authzSession->addAndAuthorizeUser(&_txn, UserName("spencer", "test")));
+
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ User* user = authzSession->lookupUser(UserName("spencer", "test"));
+ ASSERT(user->isValid());
+
+ // Change the user to be read-only
+ int ignored;
+ managerState->setFindsShouldFail(true);
+ managerState->remove(
+ &_txn, AuthorizationManager::usersCollectionNamespace, BSONObj(), BSONObj(), &ignored);
+ ASSERT_OK(managerState->insertPrivilegeDocument(&_txn,
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "read"
+ << "db"
+ << "test"))),
+ BSONObj()));
+
+ // Even though the user's privileges have been reduced, since we've configured user
+ // document lookup to fail, the authz session should continue to use its known out-of-date
+ // privilege data.
+ authzManager->invalidateUserByName(user->getName());
+ authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+
+ // Once we configure document lookup to succeed again, authorization checks should
+ // observe the new values.
+ managerState->setFindsShouldFail(false);
+ authzSession->startRequest(&_txn); // Refreshes cached data for invalid users
+ ASSERT_TRUE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::find));
+ ASSERT_FALSE(
+ authzSession->isAuthorizedForActionsOnResource(testFooCollResource, ActionType::insert));
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state.cpp b/src/mongo/db/auth/authz_manager_external_state.cpp
index 62d6683b78b..fae54cbf5e9 100644
--- a/src/mongo/db/auth/authz_manager_external_state.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state.cpp
@@ -32,9 +32,9 @@
namespace mongo {
- stdx::function<std::unique_ptr<AuthzManagerExternalState>()> AuthzManagerExternalState::create;
+stdx::function<std::unique_ptr<AuthzManagerExternalState>()> AuthzManagerExternalState::create;
- AuthzManagerExternalState::AuthzManagerExternalState() = default;
- AuthzManagerExternalState::~AuthzManagerExternalState() = default;
+AuthzManagerExternalState::AuthzManagerExternalState() = default;
+AuthzManagerExternalState::~AuthzManagerExternalState() = default;
} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state.h b/src/mongo/db/auth/authz_manager_external_state.h
index 6fd94effb69..e3bdcdb8c43 100644
--- a/src/mongo/db/auth/authz_manager_external_state.h
+++ b/src/mongo/db/auth/authz_manager_external_state.h
@@ -41,105 +41,101 @@
namespace mongo {
- class AuthorizationManager;
- class AuthzSessionExternalState;
- class OperationContext;
+class AuthorizationManager;
+class AuthzSessionExternalState;
+class OperationContext;
+
+/**
+ * Public interface for a class that encapsulates all the information related to system
+ * state not stored in AuthorizationManager. This is primarily to make AuthorizationManager
+ * easier to test as well as to allow different implementations for mongos and mongod.
+ */
+class AuthzManagerExternalState {
+ MONGO_DISALLOW_COPYING(AuthzManagerExternalState);
+
+public:
+ static stdx::function<std::unique_ptr<AuthzManagerExternalState>()> create;
+
+ virtual ~AuthzManagerExternalState();
/**
- * Public interface for a class that encapsulates all the information related to system
- * state not stored in AuthorizationManager. This is primarily to make AuthorizationManager
- * easier to test as well as to allow different implementations for mongos and mongod.
+ * Initializes the external state object. Must be called after construction and before
+ * calling other methods. Object may not be used after this method returns something other
+ * than Status::OK().
*/
- class AuthzManagerExternalState {
- MONGO_DISALLOW_COPYING(AuthzManagerExternalState);
-
- public:
-
- static stdx::function<std::unique_ptr<AuthzManagerExternalState>()> create;
-
- virtual ~AuthzManagerExternalState();
-
- /**
- * Initializes the external state object. Must be called after construction and before
- * calling other methods. Object may not be used after this method returns something other
- * than Status::OK().
- */
- virtual Status initialize(OperationContext* txn) = 0;
-
- /**
- * Creates an external state manipulator for an AuthorizationSession whose
- * AuthorizationManager uses this object as its own external state manipulator.
- */
- virtual std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) = 0;
-
- /**
- * Retrieves the schema version of the persistent data describing users and roles.
- * Will leave *outVersion unmodified on non-OK status return values.
- */
- virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion) = 0;
-
- /**
- * Writes into "result" a document describing the named user and returns Status::OK(). The
- * description includes the user credentials, if present, the user's role membership and
- * delegation information, a full list of the user's privileges, and a full list of the
- * user's roles, including those roles held implicitly through other roles (indirect roles).
- * In the event that some of this information is inconsistent, the document will contain a
- * "warnings" array, with std::string messages describing inconsistencies.
- *
- * If the user does not exist, returns ErrorCodes::UserNotFound.
- */
- virtual Status getUserDescription(
- OperationContext* txn, const UserName& userName, BSONObj* result) = 0;
-
- /**
- * Writes into "result" a document describing the named role and returns Status::OK(). The
- * description includes the roles in which the named role has membership and a full list of
- * the roles of which the named role is a member, including those roles memberships held
- * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
- * description documents will also include a full list of the role's privileges.
- * In the event that some of this information is inconsistent, the document will contain a
- * "warnings" array, with std::string messages describing inconsistencies.
- *
- * If the role does not exist, returns ErrorCodes::RoleNotFound.
- */
- virtual Status getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result) = 0;
-
- /**
- * Writes into "result" documents describing the roles that are defined on the given
- * database. Each role description document includes the other roles in which the role has
- * membership and a full list of the roles of which the named role is a member,
- * including those roles memberships held implicitly through other roles (indirect roles).
- * If showPrivileges is true, then the description documents will also include a full list
- * of the role's privileges. If showBuiltinRoles is true, then the result array will
- * contain description documents for all the builtin roles for the given database, if it
- * is false the result will just include user defined roles.
- * In the event that some of the information in a given role description is inconsistent,
- * the document will contain a "warnings" array, with std::string messages describing
- * inconsistencies.
- */
- virtual Status getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- std::vector<BSONObj>* result) = 0;
-
- /**
- * Returns true if there exists at least one privilege document in the system.
- */
- virtual bool hasAnyPrivilegeDocuments(OperationContext* txn) = 0;
-
- virtual void logOp(
- OperationContext* txn,
- const char* op,
- const char* ns,
- const BSONObj& o,
- BSONObj* o2) {}
-
-
- protected:
- AuthzManagerExternalState(); // This class should never be instantiated directly.
- };
-
-} // namespace mongo
+ virtual Status initialize(OperationContext* txn) = 0;
+
+ /**
+ * Creates an external state manipulator for an AuthorizationSession whose
+ * AuthorizationManager uses this object as its own external state manipulator.
+ */
+ virtual std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
+ AuthorizationManager* authzManager) = 0;
+
+ /**
+ * Retrieves the schema version of the persistent data describing users and roles.
+ * Will leave *outVersion unmodified on non-OK status return values.
+ */
+ virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion) = 0;
+
+ /**
+ * Writes into "result" a document describing the named user and returns Status::OK(). The
+ * description includes the user credentials, if present, the user's role membership and
+ * delegation information, a full list of the user's privileges, and a full list of the
+ * user's roles, including those roles held implicitly through other roles (indirect roles).
+ * In the event that some of this information is inconsistent, the document will contain a
+ * "warnings" array, with std::string messages describing inconsistencies.
+ *
+ * If the user does not exist, returns ErrorCodes::UserNotFound.
+ */
+ virtual Status getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result) = 0;
+
+ /**
+ * Writes into "result" a document describing the named role and returns Status::OK(). The
+ * description includes the roles in which the named role has membership and a full list of
+ * the roles of which the named role is a member, including those roles memberships held
+ * implicitly through other roles (indirect roles). If "showPrivileges" is true, then the
+ * description documents will also include a full list of the role's privileges.
+ * In the event that some of this information is inconsistent, the document will contain a
+ * "warnings" array, with std::string messages describing inconsistencies.
+ *
+ * If the role does not exist, returns ErrorCodes::RoleNotFound.
+ */
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) = 0;
+
+ /**
+ * Writes into "result" documents describing the roles that are defined on the given
+ * database. Each role description document includes the other roles in which the role has
+ * membership and a full list of the roles of which the named role is a member,
+ * including those roles memberships held implicitly through other roles (indirect roles).
+ * If showPrivileges is true, then the description documents will also include a full list
+ * of the role's privileges. If showBuiltinRoles is true, then the result array will
+ * contain description documents for all the builtin roles for the given database, if it
+ * is false the result will just include user defined roles.
+ * In the event that some of the information in a given role description is inconsistent,
+ * the document will contain a "warnings" array, with std::string messages describing
+ * inconsistencies.
+ */
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ std::vector<BSONObj>* result) = 0;
+
+ /**
+ * Returns true if there exists at least one privilege document in the system.
+ */
+ virtual bool hasAnyPrivilegeDocuments(OperationContext* txn) = 0;
+
+ virtual void logOp(
+ OperationContext* txn, const char* op, const char* ns, const BSONObj& o, BSONObj* o2) {}
+
+
+protected:
+ AuthzManagerExternalState(); // This class should never be instantiated directly.
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_d.cpp b/src/mongo/db/auth/authz_manager_external_state_d.cpp
index 1c529f037d3..601c14decff 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_d.cpp
@@ -51,49 +51,43 @@
namespace mongo {
- AuthzManagerExternalStateMongod::AuthzManagerExternalStateMongod() = default;
- AuthzManagerExternalStateMongod::~AuthzManagerExternalStateMongod() = default;
+AuthzManagerExternalStateMongod::AuthzManagerExternalStateMongod() = default;
+AuthzManagerExternalStateMongod::~AuthzManagerExternalStateMongod() = default;
- std::unique_ptr<AuthzSessionExternalState>
- AuthzManagerExternalStateMongod::makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) {
+std::unique_ptr<AuthzSessionExternalState>
+AuthzManagerExternalStateMongod::makeAuthzSessionExternalState(AuthorizationManager* authzManager) {
+ return stdx::make_unique<AuthzSessionExternalStateMongod>(authzManager);
+}
- return stdx::make_unique<AuthzSessionExternalStateMongod>(authzManager);
+Status AuthzManagerExternalStateMongod::query(
+ OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& projection,
+ const stdx::function<void(const BSONObj&)>& resultProcessor) {
+ try {
+ DBDirectClient client(txn);
+ client.query(resultProcessor, collectionName.ns(), query, &projection);
+ return Status::OK();
+ } catch (const DBException& e) {
+ return e.toStatus();
}
+}
- Status AuthzManagerExternalStateMongod::query(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& projection,
- const stdx::function<void(const BSONObj&)>& resultProcessor) {
- try {
- DBDirectClient client(txn);
- client.query(resultProcessor, collectionName.ns(), query, &projection);
- return Status::OK();
- } catch (const DBException& e) {
- return e.toStatus();
- }
- }
-
- Status AuthzManagerExternalStateMongod::findOne(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result) {
-
- AutoGetCollectionForRead ctx(txn, collectionName);
+Status AuthzManagerExternalStateMongod::findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result) {
+ AutoGetCollectionForRead ctx(txn, collectionName);
- BSONObj found;
- if (Helpers::findOne(txn,
- ctx.getCollection(),
- query,
- found)) {
- *result = found.getOwned();
- return Status::OK();
- }
- return Status(ErrorCodes::NoMatchingDocument, mongoutils::str::stream() <<
- "No document in " << collectionName.ns() << " matches " << query);
+ BSONObj found;
+ if (Helpers::findOne(txn, ctx.getCollection(), query, found)) {
+ *result = found.getOwned();
+ return Status::OK();
}
+ return Status(ErrorCodes::NoMatchingDocument,
+ mongoutils::str::stream() << "No document in " << collectionName.ns()
+ << " matches " << query);
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_d.h b/src/mongo/db/auth/authz_manager_external_state_d.h
index fd2c6537166..f0fb7f91568 100644
--- a/src/mongo/db/auth/authz_manager_external_state_d.h
+++ b/src/mongo/db/auth/authz_manager_external_state_d.h
@@ -39,28 +39,28 @@
namespace mongo {
- /**
- * The implementation of AuthzManagerExternalState functionality for mongod.
- */
- class AuthzManagerExternalStateMongod : public AuthzManagerExternalStateLocal {
- MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMongod);
+/**
+ * The implementation of AuthzManagerExternalState functionality for mongod.
+ */
+class AuthzManagerExternalStateMongod : public AuthzManagerExternalStateLocal {
+ MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMongod);
- public:
- AuthzManagerExternalStateMongod();
- virtual ~AuthzManagerExternalStateMongod();
+public:
+ AuthzManagerExternalStateMongod();
+ virtual ~AuthzManagerExternalStateMongod();
- std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) override;
+ std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
+ AuthorizationManager* authzManager) override;
- virtual Status findOne(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result);
- virtual Status query(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& projection,
- const stdx::function<void(const BSONObj&)>& resultProcessor);
- };
+ virtual Status findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result);
+ virtual Status query(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& projection,
+ const stdx::function<void(const BSONObj&)>& resultProcessor);
+};
-} // namespace mongo
+} // namespace mongo
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 e1f4b8e0301..ed0c8e560a5 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_local.cpp
@@ -42,422 +42,385 @@
namespace mongo {
- using std::vector;
+using std::vector;
- Status AuthzManagerExternalStateLocal::initialize(OperationContext* txn) {
- Status status = _initializeRoleGraph(txn);
- if (!status.isOK()) {
- if (status == ErrorCodes::GraphContainsCycle) {
- error() << "Cycle detected in admin.system.roles; role inheritance disabled. "
- "Remove the listed cycle and any others to re-enable role inheritance. " <<
- status.reason();
- }
- else {
- error() << "Could not generate role graph from admin.system.roles; "
- "only system roles available: " << status;
- }
+Status AuthzManagerExternalStateLocal::initialize(OperationContext* txn) {
+ Status status = _initializeRoleGraph(txn);
+ if (!status.isOK()) {
+ if (status == ErrorCodes::GraphContainsCycle) {
+ error() << "Cycle detected in admin.system.roles; role inheritance disabled. "
+ "Remove the listed cycle and any others to re-enable role inheritance. "
+ << status.reason();
+ } else {
+ error() << "Could not generate role graph from admin.system.roles; "
+ "only system roles available: " << status;
}
-
- return Status::OK();
}
- Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(
- OperationContext* txn, int* outVersion) {
- BSONObj versionDoc;
- Status status = findOne(txn,
- AuthorizationManager::versionCollectionNamespace,
- AuthorizationManager::versionDocumentQuery,
- &versionDoc);
- if (status.isOK()) {
- BSONElement versionElement = versionDoc[AuthorizationManager::schemaVersionFieldName];
- if (versionElement.isNumber()) {
- *outVersion = versionElement.numberInt();
- return Status::OK();
- }
- else if (versionElement.eoo()) {
- return Status(ErrorCodes::NoSuchKey, mongoutils::str::stream() <<
- "No " << AuthorizationManager::schemaVersionFieldName <<
- " field in version document.");
- }
- else {
- return Status(ErrorCodes::TypeMismatch, mongoutils::str::stream() <<
- "Could not determine schema version of authorization data. "
- "Bad (non-numeric) type " << typeName(versionElement.type()) <<
- " (" << versionElement.type() << ") for " <<
- AuthorizationManager::schemaVersionFieldName <<
- " field in version document");
- }
- }
- else if (status == ErrorCodes::NoMatchingDocument) {
- *outVersion = AuthorizationManager::schemaVersion28SCRAM;
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(OperationContext* txn,
+ int* outVersion) {
+ BSONObj versionDoc;
+ Status status = findOne(txn,
+ AuthorizationManager::versionCollectionNamespace,
+ AuthorizationManager::versionDocumentQuery,
+ &versionDoc);
+ if (status.isOK()) {
+ BSONElement versionElement = versionDoc[AuthorizationManager::schemaVersionFieldName];
+ if (versionElement.isNumber()) {
+ *outVersion = versionElement.numberInt();
return Status::OK();
+ } else if (versionElement.eoo()) {
+ return Status(ErrorCodes::NoSuchKey,
+ mongoutils::str::stream() << "No "
+ << AuthorizationManager::schemaVersionFieldName
+ << " field in version document.");
+ } else {
+ return Status(ErrorCodes::TypeMismatch,
+ mongoutils::str::stream()
+ << "Could not determine schema version of authorization data. "
+ "Bad (non-numeric) type " << typeName(versionElement.type())
+ << " (" << versionElement.type() << ") for "
+ << AuthorizationManager::schemaVersionFieldName
+ << " field in version document");
}
- else {
- return status;
- }
+ } else if (status == ErrorCodes::NoMatchingDocument) {
+ *outVersion = AuthorizationManager::schemaVersion28SCRAM;
+ return Status::OK();
+ } else {
+ return status;
}
+}
namespace {
- void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) {
- fassert(17153, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME,
- role.getRole()));
- fassert(17154, object.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME,
- role.getDB()));
- }
-
- void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) {
- for (; roles.more(); roles.next()) {
- mutablebson::Element roleElement = array.getDocument().makeElementObject("");
- addRoleNameToObjectElement(roleElement, roles.get());
- fassert(17155, array.pushBack(roleElement));
- }
+void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) {
+ fassert(17153, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole()));
+ fassert(17154, object.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, role.getDB()));
+}
+
+void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) {
+ for (; roles.more(); roles.next()) {
+ mutablebson::Element roleElement = array.getDocument().makeElementObject("");
+ addRoleNameToObjectElement(roleElement, roles.get());
+ fassert(17155, array.pushBack(roleElement));
}
-
- void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,
- mutablebson::Element warningsElement,
- const PrivilegeVector& privileges) {
- std::string errmsg;
- for (size_t i = 0; i < privileges.size(); ++i) {
- ParsedPrivilege pp;
- if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) {
- fassert(17156, privilegesElement.appendObject("", pp.toBSON()));
- } else {
- fassert(17157,
- warningsElement.appendString(
+}
+
+void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,
+ mutablebson::Element warningsElement,
+ const PrivilegeVector& privileges) {
+ std::string errmsg;
+ for (size_t i = 0; i < privileges.size(); ++i) {
+ ParsedPrivilege pp;
+ if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) {
+ fassert(17156, privilegesElement.appendObject("", pp.toBSON()));
+ } else {
+ fassert(17157,
+ warningsElement.appendString(
"",
- std::string(mongoutils::str::stream() <<
- "Skipped privileges on resource " <<
- privileges[i].getResourcePattern().toString() <<
- ". Reason: " << errmsg)));
- }
+ std::string(mongoutils::str::stream()
+ << "Skipped privileges on resource "
+ << privileges[i].getResourcePattern().toString()
+ << ". Reason: " << errmsg)));
}
}
+}
} // namespace
- bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* txn) {
- BSONObj userBSONObj;
- Status status = findOne(
- txn,
- AuthorizationManager::usersCollectionNamespace,
- BSONObj(),
- &userBSONObj);
- // If we were unable to complete the query,
- // it's best to assume that there _are_ privilege documents.
- return status != ErrorCodes::NoMatchingDocument;
- }
-
- Status AuthzManagerExternalStateLocal::getUserDescription(
- OperationContext* txn,
- const UserName& userName,
- BSONObj* result) {
-
- BSONObj userDoc;
- Status status = _getUserDocument(txn, userName, &userDoc);
- if (!status.isOK())
- return status;
+bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* txn) {
+ BSONObj userBSONObj;
+ Status status =
+ findOne(txn, AuthorizationManager::usersCollectionNamespace, BSONObj(), &userBSONObj);
+ // If we were unable to complete the query,
+ // it's best to assume that there _are_ privilege documents.
+ return status != ErrorCodes::NoMatchingDocument;
+}
+
+Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result) {
+ BSONObj userDoc;
+ Status status = _getUserDocument(txn, userName, &userDoc);
+ if (!status.isOK())
+ return status;
- BSONElement directRolesElement;
- status = bsonExtractTypedField(userDoc, "roles", Array, &directRolesElement);
- if (!status.isOK())
- return status;
- std::vector<RoleName> directRoles;
- status = V2UserDocumentParser::parseRoleVector(BSONArray(directRolesElement.Obj()),
- &directRoles);
- if (!status.isOK())
- return status;
+ BSONElement directRolesElement;
+ status = bsonExtractTypedField(userDoc, "roles", Array, &directRolesElement);
+ if (!status.isOK())
+ return status;
+ std::vector<RoleName> directRoles;
+ status =
+ V2UserDocumentParser::parseRoleVector(BSONArray(directRolesElement.Obj()), &directRoles);
+ if (!status.isOK())
+ return status;
- unordered_set<RoleName> indirectRoles;
- PrivilegeVector allPrivileges;
- bool isRoleGraphInconsistent;
- {
- stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
- isRoleGraphInconsistent = _roleGraphState == roleGraphStateConsistent;
- for (size_t i = 0; i < directRoles.size(); ++i) {
- const RoleName& role(directRoles[i]);
- indirectRoles.insert(role);
- if (isRoleGraphInconsistent) {
- for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(role);
- subordinates.more();
- subordinates.next()) {
-
- indirectRoles.insert(subordinates.get());
- }
- }
- const PrivilegeVector& rolePrivileges(
- isRoleGraphInconsistent ?
- _roleGraph.getAllPrivileges(role) :
- _roleGraph.getDirectPrivileges(role));
- for (PrivilegeVector::const_iterator priv = rolePrivileges.begin(),
- end = rolePrivileges.end();
- priv != end;
- ++priv) {
-
- Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, *priv);
+ unordered_set<RoleName> indirectRoles;
+ PrivilegeVector allPrivileges;
+ bool isRoleGraphInconsistent;
+ {
+ stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
+ isRoleGraphInconsistent = _roleGraphState == roleGraphStateConsistent;
+ for (size_t i = 0; i < directRoles.size(); ++i) {
+ const RoleName& role(directRoles[i]);
+ indirectRoles.insert(role);
+ if (isRoleGraphInconsistent) {
+ for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(role);
+ subordinates.more();
+ subordinates.next()) {
+ indirectRoles.insert(subordinates.get());
}
}
+ const PrivilegeVector& rolePrivileges(isRoleGraphInconsistent
+ ? _roleGraph.getAllPrivileges(role)
+ : _roleGraph.getDirectPrivileges(role));
+ for (PrivilegeVector::const_iterator priv = rolePrivileges.begin(),
+ end = rolePrivileges.end();
+ priv != end;
+ ++priv) {
+ Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, *priv);
+ }
}
-
- mutablebson::Document resultDoc(userDoc, mutablebson::Document::kInPlaceDisabled);
- mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
- mutablebson::Element privilegesElement = resultDoc.makeElementArray("inheritedPrivileges");
- mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
- fassert(17159, resultDoc.root().pushBack(inheritedRolesElement));
- fassert(17158, resultDoc.root().pushBack(privilegesElement));
- if (!isRoleGraphInconsistent) {
- fassert(17160, warningsElement.appendString(
- "", "Role graph inconsistent, only direct privileges available."));
- }
- addRoleNameObjectsToArrayElement(inheritedRolesElement,
- makeRoleNameIteratorForContainer(indirectRoles));
- addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement, warningsElement, allPrivileges);
- if (warningsElement.hasChildren()) {
- fassert(17161, resultDoc.root().pushBack(warningsElement));
- }
- *result = resultDoc.getObject();
- return Status::OK();
}
- Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* txn,
- const UserName& userName,
- BSONObj* userDoc) {
- Status status = findOne(
- txn,
- AuthorizationManager::usersCollectionNamespace,
- BSON(AuthorizationManager::USER_NAME_FIELD_NAME << userName.getUser() <<
- AuthorizationManager::USER_DB_FIELD_NAME << userName.getDB()),
- userDoc);
- if (status == ErrorCodes::NoMatchingDocument) {
- status = Status(ErrorCodes::UserNotFound, mongoutils::str::stream() <<
- "Could not find user " << userName.getFullName());
- }
- return status;
+ mutablebson::Document resultDoc(userDoc, mutablebson::Document::kInPlaceDisabled);
+ mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
+ mutablebson::Element privilegesElement = resultDoc.makeElementArray("inheritedPrivileges");
+ mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
+ fassert(17159, resultDoc.root().pushBack(inheritedRolesElement));
+ fassert(17158, resultDoc.root().pushBack(privilegesElement));
+ if (!isRoleGraphInconsistent) {
+ fassert(17160,
+ warningsElement.appendString(
+ "", "Role graph inconsistent, only direct privileges available."));
}
-
- Status AuthzManagerExternalStateLocal::getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result) {
- stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
- return _getRoleDescription_inlock(roleName, showPrivileges, result);
+ addRoleNameObjectsToArrayElement(inheritedRolesElement,
+ makeRoleNameIteratorForContainer(indirectRoles));
+ addPrivilegeObjectsOrWarningsToArrayElement(privilegesElement, warningsElement, allPrivileges);
+ if (warningsElement.hasChildren()) {
+ fassert(17161, resultDoc.root().pushBack(warningsElement));
+ }
+ *result = resultDoc.getObject();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* userDoc) {
+ Status status = findOne(txn,
+ AuthorizationManager::usersCollectionNamespace,
+ BSON(AuthorizationManager::USER_NAME_FIELD_NAME
+ << userName.getUser() << AuthorizationManager::USER_DB_FIELD_NAME
+ << userName.getDB()),
+ userDoc);
+ if (status == ErrorCodes::NoMatchingDocument) {
+ status =
+ Status(ErrorCodes::UserNotFound,
+ mongoutils::str::stream() << "Could not find user " << userName.getFullName());
+ }
+ return status;
+}
+
+Status AuthzManagerExternalStateLocal::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
+ stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
+ return _getRoleDescription_inlock(roleName, showPrivileges, result);
+}
+
+Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
+ if (!_roleGraph.roleExists(roleName))
+ return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());
+
+ mutablebson::Document resultDoc;
+ fassert(17162,
+ resultDoc.root().appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME,
+ roleName.getRole()));
+ fassert(
+ 17163,
+ resultDoc.root().appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB()));
+ fassert(17267, resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
+ mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
+ fassert(17164, resultDoc.root().pushBack(rolesElement));
+ mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
+ fassert(17165, resultDoc.root().pushBack(inheritedRolesElement));
+ mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges");
+ mutablebson::Element inheritedPrivilegesElement =
+ resultDoc.makeElementArray("inheritedPrivileges");
+ if (showPrivileges) {
+ fassert(17166, resultDoc.root().pushBack(privilegesElement));
}
+ mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
- Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result) {
- if (!_roleGraph.roleExists(roleName))
- return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());
-
- mutablebson::Document resultDoc;
- fassert(17162, resultDoc.root().appendString(
- AuthorizationManager::ROLE_NAME_FIELD_NAME, roleName.getRole()));
- fassert(17163, resultDoc.root().appendString(
- AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB()));
- fassert(17267,
- resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
- mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
- fassert(17164, resultDoc.root().pushBack(rolesElement));
- mutablebson::Element inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
- fassert(17165, resultDoc.root().pushBack(inheritedRolesElement));
- mutablebson::Element privilegesElement = resultDoc.makeElementArray("privileges");
- mutablebson::Element inheritedPrivilegesElement =
- resultDoc.makeElementArray("inheritedPrivileges");
+ addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
+ if (_roleGraphState == roleGraphStateConsistent) {
+ addRoleNameObjectsToArrayElement(inheritedRolesElement,
+ _roleGraph.getIndirectSubordinates(roleName));
if (showPrivileges) {
- fassert(17166, resultDoc.root().pushBack(privilegesElement));
- }
- mutablebson::Element warningsElement = resultDoc.makeElementArray("warnings");
-
- addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
- if (_roleGraphState == roleGraphStateConsistent) {
- addRoleNameObjectsToArrayElement(
- inheritedRolesElement, _roleGraph.getIndirectSubordinates(roleName));
- if (showPrivileges) {
- addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement,
- warningsElement,
- _roleGraph.getDirectPrivileges(roleName));
-
- addPrivilegeObjectsOrWarningsToArrayElement(
- inheritedPrivilegesElement,
- warningsElement,
- _roleGraph.getAllPrivileges(roleName));
-
- fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement));
- }
- }
- else if (showPrivileges) {
- warningsElement.appendString(
- "", "Role graph state inconsistent; only direct privileges available.");
addPrivilegeObjectsOrWarningsToArrayElement(
- privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
- }
- if (warningsElement.hasChildren()) {
- fassert(17167, resultDoc.root().pushBack(warningsElement));
- }
- *result = resultDoc.getObject();
- return Status::OK();
- }
+ privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
- Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- vector<BSONObj>* result) {
- stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
+ addPrivilegeObjectsOrWarningsToArrayElement(
+ inheritedPrivilegesElement, warningsElement, _roleGraph.getAllPrivileges(roleName));
- for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname);
- it.more(); it.next()) {
- if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) {
- continue;
- }
- BSONObj roleDoc;
- Status status = _getRoleDescription_inlock(it.get(), showPrivileges, &roleDoc);
- if (!status.isOK()) {
- return status;
- }
- result->push_back(roleDoc);
+ fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement));
}
- return Status::OK();
+ } else if (showPrivileges) {
+ warningsElement.appendString(
+ "", "Role graph state inconsistent; only direct privileges available.");
+ addPrivilegeObjectsOrWarningsToArrayElement(
+ privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
}
+ if (warningsElement.hasChildren()) {
+ fassert(17167, resultDoc.root().pushBack(warningsElement));
+ }
+ *result = resultDoc.getObject();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ vector<BSONObj>* result) {
+ stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
+
+ for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname); it.more(); it.next()) {
+ if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) {
+ continue;
+ }
+ BSONObj roleDoc;
+ Status status = _getRoleDescription_inlock(it.get(), showPrivileges, &roleDoc);
+ if (!status.isOK()) {
+ return status;
+ }
+ result->push_back(roleDoc);
+ }
+ return Status::OK();
+}
namespace {
- /**
- * Adds the role described in "doc" to "roleGraph". If the role cannot be added, due to
- * some error in "doc", logs a warning.
- */
- void addRoleFromDocumentOrWarn(RoleGraph* roleGraph, const BSONObj& doc) {
- Status status = roleGraph->addRoleFromDocument(doc);
- if (!status.isOK()) {
- warning() << "Skipping invalid admin.system.roles document while calculating privileges"
- " for user-defined roles: " << status << "; document " << doc;
- }
+/**
+ * Adds the role described in "doc" to "roleGraph". If the role cannot be added, due to
+ * some error in "doc", logs a warning.
+ */
+void addRoleFromDocumentOrWarn(RoleGraph* roleGraph, const BSONObj& doc) {
+ Status status = roleGraph->addRoleFromDocument(doc);
+ if (!status.isOK()) {
+ warning() << "Skipping invalid admin.system.roles document while calculating privileges"
+ " for user-defined roles: " << status << "; document " << doc;
}
+}
} // namespace
- Status AuthzManagerExternalStateLocal::_initializeRoleGraph(OperationContext* txn) {
- stdx::lock_guard<stdx::mutex> lkInitialzeRoleGraph(_roleGraphMutex);
+Status AuthzManagerExternalStateLocal::_initializeRoleGraph(OperationContext* txn) {
+ stdx::lock_guard<stdx::mutex> lkInitialzeRoleGraph(_roleGraphMutex);
- _roleGraphState = roleGraphStateInitial;
- _roleGraph = RoleGraph();
+ _roleGraphState = roleGraphStateInitial;
+ _roleGraph = RoleGraph();
- RoleGraph newRoleGraph;
- Status status = query(
- txn,
- AuthorizationManager::rolesCollectionNamespace,
- BSONObj(),
- BSONObj(),
- stdx::bind(addRoleFromDocumentOrWarn, &newRoleGraph, stdx::placeholders::_1));
- if (!status.isOK())
- return status;
+ RoleGraph newRoleGraph;
+ Status status =
+ query(txn,
+ AuthorizationManager::rolesCollectionNamespace,
+ BSONObj(),
+ BSONObj(),
+ stdx::bind(addRoleFromDocumentOrWarn, &newRoleGraph, stdx::placeholders::_1));
+ if (!status.isOK())
+ return status;
- status = newRoleGraph.recomputePrivilegeData();
+ status = newRoleGraph.recomputePrivilegeData();
+
+ RoleGraphState newState;
+ if (status == ErrorCodes::GraphContainsCycle) {
+ error() << "Inconsistent role graph during authorization manager initialization. Only "
+ "direct privileges available. " << status.reason();
+ newState = roleGraphStateHasCycle;
+ status = Status::OK();
+ } else if (status.isOK()) {
+ newState = roleGraphStateConsistent;
+ } else {
+ newState = roleGraphStateInitial;
+ }
- RoleGraphState newState;
- if (status == ErrorCodes::GraphContainsCycle) {
- error() << "Inconsistent role graph during authorization manager initialization. Only "
- "direct privileges available. " << status.reason();
- newState = roleGraphStateHasCycle;
- status = Status::OK();
- }
- else if (status.isOK()) {
- newState = roleGraphStateConsistent;
- }
- else {
- newState = roleGraphStateInitial;
+ if (status.isOK()) {
+ _roleGraph.swap(newRoleGraph);
+ _roleGraphState = newState;
+ }
+ return status;
+}
+
+class AuthzManagerExternalStateLocal::AuthzManagerLogOpHandler : public RecoveryUnit::Change {
+public:
+ // None of the parameters below (except externalState) need to live longer than
+ // the instantiations of this class
+ AuthzManagerLogOpHandler(AuthzManagerExternalStateLocal* externalState,
+ const char* op,
+ const char* ns,
+ const BSONObj& o,
+ const BSONObj* o2)
+ : _externalState(externalState),
+ _op(op),
+ _ns(ns),
+ _o(o.getOwned()),
+
+ _isO2Set(o2 ? true : false),
+ _o2(_isO2Set ? o2->getOwned() : BSONObj()) {}
+
+ virtual void commit() {
+ stdx::lock_guard<stdx::mutex> lk(_externalState->_roleGraphMutex);
+ Status status = _externalState->_roleGraph.handleLogOp(
+ _op.c_str(), NamespaceString(_ns.c_str()), _o, _isO2Set ? &_o2 : NULL);
+
+ if (status == ErrorCodes::OplogOperationUnsupported) {
+ _externalState->_roleGraph = RoleGraph();
+ _externalState->_roleGraphState = _externalState->roleGraphStateInitial;
+ BSONObjBuilder oplogEntryBuilder;
+ oplogEntryBuilder << "op" << _op << "ns" << _ns << "o" << _o;
+ if (_isO2Set)
+ oplogEntryBuilder << "o2" << _o2;
+ error() << "Unsupported modification to roles collection in oplog; "
+ "restart this process to reenable user-defined roles; " << status.reason()
+ << "; Oplog entry: " << oplogEntryBuilder.done();
+ } else if (!status.isOK()) {
+ warning() << "Skipping bad update to roles collection in oplog. " << status
+ << " Oplog entry: " << _op;
}
-
- if (status.isOK()) {
- _roleGraph.swap(newRoleGraph);
- _roleGraphState = newState;
+ status = _externalState->_roleGraph.recomputePrivilegeData();
+ if (status == ErrorCodes::GraphContainsCycle) {
+ _externalState->_roleGraphState = _externalState->roleGraphStateHasCycle;
+ error() << "Inconsistent role graph during authorization manager initialization. "
+ "Only direct privileges available. " << status.reason()
+ << " after applying oplog entry " << _op;
+ } else {
+ fassert(17183, status);
+ _externalState->_roleGraphState = _externalState->roleGraphStateConsistent;
}
- return status;
}
- class AuthzManagerExternalStateLocal::AuthzManagerLogOpHandler : public RecoveryUnit::Change {
- public:
+ virtual void rollback() {}
- // None of the parameters below (except externalState) need to live longer than
- // the instantiations of this class
- AuthzManagerLogOpHandler(AuthzManagerExternalStateLocal* externalState,
- const char* op,
- const char* ns,
- const BSONObj& o,
- const BSONObj* o2):
- _externalState(externalState),
- _op(op),
- _ns(ns),
- _o(o.getOwned()),
+private:
+ AuthzManagerExternalStateLocal* _externalState;
+ const std::string _op;
+ const std::string _ns;
+ const BSONObj _o;
- _isO2Set(o2 ? true : false),
- _o2(_isO2Set ? o2->getOwned() : BSONObj()) {
+ const bool _isO2Set;
+ const BSONObj _o2;
+};
- }
-
- virtual void commit() {
- stdx::lock_guard<stdx::mutex> lk(_externalState->_roleGraphMutex);
- Status status = _externalState->_roleGraph.handleLogOp(_op.c_str(),
- NamespaceString(_ns.c_str()),
- _o,
- _isO2Set ? &_o2 : NULL);
-
- if (status == ErrorCodes::OplogOperationUnsupported) {
- _externalState->_roleGraph = RoleGraph();
- _externalState->_roleGraphState = _externalState->roleGraphStateInitial;
- BSONObjBuilder oplogEntryBuilder;
- oplogEntryBuilder << "op" << _op << "ns" << _ns << "o" << _o;
- if (_isO2Set)
- oplogEntryBuilder << "o2" << _o2;
- error() << "Unsupported modification to roles collection in oplog; "
- "restart this process to reenable user-defined roles; " << status.reason() <<
- "; Oplog entry: " << oplogEntryBuilder.done();
- }
- else if (!status.isOK()) {
- warning() << "Skipping bad update to roles collection in oplog. " << status <<
- " Oplog entry: " << _op;
- }
- status = _externalState->_roleGraph.recomputePrivilegeData();
- if (status == ErrorCodes::GraphContainsCycle) {
- _externalState->_roleGraphState = _externalState->roleGraphStateHasCycle;
- error() << "Inconsistent role graph during authorization manager initialization. "
- "Only direct privileges available. " << status.reason() <<
- " after applying oplog entry " << _op;
- }
- else {
- fassert(17183, status);
- _externalState->_roleGraphState = _externalState->roleGraphStateConsistent;
- }
-
- }
-
- virtual void rollback() { }
-
- private:
- AuthzManagerExternalStateLocal* _externalState;
- const std::string _op;
- const std::string _ns;
- const BSONObj _o;
-
- const bool _isO2Set;
- const BSONObj _o2;
- };
-
- void AuthzManagerExternalStateLocal::logOp(
- OperationContext* txn,
- const char* op,
- const char* ns,
- const BSONObj& o,
- BSONObj* o2) {
-
- if (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
- ns == AuthorizationManager::adminCommandNamespace.ns()) {
-
- txn->recoveryUnit()->registerChange(new AuthzManagerLogOpHandler(this,
- op,
- ns,
- o,
- o2));
- }
+void AuthzManagerExternalStateLocal::logOp(
+ OperationContext* txn, const char* op, const char* ns, const BSONObj& o, BSONObj* o2) {
+ if (ns == AuthorizationManager::rolesCollectionNamespace.ns() ||
+ ns == AuthorizationManager::adminCommandNamespace.ns()) {
+ txn->recoveryUnit()->registerChange(new AuthzManagerLogOpHandler(this, op, ns, o, o2));
}
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_local.h b/src/mongo/db/auth/authz_manager_external_state_local.h
index f8243aff00e..fe4a90ed1cd 100644
--- a/src/mongo/db/auth/authz_manager_external_state_local.h
+++ b/src/mongo/db/auth/authz_manager_external_state_local.h
@@ -40,105 +40,103 @@
namespace mongo {
+/**
+ * Common implementation of AuthzManagerExternalState for systems where role
+ * and user information are stored locally.
+ */
+class AuthzManagerExternalStateLocal : public AuthzManagerExternalState {
+ MONGO_DISALLOW_COPYING(AuthzManagerExternalStateLocal);
+
+public:
+ virtual ~AuthzManagerExternalStateLocal() = default;
+
+ virtual Status initialize(OperationContext* txn);
+
+ virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion);
+ virtual Status getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result);
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ std::vector<BSONObj>* result);
+
+ bool hasAnyPrivilegeDocuments(OperationContext* txn) override;
+
/**
- * Common implementation of AuthzManagerExternalState for systems where role
- * and user information are stored locally.
+ * Finds a document matching "query" in "collectionName", and store a shared-ownership
+ * copy into "result".
+ *
+ * Returns Status::OK() on success. If no match is found, returns
+ * ErrorCodes::NoMatchingDocument. Other errors returned as appropriate.
*/
- class AuthzManagerExternalStateLocal : public AuthzManagerExternalState {
- MONGO_DISALLOW_COPYING(AuthzManagerExternalStateLocal);
-
- public:
- virtual ~AuthzManagerExternalStateLocal() = default;
-
- virtual Status initialize(OperationContext* txn);
-
- virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion);
- virtual Status getUserDescription(
- OperationContext* txn, const UserName& userName, BSONObj* result);
- virtual Status getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result);
- virtual Status getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- std::vector<BSONObj>* result);
-
- bool hasAnyPrivilegeDocuments(OperationContext* txn) override;
-
- /**
- * Finds a document matching "query" in "collectionName", and store a shared-ownership
- * copy into "result".
- *
- * Returns Status::OK() on success. If no match is found, returns
- * ErrorCodes::NoMatchingDocument. Other errors returned as appropriate.
- */
- virtual Status findOne(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result) = 0;
-
- /**
- * Finds all documents matching "query" in "collectionName". For each document returned,
- * calls the function resultProcessor on it.
- */
- virtual Status query(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& projection,
- const stdx::function<void(const BSONObj&)>& resultProcessor) = 0;
-
- virtual void logOp(
- OperationContext* txn,
- const char* op,
- const char* ns,
- const BSONObj& o,
- BSONObj* o2);
-
- protected:
- AuthzManagerExternalStateLocal() = default;
-
- /**
- * Fetches the user document for "userName" from local storage, and stores it into "result".
- */
- virtual Status _getUserDocument(OperationContext* txn,
- const UserName& userName,
- BSONObj* result);
- private:
- enum RoleGraphState {
- roleGraphStateInitial = 0,
- roleGraphStateConsistent,
- roleGraphStateHasCycle
- };
-
- /**
- * RecoveryUnit::Change subclass used to commit work for AuthzManager logOp listener.
- */
- class AuthzManagerLogOpHandler;
-
- /**
- * Initializes the role graph from the contents of the admin.system.roles collection.
- */
- Status _initializeRoleGraph(OperationContext* txn);
-
- Status _getRoleDescription_inlock(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result);
- /**
- * Eventually consistent, in-memory representation of all roles in the system (both
- * user-defined and built-in). Synchronized via _roleGraphMutex.
- */
- RoleGraph _roleGraph;
-
- /**
- * State of _roleGraph, one of "initial", "consistent" and "has cycle". Synchronized via
- * _roleGraphMutex.
- */
- RoleGraphState _roleGraphState = roleGraphStateInitial;
-
- /**
- * Guards _roleGraphState and _roleGraph.
- */
- stdx::mutex _roleGraphMutex;
+ virtual Status findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result) = 0;
+
+ /**
+ * Finds all documents matching "query" in "collectionName". For each document returned,
+ * calls the function resultProcessor on it.
+ */
+ virtual Status query(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& projection,
+ const stdx::function<void(const BSONObj&)>& resultProcessor) = 0;
+
+ virtual void logOp(
+ OperationContext* txn, const char* op, const char* ns, const BSONObj& o, BSONObj* o2);
+
+protected:
+ AuthzManagerExternalStateLocal() = default;
+
+ /**
+ * Fetches the user document for "userName" from local storage, and stores it into "result".
+ */
+ virtual Status _getUserDocument(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result);
+
+private:
+ enum RoleGraphState {
+ roleGraphStateInitial = 0,
+ roleGraphStateConsistent,
+ roleGraphStateHasCycle
};
-} // namespace mongo
+ /**
+ * RecoveryUnit::Change subclass used to commit work for AuthzManager logOp listener.
+ */
+ class AuthzManagerLogOpHandler;
+
+ /**
+ * Initializes the role graph from the contents of the admin.system.roles collection.
+ */
+ Status _initializeRoleGraph(OperationContext* txn);
+
+ Status _getRoleDescription_inlock(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
+ /**
+ * Eventually consistent, in-memory representation of all roles in the system (both
+ * user-defined and built-in). Synchronized via _roleGraphMutex.
+ */
+ RoleGraph _roleGraph;
+
+ /**
+ * State of _roleGraph, one of "initial", "consistent" and "has cycle". Synchronized via
+ * _roleGraphMutex.
+ */
+ RoleGraphState _roleGraphState = roleGraphStateInitial;
+
+ /**
+ * Guards _roleGraphState and _roleGraph.
+ */
+ stdx::mutex _roleGraphMutex;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.cpp b/src/mongo/db/auth/authz_manager_external_state_mock.cpp
index dc15b46f58d..6dc24b6a8d6 100644
--- a/src/mongo/db/auth/authz_manager_external_state_mock.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_mock.cpp
@@ -48,282 +48,249 @@
namespace mongo {
namespace {
- void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) {
- fassert(17175, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole()));
- fassert(17176, object.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, role.getDB()));
+void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) {
+ fassert(17175, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole()));
+ fassert(17176, object.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, role.getDB()));
+}
+
+void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) {
+ for (; roles.more(); roles.next()) {
+ mutablebson::Element roleElement = array.getDocument().makeElementObject("");
+ addRoleNameToObjectElement(roleElement, roles.get());
+ fassert(17177, array.pushBack(roleElement));
}
-
- void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) {
- for (; roles.more(); roles.next()) {
- mutablebson::Element roleElement = array.getDocument().makeElementObject("");
- addRoleNameToObjectElement(roleElement, roles.get());
- fassert(17177, array.pushBack(roleElement));
- }
- }
-
- void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,
- mutablebson::Element warningsElement,
- const PrivilegeVector& privileges) {
- std::string errmsg;
- for (size_t i = 0; i < privileges.size(); ++i) {
- ParsedPrivilege pp;
- if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) {
- fassert(17178, privilegesElement.appendObject("", pp.toBSON()));
- } else {
- fassert(17179,
- warningsElement.appendString(
+}
+
+void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,
+ mutablebson::Element warningsElement,
+ const PrivilegeVector& privileges) {
+ std::string errmsg;
+ for (size_t i = 0; i < privileges.size(); ++i) {
+ ParsedPrivilege pp;
+ if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) {
+ fassert(17178, privilegesElement.appendObject("", pp.toBSON()));
+ } else {
+ fassert(17179,
+ warningsElement.appendString(
"",
- std::string(mongoutils::str::stream() <<
- "Skipped privileges on resource " <<
- privileges[i].getResourcePattern().toString() <<
- ". Reason: " << errmsg)));
- }
+ std::string(mongoutils::str::stream()
+ << "Skipped privileges on resource "
+ << privileges[i].getResourcePattern().toString()
+ << ". Reason: " << errmsg)));
}
}
+}
} // namespace
- AuthzManagerExternalStateMock::AuthzManagerExternalStateMock() : _authzManager(NULL) {}
- AuthzManagerExternalStateMock::~AuthzManagerExternalStateMock() {}
-
- void AuthzManagerExternalStateMock::setAuthorizationManager(
- AuthorizationManager* authzManager) {
- _authzManager = authzManager;
+AuthzManagerExternalStateMock::AuthzManagerExternalStateMock() : _authzManager(NULL) {}
+AuthzManagerExternalStateMock::~AuthzManagerExternalStateMock() {}
+
+void AuthzManagerExternalStateMock::setAuthorizationManager(AuthorizationManager* authzManager) {
+ _authzManager = authzManager;
+}
+
+void AuthzManagerExternalStateMock::setAuthzVersion(int version) {
+ OperationContextNoop opCtx;
+ uassertStatusOK(
+ updateOne(&opCtx,
+ AuthorizationManager::versionCollectionNamespace,
+ AuthorizationManager::versionDocumentQuery,
+ BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName << version)),
+ true,
+ BSONObj()));
+}
+
+std::unique_ptr<AuthzSessionExternalState>
+AuthzManagerExternalStateMock::makeAuthzSessionExternalState(AuthorizationManager* authzManager) {
+ return stdx::make_unique<AuthzSessionExternalStateMock>(authzManager);
+}
+
+Status AuthzManagerExternalStateMock::findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result) {
+ BSONObjCollection::iterator iter;
+ Status status = _findOneIter(collectionName, query, &iter);
+ if (!status.isOK())
+ return status;
+ *result = iter->copy();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMock::query(
+ OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj&,
+ const stdx::function<void(const BSONObj&)>& resultProcessor) {
+ std::vector<BSONObjCollection::iterator> iterVector;
+ Status status = _queryVector(collectionName, query, &iterVector);
+ if (!status.isOK()) {
+ return status;
}
-
- void AuthzManagerExternalStateMock::setAuthzVersion(int version) {
- OperationContextNoop opCtx;
- uassertStatusOK(
- updateOne(&opCtx,
- AuthorizationManager::versionCollectionNamespace,
- AuthorizationManager::versionDocumentQuery,
- BSON("$set" << BSON(AuthorizationManager::schemaVersionFieldName <<
- version)),
- true,
- BSONObj()));
+ try {
+ for (std::vector<BSONObjCollection::iterator>::iterator it = iterVector.begin();
+ it != iterVector.end();
+ ++it) {
+ resultProcessor(**it);
+ }
+ } catch (const DBException& ex) {
+ status = ex.toStatus();
}
-
- std::unique_ptr<AuthzSessionExternalState>
- AuthzManagerExternalStateMock::makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) {
-
- return stdx::make_unique<AuthzSessionExternalStateMock>(authzManager);
+ return status;
+}
+
+Status AuthzManagerExternalStateMock::insert(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& document,
+ const BSONObj&) {
+ BSONObj toInsert;
+ if (document["_id"].eoo()) {
+ BSONObjBuilder docWithIdBuilder;
+ docWithIdBuilder.append("_id", OID::gen());
+ docWithIdBuilder.appendElements(document);
+ toInsert = docWithIdBuilder.obj();
+ } else {
+ toInsert = document.copy();
}
+ _documents[collectionName].push_back(toInsert);
- Status AuthzManagerExternalStateMock::findOne(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result) {
- BSONObjCollection::iterator iter;
- Status status = _findOneIter(collectionName, query, &iter);
- if (!status.isOK())
- return status;
- *result = iter->copy();
- return Status::OK();
+ if (_authzManager) {
+ _authzManager->logOp(txn, "i", collectionName.ns().c_str(), toInsert, NULL);
}
- Status AuthzManagerExternalStateMock::query(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj&,
- const stdx::function<void(const BSONObj&)>& resultProcessor) {
- std::vector<BSONObjCollection::iterator> iterVector;
- Status status = _queryVector(collectionName, query, &iterVector);
- if (!status.isOK()) {
- return status;
- }
- try {
- for (std::vector<BSONObjCollection::iterator>::iterator it = iterVector.begin();
- it != iterVector.end(); ++it) {
- resultProcessor(**it);
- }
- }
- catch (const DBException& ex) {
- status = ex.toStatus();
- }
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMock::insertPrivilegeDocument(OperationContext* txn,
+ const BSONObj& userObj,
+ const BSONObj& writeConcern) {
+ return insert(txn, AuthorizationManager::usersCollectionNamespace, userObj, writeConcern);
+}
+
+Status AuthzManagerExternalStateMock::updateOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& updatePattern,
+ bool upsert,
+ const BSONObj& writeConcern) {
+ namespace mmb = mutablebson;
+ UpdateDriver::Options updateOptions;
+ UpdateDriver driver(updateOptions);
+ Status status = driver.parse(updatePattern);
+ if (!status.isOK())
return status;
- }
- Status AuthzManagerExternalStateMock::insert(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& document,
- const BSONObj&) {
- BSONObj toInsert;
- if (document["_id"].eoo()) {
- BSONObjBuilder docWithIdBuilder;
- docWithIdBuilder.append("_id", OID::gen());
- docWithIdBuilder.appendElements(document);
- toInsert = docWithIdBuilder.obj();
- }
- else {
- toInsert = document.copy();
- }
- _documents[collectionName].push_back(toInsert);
+ BSONObjCollection::iterator iter;
+ status = _findOneIter(collectionName, query, &iter);
+ mmb::Document document;
+ if (status.isOK()) {
+ document.reset(*iter, mmb::Document::kInPlaceDisabled);
+ BSONObj logObj;
+ status = driver.update(StringData(), &document, &logObj);
+ if (!status.isOK())
+ return status;
+ BSONObj newObj = document.getObject().copy();
+ *iter = newObj;
+ BSONObj idQuery = driver.makeOplogEntryQuery(newObj, false);
if (_authzManager) {
- _authzManager->logOp(
- txn,
- "i",
- collectionName.ns().c_str(),
- toInsert,
- NULL);
+ _authzManager->logOp(txn, "u", collectionName.ns().c_str(), logObj, &idQuery);
}
return Status::OK();
- }
-
- Status AuthzManagerExternalStateMock::insertPrivilegeDocument(
- OperationContext* txn,
- const BSONObj& userObj,
- const BSONObj& writeConcern) {
- return insert(txn, AuthorizationManager::usersCollectionNamespace, userObj, writeConcern);
- }
-
- Status AuthzManagerExternalStateMock::updateOne(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& updatePattern,
- bool upsert,
- const BSONObj& writeConcern) {
-
- namespace mmb = mutablebson;
- UpdateDriver::Options updateOptions;
- UpdateDriver driver(updateOptions);
- Status status = driver.parse(updatePattern);
- if (!status.isOK())
- return status;
-
- BSONObjCollection::iterator iter;
- status = _findOneIter(collectionName, query, &iter);
- mmb::Document document;
- if (status.isOK()) {
- document.reset(*iter, mmb::Document::kInPlaceDisabled);
- BSONObj logObj;
- status = driver.update(StringData(), &document, &logObj);
- if (!status.isOK())
- return status;
- BSONObj newObj = document.getObject().copy();
- *iter = newObj;
- BSONObj idQuery = driver.makeOplogEntryQuery(newObj, false);
-
- if (_authzManager) {
- _authzManager->logOp(
- txn,
- "u",
- collectionName.ns().c_str(),
- logObj,
- &idQuery);
- }
-
- return Status::OK();
+ } else if (status == ErrorCodes::NoMatchingDocument && upsert) {
+ if (query.hasField("_id")) {
+ document.root().appendElement(query["_id"]);
}
- else if (status == ErrorCodes::NoMatchingDocument && upsert) {
- if (query.hasField("_id")) {
- document.root().appendElement(query["_id"]);
- }
- status = driver.populateDocumentWithQueryFields(query, NULL, document);
- if (!status.isOK()) {
- return status;
- }
- status = driver.update(StringData(), &document);
- if (!status.isOK()) {
- return status;
- }
- return insert(txn, collectionName, document.getObject(), writeConcern);
+ status = driver.populateDocumentWithQueryFields(query, NULL, document);
+ if (!status.isOK()) {
+ return status;
}
- else {
+ status = driver.update(StringData(), &document);
+ if (!status.isOK()) {
return status;
}
+ return insert(txn, collectionName, document.getObject(), writeConcern);
+ } else {
+ return status;
}
+}
+
+Status AuthzManagerExternalStateMock::update(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& updatePattern,
+ bool upsert,
+ bool multi,
+ const BSONObj& writeConcern,
+ int* nMatched) {
+ return Status(ErrorCodes::InternalError,
+ "AuthzManagerExternalStateMock::update not implemented in mock.");
+}
+
+Status AuthzManagerExternalStateMock::remove(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj&,
+ int* numRemoved) {
+ int n = 0;
+ BSONObjCollection::iterator iter;
+ while (_findOneIter(collectionName, query, &iter).isOK()) {
+ BSONObj idQuery = (*iter)["_id"].wrap();
+ _documents[collectionName].erase(iter);
+ ++n;
- Status AuthzManagerExternalStateMock::update(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& updatePattern,
- bool upsert,
- bool multi,
- const BSONObj& writeConcern,
- int* nMatched) {
- return Status(ErrorCodes::InternalError,
- "AuthzManagerExternalStateMock::update not implemented in mock.");
- }
-
- Status AuthzManagerExternalStateMock::remove(
- OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj&,
- int* numRemoved) {
- int n = 0;
- BSONObjCollection::iterator iter;
- while (_findOneIter(collectionName, query, &iter).isOK()) {
- BSONObj idQuery = (*iter)["_id"].wrap();
- _documents[collectionName].erase(iter);
- ++n;
-
- if (_authzManager) {
- _authzManager->logOp(
- txn,
- "d",
- collectionName.ns().c_str(),
- idQuery,
- NULL);
- }
-
+ if (_authzManager) {
+ _authzManager->logOp(txn, "d", collectionName.ns().c_str(), idQuery, NULL);
}
- *numRemoved = n;
- return Status::OK();
}
-
- std::vector<BSONObj> AuthzManagerExternalStateMock::getCollectionContents(
- const NamespaceString& collectionName) {
- return mapFindWithDefault(_documents, collectionName, std::vector<BSONObj>());
+ *numRemoved = n;
+ return Status::OK();
+}
+
+std::vector<BSONObj> AuthzManagerExternalStateMock::getCollectionContents(
+ const NamespaceString& collectionName) {
+ return mapFindWithDefault(_documents, collectionName, std::vector<BSONObj>());
+}
+
+Status AuthzManagerExternalStateMock::_findOneIter(const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObjCollection::iterator* result) {
+ std::vector<BSONObjCollection::iterator> iterVector;
+ Status status = _queryVector(collectionName, query, &iterVector);
+ if (!status.isOK()) {
+ return status;
}
-
- Status AuthzManagerExternalStateMock::_findOneIter(
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObjCollection::iterator* result) {
- std::vector<BSONObjCollection::iterator> iterVector;
- Status status = _queryVector(collectionName, query, &iterVector);
- if (!status.isOK()) {
- return status;
- }
- if (!iterVector.size()) {
- return Status(ErrorCodes::NoMatchingDocument, "No matching document");
- }
- *result = iterVector.front();
- return Status::OK();
+ if (!iterVector.size()) {
+ return Status(ErrorCodes::NoMatchingDocument, "No matching document");
}
+ *result = iterVector.front();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMock::_queryVector(
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ std::vector<BSONObjCollection::iterator>* result) {
+ StatusWithMatchExpression parseResult =
+ MatchExpressionParser::parse(query, MatchExpressionParser::WhereCallback());
+ if (!parseResult.isOK()) {
+ return parseResult.getStatus();
+ }
+ const std::unique_ptr<MatchExpression> matcher(parseResult.getValue());
- Status AuthzManagerExternalStateMock::_queryVector(
- const NamespaceString& collectionName,
- const BSONObj& query,
- std::vector<BSONObjCollection::iterator>* result) {
-
- StatusWithMatchExpression parseResult =
- MatchExpressionParser::parse(query, MatchExpressionParser::WhereCallback());
- if (!parseResult.isOK()) {
- return parseResult.getStatus();
- }
- const std::unique_ptr<MatchExpression> matcher(parseResult.getValue());
-
- NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName);
- if (mapIt == _documents.end())
- return Status::OK();
-
- for (BSONObjCollection::iterator vecIt = mapIt->second.begin();
- vecIt != mapIt->second.end();
- ++vecIt) {
+ NamespaceDocumentMap::iterator mapIt = _documents.find(collectionName);
+ if (mapIt == _documents.end())
+ return Status::OK();
- if (matcher->matchesBSON(*vecIt)) {
- result->push_back(vecIt);
- }
+ for (BSONObjCollection::iterator vecIt = mapIt->second.begin(); vecIt != mapIt->second.end();
+ ++vecIt) {
+ if (matcher->matchesBSON(*vecIt)) {
+ result->push_back(vecIt);
}
- return Status::OK();
}
+ return Status::OK();
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_mock.h b/src/mongo/db/auth/authz_manager_external_state_mock.h
index 43f3abb2546..d6b457e0de9 100644
--- a/src/mongo/db/auth/authz_manager_external_state_mock.h
+++ b/src/mongo/db/auth/authz_manager_external_state_mock.h
@@ -42,88 +42,87 @@
namespace mongo {
- class AuthorizationManager;
+class AuthorizationManager;
- /**
- * Mock of the AuthzManagerExternalState class used only for testing.
- */
- class AuthzManagerExternalStateMock : public AuthzManagerExternalStateLocal {
- MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMock);
+/**
+ * Mock of the AuthzManagerExternalState class used only for testing.
+ */
+class AuthzManagerExternalStateMock : public AuthzManagerExternalStateLocal {
+ MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMock);
- public:
+public:
+ AuthzManagerExternalStateMock();
+ virtual ~AuthzManagerExternalStateMock();
- AuthzManagerExternalStateMock();
- virtual ~AuthzManagerExternalStateMock();
+ void setAuthorizationManager(AuthorizationManager* authzManager);
+ void setAuthzVersion(int version);
- void setAuthorizationManager(AuthorizationManager* authzManager);
- void setAuthzVersion(int version);
+ std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
+ AuthorizationManager* authzManager) override;
- std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) override;
+ virtual Status findOne(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObj* result);
- virtual Status findOne(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObj* result);
+ virtual Status query(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& projection, // Currently unused in mock
+ const stdx::function<void(const BSONObj&)>& resultProcessor);
- virtual Status query(OperationContext* txn,
+ /**
+ * Inserts the given user object into the "admin" database.
+ */
+ Status insertPrivilegeDocument(OperationContext* txn,
+ const BSONObj& userObj,
+ const BSONObj& writeConcern);
+
+ // This implementation does not understand uniqueness constraints.
+ virtual Status insert(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& document,
+ const BSONObj& writeConcern);
+
+ // This implementation does not understand uniqueness constraints, ignores writeConcern,
+ // and only correctly handles some upsert behaviors.
+ virtual Status updateOne(OperationContext* txn,
const NamespaceString& collectionName,
const BSONObj& query,
- const BSONObj& projection, // Currently unused in mock
- const stdx::function<void(const BSONObj&)>& resultProcessor);
-
- /**
- * Inserts the given user object into the "admin" database.
- */
- Status insertPrivilegeDocument(OperationContext* txn,
- const BSONObj& userObj,
- const BSONObj& writeConcern);
-
- // This implementation does not understand uniqueness constraints.
- virtual Status insert(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& document,
- const BSONObj& writeConcern);
-
- // This implementation does not understand uniqueness constraints, ignores writeConcern,
- // and only correctly handles some upsert behaviors.
- virtual Status updateOne(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& updatePattern,
- bool upsert,
- const BSONObj& writeConcern);
- virtual Status update(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& updatePattern,
- bool upsert,
- bool multi,
- const BSONObj& writeConcern,
- int* nMatched);
- virtual Status remove(OperationContext* txn,
- const NamespaceString& collectionName,
- const BSONObj& query,
- const BSONObj& writeConcern,
- int* numRemoved);
-
- std::vector<BSONObj> getCollectionContents(const NamespaceString& collectionName);
-
- private:
- typedef std::vector<BSONObj> BSONObjCollection;
- typedef std::map<NamespaceString, BSONObjCollection> NamespaceDocumentMap;
-
- Status _findOneIter(const NamespaceString& collectionName,
- const BSONObj& query,
- BSONObjCollection::iterator* result);
-
- Status _queryVector(const NamespaceString& collectionName,
- const BSONObj& query,
- std::vector<BSONObjCollection::iterator>* result);
-
-
- AuthorizationManager* _authzManager; // For reporting logOps.
- NamespaceDocumentMap _documents; // Mock database.
- };
-
-} // namespace mongo
+ const BSONObj& updatePattern,
+ bool upsert,
+ const BSONObj& writeConcern);
+ virtual Status update(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& updatePattern,
+ bool upsert,
+ bool multi,
+ const BSONObj& writeConcern,
+ int* nMatched);
+ virtual Status remove(OperationContext* txn,
+ const NamespaceString& collectionName,
+ const BSONObj& query,
+ const BSONObj& writeConcern,
+ int* numRemoved);
+
+ std::vector<BSONObj> getCollectionContents(const NamespaceString& collectionName);
+
+private:
+ typedef std::vector<BSONObj> BSONObjCollection;
+ typedef std::map<NamespaceString, BSONObjCollection> NamespaceDocumentMap;
+
+ Status _findOneIter(const NamespaceString& collectionName,
+ const BSONObj& query,
+ BSONObjCollection::iterator* result);
+
+ Status _queryVector(const NamespaceString& collectionName,
+ const BSONObj& query,
+ std::vector<BSONObjCollection::iterator>* result);
+
+
+ AuthorizationManager* _authzManager; // For reporting logOps.
+ NamespaceDocumentMap _documents; // Mock database.
+};
+
+} // namespace mongo
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 11d0bf3720f..91ca85ee3ef 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_manager_external_state_s.cpp
@@ -49,153 +49,139 @@
namespace mongo {
- AuthzManagerExternalStateMongos::AuthzManagerExternalStateMongos() = default;
-
- AuthzManagerExternalStateMongos::~AuthzManagerExternalStateMongos() = default;
-
- Status AuthzManagerExternalStateMongos::initialize(OperationContext* txn) {
- return Status::OK();
+AuthzManagerExternalStateMongos::AuthzManagerExternalStateMongos() = default;
+
+AuthzManagerExternalStateMongos::~AuthzManagerExternalStateMongos() = default;
+
+Status AuthzManagerExternalStateMongos::initialize(OperationContext* txn) {
+ return Status::OK();
+}
+
+std::unique_ptr<AuthzSessionExternalState>
+AuthzManagerExternalStateMongos::makeAuthzSessionExternalState(AuthorizationManager* authzManager) {
+ return stdx::make_unique<AuthzSessionExternalStateMongos>(authzManager);
+}
+
+Status AuthzManagerExternalStateMongos::getStoredAuthorizationVersion(OperationContext* txn,
+ int* outVersion) {
+ // Note: we are treating
+ // { 'getParameter' : 1, <authSchemaVersionServerParameter> : 1 }
+ // as a user management command since this is the *only* part of mongos
+ // that runs this command
+ BSONObj getParameterCmd = BSON("getParameter" << 1 << authSchemaVersionServerParameter << 1);
+ BSONObjBuilder builder;
+ const bool ok =
+ grid.catalogManager()->runUserManagementReadCommand("admin", getParameterCmd, &builder);
+ BSONObj cmdResult = builder.obj();
+ if (!ok) {
+ return Command::getStatusFromCommandResult(cmdResult);
}
- std::unique_ptr<AuthzSessionExternalState>
- AuthzManagerExternalStateMongos::makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) {
-
- return stdx::make_unique<AuthzSessionExternalStateMongos>(authzManager);
+ BSONElement versionElement = cmdResult[authSchemaVersionServerParameter];
+ if (versionElement.eoo()) {
+ return Status(ErrorCodes::UnknownError, "getParameter misbehaved.");
}
-
- Status AuthzManagerExternalStateMongos::getStoredAuthorizationVersion(
- OperationContext* txn, int* outVersion) {
- // Note: we are treating
- // { 'getParameter' : 1, <authSchemaVersionServerParameter> : 1 }
- // as a user management command since this is the *only* part of mongos
- // that runs this command
- BSONObj getParameterCmd = BSON("getParameter" << 1 <<
- authSchemaVersionServerParameter << 1);
- BSONObjBuilder builder;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand("admin",
- getParameterCmd,
- &builder);
- BSONObj cmdResult = builder.obj();
- if (!ok) {
- return Command::getStatusFromCommandResult(cmdResult);
- }
-
- BSONElement versionElement = cmdResult[authSchemaVersionServerParameter];
- if (versionElement.eoo()) {
- return Status(ErrorCodes::UnknownError, "getParameter misbehaved.");
- }
- *outVersion = versionElement.numberInt();
-
- return Status::OK();
+ *outVersion = versionElement.numberInt();
+
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMongos::getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result) {
+ BSONObj usersInfoCmd =
+ BSON("usersInfo" << BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME
+ << userName.getUser()
+ << AuthorizationManager::USER_DB_FIELD_NAME
+ << userName.getDB())) << "showPrivileges" << true
+ << "showCredentials" << true);
+ BSONObjBuilder builder;
+ const bool ok =
+ grid.catalogManager()->runUserManagementReadCommand("admin", usersInfoCmd, &builder);
+ BSONObj cmdResult = builder.obj();
+ if (!ok) {
+ return Command::getStatusFromCommandResult(cmdResult);
}
- Status AuthzManagerExternalStateMongos::getUserDescription(
- OperationContext* txn, const UserName& userName, BSONObj* result) {
- BSONObj usersInfoCmd = BSON("usersInfo" <<
- BSON_ARRAY(BSON(AuthorizationManager::USER_NAME_FIELD_NAME <<
- userName.getUser() <<
- AuthorizationManager::USER_DB_FIELD_NAME <<
- userName.getDB())) <<
- "showPrivileges" << true <<
- "showCredentials" << true);
- BSONObjBuilder builder;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand("admin",
- usersInfoCmd,
- &builder);
- BSONObj cmdResult = builder.obj();
- if (!ok) {
- return Command::getStatusFromCommandResult(cmdResult);
- }
-
- std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
- if (foundUsers.size() == 0) {
- return Status(ErrorCodes::UserNotFound,
- "User \"" + userName.toString() + "\" not found");
- }
-
- if (foundUsers.size() > 1) {
- return Status(ErrorCodes::UserDataInconsistent,
- str::stream() << "Found multiple users on the \""
- << userName.getDB() << "\" database with name \""
- << userName.getUser() << "\"");
- }
- *result = foundUsers[0].Obj().getOwned();
- return Status::OK();
+ std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
+ if (foundUsers.size() == 0) {
+ return Status(ErrorCodes::UserNotFound, "User \"" + userName.toString() + "\" not found");
}
- Status AuthzManagerExternalStateMongos::getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result) {
- BSONObj rolesInfoCmd = BSON("rolesInfo" <<
- BSON_ARRAY(BSON(AuthorizationManager::ROLE_NAME_FIELD_NAME <<
- roleName.getRole() <<
- AuthorizationManager::ROLE_DB_FIELD_NAME <<
- roleName.getDB())) <<
- "showPrivileges" << showPrivileges);
- BSONObjBuilder builder;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand("admin",
- rolesInfoCmd,
- &builder);
- BSONObj cmdResult = builder.obj();
- if (!ok) {
- return Command::getStatusFromCommandResult(cmdResult);
- }
-
- std::vector<BSONElement> foundRoles = cmdResult["roles"].Array();
- if (foundRoles.size() == 0) {
- return Status(ErrorCodes::RoleNotFound,
- "Role \"" + roleName.toString() + "\" not found");
- }
-
- if (foundRoles.size() > 1) {
- return Status(ErrorCodes::RoleDataInconsistent,
- str::stream() << "Found multiple roles on the \""
- << roleName.getDB() << "\" database with name \""
- << roleName.getRole() << "\"");
- }
- *result = foundRoles[0].Obj().getOwned();
- return Status::OK();
+ if (foundUsers.size() > 1) {
+ return Status(ErrorCodes::UserDataInconsistent,
+ str::stream() << "Found multiple users on the \"" << userName.getDB()
+ << "\" database with name \"" << userName.getUser() << "\"");
+ }
+ *result = foundUsers[0].Obj().getOwned();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMongos::getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result) {
+ BSONObj rolesInfoCmd =
+ BSON("rolesInfo" << BSON_ARRAY(BSON(
+ AuthorizationManager::ROLE_NAME_FIELD_NAME
+ << roleName.getRole() << AuthorizationManager::ROLE_DB_FIELD_NAME
+ << roleName.getDB())) << "showPrivileges" << showPrivileges);
+ BSONObjBuilder builder;
+ const bool ok =
+ grid.catalogManager()->runUserManagementReadCommand("admin", rolesInfoCmd, &builder);
+ BSONObj cmdResult = builder.obj();
+ if (!ok) {
+ return Command::getStatusFromCommandResult(cmdResult);
}
- Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- std::vector<BSONObj>* result) {
- BSONObj rolesInfoCmd = BSON("rolesInfo" << 1 <<
- "showPrivileges" << showPrivileges <<
- "showBuiltinRoles" << showBuiltinRoles);
- BSONObjBuilder builder;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand(dbname,
- rolesInfoCmd,
- &builder);
- BSONObj cmdResult = builder.obj();
- if (!ok) {
- return Command::getStatusFromCommandResult(cmdResult);
- }
- for (BSONObjIterator it(cmdResult["roles"].Obj()); it.more(); it.next()) {
- result->push_back((*it).Obj().getOwned());
- }
- return Status::OK();
+ std::vector<BSONElement> foundRoles = cmdResult["roles"].Array();
+ if (foundRoles.size() == 0) {
+ return Status(ErrorCodes::RoleNotFound, "Role \"" + roleName.toString() + "\" not found");
}
- bool AuthzManagerExternalStateMongos::hasAnyPrivilegeDocuments(OperationContext* txn) {
- BSONObj usersInfoCmd = BSON("usersInfo" << 1);
- BSONObjBuilder builder;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand("admin",
- usersInfoCmd,
- &builder);
- if (!ok) {
- // If we were unable to complete the query,
- // it's best to assume that there _are_ privilege documents. This might happen
- // if the node contaning the users collection becomes transiently unavailable.
- // See SERVER-12616, for example.
- return true;
- }
-
- BSONObj cmdResult = builder.obj();
- std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
- return foundUsers.size() > 0;
+ if (foundRoles.size() > 1) {
+ return Status(ErrorCodes::RoleDataInconsistent,
+ str::stream() << "Found multiple roles on the \"" << roleName.getDB()
+ << "\" database with name \"" << roleName.getRole() << "\"");
}
+ *result = foundRoles[0].Obj().getOwned();
+ return Status::OK();
+}
+
+Status AuthzManagerExternalStateMongos::getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ std::vector<BSONObj>* result) {
+ BSONObj rolesInfoCmd = BSON("rolesInfo" << 1 << "showPrivileges" << showPrivileges
+ << "showBuiltinRoles" << showBuiltinRoles);
+ BSONObjBuilder builder;
+ const bool ok =
+ grid.catalogManager()->runUserManagementReadCommand(dbname, rolesInfoCmd, &builder);
+ BSONObj cmdResult = builder.obj();
+ if (!ok) {
+ return Command::getStatusFromCommandResult(cmdResult);
+ }
+ for (BSONObjIterator it(cmdResult["roles"].Obj()); it.more(); it.next()) {
+ result->push_back((*it).Obj().getOwned());
+ }
+ return Status::OK();
+}
+
+bool AuthzManagerExternalStateMongos::hasAnyPrivilegeDocuments(OperationContext* txn) {
+ BSONObj usersInfoCmd = BSON("usersInfo" << 1);
+ BSONObjBuilder builder;
+ const bool ok =
+ grid.catalogManager()->runUserManagementReadCommand("admin", usersInfoCmd, &builder);
+ if (!ok) {
+ // If we were unable to complete the query,
+ // it's best to assume that there _are_ privilege documents. This might happen
+ // if the node contaning the users collection becomes transiently unavailable.
+ // See SERVER-12616, for example.
+ return true;
+ }
+
+ BSONObj cmdResult = builder.obj();
+ std::vector<BSONElement> foundUsers = cmdResult["users"].Array();
+ return foundUsers.size() > 0;
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_manager_external_state_s.h b/src/mongo/db/auth/authz_manager_external_state_s.h
index 047ff018006..8de98a53552 100644
--- a/src/mongo/db/auth/authz_manager_external_state_s.h
+++ b/src/mongo/db/auth/authz_manager_external_state_s.h
@@ -40,31 +40,32 @@
namespace mongo {
- /**
- * The implementation of AuthzManagerExternalState functionality for mongos.
- */
- class AuthzManagerExternalStateMongos : public AuthzManagerExternalState{
- MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMongos);
+/**
+ * The implementation of AuthzManagerExternalState functionality for mongos.
+ */
+class AuthzManagerExternalStateMongos : public AuthzManagerExternalState {
+ MONGO_DISALLOW_COPYING(AuthzManagerExternalStateMongos);
- public:
- AuthzManagerExternalStateMongos();
- virtual ~AuthzManagerExternalStateMongos();
+public:
+ AuthzManagerExternalStateMongos();
+ virtual ~AuthzManagerExternalStateMongos();
- virtual Status initialize(OperationContext* txn);
- std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
- AuthorizationManager* authzManager) override;
- virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion);
- virtual Status getUserDescription(
- OperationContext* txn, const UserName& userName, BSONObj* result);
- virtual Status getRoleDescription(const RoleName& roleName,
- bool showPrivileges,
- BSONObj* result);
- virtual Status getRoleDescriptionsForDB(const std::string dbname,
- bool showPrivileges,
- bool showBuiltinRoles,
- std::vector<BSONObj>* result);
+ virtual Status initialize(OperationContext* txn);
+ std::unique_ptr<AuthzSessionExternalState> makeAuthzSessionExternalState(
+ AuthorizationManager* authzManager) override;
+ virtual Status getStoredAuthorizationVersion(OperationContext* txn, int* outVersion);
+ virtual Status getUserDescription(OperationContext* txn,
+ const UserName& userName,
+ BSONObj* result);
+ virtual Status getRoleDescription(const RoleName& roleName,
+ bool showPrivileges,
+ BSONObj* result);
+ virtual Status getRoleDescriptionsForDB(const std::string dbname,
+ bool showPrivileges,
+ bool showBuiltinRoles,
+ std::vector<BSONObj>* result);
- bool hasAnyPrivilegeDocuments(OperationContext* txn) override;
- };
+ bool hasAnyPrivilegeDocuments(OperationContext* txn) override;
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state.cpp b/src/mongo/db/auth/authz_session_external_state.cpp
index 2a7db8c1ff1..c0dceb9617b 100644
--- a/src/mongo/db/auth/authz_session_external_state.cpp
+++ b/src/mongo/db/auth/authz_session_external_state.cpp
@@ -35,12 +35,12 @@
namespace mongo {
- AuthzSessionExternalState::AuthzSessionExternalState(AuthorizationManager* authzManager) :
- _authzManager(authzManager) {}
- AuthzSessionExternalState::~AuthzSessionExternalState() {}
+AuthzSessionExternalState::AuthzSessionExternalState(AuthorizationManager* authzManager)
+ : _authzManager(authzManager) {}
+AuthzSessionExternalState::~AuthzSessionExternalState() {}
- AuthorizationManager& AuthzSessionExternalState::getAuthorizationManager() {
- return *_authzManager;
- }
+AuthorizationManager& AuthzSessionExternalState::getAuthorizationManager() {
+ return *_authzManager;
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state.h b/src/mongo/db/auth/authz_session_external_state.h
index d49e5eb35bf..5ce7ab155ed 100644
--- a/src/mongo/db/auth/authz_session_external_state.h
+++ b/src/mongo/db/auth/authz_session_external_state.h
@@ -38,53 +38,52 @@
namespace mongo {
- class Principal;
- class OperationContext;
+class Principal;
+class OperationContext;
- /**
- * Public interface for a class that encapsulates all the session information related to system
- * state not stored in AuthorizationSession. This is primarily to make AuthorizationSession
- * easier to test as well as to allow different implementations in mongos and mongod.
- */
- class AuthzSessionExternalState {
- MONGO_DISALLOW_COPYING(AuthzSessionExternalState);
-
- public:
-
- virtual ~AuthzSessionExternalState();
-
- AuthorizationManager& getAuthorizationManager();
-
- // Returns true if this connection should be treated as if it has full access to do
- // anything, regardless of the current auth state. Currently the reasons why this could be
- // are that auth isn't enabled or the connection is a "god" connection.
- virtual bool shouldIgnoreAuthChecks() const = 0;
-
- // Returns true if this connection should be treated as a localhost connection with no
- // admin authentication users created. This condition is used to allow the creation of
- // the first user on a server with authorization enabled.
- // NOTE: _checkShouldAllowLocalhost MUST be called at least once before any call to
- // shouldAllowLocalhost or we could ignore auth checks incorrectly.
- virtual bool shouldAllowLocalhost() const = 0;
-
- // Returns true if this connection should allow extra server configuration actions under
- // the localhost exception. This condition is used to allow special privileges on arbiters.
- // See SERVER-5479 for details on when this may be removed.
- virtual bool serverIsArbiter() const = 0;
-
- // Should be called at the beginning of every new request. This performs the checks
- // necessary to determine if localhost connections should be given full access.
- virtual void startRequest(OperationContext* txn) = 0;
-
- protected:
- // This class should never be instantiated directly.
- AuthzSessionExternalState(AuthorizationManager* authzManager);
-
- // Pointer to the authorization manager associated with the authorization session
- // that owns this object.
- //
- // TODO(schwerin): Eliminate this back pointer.
- AuthorizationManager* _authzManager;
- };
-
-} // namespace mongo
+/**
+ * Public interface for a class that encapsulates all the session information related to system
+ * state not stored in AuthorizationSession. This is primarily to make AuthorizationSession
+ * easier to test as well as to allow different implementations in mongos and mongod.
+ */
+class AuthzSessionExternalState {
+ MONGO_DISALLOW_COPYING(AuthzSessionExternalState);
+
+public:
+ virtual ~AuthzSessionExternalState();
+
+ AuthorizationManager& getAuthorizationManager();
+
+ // Returns true if this connection should be treated as if it has full access to do
+ // anything, regardless of the current auth state. Currently the reasons why this could be
+ // are that auth isn't enabled or the connection is a "god" connection.
+ virtual bool shouldIgnoreAuthChecks() const = 0;
+
+ // Returns true if this connection should be treated as a localhost connection with no
+ // admin authentication users created. This condition is used to allow the creation of
+ // the first user on a server with authorization enabled.
+ // NOTE: _checkShouldAllowLocalhost MUST be called at least once before any call to
+ // shouldAllowLocalhost or we could ignore auth checks incorrectly.
+ virtual bool shouldAllowLocalhost() const = 0;
+
+ // Returns true if this connection should allow extra server configuration actions under
+ // the localhost exception. This condition is used to allow special privileges on arbiters.
+ // See SERVER-5479 for details on when this may be removed.
+ virtual bool serverIsArbiter() const = 0;
+
+ // Should be called at the beginning of every new request. This performs the checks
+ // necessary to determine if localhost connections should be given full access.
+ virtual void startRequest(OperationContext* txn) = 0;
+
+protected:
+ // This class should never be instantiated directly.
+ AuthzSessionExternalState(AuthorizationManager* authzManager);
+
+ // Pointer to the authorization manager associated with the authorization session
+ // that owns this object.
+ //
+ // TODO(schwerin): Eliminate this back pointer.
+ AuthorizationManager* _authzManager;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_d.cpp b/src/mongo/db/auth/authz_session_external_state_d.cpp
index 65f30b5ce75..dee99046240 100644
--- a/src/mongo/db/auth/authz_session_external_state_d.cpp
+++ b/src/mongo/db/auth/authz_session_external_state_d.cpp
@@ -43,29 +43,28 @@
namespace mongo {
- AuthzSessionExternalStateMongod::AuthzSessionExternalStateMongod(
- AuthorizationManager* authzManager) :
- AuthzSessionExternalStateServerCommon(authzManager) {}
- AuthzSessionExternalStateMongod::~AuthzSessionExternalStateMongod() {}
+AuthzSessionExternalStateMongod::AuthzSessionExternalStateMongod(AuthorizationManager* authzManager)
+ : AuthzSessionExternalStateServerCommon(authzManager) {}
+AuthzSessionExternalStateMongod::~AuthzSessionExternalStateMongod() {}
- void AuthzSessionExternalStateMongod::startRequest(OperationContext* txn) {
- // No locks should be held as this happens before any database accesses occur
- dassert(!txn->lockState()->isLocked());
+void AuthzSessionExternalStateMongod::startRequest(OperationContext* txn) {
+ // No locks should be held as this happens before any database accesses occur
+ dassert(!txn->lockState()->isLocked());
- _checkShouldAllowLocalhost(txn);
- }
+ _checkShouldAllowLocalhost(txn);
+}
- bool AuthzSessionExternalStateMongod::shouldIgnoreAuthChecks() const {
- // TODO(spencer): get "isInDirectClient" from OperationContext
- return cc().isInDirectClient() ||
- AuthzSessionExternalStateServerCommon::shouldIgnoreAuthChecks();
- }
+bool AuthzSessionExternalStateMongod::shouldIgnoreAuthChecks() const {
+ // TODO(spencer): get "isInDirectClient" from OperationContext
+ return cc().isInDirectClient() ||
+ AuthzSessionExternalStateServerCommon::shouldIgnoreAuthChecks();
+}
- bool AuthzSessionExternalStateMongod::serverIsArbiter() const {
- // Arbiters have access to extra privileges under localhost. See SERVER-5479.
- return (repl::getGlobalReplicationCoordinator()->getReplicationMode() ==
+bool AuthzSessionExternalStateMongod::serverIsArbiter() const {
+ // Arbiters have access to extra privileges under localhost. See SERVER-5479.
+ return (repl::getGlobalReplicationCoordinator()->getReplicationMode() ==
repl::ReplicationCoordinator::modeReplSet &&
- repl::getGlobalReplicationCoordinator()->getMemberState().arbiter());
- }
+ repl::getGlobalReplicationCoordinator()->getMemberState().arbiter());
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_d.h b/src/mongo/db/auth/authz_session_external_state_d.h
index 8af5cc41e61..0761ad5ea39 100644
--- a/src/mongo/db/auth/authz_session_external_state_d.h
+++ b/src/mongo/db/auth/authz_session_external_state_d.h
@@ -35,21 +35,21 @@
namespace mongo {
- /**
- * The implementation of AuthzSessionExternalState functionality for mongod.
- */
- class AuthzSessionExternalStateMongod : public AuthzSessionExternalStateServerCommon {
- MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMongod);
+/**
+ * The implementation of AuthzSessionExternalState functionality for mongod.
+ */
+class AuthzSessionExternalStateMongod : public AuthzSessionExternalStateServerCommon {
+ MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMongod);
- public:
- AuthzSessionExternalStateMongod(AuthorizationManager* authzManager);
- virtual ~AuthzSessionExternalStateMongod();
+public:
+ AuthzSessionExternalStateMongod(AuthorizationManager* authzManager);
+ virtual ~AuthzSessionExternalStateMongod();
- virtual bool shouldIgnoreAuthChecks() const;
+ virtual bool shouldIgnoreAuthChecks() const;
- virtual bool serverIsArbiter() const;
+ virtual bool serverIsArbiter() const;
- virtual void startRequest(OperationContext* txn);
- };
+ virtual void startRequest(OperationContext* txn);
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_mock.h b/src/mongo/db/auth/authz_session_external_state_mock.h
index 6e26c1bbceb..f1aa1d7a166 100644
--- a/src/mongo/db/auth/authz_session_external_state_mock.h
+++ b/src/mongo/db/auth/authz_session_external_state_mock.h
@@ -35,43 +35,45 @@
namespace mongo {
- /**
- * Mock of the AuthzSessionExternalState class used only for testing.
- */
- class AuthzSessionExternalStateMock : public AuthzSessionExternalState {
- MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMock);
+/**
+ * Mock of the AuthzSessionExternalState class used only for testing.
+ */
+class AuthzSessionExternalStateMock : public AuthzSessionExternalState {
+ MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMock);
- public:
- AuthzSessionExternalStateMock(AuthorizationManager* authzManager) :
- AuthzSessionExternalState(authzManager), _ignoreAuthChecksReturnValue(false),
- _allowLocalhostReturnValue(false), _serverIsArbiterReturnValue(false) {}
+public:
+ AuthzSessionExternalStateMock(AuthorizationManager* authzManager)
+ : AuthzSessionExternalState(authzManager),
+ _ignoreAuthChecksReturnValue(false),
+ _allowLocalhostReturnValue(false),
+ _serverIsArbiterReturnValue(false) {}
- virtual bool shouldIgnoreAuthChecks() const {
- return _ignoreAuthChecksReturnValue;
- }
+ virtual bool shouldIgnoreAuthChecks() const {
+ return _ignoreAuthChecksReturnValue;
+ }
- virtual bool shouldAllowLocalhost() const {
- return _allowLocalhostReturnValue;
- }
+ virtual bool shouldAllowLocalhost() const {
+ return _allowLocalhostReturnValue;
+ }
- virtual bool serverIsArbiter() const {
- return _serverIsArbiterReturnValue;
- }
+ virtual bool serverIsArbiter() const {
+ return _serverIsArbiterReturnValue;
+ }
- void setReturnValueForShouldIgnoreAuthChecks(bool returnValue) {
- _ignoreAuthChecksReturnValue = returnValue;
- }
+ void setReturnValueForShouldIgnoreAuthChecks(bool returnValue) {
+ _ignoreAuthChecksReturnValue = returnValue;
+ }
- void setReturnValueForShouldAllowLocalhost(bool returnValue) {
- _allowLocalhostReturnValue = returnValue;
- }
+ void setReturnValueForShouldAllowLocalhost(bool returnValue) {
+ _allowLocalhostReturnValue = returnValue;
+ }
- virtual void startRequest(OperationContext* txn) {}
+ virtual void startRequest(OperationContext* txn) {}
- private:
- bool _ignoreAuthChecksReturnValue;
- bool _allowLocalhostReturnValue;
- bool _serverIsArbiterReturnValue;
- };
+private:
+ bool _ignoreAuthChecksReturnValue;
+ bool _allowLocalhostReturnValue;
+ bool _serverIsArbiterReturnValue;
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_s.cpp b/src/mongo/db/auth/authz_session_external_state_s.cpp
index 4009670c6c4..9576bc79a2d 100644
--- a/src/mongo/db/auth/authz_session_external_state_s.cpp
+++ b/src/mongo/db/auth/authz_session_external_state_s.cpp
@@ -38,13 +38,12 @@
namespace mongo {
- AuthzSessionExternalStateMongos::AuthzSessionExternalStateMongos(
- AuthorizationManager* authzManager) :
- AuthzSessionExternalStateServerCommon(authzManager) {}
- AuthzSessionExternalStateMongos::~AuthzSessionExternalStateMongos() {}
+AuthzSessionExternalStateMongos::AuthzSessionExternalStateMongos(AuthorizationManager* authzManager)
+ : AuthzSessionExternalStateServerCommon(authzManager) {}
+AuthzSessionExternalStateMongos::~AuthzSessionExternalStateMongos() {}
- void AuthzSessionExternalStateMongos::startRequest(OperationContext* txn) {
- _checkShouldAllowLocalhost(txn);
- }
+void AuthzSessionExternalStateMongos::startRequest(OperationContext* txn) {
+ _checkShouldAllowLocalhost(txn);
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_s.h b/src/mongo/db/auth/authz_session_external_state_s.h
index 777082faadc..7db5078db2a 100644
--- a/src/mongo/db/auth/authz_session_external_state_s.h
+++ b/src/mongo/db/auth/authz_session_external_state_s.h
@@ -35,17 +35,17 @@
namespace mongo {
- /**
- * The implementation of AuthzSessionExternalState functionality for mongos.
- */
- class AuthzSessionExternalStateMongos : public AuthzSessionExternalStateServerCommon {
- MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMongos);
+/**
+ * The implementation of AuthzSessionExternalState functionality for mongos.
+ */
+class AuthzSessionExternalStateMongos : public AuthzSessionExternalStateServerCommon {
+ MONGO_DISALLOW_COPYING(AuthzSessionExternalStateMongos);
- public:
- AuthzSessionExternalStateMongos(AuthorizationManager* authzManager);
- virtual ~AuthzSessionExternalStateMongos();
+public:
+ AuthzSessionExternalStateMongos(AuthorizationManager* authzManager);
+ virtual ~AuthzSessionExternalStateMongos();
- virtual void startRequest(OperationContext* txn);
- };
+ virtual void startRequest(OperationContext* txn);
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_server_common.cpp b/src/mongo/db/auth/authz_session_external_state_server_common.cpp
index 453980e19f7..a85ab1c5ac2 100644
--- a/src/mongo/db/auth/authz_session_external_state_server_common.cpp
+++ b/src/mongo/db/auth/authz_session_external_state_server_common.cpp
@@ -42,50 +42,49 @@
namespace mongo {
namespace {
- MONGO_EXPORT_STARTUP_SERVER_PARAMETER(enableLocalhostAuthBypass, bool, true);
-} // namespace
+MONGO_EXPORT_STARTUP_SERVER_PARAMETER(enableLocalhostAuthBypass, bool, true);
+} // namespace
- // NOTE: we default _allowLocalhost to true under the assumption that _checkShouldAllowLocalhost
- // will always be called before any calls to shouldAllowLocalhost. If this is not the case,
- // it could cause a security hole.
- AuthzSessionExternalStateServerCommon::AuthzSessionExternalStateServerCommon(
- AuthorizationManager* authzManager) :
- AuthzSessionExternalState(authzManager),
- _allowLocalhost(enableLocalhostAuthBypass) {}
- AuthzSessionExternalStateServerCommon::~AuthzSessionExternalStateServerCommon() {}
+// NOTE: we default _allowLocalhost to true under the assumption that _checkShouldAllowLocalhost
+// will always be called before any calls to shouldAllowLocalhost. If this is not the case,
+// it could cause a security hole.
+AuthzSessionExternalStateServerCommon::AuthzSessionExternalStateServerCommon(
+ AuthorizationManager* authzManager)
+ : AuthzSessionExternalState(authzManager), _allowLocalhost(enableLocalhostAuthBypass) {}
+AuthzSessionExternalStateServerCommon::~AuthzSessionExternalStateServerCommon() {}
- void AuthzSessionExternalStateServerCommon::_checkShouldAllowLocalhost(OperationContext* txn) {
- if (!_authzManager->isAuthEnabled())
- return;
- // If we know that an admin user exists, don't re-check.
- if (!_allowLocalhost)
- return;
- // Don't bother checking if we're not on a localhost connection
- if (!ClientBasic::getCurrent()->getIsLocalHostConnection()) {
- _allowLocalhost = false;
- return;
- }
+void AuthzSessionExternalStateServerCommon::_checkShouldAllowLocalhost(OperationContext* txn) {
+ if (!_authzManager->isAuthEnabled())
+ return;
+ // If we know that an admin user exists, don't re-check.
+ if (!_allowLocalhost)
+ return;
+ // Don't bother checking if we're not on a localhost connection
+ if (!ClientBasic::getCurrent()->getIsLocalHostConnection()) {
+ _allowLocalhost = false;
+ return;
+ }
- _allowLocalhost = !_authzManager->hasAnyPrivilegeDocuments(txn);
- if (_allowLocalhost) {
- ONCE {
- log() << "note: no users configured in admin.system.users, allowing localhost "
- "access" << std::endl;
- }
+ _allowLocalhost = !_authzManager->hasAnyPrivilegeDocuments(txn);
+ if (_allowLocalhost) {
+ ONCE {
+ log() << "note: no users configured in admin.system.users, allowing localhost "
+ "access" << std::endl;
}
}
+}
- bool AuthzSessionExternalStateServerCommon::serverIsArbiter() const {
- return false;
- }
+bool AuthzSessionExternalStateServerCommon::serverIsArbiter() const {
+ return false;
+}
- bool AuthzSessionExternalStateServerCommon::shouldAllowLocalhost() const {
- ClientBasic* client = ClientBasic::getCurrent();
- return _allowLocalhost && client->getIsLocalHostConnection();
- }
+bool AuthzSessionExternalStateServerCommon::shouldAllowLocalhost() const {
+ ClientBasic* client = ClientBasic::getCurrent();
+ return _allowLocalhost && client->getIsLocalHostConnection();
+}
- bool AuthzSessionExternalStateServerCommon::shouldIgnoreAuthChecks() const {
- return !_authzManager->isAuthEnabled();
- }
+bool AuthzSessionExternalStateServerCommon::shouldIgnoreAuthChecks() const {
+ return !_authzManager->isAuthEnabled();
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/authz_session_external_state_server_common.h b/src/mongo/db/auth/authz_session_external_state_server_common.h
index f96035dcade..df7ceb6c9f4 100644
--- a/src/mongo/db/auth/authz_session_external_state_server_common.h
+++ b/src/mongo/db/auth/authz_session_external_state_server_common.h
@@ -35,31 +35,29 @@
namespace mongo {
- /**
- * The implementation of AuthzSessionExternalState functionality common to mongod and mongos.
- */
- class AuthzSessionExternalStateServerCommon : public AuthzSessionExternalState {
- MONGO_DISALLOW_COPYING(AuthzSessionExternalStateServerCommon);
-
- public:
- virtual ~AuthzSessionExternalStateServerCommon();
-
- virtual bool shouldAllowLocalhost() const;
- virtual bool shouldIgnoreAuthChecks() const;
- virtual bool serverIsArbiter() const;
+/**
+ * The implementation of AuthzSessionExternalState functionality common to mongod and mongos.
+ */
+class AuthzSessionExternalStateServerCommon : public AuthzSessionExternalState {
+ MONGO_DISALLOW_COPYING(AuthzSessionExternalStateServerCommon);
- protected:
- AuthzSessionExternalStateServerCommon(AuthorizationManager* authzManager);
+public:
+ virtual ~AuthzSessionExternalStateServerCommon();
- // Checks whether or not localhost connections should be given full access and stores the
- // result in _allowLocalhost. Currently localhost connections are only given full access
- // if there are no users in the admin database.
- void _checkShouldAllowLocalhost(OperationContext* txn);
+ virtual bool shouldAllowLocalhost() const;
+ virtual bool shouldIgnoreAuthChecks() const;
+ virtual bool serverIsArbiter() const;
- private:
+protected:
+ AuthzSessionExternalStateServerCommon(AuthorizationManager* authzManager);
- bool _allowLocalhost;
+ // Checks whether or not localhost connections should be given full access and stores the
+ // result in _allowLocalhost. Currently localhost connections are only given full access
+ // if there are no users in the admin database.
+ void _checkShouldAllowLocalhost(OperationContext* txn);
- };
+private:
+ bool _allowLocalhost;
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/impersonation_session.cpp b/src/mongo/db/auth/impersonation_session.cpp
index eb8bf54a6f9..228d5567807 100644
--- a/src/mongo/db/auth/impersonation_session.cpp
+++ b/src/mongo/db/auth/impersonation_session.cpp
@@ -44,36 +44,29 @@
namespace mongo {
- ImpersonationSessionGuard::ImpersonationSessionGuard(OperationContext* txn)
- : _txn(txn) {
+ImpersonationSessionGuard::ImpersonationSessionGuard(OperationContext* txn) : _txn(txn) {
+ auto authSession = AuthorizationSession::get(_txn->getClient());
- auto authSession = AuthorizationSession::get(_txn->getClient());
+ const auto& impersonatedUsersAndRoles =
+ rpc::AuditMetadata::get(txn).getImpersonatedUsersAndRoles();
- const auto& impersonatedUsersAndRoles =
- rpc::AuditMetadata::get(txn).getImpersonatedUsersAndRoles();
+ if (impersonatedUsersAndRoles != boost::none) {
+ uassert(ErrorCodes::Unauthorized,
+ "Unauthorized use of impersonation metadata.",
+ authSession->isAuthorizedForPrivilege(
+ Privilege(ResourcePattern::forClusterResource(), ActionType::impersonate)));
- if (impersonatedUsersAndRoles != boost::none) {
+ fassert(ErrorCodes::InternalError, !authSession->isImpersonating());
- uassert(ErrorCodes::Unauthorized,
- "Unauthorized use of impersonation metadata.",
- authSession->isAuthorizedForPrivilege(
- Privilege(ResourcePattern::forClusterResource(),
- ActionType::impersonate)));
-
- fassert(ErrorCodes::InternalError, !authSession->isImpersonating());
-
- authSession->setImpersonatedUserData(std::get<0>(*impersonatedUsersAndRoles),
- std::get<1>(*impersonatedUsersAndRoles));
- _active = true;
- }
+ authSession->setImpersonatedUserData(std::get<0>(*impersonatedUsersAndRoles),
+ std::get<1>(*impersonatedUsersAndRoles));
+ _active = true;
}
+}
- ImpersonationSessionGuard::~ImpersonationSessionGuard() {
- DESTRUCTOR_GUARD(
- if (_active) {
- AuthorizationSession::get(_txn->getClient())->clearImpersonatedUserData();
- }
- )
- }
+ImpersonationSessionGuard::~ImpersonationSessionGuard() {
+ DESTRUCTOR_GUARD(
+ if (_active) { AuthorizationSession::get(_txn->getClient())->clearImpersonatedUserData(); })
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/impersonation_session.h b/src/mongo/db/auth/impersonation_session.h
index ab05bf1e5c3..af2dab2f774 100644
--- a/src/mongo/db/auth/impersonation_session.h
+++ b/src/mongo/db/auth/impersonation_session.h
@@ -29,20 +29,22 @@
#include "mongo/base/disallow_copying.h"
namespace mongo {
- class OperationContext;
+class OperationContext;
- /**
- * RAII class to optionally set an impersonated username list into the authorization session
- * for the duration of the life of this object.
- */
- class ImpersonationSessionGuard {
- MONGO_DISALLOW_COPYING(ImpersonationSessionGuard);
- public:
- ImpersonationSessionGuard(OperationContext* txn);
- ~ImpersonationSessionGuard();
- private:
- OperationContext* _txn;
- bool _active{false};
- };
+/**
+ * RAII class to optionally set an impersonated username list into the authorization session
+ * for the duration of the life of this object.
+ */
+class ImpersonationSessionGuard {
+ MONGO_DISALLOW_COPYING(ImpersonationSessionGuard);
+
+public:
+ ImpersonationSessionGuard(OperationContext* txn);
+ ~ImpersonationSessionGuard();
+
+private:
+ OperationContext* _txn;
+ bool _active{false};
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/internal_user_auth.cpp b/src/mongo/db/auth/internal_user_auth.cpp
index 6c8190845ea..5cc90c29ea9 100644
--- a/src/mongo/db/auth/internal_user_auth.cpp
+++ b/src/mongo/db/auth/internal_user_auth.cpp
@@ -39,78 +39,78 @@
#include "mongo/util/log.h"
namespace mongo {
- namespace mmb = mongo::mutablebson;
+namespace mmb = mongo::mutablebson;
- // not guarded by the authParams mutex never changed in
- // multi-threaded operation
- static bool authParamsSet = false;
+// not guarded by the authParams mutex never changed in
+// multi-threaded operation
+static bool authParamsSet = false;
- // Store default authentication parameters for internal authentication to cluster members,
- // guarded by the authParams mutex
- static BSONObj authParams;
+// Store default authentication parameters for internal authentication to cluster members,
+// guarded by the authParams mutex
+static BSONObj authParams;
- static stdx::mutex authParamMutex;
+static stdx::mutex authParamMutex;
- bool isInternalAuthSet() {
- return authParamsSet;
- }
-
- void setInternalUserAuthParams(const BSONObj& authParamsIn) {
- if (!isInternalAuthSet()) {
- authParamsSet = true;
- }
- stdx::lock_guard<stdx::mutex> lk(authParamMutex);
+bool isInternalAuthSet() {
+ return authParamsSet;
+}
- if (authParamsIn["mechanism"].String() != "SCRAM-SHA-1") {
- authParams = authParamsIn.copy();
- return;
- }
+void setInternalUserAuthParams(const BSONObj& authParamsIn) {
+ if (!isInternalAuthSet()) {
+ authParamsSet = true;
+ }
+ stdx::lock_guard<stdx::mutex> lk(authParamMutex);
- // Create authParams for legacy MONGODB-CR authentication for 2.6/3.0 mixed
- // mode if applicable.
- mmb::Document fallback(authParamsIn);
- fallback.root().findFirstChildNamed("mechanism").setValueString("MONGODB-CR");
+ if (authParamsIn["mechanism"].String() != "SCRAM-SHA-1") {
+ authParams = authParamsIn.copy();
+ return;
+ }
- mmb::Document doc(authParamsIn);
- mmb::Element fallbackEl = doc.makeElementObject("fallbackParams");
- fallbackEl.setValueObject(fallback.getObject());
- doc.root().pushBack(fallbackEl);
- authParams = doc.getObject().copy();
+ // Create authParams for legacy MONGODB-CR authentication for 2.6/3.0 mixed
+ // mode if applicable.
+ mmb::Document fallback(authParamsIn);
+ fallback.root().findFirstChildNamed("mechanism").setValueString("MONGODB-CR");
+
+ mmb::Document doc(authParamsIn);
+ mmb::Element fallbackEl = doc.makeElementObject("fallbackParams");
+ fallbackEl.setValueObject(fallback.getObject());
+ doc.root().pushBack(fallbackEl);
+ authParams = doc.getObject().copy();
+}
+
+BSONObj getInternalUserAuthParamsWithFallback() {
+ if (!authParamsSet) {
+ return BSONObj();
}
- BSONObj getInternalUserAuthParamsWithFallback() {
- if (!authParamsSet) {
- return BSONObj();
- }
+ stdx::lock_guard<stdx::mutex> lk(authParamMutex);
+ return authParams.copy();
+}
- stdx::lock_guard<stdx::mutex> lk(authParamMutex);
- return authParams.copy();
+BSONObj getFallbackAuthParams(BSONObj params) {
+ if (params["fallbackParams"].type() != Object) {
+ return BSONObj();
}
+ return params["fallbackParams"].Obj();
+}
- BSONObj getFallbackAuthParams(BSONObj params) {
- if (params["fallbackParams"].type() != Object) {
- return BSONObj();
+bool authenticateInternalUser(DBClientWithCommands* conn) {
+ if (!isInternalAuthSet()) {
+ if (!serverGlobalParams.quiet) {
+ log() << "ERROR: No authentication parameters set for internal user";
}
- return params["fallbackParams"].Obj();
+ return false;
}
- bool authenticateInternalUser(DBClientWithCommands* conn) {
- if (!isInternalAuthSet()) {
- if (!serverGlobalParams.quiet) {
- log() << "ERROR: No authentication parameters set for internal user";
- }
- return false;
- }
-
- try {
- conn->auth(getInternalUserAuthParamsWithFallback());
- return true;
- } catch(const UserException& ex) {
- if (!serverGlobalParams.quiet) {
- log() << "can't authenticate to " << conn->toString()
- << " as internal user, error: "<< ex.what();
- }
- return false;
+ try {
+ conn->auth(getInternalUserAuthParamsWithFallback());
+ return true;
+ } catch (const UserException& ex) {
+ if (!serverGlobalParams.quiet) {
+ log() << "can't authenticate to " << conn->toString()
+ << " as internal user, error: " << ex.what();
}
+ return false;
}
-} // namespace mongo
+}
+} // namespace mongo
diff --git a/src/mongo/db/auth/internal_user_auth.h b/src/mongo/db/auth/internal_user_auth.h
index 547c9ba76a8..772eef1b322 100644
--- a/src/mongo/db/auth/internal_user_auth.h
+++ b/src/mongo/db/auth/internal_user_auth.h
@@ -29,40 +29,40 @@
#pragma once
namespace mongo {
- class BSONObj;
- class DBClientWithCommands;
+class BSONObj;
+class DBClientWithCommands;
- /**
- * @return true if internal authentication parameters has been set up
- */
- bool isInternalAuthSet();
+/**
+ * @return true if internal authentication parameters has been set up
+ */
+bool isInternalAuthSet();
- /**
- * This method initializes the authParams object with authentication
- * credentials to be used by authenticateInternalUser.
- */
- void setInternalUserAuthParams(const BSONObj& authParamsIn);
+/**
+ * This method initializes the authParams object with authentication
+ * credentials to be used by authenticateInternalUser.
+ */
+void setInternalUserAuthParams(const BSONObj& authParamsIn);
- /**
- * Returns a copy of the authParams object to be used by authenticateInternalUser
- *
- * The format of the return object is { authparams, fallbackParams:params}
- *
- * If SCRAM-SHA-1 is the internal auth mechanism the fallbackParams sub document is
- * for MONGODB-CR auth is included. For MONGODB-XC509 no fallbackParams document is
- * returned.
- **/
- BSONObj getInternalUserAuthParamsWithFallback();
+/**
+ * Returns a copy of the authParams object to be used by authenticateInternalUser
+ *
+ * The format of the return object is { authparams, fallbackParams:params}
+ *
+ * If SCRAM-SHA-1 is the internal auth mechanism the fallbackParams sub document is
+ * for MONGODB-CR auth is included. For MONGODB-XC509 no fallbackParams document is
+ * returned.
+ **/
+BSONObj getInternalUserAuthParamsWithFallback();
- /**
- * Returns a copy of the fallback parameter portion of an internal auth parameter object
- **/
- BSONObj getFallbackAuthParams(BSONObj params);
+/**
+ * Returns a copy of the fallback parameter portion of an internal auth parameter object
+ **/
+BSONObj getFallbackAuthParams(BSONObj params);
- /**
- * Authenticates to another cluster member using appropriate authentication data.
- * Uses getInternalUserAuthParams() to retrive authentication parameters.
- * @return true if the authentication was succesful
- */
- bool authenticateInternalUser(DBClientWithCommands* conn);
-} // namespace mongo
+/**
+* Authenticates to another cluster member using appropriate authentication data.
+* Uses getInternalUserAuthParams() to retrive authentication parameters.
+* @return true if the authentication was succesful
+*/
+bool authenticateInternalUser(DBClientWithCommands* conn);
+} // namespace mongo
diff --git a/src/mongo/db/auth/mongo_authentication_session.cpp b/src/mongo/db/auth/mongo_authentication_session.cpp
index 117f3a047f2..2e25b4f561c 100644
--- a/src/mongo/db/auth/mongo_authentication_session.cpp
+++ b/src/mongo/db/auth/mongo_authentication_session.cpp
@@ -29,11 +29,9 @@
namespace mongo {
- MongoAuthenticationSession::MongoAuthenticationSession(nonce64 nonce) :
- AuthenticationSession(AuthenticationSession::SESSION_TYPE_MONGO),
- _nonce(nonce) {
- }
+MongoAuthenticationSession::MongoAuthenticationSession(nonce64 nonce)
+ : AuthenticationSession(AuthenticationSession::SESSION_TYPE_MONGO), _nonce(nonce) {}
- MongoAuthenticationSession::~MongoAuthenticationSession() {}
+MongoAuthenticationSession::~MongoAuthenticationSession() {}
} // namespace mongo
diff --git a/src/mongo/db/auth/mongo_authentication_session.h b/src/mongo/db/auth/mongo_authentication_session.h
index 19d2c44d387..e60bcf6ac85 100644
--- a/src/mongo/db/auth/mongo_authentication_session.h
+++ b/src/mongo/db/auth/mongo_authentication_session.h
@@ -31,24 +31,27 @@
namespace mongo {
- typedef unsigned long long nonce64;
-
- /**
- * Authentication session data for a nonce-challenge-response authentication of the
- * type used in the Mongo nonce-authenticate protocol.
- *
- * The only session data is the nonce sent to the client.
- */
- class MongoAuthenticationSession : public AuthenticationSession {
- MONGO_DISALLOW_COPYING(MongoAuthenticationSession);
- public:
- explicit MongoAuthenticationSession(nonce64 nonce);
- virtual ~MongoAuthenticationSession();
-
- nonce64 getNonce() const { return _nonce; }
-
- private:
- const nonce64 _nonce;
- };
+typedef unsigned long long nonce64;
+
+/**
+ * Authentication session data for a nonce-challenge-response authentication of the
+ * type used in the Mongo nonce-authenticate protocol.
+ *
+ * The only session data is the nonce sent to the client.
+ */
+class MongoAuthenticationSession : public AuthenticationSession {
+ MONGO_DISALLOW_COPYING(MongoAuthenticationSession);
+
+public:
+ explicit MongoAuthenticationSession(nonce64 nonce);
+ virtual ~MongoAuthenticationSession();
+
+ nonce64 getNonce() const {
+ return _nonce;
+ }
+
+private:
+ const nonce64 _nonce;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/native_sasl_authentication_session.cpp b/src/mongo/db/auth/native_sasl_authentication_session.cpp
index e2392beacd3..9566ba37487 100644
--- a/src/mongo/db/auth/native_sasl_authentication_session.cpp
+++ b/src/mongo/db/auth/native_sasl_authentication_session.cpp
@@ -52,122 +52,108 @@
namespace mongo {
- using std::unique_ptr;
+using std::unique_ptr;
namespace {
- SaslAuthenticationSession* createNativeSaslAuthenticationSession(
- AuthorizationSession* authzSession,
- const std::string& mechanism) {
- return new NativeSaslAuthenticationSession(authzSession);
- }
-
- MONGO_INITIALIZER(NativeSaslServerCore)(InitializerContext* context) {
- if (saslGlobalParams.hostName.empty())
- saslGlobalParams.hostName = getHostNameCached();
- if (saslGlobalParams.serviceName.empty())
- saslGlobalParams.serviceName = "mongodb";
-
- SaslAuthenticationSession::create = createNativeSaslAuthenticationSession;
- return Status::OK();
- }
-
- // PostSaslCommands is reversely dependent on CyrusSaslCommands having been run
- MONGO_INITIALIZER_WITH_PREREQUISITES(PostSaslCommands,
- ("NativeSaslServerCore"))
- (InitializerContext*) {
-
- AuthorizationManager authzManager(stdx::make_unique<AuthzManagerExternalStateMock>());
- std::unique_ptr<AuthorizationSession> authzSession =
- authzManager.makeAuthorizationSession();
-
- for (size_t i = 0; i < saslGlobalParams.authenticationMechanisms.size(); ++i) {
- const std::string& mechanism = saslGlobalParams.authenticationMechanisms[i];
- if (mechanism == "MONGODB-CR" || mechanism == "MONGODB-X509") {
- // Not a SASL mechanism; no need to smoke test built-in mechanisms.
- continue;
- }
- unique_ptr<SaslAuthenticationSession>
- session(SaslAuthenticationSession::create(authzSession.get(), mechanism));
- Status status = session->start("test",
- mechanism,
- saslGlobalParams.serviceName,
- saslGlobalParams.hostName,
- 1,
- true);
- if (!status.isOK())
- return status;
+SaslAuthenticationSession* createNativeSaslAuthenticationSession(AuthorizationSession* authzSession,
+ const std::string& mechanism) {
+ return new NativeSaslAuthenticationSession(authzSession);
+}
+
+MONGO_INITIALIZER(NativeSaslServerCore)(InitializerContext* context) {
+ if (saslGlobalParams.hostName.empty())
+ saslGlobalParams.hostName = getHostNameCached();
+ if (saslGlobalParams.serviceName.empty())
+ saslGlobalParams.serviceName = "mongodb";
+
+ SaslAuthenticationSession::create = createNativeSaslAuthenticationSession;
+ return Status::OK();
+}
+
+// PostSaslCommands is reversely dependent on CyrusSaslCommands having been run
+MONGO_INITIALIZER_WITH_PREREQUISITES(PostSaslCommands, ("NativeSaslServerCore"))
+(InitializerContext*) {
+ AuthorizationManager authzManager(stdx::make_unique<AuthzManagerExternalStateMock>());
+ std::unique_ptr<AuthorizationSession> authzSession = authzManager.makeAuthorizationSession();
+
+ for (size_t i = 0; i < saslGlobalParams.authenticationMechanisms.size(); ++i) {
+ const std::string& mechanism = saslGlobalParams.authenticationMechanisms[i];
+ if (mechanism == "MONGODB-CR" || mechanism == "MONGODB-X509") {
+ // Not a SASL mechanism; no need to smoke test built-in mechanisms.
+ continue;
}
-
- return Status::OK();
- }
-} //namespace
-
- NativeSaslAuthenticationSession::NativeSaslAuthenticationSession(
- AuthorizationSession* authzSession) :
- SaslAuthenticationSession(authzSession),
- _mechanism("") {
+ unique_ptr<SaslAuthenticationSession> session(
+ SaslAuthenticationSession::create(authzSession.get(), mechanism));
+ Status status = session->start(
+ "test", mechanism, saslGlobalParams.serviceName, saslGlobalParams.hostName, 1, true);
+ if (!status.isOK())
+ return status;
}
- NativeSaslAuthenticationSession::~NativeSaslAuthenticationSession() {}
+ return Status::OK();
+}
+} // namespace
- Status NativeSaslAuthenticationSession::start(StringData authenticationDatabase,
- StringData mechanism,
- StringData serviceName,
- StringData serviceHostname,
- int64_t conversationId,
- bool autoAuthorize) {
- fassert(18626, conversationId > 0);
+NativeSaslAuthenticationSession::NativeSaslAuthenticationSession(AuthorizationSession* authzSession)
+ : SaslAuthenticationSession(authzSession), _mechanism("") {}
- if (_conversationId != 0) {
- return Status(ErrorCodes::AlreadyInitialized,
- "Cannot call start() twice on same NativeSaslAuthenticationSession.");
- }
+NativeSaslAuthenticationSession::~NativeSaslAuthenticationSession() {}
- _authenticationDatabase = authenticationDatabase.toString();
- _mechanism = mechanism.toString();
- _serviceName = serviceName.toString();
- _serviceHostname = serviceHostname.toString();
- _conversationId = conversationId;
- _autoAuthorize = autoAuthorize;
+Status NativeSaslAuthenticationSession::start(StringData authenticationDatabase,
+ StringData mechanism,
+ StringData serviceName,
+ StringData serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize) {
+ fassert(18626, conversationId > 0);
- if (mechanism == "PLAIN") {
- _saslConversation.reset(new SaslPLAINServerConversation(this));
- }
- else if (mechanism == "SCRAM-SHA-1") {
- _saslConversation.reset(new SaslSCRAMSHA1ServerConversation(this));
- }
- else {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "SASL mechanism " << mechanism <<
- " is not supported");
- }
+ if (_conversationId != 0) {
+ return Status(ErrorCodes::AlreadyInitialized,
+ "Cannot call start() twice on same NativeSaslAuthenticationSession.");
+ }
- return Status::OK();
+ _authenticationDatabase = authenticationDatabase.toString();
+ _mechanism = mechanism.toString();
+ _serviceName = serviceName.toString();
+ _serviceHostname = serviceHostname.toString();
+ _conversationId = conversationId;
+ _autoAuthorize = autoAuthorize;
+
+ if (mechanism == "PLAIN") {
+ _saslConversation.reset(new SaslPLAINServerConversation(this));
+ } else if (mechanism == "SCRAM-SHA-1") {
+ _saslConversation.reset(new SaslSCRAMSHA1ServerConversation(this));
+ } else {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SASL mechanism " << mechanism
+ << " is not supported");
}
- Status NativeSaslAuthenticationSession::step(StringData inputData,
- std::string* outputData) {
- if (!_saslConversation) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() <<
- "The authentication session has not been properly initialized");
- }
+ return Status::OK();
+}
- StatusWith<bool> status = _saslConversation->step(inputData, outputData);
- if (status.isOK()) {
- _done = status.getValue();
- } else {
- _done = true;
- }
- return status.getStatus();
+Status NativeSaslAuthenticationSession::step(StringData inputData, std::string* outputData) {
+ if (!_saslConversation) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "The authentication session has not been properly initialized");
}
- std::string NativeSaslAuthenticationSession::getPrincipalId() const {
- return _saslConversation->getPrincipalId();
+ StatusWith<bool> status = _saslConversation->step(inputData, outputData);
+ if (status.isOK()) {
+ _done = status.getValue();
+ } else {
+ _done = true;
}
+ return status.getStatus();
+}
- const char* NativeSaslAuthenticationSession::getMechanism() const {
- return _mechanism.c_str();
- }
+std::string NativeSaslAuthenticationSession::getPrincipalId() const {
+ return _saslConversation->getPrincipalId();
+}
+
+const char* NativeSaslAuthenticationSession::getMechanism() const {
+ return _mechanism.c_str();
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/native_sasl_authentication_session.h b/src/mongo/db/auth/native_sasl_authentication_session.h
index 57b90a145be..156ffe685f3 100644
--- a/src/mongo/db/auth/native_sasl_authentication_session.h
+++ b/src/mongo/db/auth/native_sasl_authentication_session.h
@@ -40,31 +40,31 @@
namespace mongo {
- /**
- * Authentication session data for the server side of SASL authentication.
- */
- class NativeSaslAuthenticationSession : public SaslAuthenticationSession {
- MONGO_DISALLOW_COPYING(NativeSaslAuthenticationSession);
- public:
+/**
+ * Authentication session data for the server side of SASL authentication.
+ */
+class NativeSaslAuthenticationSession : public SaslAuthenticationSession {
+ MONGO_DISALLOW_COPYING(NativeSaslAuthenticationSession);
- explicit NativeSaslAuthenticationSession(AuthorizationSession* authSession);
- virtual ~NativeSaslAuthenticationSession();
+public:
+ explicit NativeSaslAuthenticationSession(AuthorizationSession* authSession);
+ virtual ~NativeSaslAuthenticationSession();
- virtual Status start(StringData authenticationDatabase,
- StringData mechanism,
- StringData serviceName,
- StringData serviceHostname,
- int64_t conversationId,
- bool autoAuthorize);
+ virtual Status start(StringData authenticationDatabase,
+ StringData mechanism,
+ StringData serviceName,
+ StringData serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize);
- virtual Status step(StringData inputData, std::string* outputData);
+ virtual Status step(StringData inputData, std::string* outputData);
- virtual std::string getPrincipalId() const;
+ virtual std::string getPrincipalId() const;
- virtual const char* getMechanism() const;
+ virtual const char* getMechanism() const;
- private:
- std::string _mechanism;
- std::unique_ptr<SaslServerConversation> _saslConversation;
- };
+private:
+ std::string _mechanism;
+ std::unique_ptr<SaslServerConversation> _saslConversation;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/privilege.cpp b/src/mongo/db/auth/privilege.cpp
index c98385f2bf6..25cd3141151 100644
--- a/src/mongo/db/auth/privilege.cpp
+++ b/src/mongo/db/auth/privilege.cpp
@@ -32,47 +32,46 @@
namespace mongo {
- void Privilege::addPrivilegeToPrivilegeVector(PrivilegeVector* privileges,
- const Privilege& privilegeToAdd) {
- for (PrivilegeVector::iterator it = privileges->begin(); it != privileges->end(); ++it) {
- if (it->getResourcePattern() == privilegeToAdd.getResourcePattern()) {
- it->addActions(privilegeToAdd.getActions());
- return;
- }
+void Privilege::addPrivilegeToPrivilegeVector(PrivilegeVector* privileges,
+ const Privilege& privilegeToAdd) {
+ for (PrivilegeVector::iterator it = privileges->begin(); it != privileges->end(); ++it) {
+ if (it->getResourcePattern() == privilegeToAdd.getResourcePattern()) {
+ it->addActions(privilegeToAdd.getActions());
+ return;
}
- // No privilege exists yet for this resource
- privileges->push_back(privilegeToAdd);
}
+ // No privilege exists yet for this resource
+ privileges->push_back(privilegeToAdd);
+}
- Privilege::Privilege(const ResourcePattern& resource, const ActionType& action) :
- _resource(resource) {
+Privilege::Privilege(const ResourcePattern& resource, const ActionType& action)
+ : _resource(resource) {
+ _actions.addAction(action);
+}
+Privilege::Privilege(const ResourcePattern& resource, const ActionSet& actions)
+ : _resource(resource), _actions(actions) {}
- _actions.addAction(action);
- }
- Privilege::Privilege(const ResourcePattern& resource, const ActionSet& actions) :
- _resource(resource), _actions(actions) {}
+void Privilege::addActions(const ActionSet& actionsToAdd) {
+ _actions.addAllActionsFromSet(actionsToAdd);
+}
- void Privilege::addActions(const ActionSet& actionsToAdd) {
- _actions.addAllActionsFromSet(actionsToAdd);
- }
+void Privilege::removeActions(const ActionSet& actionsToRemove) {
+ _actions.removeAllActionsFromSet(actionsToRemove);
+}
- void Privilege::removeActions(const ActionSet& actionsToRemove) {
- _actions.removeAllActionsFromSet(actionsToRemove);
- }
+bool Privilege::includesAction(const ActionType& action) const {
+ return _actions.contains(action);
+}
- bool Privilege::includesAction(const ActionType& action) const {
- return _actions.contains(action);
- }
+bool Privilege::includesActions(const ActionSet& actions) const {
+ return _actions.isSupersetOf(actions);
+}
- bool Privilege::includesActions(const ActionSet& actions) const {
- return _actions.isSupersetOf(actions);
- }
-
- BSONObj Privilege::toBSON() const {
- ParsedPrivilege pp;
- std::string errmsg;
- invariant(ParsedPrivilege::privilegeToParsedPrivilege(*this, &pp, &errmsg));
- return pp.toBSON();
- }
+BSONObj Privilege::toBSON() const {
+ ParsedPrivilege pp;
+ std::string errmsg;
+ invariant(ParsedPrivilege::privilegeToParsedPrivilege(*this, &pp, &errmsg));
+ return pp.toBSON();
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/privilege.h b/src/mongo/db/auth/privilege.h
index 1009172a30c..da6b054e012 100644
--- a/src/mongo/db/auth/privilege.h
+++ b/src/mongo/db/auth/privilege.h
@@ -36,47 +36,50 @@
namespace mongo {
- class Privilege;
- typedef std::vector<Privilege> PrivilegeVector;
+class Privilege;
+typedef std::vector<Privilege> PrivilegeVector;
+/**
+ * A representation of the permission to perform a set of actions on a resource.
+ */
+class Privilege {
+public:
/**
- * A representation of the permission to perform a set of actions on a resource.
+ * Adds "privilegeToAdd" to "privileges", de-duping "privilegeToAdd" if the vector already
+ * contains a privilege on the same resource.
+ *
+ * This method is the preferred way to add privileges to privilege vectors.
*/
- class Privilege {
- public:
- /**
- * Adds "privilegeToAdd" to "privileges", de-duping "privilegeToAdd" if the vector already
- * contains a privilege on the same resource.
- *
- * This method is the preferred way to add privileges to privilege vectors.
- */
- static void addPrivilegeToPrivilegeVector(PrivilegeVector* privileges,
- const Privilege& privilegeToAdd);
-
+ static void addPrivilegeToPrivilegeVector(PrivilegeVector* privileges,
+ const Privilege& privilegeToAdd);
- Privilege() {};
- Privilege(const ResourcePattern& resource, const ActionType& action);
- Privilege(const ResourcePattern& resource, const ActionSet& actions);
- ~Privilege() {}
- const ResourcePattern& getResourcePattern() const { return _resource; }
+ Privilege(){};
+ Privilege(const ResourcePattern& resource, const ActionType& action);
+ Privilege(const ResourcePattern& resource, const ActionSet& actions);
+ ~Privilege() {}
- const ActionSet& getActions() const { return _actions; }
+ const ResourcePattern& getResourcePattern() const {
+ return _resource;
+ }
- void addActions(const ActionSet& actionsToAdd);
- void removeActions(const ActionSet& actionsToRemove);
+ const ActionSet& getActions() const {
+ return _actions;
+ }
- // Checks if the given action is present in the Privilege.
- bool includesAction(const ActionType& action) const;
- // Checks if the given actions are present in the Privilege.
- bool includesActions(const ActionSet& actions) const;
+ void addActions(const ActionSet& actionsToAdd);
+ void removeActions(const ActionSet& actionsToRemove);
- BSONObj toBSON() const;
+ // Checks if the given action is present in the Privilege.
+ bool includesAction(const ActionType& action) const;
+ // Checks if the given actions are present in the Privilege.
+ bool includesActions(const ActionSet& actions) const;
- private:
+ BSONObj toBSON() const;
- ResourcePattern _resource;
- ActionSet _actions; // bitmask of actions this privilege grants
- };
+private:
+ ResourcePattern _resource;
+ ActionSet _actions; // bitmask of actions this privilege grants
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/privilege_parser.cpp b/src/mongo/db/auth/privilege_parser.cpp
index f1b4777a74c..140fbde2d7f 100644
--- a/src/mongo/db/auth/privilege_parser.cpp
+++ b/src/mongo/db/auth/privilege_parser.cpp
@@ -37,420 +37,429 @@
namespace mongo {
- using std::string;
- using std::vector;
+using std::string;
+using std::vector;
- using mongoutils::str::stream;
+using mongoutils::str::stream;
- const BSONField<bool> ParsedResource::anyResource("anyResource");
- const BSONField<bool> ParsedResource::cluster("cluster");
- const BSONField<string> ParsedResource::db("db");
- const BSONField<string> ParsedResource::collection("collection");
+const BSONField<bool> ParsedResource::anyResource("anyResource");
+const BSONField<bool> ParsedResource::cluster("cluster");
+const BSONField<string> ParsedResource::db("db");
+const BSONField<string> ParsedResource::collection("collection");
- ParsedResource::ParsedResource() {
- clear();
- }
-
- ParsedResource::~ParsedResource() {
- }
-
- bool ParsedResource::isValid(std::string* errMsg) const {
- std::string dummy;
- if (errMsg == NULL) {
- errMsg = &dummy;
- }
+ParsedResource::ParsedResource() {
+ clear();
+}
- int numCandidateTypes = 0;
- if (isAnyResourceSet()) ++numCandidateTypes;
- if (isClusterSet()) ++numCandidateTypes;
- if (isDbSet() || isCollectionSet()) ++numCandidateTypes;
+ParsedResource::~ParsedResource() {}
- if (isDbSet() != isCollectionSet()) {
- *errMsg = stream() << "resource must set both " << db.name() << " and " <<
- collection.name() << " or neither, but not exactly one.";
- return false;
- }
- if (numCandidateTypes != 1) {
- *errMsg = stream() << "resource must have exactly " << db.name() << " and " <<
- collection.name() << " set, or have only " << cluster.name() << " set " <<
- " or have only " << anyResource.name() << " set";
- return false;
- }
- if (isAnyResourceSet() && !getAnyResource()) {
- *errMsg = stream() << anyResource.name() << " must be true when specified";
- return false;
- }
- if (isClusterSet() && !getCluster()) {
- *errMsg = stream() << cluster.name() << " must be true when specified";
- return false;
- }
- if (isDbSet() && (!NamespaceString::validDBName(getDb()) && !getDb().empty())) {
- *errMsg = stream() << getDb() << " is not a valid database name";
- return false;
- }
- if (isCollectionSet() && (!NamespaceString::validCollectionName(getCollection()) &&
- !getCollection().empty())) {
- *errMsg = stream() << getCollection() << " is not a valid collection name";
- return false;
- }
- return true;
+bool ParsedResource::isValid(std::string* errMsg) const {
+ std::string dummy;
+ if (errMsg == NULL) {
+ errMsg = &dummy;
}
- BSONObj ParsedResource::toBSON() const {
- BSONObjBuilder builder;
-
- if (_isAnyResourceSet) builder.append(anyResource(), _anyResource);
-
- if (_isClusterSet) builder.append(cluster(), _cluster);
-
- if (_isDbSet) builder.append(db(), _db);
-
- if (_isCollectionSet) builder.append(collection(), _collection);
+ int numCandidateTypes = 0;
+ if (isAnyResourceSet())
+ ++numCandidateTypes;
+ if (isClusterSet())
+ ++numCandidateTypes;
+ if (isDbSet() || isCollectionSet())
+ ++numCandidateTypes;
- return builder.obj();
+ if (isDbSet() != isCollectionSet()) {
+ *errMsg = stream() << "resource must set both " << db.name() << " and " << collection.name()
+ << " or neither, but not exactly one.";
+ return false;
}
-
- bool ParsedResource::parseBSON(const BSONObj& source, string* errMsg) {
- clear();
-
- std::string dummy;
- if (!errMsg) errMsg = &dummy;
-
- FieldParser::FieldState fieldState;
- fieldState = FieldParser::extract(source, anyResource, &_anyResource, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isAnyResourceSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, cluster, &_cluster, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isClusterSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, db, &_db, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isDbSet = fieldState == FieldParser::FIELD_SET;
-
- fieldState = FieldParser::extract(source, collection, &_collection, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isCollectionSet = fieldState == FieldParser::FIELD_SET;
-
- return true;
+ if (numCandidateTypes != 1) {
+ *errMsg = stream() << "resource must have exactly " << db.name() << " and "
+ << collection.name() << " set, or have only " << cluster.name()
+ << " set "
+ << " or have only " << anyResource.name() << " set";
+ return false;
}
-
- void ParsedResource::clear() {
- _anyResource = false;
- _isAnyResourceSet = false;
-
- _cluster = false;
- _isClusterSet = false;
-
- _db.clear();
- _isDbSet = false;
-
- _collection.clear();
- _isCollectionSet = false;
-
+ if (isAnyResourceSet() && !getAnyResource()) {
+ *errMsg = stream() << anyResource.name() << " must be true when specified";
+ return false;
}
-
- void ParsedResource::cloneTo(ParsedResource* other) const {
- other->clear();
-
- other->_anyResource = _anyResource;
- other->_isAnyResourceSet = _isAnyResourceSet;
-
- other->_cluster = _cluster;
- other->_isClusterSet = _isClusterSet;
-
- other->_db = _db;
- other->_isDbSet = _isDbSet;
-
- other->_collection = _collection;
- other->_isCollectionSet = _isCollectionSet;
+ if (isClusterSet() && !getCluster()) {
+ *errMsg = stream() << cluster.name() << " must be true when specified";
+ return false;
}
-
- std::string ParsedResource::toString() const {
- return toBSON().toString();
+ if (isDbSet() && (!NamespaceString::validDBName(getDb()) && !getDb().empty())) {
+ *errMsg = stream() << getDb() << " is not a valid database name";
+ return false;
}
-
- void ParsedResource::setAnyResource(bool anyResource) {
- _anyResource = anyResource;
- _isAnyResourceSet = true;
+ if (isCollectionSet() &&
+ (!NamespaceString::validCollectionName(getCollection()) && !getCollection().empty())) {
+ *errMsg = stream() << getCollection() << " is not a valid collection name";
+ return false;
}
+ return true;
+}
- void ParsedResource::unsetAnyResource() {
- _isAnyResourceSet = false;
- }
+BSONObj ParsedResource::toBSON() const {
+ BSONObjBuilder builder;
- bool ParsedResource::isAnyResourceSet() const {
- return _isAnyResourceSet;
- }
+ if (_isAnyResourceSet)
+ builder.append(anyResource(), _anyResource);
- bool ParsedResource::getAnyResource() const {
- dassert(_isAnyResourceSet);
- return _anyResource;
- }
-
- void ParsedResource::setCluster(bool cluster) {
- _cluster = cluster;
- _isClusterSet = true;
- }
+ if (_isClusterSet)
+ builder.append(cluster(), _cluster);
- void ParsedResource::unsetCluster() {
- _isClusterSet = false;
- }
+ if (_isDbSet)
+ builder.append(db(), _db);
- bool ParsedResource::isClusterSet() const {
- return _isClusterSet;
- }
+ if (_isCollectionSet)
+ builder.append(collection(), _collection);
- bool ParsedResource::getCluster() const {
- dassert(_isClusterSet);
- return _cluster;
- }
+ return builder.obj();
+}
- void ParsedResource::setDb(StringData db) {
- _db = db.toString();
- _isDbSet = true;
- }
+bool ParsedResource::parseBSON(const BSONObj& source, string* errMsg) {
+ clear();
- void ParsedResource::unsetDb() {
- _isDbSet = false;
- }
+ std::string dummy;
+ if (!errMsg)
+ errMsg = &dummy;
- bool ParsedResource::isDbSet() const {
- return _isDbSet;
- }
+ FieldParser::FieldState fieldState;
+ fieldState = FieldParser::extract(source, anyResource, &_anyResource, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isAnyResourceSet = fieldState == FieldParser::FIELD_SET;
- const std::string& ParsedResource::getDb() const {
- dassert(_isDbSet);
- return _db;
- }
+ fieldState = FieldParser::extract(source, cluster, &_cluster, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isClusterSet = fieldState == FieldParser::FIELD_SET;
- void ParsedResource::setCollection(StringData collection) {
- _collection = collection.toString();
- _isCollectionSet = true;
- }
+ fieldState = FieldParser::extract(source, db, &_db, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isDbSet = fieldState == FieldParser::FIELD_SET;
- void ParsedResource::unsetCollection() {
- _isCollectionSet = false;
- }
+ fieldState = FieldParser::extract(source, collection, &_collection, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isCollectionSet = fieldState == FieldParser::FIELD_SET;
- bool ParsedResource::isCollectionSet() const {
- return _isCollectionSet;
- }
+ return true;
+}
+
+void ParsedResource::clear() {
+ _anyResource = false;
+ _isAnyResourceSet = false;
+
+ _cluster = false;
+ _isClusterSet = false;
+
+ _db.clear();
+ _isDbSet = false;
- const std::string& ParsedResource::getCollection() const {
- dassert(_isCollectionSet);
- return _collection;
- }
+ _collection.clear();
+ _isCollectionSet = false;
+}
+
+void ParsedResource::cloneTo(ParsedResource* other) const {
+ other->clear();
- const BSONField<std::vector<string> > ParsedPrivilege::actions("actions");
- const BSONField<ParsedResource> ParsedPrivilege::resource("resource");
+ other->_anyResource = _anyResource;
+ other->_isAnyResourceSet = _isAnyResourceSet;
- ParsedPrivilege::ParsedPrivilege() {
- clear();
- }
+ other->_cluster = _cluster;
+ other->_isClusterSet = _isClusterSet;
- ParsedPrivilege::~ParsedPrivilege() {
- }
+ other->_db = _db;
+ other->_isDbSet = _isDbSet;
- bool ParsedPrivilege::isValid(std::string* errMsg) const {
- std::string dummy;
- if (errMsg == NULL) {
- errMsg = &dummy;
- }
+ other->_collection = _collection;
+ other->_isCollectionSet = _isCollectionSet;
+}
- // All the mandatory fields must be present.
- if (!_isActionsSet || !_actions.size()) {
- *errMsg = stream() << "missing " << actions.name() << " field";
- return false;
- }
+std::string ParsedResource::toString() const {
+ return toBSON().toString();
+}
- if (!_isResourceSet) {
- *errMsg = stream() << "missing " << resource.name() << " field";
- return false;
- }
+void ParsedResource::setAnyResource(bool anyResource) {
+ _anyResource = anyResource;
+ _isAnyResourceSet = true;
+}
- return getResource().isValid(errMsg);
- }
+void ParsedResource::unsetAnyResource() {
+ _isAnyResourceSet = false;
+}
- BSONObj ParsedPrivilege::toBSON() const {
- BSONObjBuilder builder;
+bool ParsedResource::isAnyResourceSet() const {
+ return _isAnyResourceSet;
+}
- if (_isResourceSet) builder.append(resource(), _resource.toBSON());
+bool ParsedResource::getAnyResource() const {
+ dassert(_isAnyResourceSet);
+ return _anyResource;
+}
- if (_isActionsSet) {
- BSONArrayBuilder actionsBuilder(builder.subarrayStart(actions()));
- for (std::vector<string>::const_iterator it = _actions.begin();
- it != _actions.end();
- ++it) {
- actionsBuilder.append(*it);
- }
- actionsBuilder.doneFast();
- }
+void ParsedResource::setCluster(bool cluster) {
+ _cluster = cluster;
+ _isClusterSet = true;
+}
- return builder.obj().getOwned();
- }
+void ParsedResource::unsetCluster() {
+ _isClusterSet = false;
+}
- bool ParsedPrivilege::parseBSON(const BSONObj& source, string* errMsg) {
- clear();
+bool ParsedResource::isClusterSet() const {
+ return _isClusterSet;
+}
- std::string dummy;
- if (!errMsg) errMsg = &dummy;
+bool ParsedResource::getCluster() const {
+ dassert(_isClusterSet);
+ return _cluster;
+}
- FieldParser::FieldState fieldState;
- fieldState = FieldParser::extract(source, actions, &_actions, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isActionsSet = fieldState == FieldParser::FIELD_SET;
+void ParsedResource::setDb(StringData db) {
+ _db = db.toString();
+ _isDbSet = true;
+}
- fieldState = FieldParser::extract(source, resource, &_resource, errMsg);
- if (fieldState == FieldParser::FIELD_INVALID) return false;
- _isResourceSet = fieldState == FieldParser::FIELD_SET;
+void ParsedResource::unsetDb() {
+ _isDbSet = false;
+}
- return true;
- }
+bool ParsedResource::isDbSet() const {
+ return _isDbSet;
+}
- void ParsedPrivilege::clear() {
- _actions.clear();
- _isActionsSet = false;
- _resource.clear();
- _isResourceSet = false;
+const std::string& ParsedResource::getDb() const {
+ dassert(_isDbSet);
+ return _db;
+}
- }
+void ParsedResource::setCollection(StringData collection) {
+ _collection = collection.toString();
+ _isCollectionSet = true;
+}
- std::string ParsedPrivilege::toString() const {
- return toBSON().toString();
- }
+void ParsedResource::unsetCollection() {
+ _isCollectionSet = false;
+}
- void ParsedPrivilege::setActions(const std::vector<string>& actions) {
- for (std::vector<string>::const_iterator it = actions.begin();
- it != actions.end();
- ++it) {
- addToActions((*it));
- }
- _isActionsSet = actions.size() > 0;
- }
+bool ParsedResource::isCollectionSet() const {
+ return _isCollectionSet;
+}
- void ParsedPrivilege::addToActions(const string& actions) {
- _actions.push_back(actions);
- _isActionsSet = true;
- }
+const std::string& ParsedResource::getCollection() const {
+ dassert(_isCollectionSet);
+ return _collection;
+}
- void ParsedPrivilege::unsetActions() {
- _actions.clear();
- _isActionsSet = false;
- }
+const BSONField<std::vector<string>> ParsedPrivilege::actions("actions");
+const BSONField<ParsedResource> ParsedPrivilege::resource("resource");
- bool ParsedPrivilege::isActionsSet() const {
- return _isActionsSet;
- }
+ParsedPrivilege::ParsedPrivilege() {
+ clear();
+}
- size_t ParsedPrivilege::sizeActions() const {
- return _actions.size();
- }
+ParsedPrivilege::~ParsedPrivilege() {}
- const std::vector<string>& ParsedPrivilege::getActions() const {
- dassert(_isActionsSet);
- return _actions;
+bool ParsedPrivilege::isValid(std::string* errMsg) const {
+ std::string dummy;
+ if (errMsg == NULL) {
+ errMsg = &dummy;
}
- const string& ParsedPrivilege::getActionsAt(size_t pos) const {
- dassert(_isActionsSet);
- dassert(_actions.size() > pos);
- return _actions.at(pos);
+ // All the mandatory fields must be present.
+ if (!_isActionsSet || !_actions.size()) {
+ *errMsg = stream() << "missing " << actions.name() << " field";
+ return false;
}
- void ParsedPrivilege::setResource(const ParsedResource& resource) {
- resource.cloneTo(&_resource);
- _isResourceSet = true;
+ if (!_isResourceSet) {
+ *errMsg = stream() << "missing " << resource.name() << " field";
+ return false;
}
- void ParsedPrivilege::unsetResource() {
- _isResourceSet = false;
- }
+ return getResource().isValid(errMsg);
+}
- bool ParsedPrivilege::isResourceSet() const {
- return _isResourceSet;
- }
+BSONObj ParsedPrivilege::toBSON() const {
+ BSONObjBuilder builder;
- const ParsedResource& ParsedPrivilege::getResource() const {
- dassert(_isResourceSet);
- return _resource;
- }
-
- bool ParsedPrivilege::parsedPrivilegeToPrivilege(const ParsedPrivilege& parsedPrivilege,
- Privilege* result,
- std::string* errmsg) {
- if (!parsedPrivilege.isValid(errmsg)) {
- return false;
- }
+ if (_isResourceSet)
+ builder.append(resource(), _resource.toBSON());
- // Build actions
- ActionSet actions;
- const vector<std::string>& parsedActions = parsedPrivilege.getActions();
- Status status = ActionSet::parseActionSetFromStringVector(parsedActions, &actions);
- if (!status.isOK()) {
- *errmsg = status.reason();
- return false;
+ if (_isActionsSet) {
+ BSONArrayBuilder actionsBuilder(builder.subarrayStart(actions()));
+ for (std::vector<string>::const_iterator it = _actions.begin(); it != _actions.end();
+ ++it) {
+ actionsBuilder.append(*it);
}
-
- // Build resource
- ResourcePattern resource;
- const ParsedResource& parsedResource = parsedPrivilege.getResource();
- if (parsedResource.isAnyResourceSet() && parsedResource.getAnyResource()) {
- resource = ResourcePattern::forAnyResource();
- } else if (parsedResource.isClusterSet() && parsedResource.getCluster()) {
- resource = ResourcePattern::forClusterResource();
+ actionsBuilder.doneFast();
+ }
+
+ return builder.obj().getOwned();
+}
+
+bool ParsedPrivilege::parseBSON(const BSONObj& source, string* errMsg) {
+ clear();
+
+ std::string dummy;
+ if (!errMsg)
+ errMsg = &dummy;
+
+ FieldParser::FieldState fieldState;
+ fieldState = FieldParser::extract(source, actions, &_actions, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isActionsSet = fieldState == FieldParser::FIELD_SET;
+
+ fieldState = FieldParser::extract(source, resource, &_resource, errMsg);
+ if (fieldState == FieldParser::FIELD_INVALID)
+ return false;
+ _isResourceSet = fieldState == FieldParser::FIELD_SET;
+
+ return true;
+}
+
+void ParsedPrivilege::clear() {
+ _actions.clear();
+ _isActionsSet = false;
+ _resource.clear();
+ _isResourceSet = false;
+}
+
+std::string ParsedPrivilege::toString() const {
+ return toBSON().toString();
+}
+
+void ParsedPrivilege::setActions(const std::vector<string>& actions) {
+ for (std::vector<string>::const_iterator it = actions.begin(); it != actions.end(); ++it) {
+ addToActions((*it));
+ }
+ _isActionsSet = actions.size() > 0;
+}
+
+void ParsedPrivilege::addToActions(const string& actions) {
+ _actions.push_back(actions);
+ _isActionsSet = true;
+}
+
+void ParsedPrivilege::unsetActions() {
+ _actions.clear();
+ _isActionsSet = false;
+}
+
+bool ParsedPrivilege::isActionsSet() const {
+ return _isActionsSet;
+}
+
+size_t ParsedPrivilege::sizeActions() const {
+ return _actions.size();
+}
+
+const std::vector<string>& ParsedPrivilege::getActions() const {
+ dassert(_isActionsSet);
+ return _actions;
+}
+
+const string& ParsedPrivilege::getActionsAt(size_t pos) const {
+ dassert(_isActionsSet);
+ dassert(_actions.size() > pos);
+ return _actions.at(pos);
+}
+
+void ParsedPrivilege::setResource(const ParsedResource& resource) {
+ resource.cloneTo(&_resource);
+ _isResourceSet = true;
+}
+
+void ParsedPrivilege::unsetResource() {
+ _isResourceSet = false;
+}
+
+bool ParsedPrivilege::isResourceSet() const {
+ return _isResourceSet;
+}
+
+const ParsedResource& ParsedPrivilege::getResource() const {
+ dassert(_isResourceSet);
+ return _resource;
+}
+
+bool ParsedPrivilege::parsedPrivilegeToPrivilege(const ParsedPrivilege& parsedPrivilege,
+ Privilege* result,
+ std::string* errmsg) {
+ if (!parsedPrivilege.isValid(errmsg)) {
+ return false;
+ }
+
+ // Build actions
+ ActionSet actions;
+ const vector<std::string>& parsedActions = parsedPrivilege.getActions();
+ Status status = ActionSet::parseActionSetFromStringVector(parsedActions, &actions);
+ if (!status.isOK()) {
+ *errmsg = status.reason();
+ return false;
+ }
+
+ // Build resource
+ ResourcePattern resource;
+ const ParsedResource& parsedResource = parsedPrivilege.getResource();
+ if (parsedResource.isAnyResourceSet() && parsedResource.getAnyResource()) {
+ resource = ResourcePattern::forAnyResource();
+ } else if (parsedResource.isClusterSet() && parsedResource.getCluster()) {
+ resource = ResourcePattern::forClusterResource();
+ } else {
+ if (parsedResource.isDbSet() && !parsedResource.getDb().empty()) {
+ if (parsedResource.isCollectionSet() && !parsedResource.getCollection().empty()) {
+ resource = ResourcePattern::forExactNamespace(
+ NamespaceString(parsedResource.getDb(), parsedResource.getCollection()));
+ } else {
+ resource = ResourcePattern::forDatabaseName(parsedResource.getDb());
+ }
} else {
- if (parsedResource.isDbSet() && !parsedResource.getDb().empty()) {
- if (parsedResource.isCollectionSet() && !parsedResource.getCollection().empty()) {
- resource = ResourcePattern::forExactNamespace(
- NamespaceString(parsedResource.getDb(),
- parsedResource.getCollection()));
- } else {
- resource = ResourcePattern::forDatabaseName(parsedResource.getDb());
- }
+ if (parsedResource.isCollectionSet() && !parsedResource.getCollection().empty()) {
+ resource = ResourcePattern::forCollectionName(parsedResource.getCollection());
} else {
- if (parsedResource.isCollectionSet() && !parsedResource.getCollection().empty()) {
- resource = ResourcePattern::forCollectionName(parsedResource.getCollection());
- } else {
- resource = ResourcePattern::forAnyNormalResource();
- }
+ resource = ResourcePattern::forAnyNormalResource();
}
}
-
- *result = Privilege(resource, actions);
- return true;
}
- bool ParsedPrivilege::privilegeToParsedPrivilege(const Privilege& privilege,
- ParsedPrivilege* result,
- std::string* errmsg) {
- ParsedResource parsedResource;
- if (privilege.getResourcePattern().isExactNamespacePattern()) {
- parsedResource.setDb(privilege.getResourcePattern().databaseToMatch());
- parsedResource.setCollection(privilege.getResourcePattern().collectionToMatch());
- } else if (privilege.getResourcePattern().isDatabasePattern()) {
- parsedResource.setDb(privilege.getResourcePattern().databaseToMatch());
- parsedResource.setCollection("");
- } else if (privilege.getResourcePattern().isCollectionPattern()) {
- parsedResource.setDb("");
- parsedResource.setCollection(privilege.getResourcePattern().collectionToMatch());
- } else if (privilege.getResourcePattern().isAnyNormalResourcePattern()) {
- parsedResource.setDb("");
- parsedResource.setCollection("");
- } else if (privilege.getResourcePattern().isClusterResourcePattern()) {
- parsedResource.setCluster(true);
- } else if (privilege.getResourcePattern().isAnyResourcePattern()) {
- parsedResource.setAnyResource(true);
- } else {
- *errmsg = stream() << privilege.getResourcePattern().toString() <<
- " is not a valid user-grantable resource pattern";
- return false;
- }
-
- result->clear();
- result->setResource(parsedResource);
- result->setActions(privilege.getActions().getActionsAsStrings());
- return result->isValid(errmsg);
- }
-} // namespace mongo
+ *result = Privilege(resource, actions);
+ return true;
+}
+
+bool ParsedPrivilege::privilegeToParsedPrivilege(const Privilege& privilege,
+ ParsedPrivilege* result,
+ std::string* errmsg) {
+ ParsedResource parsedResource;
+ if (privilege.getResourcePattern().isExactNamespacePattern()) {
+ parsedResource.setDb(privilege.getResourcePattern().databaseToMatch());
+ parsedResource.setCollection(privilege.getResourcePattern().collectionToMatch());
+ } else if (privilege.getResourcePattern().isDatabasePattern()) {
+ parsedResource.setDb(privilege.getResourcePattern().databaseToMatch());
+ parsedResource.setCollection("");
+ } else if (privilege.getResourcePattern().isCollectionPattern()) {
+ parsedResource.setDb("");
+ parsedResource.setCollection(privilege.getResourcePattern().collectionToMatch());
+ } else if (privilege.getResourcePattern().isAnyNormalResourcePattern()) {
+ parsedResource.setDb("");
+ parsedResource.setCollection("");
+ } else if (privilege.getResourcePattern().isClusterResourcePattern()) {
+ parsedResource.setCluster(true);
+ } else if (privilege.getResourcePattern().isAnyResourcePattern()) {
+ parsedResource.setAnyResource(true);
+ } else {
+ *errmsg = stream() << privilege.getResourcePattern().toString()
+ << " is not a valid user-grantable resource pattern";
+ return false;
+ }
+
+ result->clear();
+ result->setResource(parsedResource);
+ result->setActions(privilege.getActions().getActionsAsStrings());
+ return result->isValid(errmsg);
+}
+} // namespace mongo
diff --git a/src/mongo/db/auth/privilege_parser.h b/src/mongo/db/auth/privilege_parser.h
index aec390e3973..a48297c862f 100644
--- a/src/mongo/db/auth/privilege_parser.h
+++ b/src/mongo/db/auth/privilege_parser.h
@@ -37,160 +37,160 @@
namespace mongo {
- class Privilege;
+class Privilege;
+
+/**
+ * This class is used to parse documents describing resources as they are represented as part
+ * of privileges granted to roles in the role management commands.
+ */
+class ParsedResource : BSONSerializable {
+ MONGO_DISALLOW_COPYING(ParsedResource);
+
+public:
+ //
+ // schema declarations
+ //
+
+ static const BSONField<bool> anyResource;
+ static const BSONField<bool> cluster;
+ static const BSONField<std::string> db;
+ static const BSONField<std::string> collection;
+
+ //
+ // construction / destruction
+ //
+
+ ParsedResource();
+ ~ParsedResource();
+
+ /** Copies all the fields present in 'this' to 'other'. */
+ void cloneTo(ParsedResource* other) const;
+
+ //
+ // bson serializable interface implementation
+ //
+
+ bool isValid(std::string* errMsg) const;
+ BSONObj toBSON() const;
+ bool parseBSON(const BSONObj& source, std::string* errMsg);
+ void clear();
+ virtual std::string toString() const;
+
+ //
+ // individual field accessors
+ //
+
+ void setAnyResource(bool anyResource);
+ void unsetAnyResource();
+ bool isAnyResourceSet() const;
+ bool getAnyResource() const;
+
+ void setCluster(bool cluster);
+ void unsetCluster();
+ bool isClusterSet() const;
+ bool getCluster() const;
+
+ void setDb(StringData db);
+ void unsetDb();
+ bool isDbSet() const;
+ const std::string& getDb() const;
+
+ void setCollection(StringData collection);
+ void unsetCollection();
+ bool isCollectionSet() const;
+ const std::string& getCollection() const;
+
+private:
+ // Convention: (M)andatory, (O)ptional
+
+ // (O) Only present if the resource matches anything.
+ bool _anyResource;
+ bool _isAnyResourceSet;
+
+ // (O) Only present if the resource is the cluster
+ bool _cluster;
+ bool _isClusterSet;
+
+ // (O) database portion of the resource
+ std::string _db;
+ bool _isDbSet;
+
+ // (O) collection portion of the resource
+ std::string _collection;
+ bool _isCollectionSet;
+};
+
+/**
+ * This class is used to parse documents describing privileges in the role managment commands.
+ */
+class ParsedPrivilege : BSONSerializable {
+ MONGO_DISALLOW_COPYING(ParsedPrivilege);
+
+public:
+ //
+ // schema declarations
+ //
+
+ static const BSONField<std::vector<std::string>> actions;
+ static const BSONField<ParsedResource> resource;
+
+ //
+ // construction / destruction
+ //
+
+ ParsedPrivilege();
+ ~ParsedPrivilege();
/**
- * This class is used to parse documents describing resources as they are represented as part
- * of privileges granted to roles in the role management commands.
+ * Takes a parsedPrivilege and turns it into a true Privilege object.
*/
- class ParsedResource : BSONSerializable {
- MONGO_DISALLOW_COPYING(ParsedResource);
- public:
-
- //
- // schema declarations
- //
-
- static const BSONField<bool> anyResource;
- static const BSONField<bool> cluster;
- static const BSONField<std::string> db;
- static const BSONField<std::string> collection;
-
- //
- // construction / destruction
- //
-
- ParsedResource();
- ~ParsedResource();
-
- /** Copies all the fields present in 'this' to 'other'. */
- void cloneTo(ParsedResource* other) const;
-
- //
- // bson serializable interface implementation
- //
-
- bool isValid(std::string* errMsg) const;
- BSONObj toBSON() const;
- bool parseBSON(const BSONObj& source, std::string* errMsg);
- void clear();
- virtual std::string toString() const;
-
- //
- // individual field accessors
- //
-
- void setAnyResource(bool anyResource);
- void unsetAnyResource();
- bool isAnyResourceSet() const;
- bool getAnyResource() const;
-
- void setCluster(bool cluster);
- void unsetCluster();
- bool isClusterSet() const;
- bool getCluster() const;
-
- void setDb(StringData db);
- void unsetDb();
- bool isDbSet() const;
- const std::string& getDb() const;
-
- void setCollection(StringData collection);
- void unsetCollection();
- bool isCollectionSet() const;
- const std::string& getCollection() const;
-
- private:
- // Convention: (M)andatory, (O)ptional
-
- // (O) Only present if the resource matches anything.
- bool _anyResource;
- bool _isAnyResourceSet;
-
- // (O) Only present if the resource is the cluster
- bool _cluster;
- bool _isClusterSet;
-
- // (O) database portion of the resource
- std::string _db;
- bool _isDbSet;
-
- // (O) collection portion of the resource
- std::string _collection;
- bool _isCollectionSet;
- };
-
+ static bool parsedPrivilegeToPrivilege(const ParsedPrivilege& parsedPrivilege,
+ Privilege* result,
+ std::string* errmsg);
/**
- * This class is used to parse documents describing privileges in the role managment commands.
+ * Takes a Privilege object and turns it into a ParsedPrivilege.
*/
- class ParsedPrivilege : BSONSerializable {
- MONGO_DISALLOW_COPYING(ParsedPrivilege);
- public:
-
- //
- // schema declarations
- //
-
- static const BSONField<std::vector<std::string> > actions;
- static const BSONField<ParsedResource> resource;
-
- //
- // construction / destruction
- //
-
- ParsedPrivilege();
- ~ParsedPrivilege();
-
- /**
- * Takes a parsedPrivilege and turns it into a true Privilege object.
- */
- static bool parsedPrivilegeToPrivilege(const ParsedPrivilege& parsedPrivilege,
- Privilege* result,
- std::string* errmsg);
- /**
- * Takes a Privilege object and turns it into a ParsedPrivilege.
- */
- static bool privilegeToParsedPrivilege(const Privilege& privilege,
- ParsedPrivilege* result,
- std::string* errmsg);
-
- //
- // bson serializable interface implementation
- //
-
- bool isValid(std::string* errMsg) const;
- BSONObj toBSON() const;
- bool parseBSON(const BSONObj& source, std::string* errMsg);
- void clear();
- std::string toString() const;
-
- //
- // individual field accessors
- //
-
- void setActions(const std::vector<std::string>& actions);
- void addToActions(const std::string& actions);
- void unsetActions();
- bool isActionsSet() const;
- size_t sizeActions() const;
- const std::vector<std::string>& getActions() const;
- const std::string& getActionsAt(size_t pos) const;
-
- void setResource(const ParsedResource& resource);
- void unsetResource();
- bool isResourceSet() const;
- const ParsedResource& getResource() const;
-
- private:
- // Convention: (M)andatory, (O)ptional
-
- // (M) Array of action types
- std::vector<std::string> _actions;
- bool _isActionsSet;
-
- // (M) Object describing the resource pattern of this privilege
- ParsedResource _resource;
- bool _isResourceSet;
- };
-
-} // namespace mongo
+ static bool privilegeToParsedPrivilege(const Privilege& privilege,
+ ParsedPrivilege* result,
+ std::string* errmsg);
+
+ //
+ // bson serializable interface implementation
+ //
+
+ bool isValid(std::string* errMsg) const;
+ BSONObj toBSON() const;
+ bool parseBSON(const BSONObj& source, std::string* errMsg);
+ void clear();
+ std::string toString() const;
+
+ //
+ // individual field accessors
+ //
+
+ void setActions(const std::vector<std::string>& actions);
+ void addToActions(const std::string& actions);
+ void unsetActions();
+ bool isActionsSet() const;
+ size_t sizeActions() const;
+ const std::vector<std::string>& getActions() const;
+ const std::string& getActionsAt(size_t pos) const;
+
+ void setResource(const ParsedResource& resource);
+ void unsetResource();
+ bool isResourceSet() const;
+ const ParsedResource& getResource() const;
+
+private:
+ // Convention: (M)andatory, (O)ptional
+
+ // (M) Array of action types
+ std::vector<std::string> _actions;
+ bool _isActionsSet;
+
+ // (M) Object describing the resource pattern of this privilege
+ ParsedResource _resource;
+ bool _isResourceSet;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/privilege_parser_test.cpp b/src/mongo/db/auth/privilege_parser_test.cpp
index 798a197ae91..8885922b1dd 100644
--- a/src/mongo/db/auth/privilege_parser_test.cpp
+++ b/src/mongo/db/auth/privilege_parser_test.cpp
@@ -38,173 +38,180 @@
namespace mongo {
namespace {
- TEST(PrivilegeParserTest, IsValidTest) {
- ParsedPrivilege parsedPrivilege;
- std::string errmsg;
-
- // must have resource
- parsedPrivilege.parseBSON(BSON("actions" << BSON_ARRAY("find")), &errmsg);
- ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
-
- // must have actions
- parsedPrivilege.parseBSON(BSON("resource" << BSON("cluster" << true)), &errmsg);
- ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
-
- // resource can't have cluster as well as db or collection
- parsedPrivilege.parseBSON(BSON("resource" << BSON("cluster" << true <<
- "db" << "" <<
- "collection" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
-
- // resource can't have db without collection
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
-
- // resource can't have collection without db
- parsedPrivilege.parseBSON(BSON("resource" << BSON("collection" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
-
- // Works with wildcard db and resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "" << "collection" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
-
- // Works with real db and collection
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "test" <<
- "collection" << "foo") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
-
- // Works with cluster resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("cluster" << true) <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- }
-
- TEST(PrivilegeParserTest, ConvertBetweenPrivilegeTest) {
- ParsedPrivilege parsedPrivilege;
- Privilege privilege;
- std::string errmsg;
- std::vector<std::string> actionsVector;
- actionsVector.push_back("find");
-
- // Works with wildcard db and resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "" << "collection" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
- ASSERT(privilege.getActions().contains(ActionType::find));
- ASSERT(!privilege.getActions().contains(ActionType::insert));
- ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forAnyNormalResource());
-
- ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(parsedPrivilege.isResourceSet());
- ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
- ASSERT(parsedPrivilege.getResource().isDbSet());
- ASSERT(parsedPrivilege.getResource().isCollectionSet());
- ASSERT_EQUALS("", parsedPrivilege.getResource().getDb());
- ASSERT_EQUALS("", parsedPrivilege.getResource().getCollection());
- ASSERT(parsedPrivilege.isActionsSet());
- ASSERT(actionsVector == parsedPrivilege.getActions());
-
- // Works with exact namespaces
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "test" <<
- "collection" << "foo") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
- ASSERT(privilege.getActions().contains(ActionType::find));
- ASSERT(!privilege.getActions().contains(ActionType::insert));
- ASSERT_EQUALS(privilege.getResourcePattern(),
- ResourcePattern::forExactNamespace(NamespaceString("test.foo")));
-
- ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(parsedPrivilege.isResourceSet());
- ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
- ASSERT(parsedPrivilege.getResource().isDbSet());
- ASSERT(parsedPrivilege.getResource().isCollectionSet());
- ASSERT_EQUALS("test", parsedPrivilege.getResource().getDb());
- ASSERT_EQUALS("foo", parsedPrivilege.getResource().getCollection());
- ASSERT(parsedPrivilege.isActionsSet());
- ASSERT(actionsVector == parsedPrivilege.getActions());
-
- // Works with database resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "test" <<
- "collection" << "") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
- ASSERT(privilege.getActions().contains(ActionType::find));
- ASSERT(!privilege.getActions().contains(ActionType::insert));
- ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forDatabaseName("test"));
-
- ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(parsedPrivilege.isResourceSet());
- ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
- ASSERT(parsedPrivilege.getResource().isDbSet());
- ASSERT(parsedPrivilege.getResource().isCollectionSet());
- ASSERT_EQUALS("test", parsedPrivilege.getResource().getDb());
- ASSERT_EQUALS("", parsedPrivilege.getResource().getCollection());
- ASSERT(parsedPrivilege.isActionsSet());
- ASSERT(actionsVector == parsedPrivilege.getActions());
-
- // Works with collection resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("db" << "" <<
- "collection" << "foo") <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
- ASSERT(privilege.getActions().contains(ActionType::find));
- ASSERT(!privilege.getActions().contains(ActionType::insert));
- ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forCollectionName("foo"));
-
- ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(parsedPrivilege.isResourceSet());
- ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
- ASSERT(parsedPrivilege.getResource().isDbSet());
- ASSERT(parsedPrivilege.getResource().isCollectionSet());
- ASSERT_EQUALS("", parsedPrivilege.getResource().getDb());
- ASSERT_EQUALS("foo", parsedPrivilege.getResource().getCollection());
- ASSERT(parsedPrivilege.isActionsSet());
- ASSERT(actionsVector == parsedPrivilege.getActions());
-
- // Works with cluster resource
- parsedPrivilege.parseBSON(BSON("resource" << BSON("cluster" << true) <<
- "actions" << BSON_ARRAY("find")),
- &errmsg);
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
- ASSERT(privilege.getActions().contains(ActionType::find));
- ASSERT(!privilege.getActions().contains(ActionType::insert));
- ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forClusterResource());
-
- ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
- ASSERT(parsedPrivilege.isValid(&errmsg));
- ASSERT(parsedPrivilege.isResourceSet());
- ASSERT(parsedPrivilege.getResource().isClusterSet());
- ASSERT(parsedPrivilege.getResource().getCluster());
- ASSERT_FALSE(parsedPrivilege.getResource().isDbSet());
- ASSERT_FALSE(parsedPrivilege.getResource().isCollectionSet());
- ASSERT(parsedPrivilege.isActionsSet());
- ASSERT(actionsVector == parsedPrivilege.getActions());
- }
+TEST(PrivilegeParserTest, IsValidTest) {
+ ParsedPrivilege parsedPrivilege;
+ std::string errmsg;
+
+ // must have resource
+ parsedPrivilege.parseBSON(BSON("actions" << BSON_ARRAY("find")), &errmsg);
+ ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
+
+ // must have actions
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("cluster" << true)), &errmsg);
+ ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
+
+ // resource can't have cluster as well as db or collection
+ parsedPrivilege.parseBSON(
+ BSON("resource" << BSON("cluster" << true << "db"
+ << ""
+ << "collection"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
+
+ // resource can't have db without collection
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
+
+ // resource can't have collection without db
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("collection"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT_FALSE(parsedPrivilege.isValid(&errmsg));
+
+ // Works with wildcard db and resource
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << ""
+ << "collection"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+
+ // Works with real db and collection
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << "test"
+ << "collection"
+ << "foo") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+
+ // Works with cluster resource
+ parsedPrivilege.parseBSON(
+ BSON("resource" << BSON("cluster" << true) << "actions" << BSON_ARRAY("find")), &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+}
+
+TEST(PrivilegeParserTest, ConvertBetweenPrivilegeTest) {
+ ParsedPrivilege parsedPrivilege;
+ Privilege privilege;
+ std::string errmsg;
+ std::vector<std::string> actionsVector;
+ actionsVector.push_back("find");
+
+ // Works with wildcard db and resource
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << ""
+ << "collection"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
+ ASSERT(privilege.getActions().contains(ActionType::find));
+ ASSERT(!privilege.getActions().contains(ActionType::insert));
+ ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forAnyNormalResource());
+
+ ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(parsedPrivilege.isResourceSet());
+ ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
+ ASSERT(parsedPrivilege.getResource().isDbSet());
+ ASSERT(parsedPrivilege.getResource().isCollectionSet());
+ ASSERT_EQUALS("", parsedPrivilege.getResource().getDb());
+ ASSERT_EQUALS("", parsedPrivilege.getResource().getCollection());
+ ASSERT(parsedPrivilege.isActionsSet());
+ ASSERT(actionsVector == parsedPrivilege.getActions());
+
+ // Works with exact namespaces
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << "test"
+ << "collection"
+ << "foo") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
+ ASSERT(privilege.getActions().contains(ActionType::find));
+ ASSERT(!privilege.getActions().contains(ActionType::insert));
+ ASSERT_EQUALS(privilege.getResourcePattern(),
+ ResourcePattern::forExactNamespace(NamespaceString("test.foo")));
+
+ ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(parsedPrivilege.isResourceSet());
+ ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
+ ASSERT(parsedPrivilege.getResource().isDbSet());
+ ASSERT(parsedPrivilege.getResource().isCollectionSet());
+ ASSERT_EQUALS("test", parsedPrivilege.getResource().getDb());
+ ASSERT_EQUALS("foo", parsedPrivilege.getResource().getCollection());
+ ASSERT(parsedPrivilege.isActionsSet());
+ ASSERT(actionsVector == parsedPrivilege.getActions());
+
+ // Works with database resource
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << "test"
+ << "collection"
+ << "") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
+ ASSERT(privilege.getActions().contains(ActionType::find));
+ ASSERT(!privilege.getActions().contains(ActionType::insert));
+ ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forDatabaseName("test"));
+
+ ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(parsedPrivilege.isResourceSet());
+ ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
+ ASSERT(parsedPrivilege.getResource().isDbSet());
+ ASSERT(parsedPrivilege.getResource().isCollectionSet());
+ ASSERT_EQUALS("test", parsedPrivilege.getResource().getDb());
+ ASSERT_EQUALS("", parsedPrivilege.getResource().getCollection());
+ ASSERT(parsedPrivilege.isActionsSet());
+ ASSERT(actionsVector == parsedPrivilege.getActions());
+
+ // Works with collection resource
+ parsedPrivilege.parseBSON(BSON("resource" << BSON("db"
+ << ""
+ << "collection"
+ << "foo") << "actions" << BSON_ARRAY("find")),
+ &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
+ ASSERT(privilege.getActions().contains(ActionType::find));
+ ASSERT(!privilege.getActions().contains(ActionType::insert));
+ ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forCollectionName("foo"));
+
+ ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(parsedPrivilege.isResourceSet());
+ ASSERT_FALSE(parsedPrivilege.getResource().isClusterSet());
+ ASSERT(parsedPrivilege.getResource().isDbSet());
+ ASSERT(parsedPrivilege.getResource().isCollectionSet());
+ ASSERT_EQUALS("", parsedPrivilege.getResource().getDb());
+ ASSERT_EQUALS("foo", parsedPrivilege.getResource().getCollection());
+ ASSERT(parsedPrivilege.isActionsSet());
+ ASSERT(actionsVector == parsedPrivilege.getActions());
+
+ // Works with cluster resource
+ parsedPrivilege.parseBSON(
+ BSON("resource" << BSON("cluster" << true) << "actions" << BSON_ARRAY("find")), &errmsg);
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg));
+ ASSERT(privilege.getActions().contains(ActionType::find));
+ ASSERT(!privilege.getActions().contains(ActionType::insert));
+ ASSERT_EQUALS(privilege.getResourcePattern(), ResourcePattern::forClusterResource());
+
+ ASSERT(ParsedPrivilege::privilegeToParsedPrivilege(privilege, &parsedPrivilege, &errmsg));
+ ASSERT(parsedPrivilege.isValid(&errmsg));
+ ASSERT(parsedPrivilege.isResourceSet());
+ ASSERT(parsedPrivilege.getResource().isClusterSet());
+ ASSERT(parsedPrivilege.getResource().getCluster());
+ ASSERT_FALSE(parsedPrivilege.getResource().isDbSet());
+ ASSERT_FALSE(parsedPrivilege.getResource().isCollectionSet());
+ ASSERT(parsedPrivilege.isActionsSet());
+ ASSERT(actionsVector == parsedPrivilege.getActions());
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/resource_pattern.cpp b/src/mongo/db/auth/resource_pattern.cpp
index 6bf23368327..fe62ced9ca9 100644
--- a/src/mongo/db/auth/resource_pattern.cpp
+++ b/src/mongo/db/auth/resource_pattern.cpp
@@ -38,8 +38,8 @@
namespace mongo {
- std::string ResourcePattern::toString() const {
- switch (_matchType) {
+std::string ResourcePattern::toString() const {
+ switch (_matchType) {
case matchNever:
return "<no resources>";
case matchClusterResource:
@@ -56,11 +56,11 @@ namespace mongo {
return "<all resources>";
default:
return "<unknown resource pattern type>";
- }
}
+}
- std::ostream& operator<<(std::ostream& os, const ResourcePattern& pattern) {
- return os << pattern.toString();
- }
+std::ostream& operator<<(std::ostream& os, const ResourcePattern& pattern) {
+ return os << pattern.toString();
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/resource_pattern.h b/src/mongo/db/auth/resource_pattern.h
index c50876b74c5..2896c0aeb24 100644
--- a/src/mongo/db/auth/resource_pattern.h
+++ b/src/mongo/db/auth/resource_pattern.h
@@ -37,174 +37,181 @@
namespace mongo {
+/**
+ * Representation of names of various kinds of resources targetable by the access control
+ * system.
+ *
+ * Three of the types of name, "forDatabaseName", "forExactNamespace" and "forClusterResource",
+ * can represent concrete resources targeted for manipulation by database operations. All of
+ * the types also act as patterns, useful for matching against groups of concrete resources as
+ * part of the access control system. See buildResourceSearchList() in
+ * authorization_session.cpp for details.
+ */
+class ResourcePattern {
+public:
+ /**
+ * Returns a pattern that matches absolutely any resource.
+ */
+ static ResourcePattern forAnyResource() {
+ return ResourcePattern(matchAnyResource);
+ }
+
+ /**
+ * Returns a pattern that matches any database or collection resource except collections for
+ * which ns.isSystem().
+ */
+ static ResourcePattern forAnyNormalResource() {
+ return ResourcePattern(matchAnyNormalResource);
+ }
+
+ /**
+ * Returns a pattern that matches the "cluster" resource.
+ */
+ static ResourcePattern forClusterResource() {
+ return ResourcePattern(matchClusterResource);
+ }
+
+ /**
+ * Returns a pattern that matches the named database, and NamespaceStrings
+ * "ns" for which ns.isSystem() is false and ns.db() == dbname.
+ */
+ static ResourcePattern forDatabaseName(StringData dbName) {
+ return ResourcePattern(matchDatabaseName, NamespaceString(dbName, ""));
+ }
+
+ /**
+ * Returns a pattern that matches NamespaceStrings "ns" for which ns.coll() ==
+ * collectionName.
+ */
+ static ResourcePattern forCollectionName(StringData collectionName) {
+ return ResourcePattern(matchCollectionName, NamespaceString("", collectionName));
+ }
+
+ /**
+ * Returns a pattern that matches the given exact namespace string.
+ */
+ static ResourcePattern forExactNamespace(const NamespaceString& ns) {
+ return ResourcePattern(matchExactNamespace, ns);
+ }
+
+ /**
+ * Constructs a pattern that never matches.
+ */
+ ResourcePattern() : _matchType(matchNever) {}
+
+ /**
+ * Returns true if this pattern matches only exact namespaces.
+ */
+ bool isExactNamespacePattern() const {
+ return _matchType == matchExactNamespace;
+ }
+
/**
- * Representation of names of various kinds of resources targetable by the access control
- * system.
+ * Returns true if this pattern matches on the database name only.
+ */
+ bool isDatabasePattern() const {
+ return _matchType == matchDatabaseName;
+ }
+
+ /**
+ * Returns true if this pattern matches on the collection name only.
+ */
+ bool isCollectionPattern() const {
+ return _matchType == matchCollectionName;
+ }
+
+ /**
+ * Returns true if this pattern matches the cluster resource only.
+ */
+ bool isClusterResourcePattern() const {
+ return _matchType == matchClusterResource;
+ }
+
+ /**
+ * Returns true if this pattern matches only any normal resource.
+ */
+ bool isAnyNormalResourcePattern() const {
+ return _matchType == matchAnyNormalResource;
+ }
+
+ /**
+ * Returns true if this pattern matches any resource.
+ */
+ bool isAnyResourcePattern() const {
+ return _matchType == matchAnyResource;
+ }
+
+ /**
+ * Returns the namespace that this pattern matches.
*
- * Three of the types of name, "forDatabaseName", "forExactNamespace" and "forClusterResource",
- * can represent concrete resources targeted for manipulation by database operations. All of
- * the types also act as patterns, useful for matching against groups of concrete resources as
- * part of the access control system. See buildResourceSearchList() in
- * authorization_session.cpp for details.
- */
- class ResourcePattern {
- public:
- /**
- * Returns a pattern that matches absolutely any resource.
- */
- static ResourcePattern forAnyResource() {
- return ResourcePattern(matchAnyResource);
- }
-
- /**
- * Returns a pattern that matches any database or collection resource except collections for
- * which ns.isSystem().
- */
- static ResourcePattern forAnyNormalResource() {
- return ResourcePattern(matchAnyNormalResource);
- }
-
- /**
- * Returns a pattern that matches the "cluster" resource.
- */
- static ResourcePattern forClusterResource() {
- return ResourcePattern(matchClusterResource);
- }
-
- /**
- * Returns a pattern that matches the named database, and NamespaceStrings
- * "ns" for which ns.isSystem() is false and ns.db() == dbname.
- */
- static ResourcePattern forDatabaseName(StringData dbName) {
- return ResourcePattern(matchDatabaseName, NamespaceString(dbName, ""));
- }
-
- /**
- * Returns a pattern that matches NamespaceStrings "ns" for which ns.coll() ==
- * collectionName.
- */
- static ResourcePattern forCollectionName(StringData collectionName) {
- return ResourcePattern(matchCollectionName, NamespaceString("", collectionName));
- }
-
- /**
- * Returns a pattern that matches the given exact namespace string.
- */
- static ResourcePattern forExactNamespace(const NamespaceString& ns) {
- return ResourcePattern(matchExactNamespace, ns);
- }
-
- /**
- * Constructs a pattern that never matches.
- */
- ResourcePattern() : _matchType(matchNever) {}
-
- /**
- * Returns true if this pattern matches only exact namespaces.
- */
- bool isExactNamespacePattern() const {
- return _matchType == matchExactNamespace;
- }
-
- /**
- * Returns true if this pattern matches on the database name only.
- */
- bool isDatabasePattern() const {
- return _matchType == matchDatabaseName;
- }
-
- /**
- * Returns true if this pattern matches on the collection name only.
- */
- bool isCollectionPattern() const {
- return _matchType == matchCollectionName;
- }
-
- /**
- * Returns true if this pattern matches the cluster resource only.
- */
- bool isClusterResourcePattern() const {
- return _matchType == matchClusterResource;
- }
-
- /**
- * Returns true if this pattern matches only any normal resource.
- */
- bool isAnyNormalResourcePattern() const {
- return _matchType == matchAnyNormalResource;
- }
-
- /**
- * Returns true if this pattern matches any resource.
- */
- bool isAnyResourcePattern() const {
- return _matchType == matchAnyResource;
- }
-
- /**
- * Returns the namespace that this pattern matches.
- *
- * Behavior is undefined unless isExactNamespacePattern() is true.
- */
- const NamespaceString& ns() const { return _ns; }
-
- /**
- * Returns the database that this pattern matches.
- *
- * Behavior is undefined unless the pattern is of type matchDatabaseName or
- * matchExactNamespace
- */
- StringData databaseToMatch() const { return _ns.db(); }
-
- /**
- * Returns the collection that this pattern matches.
- *
- * Behavior is undefined unless the pattern is of type matchCollectionName or
- * matchExactNamespace
- */
- StringData collectionToMatch() const { return _ns.coll(); }
-
- std::string toString() const;
-
- inline size_t hash() const {
- // TODO: Choose a better hash function.
- return MONGO_HASH_NAMESPACE::hash<std::string>()(_ns.ns()) ^ _matchType;
- }
-
- bool operator==(const ResourcePattern& other) const {
- if (_matchType != other._matchType)
- return false;
- if (_ns != other._ns)
- return false;
- return true;
- }
-
- private:
- enum MatchType {
- matchNever = 0, /// Matches no resource.
- matchClusterResource = 1, /// Matches if the resource is the cluster resource.
- matchDatabaseName = 2, /// Matches if the resource's database name is _ns.db().
- matchCollectionName = 3, /// Matches if the resource's collection name is _ns.coll().
- matchExactNamespace = 4, /// Matches if the resource's namespace name is _ns.
- matchAnyNormalResource = 5, /// Matches all databases and non-system collections.
- matchAnyResource = 6 /// Matches absolutely anything.
- };
-
- explicit ResourcePattern(MatchType type) : _matchType(type) {}
- ResourcePattern(MatchType type, const NamespaceString& ns) : _matchType(type), _ns(ns) {}
-
- MatchType _matchType;
- NamespaceString _ns;
+ * Behavior is undefined unless isExactNamespacePattern() is true.
+ */
+ const NamespaceString& ns() const {
+ return _ns;
+ }
+
+ /**
+ * Returns the database that this pattern matches.
+ *
+ * Behavior is undefined unless the pattern is of type matchDatabaseName or
+ * matchExactNamespace
+ */
+ StringData databaseToMatch() const {
+ return _ns.db();
+ }
+
+ /**
+ * Returns the collection that this pattern matches.
+ *
+ * Behavior is undefined unless the pattern is of type matchCollectionName or
+ * matchExactNamespace
+ */
+ StringData collectionToMatch() const {
+ return _ns.coll();
+ }
+
+ std::string toString() const;
+
+ inline size_t hash() const {
+ // TODO: Choose a better hash function.
+ return MONGO_HASH_NAMESPACE::hash<std::string>()(_ns.ns()) ^ _matchType;
+ }
+
+ bool operator==(const ResourcePattern& other) const {
+ if (_matchType != other._matchType)
+ return false;
+ if (_ns != other._ns)
+ return false;
+ return true;
+ }
+
+private:
+ enum MatchType {
+ matchNever = 0, /// Matches no resource.
+ matchClusterResource = 1, /// Matches if the resource is the cluster resource.
+ matchDatabaseName = 2, /// Matches if the resource's database name is _ns.db().
+ matchCollectionName = 3, /// Matches if the resource's collection name is _ns.coll().
+ matchExactNamespace = 4, /// Matches if the resource's namespace name is _ns.
+ matchAnyNormalResource = 5, /// Matches all databases and non-system collections.
+ matchAnyResource = 6 /// Matches absolutely anything.
};
- std::ostream& operator<<(std::ostream& os, const ResourcePattern& pattern);
+ explicit ResourcePattern(MatchType type) : _matchType(type) {}
+ ResourcePattern(MatchType type, const NamespaceString& ns) : _matchType(type), _ns(ns) {}
+
+ MatchType _matchType;
+ NamespaceString _ns;
+};
+
+std::ostream& operator<<(std::ostream& os, const ResourcePattern& pattern);
} // namespace mongo
MONGO_HASH_NAMESPACE_START
- template <> struct hash<mongo::ResourcePattern> {
- size_t operator()(const mongo::ResourcePattern& resource) const {
- return resource.hash();
- }
- };
+template <>
+struct hash<mongo::ResourcePattern> {
+ size_t operator()(const mongo::ResourcePattern& resource) const {
+ return resource.hash();
+ }
+};
MONGO_HASH_NAMESPACE_END
diff --git a/src/mongo/db/auth/role_graph.cpp b/src/mongo/db/auth/role_graph.cpp
index 98ea177cc43..a0861b98236 100644
--- a/src/mongo/db/auth/role_graph.cpp
+++ b/src/mongo/db/auth/role_graph.cpp
@@ -40,522 +40,520 @@
namespace mongo {
namespace {
- PrivilegeVector emptyPrivilegeVector;
-} // namespace
-
- RoleGraph::RoleGraph() {};
- RoleGraph::RoleGraph(const RoleGraph& other) : _roleToSubordinates(other._roleToSubordinates),
- _roleToIndirectSubordinates(other._roleToIndirectSubordinates),
- _roleToMembers(other._roleToMembers),
- _directPrivilegesForRole(other._directPrivilegesForRole),
- _allPrivilegesForRole(other._allPrivilegesForRole),
- _allRoles(other._allRoles) {}
- RoleGraph::~RoleGraph() {};
-
- void RoleGraph::swap(RoleGraph& other) {
- using std::swap;
- swap(this->_roleToSubordinates, other._roleToSubordinates);
- swap(this->_roleToIndirectSubordinates, other._roleToIndirectSubordinates);
- swap(this->_roleToMembers, other._roleToMembers);
- swap(this->_directPrivilegesForRole, other._directPrivilegesForRole);
- swap(this->_allPrivilegesForRole, other._allPrivilegesForRole);
- swap(this->_allRoles, other._allRoles);
- }
-
- void swap(RoleGraph& lhs, RoleGraph& rhs) {
- lhs.swap(rhs);
- }
-
- bool RoleGraph::roleExists(const RoleName& role) {
- _createBuiltinRoleIfNeeded(role);
- return _roleExistsDontCreateBuiltin(role);
- }
-
- bool RoleGraph::_roleExistsDontCreateBuiltin(const RoleName& role) {
- EdgeSet::const_iterator edgeIt = _roleToSubordinates.find(role);
- if (edgeIt == _roleToSubordinates.end())
- return false;
- edgeIt = _roleToMembers.find(role);
- fassert(16825, edgeIt != _roleToMembers.end());
-
- RolePrivilegeMap::const_iterator strIt = _directPrivilegesForRole.find(role);
- if (strIt == _directPrivilegesForRole.end())
- return false;
- strIt = _allPrivilegesForRole.find(role);
- fassert(16826, strIt != _allPrivilegesForRole.end());
- return true;
- }
-
- Status RoleGraph::createRole(const RoleName& role) {
- if (roleExists(role)) {
- return Status(ErrorCodes::DuplicateKey,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " already exists",
- 0);
- }
-
- _createRoleDontCheckIfRoleExists(role);
- return Status::OK();
+PrivilegeVector emptyPrivilegeVector;
+} // namespace
+
+RoleGraph::RoleGraph(){};
+RoleGraph::RoleGraph(const RoleGraph& other)
+ : _roleToSubordinates(other._roleToSubordinates),
+ _roleToIndirectSubordinates(other._roleToIndirectSubordinates),
+ _roleToMembers(other._roleToMembers),
+ _directPrivilegesForRole(other._directPrivilegesForRole),
+ _allPrivilegesForRole(other._allPrivilegesForRole),
+ _allRoles(other._allRoles) {}
+RoleGraph::~RoleGraph(){};
+
+void RoleGraph::swap(RoleGraph& other) {
+ using std::swap;
+ swap(this->_roleToSubordinates, other._roleToSubordinates);
+ swap(this->_roleToIndirectSubordinates, other._roleToIndirectSubordinates);
+ swap(this->_roleToMembers, other._roleToMembers);
+ swap(this->_directPrivilegesForRole, other._directPrivilegesForRole);
+ swap(this->_allPrivilegesForRole, other._allPrivilegesForRole);
+ swap(this->_allRoles, other._allRoles);
+}
+
+void swap(RoleGraph& lhs, RoleGraph& rhs) {
+ lhs.swap(rhs);
+}
+
+bool RoleGraph::roleExists(const RoleName& role) {
+ _createBuiltinRoleIfNeeded(role);
+ return _roleExistsDontCreateBuiltin(role);
+}
+
+bool RoleGraph::_roleExistsDontCreateBuiltin(const RoleName& role) {
+ EdgeSet::const_iterator edgeIt = _roleToSubordinates.find(role);
+ if (edgeIt == _roleToSubordinates.end())
+ return false;
+ edgeIt = _roleToMembers.find(role);
+ fassert(16825, edgeIt != _roleToMembers.end());
+
+ RolePrivilegeMap::const_iterator strIt = _directPrivilegesForRole.find(role);
+ if (strIt == _directPrivilegesForRole.end())
+ return false;
+ strIt = _allPrivilegesForRole.find(role);
+ fassert(16826, strIt != _allPrivilegesForRole.end());
+ return true;
+}
+
+Status RoleGraph::createRole(const RoleName& role) {
+ if (roleExists(role)) {
+ return Status(ErrorCodes::DuplicateKey,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " already exists",
+ 0);
}
- void RoleGraph::_createRoleDontCheckIfRoleExists(const RoleName& role) {
- // Just reference the role in all the maps so that an entry gets created with empty
- // containers for the value.
- _roleToSubordinates[role];
- _roleToIndirectSubordinates[role];
- _roleToMembers[role];
- _directPrivilegesForRole[role];
- _allPrivilegesForRole[role];
- _allRoles.insert(role);
+ _createRoleDontCheckIfRoleExists(role);
+ return Status::OK();
+}
+
+void RoleGraph::_createRoleDontCheckIfRoleExists(const RoleName& role) {
+ // Just reference the role in all the maps so that an entry gets created with empty
+ // containers for the value.
+ _roleToSubordinates[role];
+ _roleToIndirectSubordinates[role];
+ _roleToMembers[role];
+ _directPrivilegesForRole[role];
+ _allPrivilegesForRole[role];
+ _allRoles.insert(role);
+}
+
+Status RoleGraph::deleteRole(const RoleName& role) {
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
+ 0);
}
-
- Status RoleGraph::deleteRole(const RoleName& role) {
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(role)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot delete built-in role: " <<
- role.getFullName(),
- 0);
- }
-
- for (std::vector<RoleName>::iterator it = _roleToSubordinates[role].begin();
- it != _roleToSubordinates[role].end(); ++it) {
- _roleToMembers[*it].erase(std::find(_roleToMembers[*it].begin(),
- _roleToMembers[*it].end(),
- role));
- }
- for (std::vector<RoleName>::iterator it = _roleToMembers[role].begin();
- it != _roleToMembers[role].end(); ++it) {
- _roleToSubordinates[*it].erase(std::find(_roleToSubordinates[*it].begin(),
- _roleToSubordinates[*it].end(),
- role));
- }
- _roleToSubordinates.erase(role);
- _roleToIndirectSubordinates.erase(role);
- _roleToMembers.erase(role);
- _directPrivilegesForRole.erase(role);
- _allPrivilegesForRole.erase(role);
- _allRoles.erase(role);
- return Status::OK();
+ if (isBuiltinRole(role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot delete built-in role: " << role.getFullName(),
+ 0);
}
- RoleNameIterator RoleGraph::getDirectSubordinates(const RoleName& role) {
- if (!roleExists(role))
- return RoleNameIterator(NULL);
- return makeRoleNameIteratorForContainer(_roleToSubordinates[role]);
+ for (std::vector<RoleName>::iterator it = _roleToSubordinates[role].begin();
+ it != _roleToSubordinates[role].end();
+ ++it) {
+ _roleToMembers[*it].erase(
+ std::find(_roleToMembers[*it].begin(), _roleToMembers[*it].end(), role));
}
-
- RoleNameIterator RoleGraph::getIndirectSubordinates(const RoleName& role) {
- if (!roleExists(role))
- return RoleNameIterator(NULL);
- return makeRoleNameIteratorForContainer(_roleToIndirectSubordinates[role]);
+ for (std::vector<RoleName>::iterator it = _roleToMembers[role].begin();
+ it != _roleToMembers[role].end();
+ ++it) {
+ _roleToSubordinates[*it].erase(
+ std::find(_roleToSubordinates[*it].begin(), _roleToSubordinates[*it].end(), role));
}
-
- RoleNameIterator RoleGraph::getDirectMembers(const RoleName& role) {
- if (!roleExists(role))
- return RoleNameIterator(NULL);
- return makeRoleNameIteratorForContainer(_roleToMembers[role]);
+ _roleToSubordinates.erase(role);
+ _roleToIndirectSubordinates.erase(role);
+ _roleToMembers.erase(role);
+ _directPrivilegesForRole.erase(role);
+ _allPrivilegesForRole.erase(role);
+ _allRoles.erase(role);
+ return Status::OK();
+}
+
+RoleNameIterator RoleGraph::getDirectSubordinates(const RoleName& role) {
+ if (!roleExists(role))
+ return RoleNameIterator(NULL);
+ return makeRoleNameIteratorForContainer(_roleToSubordinates[role]);
+}
+
+RoleNameIterator RoleGraph::getIndirectSubordinates(const RoleName& role) {
+ if (!roleExists(role))
+ return RoleNameIterator(NULL);
+ return makeRoleNameIteratorForContainer(_roleToIndirectSubordinates[role]);
+}
+
+RoleNameIterator RoleGraph::getDirectMembers(const RoleName& role) {
+ if (!roleExists(role))
+ return RoleNameIterator(NULL);
+ return makeRoleNameIteratorForContainer(_roleToMembers[role]);
+}
+
+const PrivilegeVector& RoleGraph::getDirectPrivileges(const RoleName& role) {
+ if (!roleExists(role))
+ return emptyPrivilegeVector;
+ return _directPrivilegesForRole.find(role)->second;
+}
+
+const PrivilegeVector& RoleGraph::getAllPrivileges(const RoleName& role) {
+ if (!roleExists(role))
+ return emptyPrivilegeVector;
+ return _allPrivilegesForRole.find(role)->second;
+}
+
+Status RoleGraph::addRoleToRole(const RoleName& recipient, const RoleName& role) {
+ if (!roleExists(recipient)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << recipient.getFullName()
+ << " does not exist");
}
-
- const PrivilegeVector& RoleGraph::getDirectPrivileges(const RoleName& role) {
- if (!roleExists(role))
- return emptyPrivilegeVector;
- return _directPrivilegesForRole.find(role)->second;
+ if (isBuiltinRole(recipient)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot grant roles to built-in role: " << role.getFullName());
}
-
- const PrivilegeVector& RoleGraph::getAllPrivileges(const RoleName& role) {
- if (!roleExists(role))
- return emptyPrivilegeVector;
- return _allPrivilegesForRole.find(role)->second;
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist");
}
- Status RoleGraph::addRoleToRole(const RoleName& recipient, const RoleName& role) {
- if (!roleExists(recipient)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << recipient.getFullName() <<
- " does not exist");
- }
- if (isBuiltinRole(recipient)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot grant roles to built-in role: " <<
- role.getFullName());
- }
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist");
- }
-
- if (std::find(_roleToSubordinates[recipient].begin(),
- _roleToSubordinates[recipient].end(),
- role) ==
- _roleToSubordinates[recipient].end()) {
- // Only add role if it's not already present
- _roleToSubordinates[recipient].push_back(role);
- _roleToMembers[role].push_back(recipient);
- }
-
- return Status::OK();
+ if (std::find(_roleToSubordinates[recipient].begin(),
+ _roleToSubordinates[recipient].end(),
+ role) == _roleToSubordinates[recipient].end()) {
+ // Only add role if it's not already present
+ _roleToSubordinates[recipient].push_back(role);
+ _roleToMembers[role].push_back(recipient);
}
- Status RoleGraph::removeRoleFromRole(const RoleName& recipient, const RoleName& role) {
- if (!roleExists(recipient)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << recipient.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(recipient)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot remove roles from built-in role: " <<
- role.getFullName(),
- 0);
- }
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
+ return Status::OK();
+}
- std::vector<RoleName>::iterator itToRm = std::find(_roleToMembers[role].begin(),
- _roleToMembers[role].end(),
- recipient);
- if (itToRm != _roleToMembers[role].end()) {
- _roleToMembers[role].erase(itToRm);
- } else {
- return Status(ErrorCodes::RolesNotRelated,
- mongoutils::str::stream() << recipient.getFullName() << " is not a member"
- " of " << role.getFullName(),
- 0);
- }
-
- itToRm = std::find(_roleToSubordinates[recipient].begin(),
- _roleToSubordinates[recipient].end(),
- role);
- fassert(16827, itToRm != _roleToSubordinates[recipient].end());
- _roleToSubordinates[recipient].erase(itToRm);
- return Status::OK();
+Status RoleGraph::removeRoleFromRole(const RoleName& recipient, const RoleName& role) {
+ if (!roleExists(recipient)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << recipient.getFullName()
+ << " does not exist",
+ 0);
}
-
- Status RoleGraph::removeAllRolesFromRole(const RoleName& victim) {
- typedef std::vector<RoleName> RoleNameVector;
- if (!roleExists(victim)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << victim.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(victim)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot remove roles from built-in role: " <<
- victim.getFullName(),
- 0);
- }
-
- RoleNameVector& subordinatesOfVictim = _roleToSubordinates[victim];
- for (RoleNameVector::const_iterator subordinateRole = subordinatesOfVictim.begin(),
- end = subordinatesOfVictim.end();
- subordinateRole != end;
- ++subordinateRole) {
-
- RoleNameVector& membersOfSubordinate = _roleToMembers[*subordinateRole];
- RoleNameVector::iterator toErase = std::find(
- membersOfSubordinate.begin(), membersOfSubordinate.end(), victim);
- fassert(17173, toErase != membersOfSubordinate.end());
- membersOfSubordinate.erase(toErase);
- }
- subordinatesOfVictim.clear();
- return Status::OK();
+ if (isBuiltinRole(recipient)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot remove roles from built-in role: " << role.getFullName(),
+ 0);
}
-
- Status RoleGraph::addPrivilegeToRole(const RoleName& role, const Privilege& privilegeToAdd) {
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(role)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot grant privileges to built-in role: "
- << role.getFullName(),
- 0);
- }
-
- _addPrivilegeToRoleNoChecks(role, privilegeToAdd);
- return Status::OK();
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
+ 0);
}
- void RoleGraph::_addPrivilegeToRoleNoChecks(const RoleName& role,
- const Privilege& privilegeToAdd) {
- Privilege::addPrivilegeToPrivilegeVector(&_directPrivilegesForRole[role], privilegeToAdd);
+ std::vector<RoleName>::iterator itToRm =
+ std::find(_roleToMembers[role].begin(), _roleToMembers[role].end(), recipient);
+ if (itToRm != _roleToMembers[role].end()) {
+ _roleToMembers[role].erase(itToRm);
+ } else {
+ return Status(ErrorCodes::RolesNotRelated,
+ mongoutils::str::stream() << recipient.getFullName() << " is not a member"
+ " of "
+ << role.getFullName(),
+ 0);
}
- // NOTE: Current runtime of this is O(n*m) where n is the size of the current PrivilegeVector
- // for the given role, and m is the size of the privilegesToAdd vector.
- // If this was a PrivilegeSet (sorted on resource) rather than a PrivilegeVector, we
- // could do this in O(n+m) instead.
- Status RoleGraph::addPrivilegesToRole(const RoleName& role,
- const PrivilegeVector& privilegesToAdd) {
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(role)) {
- return Status(ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot grant privileges to built-in role: "
- << role.getFullName(),
- 0);
- }
-
- for (PrivilegeVector::const_iterator it = privilegesToAdd.begin();
- it != privilegesToAdd.end(); ++it) {
- _addPrivilegeToRoleNoChecks(role, *it);
- }
- return Status::OK();
+ itToRm = std::find(
+ _roleToSubordinates[recipient].begin(), _roleToSubordinates[recipient].end(), role);
+ fassert(16827, itToRm != _roleToSubordinates[recipient].end());
+ _roleToSubordinates[recipient].erase(itToRm);
+ return Status::OK();
+}
+
+Status RoleGraph::removeAllRolesFromRole(const RoleName& victim) {
+ typedef std::vector<RoleName> RoleNameVector;
+ if (!roleExists(victim)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << victim.getFullName()
+ << " does not exist",
+ 0);
+ }
+ if (isBuiltinRole(victim)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot remove roles from built-in role: " << victim.getFullName(),
+ 0);
}
- Status RoleGraph::removePrivilegeFromRole(const RoleName& role,
- const Privilege& privilegeToRemove) {
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(role)) {
- return Status(
- ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot remove privileges from built-in role: " <<
- role.getFullName());
- }
+ RoleNameVector& subordinatesOfVictim = _roleToSubordinates[victim];
+ for (RoleNameVector::const_iterator subordinateRole = subordinatesOfVictim.begin(),
+ end = subordinatesOfVictim.end();
+ subordinateRole != end;
+ ++subordinateRole) {
+ RoleNameVector& membersOfSubordinate = _roleToMembers[*subordinateRole];
+ RoleNameVector::iterator toErase =
+ std::find(membersOfSubordinate.begin(), membersOfSubordinate.end(), victim);
+ fassert(17173, toErase != membersOfSubordinate.end());
+ membersOfSubordinate.erase(toErase);
+ }
+ subordinatesOfVictim.clear();
+ return Status::OK();
+}
+
+Status RoleGraph::addPrivilegeToRole(const RoleName& role, const Privilege& privilegeToAdd) {
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
+ 0);
+ }
+ if (isBuiltinRole(role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot grant privileges to built-in role: " << role.getFullName(),
+ 0);
+ }
- PrivilegeVector& currentPrivileges = _directPrivilegesForRole[role];
- for (PrivilegeVector::iterator it = currentPrivileges.begin();
- it != currentPrivileges.end(); ++it) {
-
- Privilege& curPrivilege = *it;
- if (curPrivilege.getResourcePattern() == privilegeToRemove.getResourcePattern()) {
- ActionSet curActions = curPrivilege.getActions();
-
- if (!curActions.isSupersetOf(privilegeToRemove.getActions())) {
- // Didn't possess all the actions being removed.
- return Status(ErrorCodes::PrivilegeNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not contain a privilege on " <<
- privilegeToRemove.getResourcePattern().toString() <<
- " with actions: " <<
- privilegeToRemove.getActions().toString(),
- 0);
- }
+ _addPrivilegeToRoleNoChecks(role, privilegeToAdd);
+ return Status::OK();
+}
+
+void RoleGraph::_addPrivilegeToRoleNoChecks(const RoleName& role, const Privilege& privilegeToAdd) {
+ Privilege::addPrivilegeToPrivilegeVector(&_directPrivilegesForRole[role], privilegeToAdd);
+}
+
+// NOTE: Current runtime of this is O(n*m) where n is the size of the current PrivilegeVector
+// for the given role, and m is the size of the privilegesToAdd vector.
+// If this was a PrivilegeSet (sorted on resource) rather than a PrivilegeVector, we
+// could do this in O(n+m) instead.
+Status RoleGraph::addPrivilegesToRole(const RoleName& role,
+ const PrivilegeVector& privilegesToAdd) {
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
+ 0);
+ }
+ if (isBuiltinRole(role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot grant privileges to built-in role: " << role.getFullName(),
+ 0);
+ }
- curPrivilege.removeActions(privilegeToRemove.getActions());
- if (curPrivilege.getActions().empty()) {
- currentPrivileges.erase(it);
- }
- return Status::OK();
- }
- }
- return Status(ErrorCodes::PrivilegeNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() << " does not "
- "contain any privileges on " <<
- privilegeToRemove.getResourcePattern().toString(),
+ for (PrivilegeVector::const_iterator it = privilegesToAdd.begin(); it != privilegesToAdd.end();
+ ++it) {
+ _addPrivilegeToRoleNoChecks(role, *it);
+ }
+ return Status::OK();
+}
+
+Status RoleGraph::removePrivilegeFromRole(const RoleName& role,
+ const Privilege& privilegeToRemove) {
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
0);
}
+ if (isBuiltinRole(role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot remove privileges from built-in role: " << role.getFullName());
+ }
- Status RoleGraph::removePrivilegesFromRole(const RoleName& role,
- const PrivilegeVector& privilegesToRemove) {
- for (PrivilegeVector::const_iterator it = privilegesToRemove.begin();
- it != privilegesToRemove.end(); ++it) {
- Status status = removePrivilegeFromRole(role, *it);
- if (!status.isOK()) {
- return status;
+ PrivilegeVector& currentPrivileges = _directPrivilegesForRole[role];
+ for (PrivilegeVector::iterator it = currentPrivileges.begin(); it != currentPrivileges.end();
+ ++it) {
+ Privilege& curPrivilege = *it;
+ if (curPrivilege.getResourcePattern() == privilegeToRemove.getResourcePattern()) {
+ ActionSet curActions = curPrivilege.getActions();
+
+ if (!curActions.isSupersetOf(privilegeToRemove.getActions())) {
+ // Didn't possess all the actions being removed.
+ return Status(ErrorCodes::PrivilegeNotFound,
+ mongoutils::str::stream()
+ << "Role: " << role.getFullName()
+ << " does not contain a privilege on "
+ << privilegeToRemove.getResourcePattern().toString()
+ << " with actions: " << privilegeToRemove.getActions().toString(),
+ 0);
}
- }
- return Status::OK();
- }
- Status RoleGraph::removeAllPrivilegesFromRole(const RoleName& role) {
- if (!roleExists(role)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << role.getFullName() <<
- " does not exist",
- 0);
- }
- if (isBuiltinRole(role)) {
- return Status(
- ErrorCodes::InvalidRoleModification,
- mongoutils::str::stream() << "Cannot remove privileges from built-in role: " <<
- role.getFullName());
+ curPrivilege.removeActions(privilegeToRemove.getActions());
+ if (curPrivilege.getActions().empty()) {
+ currentPrivileges.erase(it);
+ }
+ return Status::OK();
}
- _directPrivilegesForRole[role].clear();
- return Status::OK();
}
-
- Status RoleGraph::replaceRole(const RoleName& roleName,
- const std::vector<RoleName>& roles,
- const PrivilegeVector& privileges) {
- Status status = removeAllPrivilegesFromRole(roleName);
- if (status == ErrorCodes::RoleNotFound) {
- fassert(17168, createRole(roleName));
- }
- else if (!status.isOK()) {
+ return Status(ErrorCodes::PrivilegeNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not "
+ "contain any privileges on "
+ << privilegeToRemove.getResourcePattern().toString(),
+ 0);
+}
+
+Status RoleGraph::removePrivilegesFromRole(const RoleName& role,
+ const PrivilegeVector& privilegesToRemove) {
+ for (PrivilegeVector::const_iterator it = privilegesToRemove.begin();
+ it != privilegesToRemove.end();
+ ++it) {
+ Status status = removePrivilegeFromRole(role, *it);
+ if (!status.isOK()) {
return status;
}
- fassert(17169, removeAllRolesFromRole(roleName));
- for (size_t i = 0; i < roles.size(); ++i) {
- const RoleName& grantedRole = roles[i];
- status = createRole(grantedRole);
- fassert(17170, status.isOK() || status == ErrorCodes::DuplicateKey);
- fassert(17171, addRoleToRole(roleName, grantedRole));
+ }
+ return Status::OK();
+}
+
+Status RoleGraph::removeAllPrivilegesFromRole(const RoleName& role) {
+ if (!roleExists(role)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << role.getFullName()
+ << " does not exist",
+ 0);
+ }
+ if (isBuiltinRole(role)) {
+ return Status(ErrorCodes::InvalidRoleModification,
+ mongoutils::str::stream()
+ << "Cannot remove privileges from built-in role: " << role.getFullName());
+ }
+ _directPrivilegesForRole[role].clear();
+ return Status::OK();
+}
+
+Status RoleGraph::replaceRole(const RoleName& roleName,
+ const std::vector<RoleName>& roles,
+ const PrivilegeVector& privileges) {
+ Status status = removeAllPrivilegesFromRole(roleName);
+ if (status == ErrorCodes::RoleNotFound) {
+ fassert(17168, createRole(roleName));
+ } else if (!status.isOK()) {
+ return status;
+ }
+ fassert(17169, removeAllRolesFromRole(roleName));
+ for (size_t i = 0; i < roles.size(); ++i) {
+ const RoleName& grantedRole = roles[i];
+ status = createRole(grantedRole);
+ fassert(17170, status.isOK() || status == ErrorCodes::DuplicateKey);
+ fassert(17171, addRoleToRole(roleName, grantedRole));
+ }
+ fassert(17172, addPrivilegesToRole(roleName, privileges));
+ return Status::OK();
+}
+
+Status RoleGraph::recomputePrivilegeData() {
+ /*
+ * This method is used to recompute the "allPrivileges" vector for each node in the graph,
+ * as well as look for cycles. It is implemented by performing a depth-first traversal of
+ * the dependency graph, once for each node. "visitedRoles" tracks the set of role names
+ * ever visited, and it is used to prune each DFS. A node that has been visited once on any
+ * DFS is never visited again. Complexity of this implementation is O(n+m) where "n" is the
+ * number of nodes and "m" is the number of prerequisite edges. Space complexity is O(n),
+ * in both stack space and size of the "visitedRoles" set.
+ *
+ * "inProgressRoles" is used to detect and report cycles, as well as to keep track of roles
+ * we started visiting before realizing they had children that needed visiting first, so
+ * we can get back to them after visiting their children.
+ */
+
+ unordered_set<RoleName> visitedRoles;
+ for (EdgeSet::const_iterator it = _roleToSubordinates.begin(); it != _roleToSubordinates.end();
+ ++it) {
+ Status status = _recomputePrivilegeDataHelper(it->first, visitedRoles);
+ if (!status.isOK()) {
+ return status;
}
- fassert(17172, addPrivilegesToRole(roleName, privileges));
- return Status::OK();
}
+ return Status::OK();
+}
- Status RoleGraph::recomputePrivilegeData() {
- /*
- * This method is used to recompute the "allPrivileges" vector for each node in the graph,
- * as well as look for cycles. It is implemented by performing a depth-first traversal of
- * the dependency graph, once for each node. "visitedRoles" tracks the set of role names
- * ever visited, and it is used to prune each DFS. A node that has been visited once on any
- * DFS is never visited again. Complexity of this implementation is O(n+m) where "n" is the
- * number of nodes and "m" is the number of prerequisite edges. Space complexity is O(n),
- * in both stack space and size of the "visitedRoles" set.
- *
- * "inProgressRoles" is used to detect and report cycles, as well as to keep track of roles
- * we started visiting before realizing they had children that needed visiting first, so
- * we can get back to them after visiting their children.
- */
-
- unordered_set<RoleName> visitedRoles;
- for (EdgeSet::const_iterator it = _roleToSubordinates.begin();
- it != _roleToSubordinates.end(); ++it) {
- Status status = _recomputePrivilegeDataHelper(it->first, visitedRoles);
- if (!status.isOK()) {
- return status;
- }
- }
+Status RoleGraph::_recomputePrivilegeDataHelper(const RoleName& startingRole,
+ unordered_set<RoleName>& visitedRoles) {
+ if (visitedRoles.count(startingRole)) {
return Status::OK();
}
- Status RoleGraph::_recomputePrivilegeDataHelper(const RoleName& startingRole,
- unordered_set<RoleName>& visitedRoles) {
- if (visitedRoles.count(startingRole)) {
- return Status::OK();
- }
-
- std::vector<RoleName> inProgressRoles;
- inProgressRoles.push_back(startingRole);
- while (inProgressRoles.size()) {
- const RoleName currentRole = inProgressRoles.back();
- fassert(17277, !visitedRoles.count(currentRole));
+ std::vector<RoleName> inProgressRoles;
+ inProgressRoles.push_back(startingRole);
+ while (inProgressRoles.size()) {
+ const RoleName currentRole = inProgressRoles.back();
+ fassert(17277, !visitedRoles.count(currentRole));
- if (!roleExists(currentRole)) {
- return Status(ErrorCodes::RoleNotFound,
- mongoutils::str::stream() << "Role: " << currentRole.getFullName() <<
- " does not exist",
- 0);
- }
+ if (!roleExists(currentRole)) {
+ return Status(ErrorCodes::RoleNotFound,
+ mongoutils::str::stream() << "Role: " << currentRole.getFullName()
+ << " does not exist",
+ 0);
+ }
- // Check for cycles
- {
- const std::vector<RoleName>::const_iterator begin = inProgressRoles.begin();
- // The currentRole will always be last so don't look there.
- const std::vector<RoleName>::const_iterator end = --inProgressRoles.end();
- const std::vector<RoleName>::const_iterator firstOccurence =
- std::find(begin, end, currentRole);
- if (firstOccurence != end) {
- std::ostringstream os;
- os << "Cycle in dependency graph: ";
- for (std::vector<RoleName>::const_iterator it = firstOccurence;
- it != end; ++it) {
- os << it->getFullName() << " -> ";
- }
- os << currentRole.getFullName();
- return Status(ErrorCodes::GraphContainsCycle, os.str());
+ // Check for cycles
+ {
+ const std::vector<RoleName>::const_iterator begin = inProgressRoles.begin();
+ // The currentRole will always be last so don't look there.
+ const std::vector<RoleName>::const_iterator end = --inProgressRoles.end();
+ const std::vector<RoleName>::const_iterator firstOccurence =
+ std::find(begin, end, currentRole);
+ if (firstOccurence != end) {
+ std::ostringstream os;
+ os << "Cycle in dependency graph: ";
+ for (std::vector<RoleName>::const_iterator it = firstOccurence; it != end; ++it) {
+ os << it->getFullName() << " -> ";
}
+ os << currentRole.getFullName();
+ return Status(ErrorCodes::GraphContainsCycle, os.str());
}
+ }
- // Make sure we've already visited all subordinate roles before worrying about this one.
- const std::vector<RoleName>& currentRoleDirectRoles = _roleToSubordinates[currentRole];
- std::vector<RoleName>::const_iterator roleIt;
- for (roleIt = currentRoleDirectRoles.begin();
- roleIt != currentRoleDirectRoles.end(); ++roleIt) {
- const RoleName& childRole = *roleIt;
- if (!visitedRoles.count(childRole)) {
- inProgressRoles.push_back(childRole);
- break;
- }
- }
- // If roleIt didn't reach the end of currentRoleDirectRoles that means we found a child
- // of currentRole that we haven't visited yet.
- if (roleIt != currentRoleDirectRoles.end()) {
- continue;
+ // Make sure we've already visited all subordinate roles before worrying about this one.
+ const std::vector<RoleName>& currentRoleDirectRoles = _roleToSubordinates[currentRole];
+ std::vector<RoleName>::const_iterator roleIt;
+ for (roleIt = currentRoleDirectRoles.begin(); roleIt != currentRoleDirectRoles.end();
+ ++roleIt) {
+ const RoleName& childRole = *roleIt;
+ if (!visitedRoles.count(childRole)) {
+ inProgressRoles.push_back(childRole);
+ break;
}
- // At this point, we know that we've already visited all child roles of currentRole
- // and thus their "all privileges" sets are correct and can be added to currentRole's
- // "all privileges" set
-
- // Need to clear out the "all privileges" vector for the current role, and re-fill it
- // with just the direct privileges for this role.
- PrivilegeVector& currentRoleAllPrivileges = _allPrivilegesForRole[currentRole];
- currentRoleAllPrivileges = _directPrivilegesForRole[currentRole];
-
- // Need to do the same thing for the indirect roles
- unordered_set<RoleName>& currentRoleIndirectRoles =
- _roleToIndirectSubordinates[currentRole];
- currentRoleIndirectRoles.clear();
- for (std::vector<RoleName>::const_iterator it = currentRoleDirectRoles.begin();
- it != currentRoleDirectRoles.end(); ++it) {
- currentRoleIndirectRoles.insert(*it);
+ }
+ // If roleIt didn't reach the end of currentRoleDirectRoles that means we found a child
+ // of currentRole that we haven't visited yet.
+ if (roleIt != currentRoleDirectRoles.end()) {
+ continue;
+ }
+ // At this point, we know that we've already visited all child roles of currentRole
+ // and thus their "all privileges" sets are correct and can be added to currentRole's
+ // "all privileges" set
+
+ // Need to clear out the "all privileges" vector for the current role, and re-fill it
+ // with just the direct privileges for this role.
+ PrivilegeVector& currentRoleAllPrivileges = _allPrivilegesForRole[currentRole];
+ currentRoleAllPrivileges = _directPrivilegesForRole[currentRole];
+
+ // Need to do the same thing for the indirect roles
+ unordered_set<RoleName>& currentRoleIndirectRoles =
+ _roleToIndirectSubordinates[currentRole];
+ currentRoleIndirectRoles.clear();
+ for (std::vector<RoleName>::const_iterator it = currentRoleDirectRoles.begin();
+ it != currentRoleDirectRoles.end();
+ ++it) {
+ currentRoleIndirectRoles.insert(*it);
+ }
+
+ // Recursively add children's privileges to current role's "all privileges" vector, and
+ // children's roles to current roles's "indirect roles" vector.
+ for (std::vector<RoleName>::const_iterator roleIt = currentRoleDirectRoles.begin();
+ roleIt != currentRoleDirectRoles.end();
+ ++roleIt) {
+ // At this point, we already know that the "all privilege" set for the child is
+ // correct, so add those privileges to our "all privilege" set.
+ const RoleName& childRole = *roleIt;
+
+ const PrivilegeVector& childsPrivileges = _allPrivilegesForRole[childRole];
+ for (PrivilegeVector::const_iterator privIt = childsPrivileges.begin();
+ privIt != childsPrivileges.end();
+ ++privIt) {
+ Privilege::addPrivilegeToPrivilegeVector(&currentRoleAllPrivileges, *privIt);
}
- // Recursively add children's privileges to current role's "all privileges" vector, and
- // children's roles to current roles's "indirect roles" vector.
- for (std::vector<RoleName>::const_iterator roleIt = currentRoleDirectRoles.begin();
- roleIt != currentRoleDirectRoles.end(); ++roleIt) {
- // At this point, we already know that the "all privilege" set for the child is
- // correct, so add those privileges to our "all privilege" set.
- const RoleName& childRole = *roleIt;
-
- const PrivilegeVector& childsPrivileges = _allPrivilegesForRole[childRole];
- for (PrivilegeVector::const_iterator privIt = childsPrivileges.begin();
- privIt != childsPrivileges.end(); ++privIt) {
- Privilege::addPrivilegeToPrivilegeVector(&currentRoleAllPrivileges, *privIt);
- }
-
- // We also know that the "indirect roles" for the child is also correct, so we can
- // add those roles to our "indirect roles" set.
- const unordered_set<RoleName>& childsRoles = _roleToIndirectSubordinates[childRole];
- for (unordered_set<RoleName>::const_iterator childsRoleIt = childsRoles.begin();
- childsRoleIt != childsRoles.end(); ++childsRoleIt) {
- currentRoleIndirectRoles.insert(*childsRoleIt);
- }
+ // We also know that the "indirect roles" for the child is also correct, so we can
+ // add those roles to our "indirect roles" set.
+ const unordered_set<RoleName>& childsRoles = _roleToIndirectSubordinates[childRole];
+ for (unordered_set<RoleName>::const_iterator childsRoleIt = childsRoles.begin();
+ childsRoleIt != childsRoles.end();
+ ++childsRoleIt) {
+ currentRoleIndirectRoles.insert(*childsRoleIt);
}
-
- visitedRoles.insert(currentRole);
- inProgressRoles.pop_back();
}
- return Status::OK();
+
+ visitedRoles.insert(currentRole);
+ inProgressRoles.pop_back();
}
+ return Status::OK();
+}
- RoleNameIterator RoleGraph::getRolesForDatabase(const std::string& dbname) {
- _createBuiltinRolesForDBIfNeeded(dbname);
+RoleNameIterator RoleGraph::getRolesForDatabase(const std::string& dbname) {
+ _createBuiltinRolesForDBIfNeeded(dbname);
- std::set<RoleName>::const_iterator lower = _allRoles.lower_bound(RoleName("", dbname));
- std::string afterDB = dbname;
- afterDB.push_back('\0');
- std::set<RoleName>::const_iterator upper = _allRoles.lower_bound(RoleName("", afterDB));
- return makeRoleNameIterator(lower, upper);
- }
+ std::set<RoleName>::const_iterator lower = _allRoles.lower_bound(RoleName("", dbname));
+ std::string afterDB = dbname;
+ afterDB.push_back('\0');
+ std::set<RoleName>::const_iterator upper = _allRoles.lower_bound(RoleName("", afterDB));
+ return makeRoleNameIterator(lower, upper);
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/role_graph.h b/src/mongo/db/auth/role_graph.h
index bec12b6d1e2..50a0c47a857 100644
--- a/src/mongo/db/auth/role_graph.h
+++ b/src/mongo/db/auth/role_graph.h
@@ -41,273 +41,271 @@
namespace mongo {
+/**
+ * A graph of role and privilege relationships.
+ *
+ * This structure is used to store an in-memory representation of the admin.system.roledata
+ * collection, specifically the graph of which roles are members of other roles and what
+ * privileges each role has, both directly and transitively through membership in other roles.
+ * There are some restrictions on calls to getAllPrivileges(), specifically, one must call
+ * recomputePrivilegeData() before calling getAllPrivileges() if any of the mutation methods
+ * have been called on the instance since the later of its construction or the last call to
+ * recomputePrivilegeData() on the object.
+ */
+class RoleGraph {
+public:
+ /**
+ * Adds to "privileges" the privileges associated with the named built-in role, and returns
+ * true. Returns false if "role" does not name a built-in role, and does not modify
+ * "privileges". Addition of new privileges is done as with
+ * Privilege::addPrivilegeToPrivilegeVector.
+ */
+ static bool addPrivilegesForBuiltinRole(const RoleName& role, PrivilegeVector* privileges);
+
+ RoleGraph();
+ RoleGraph(const RoleGraph& other);
+ ~RoleGraph();
+
+ // Built-in roles for backwards compatibility with 2.2 and prior
+ static const std::string BUILTIN_ROLE_V0_READ;
+ static const std::string BUILTIN_ROLE_V0_READ_WRITE;
+ static const std::string BUILTIN_ROLE_V0_ADMIN_READ;
+ static const std::string BUILTIN_ROLE_V0_ADMIN_READ_WRITE;
+
+ // Swaps the contents of this RoleGraph with those of "other"
+ void swap(RoleGraph& other);
+
+ /**
+ * Adds to "privileges" the necessary privileges to do absolutely anything on the system.
+ */
+ static void generateUniversalPrivileges(PrivilegeVector* privileges);
+
+ /**
+ * Returns an iterator over the RoleNames of the "members" of the given role.
+ * Members of a role are roles that have been granted this role directly (roles that are
+ * members transitively through another role are not included). These are the "parents" of
+ * this node in the graph.
+ */
+ RoleNameIterator getDirectMembers(const RoleName& role);
+
+ /**
+ * Returns an iterator over the RoleNames of the "subordinates" of the given role.
+ * Subordinate roles are the roles that this role has been granted directly (roles
+ * that have been granted transitively through another role are not included). These are
+ * the "children" of this node in the graph.
+ */
+ RoleNameIterator getDirectSubordinates(const RoleName& role);
+
+ /**
+ * Returns an iterator that can be used to get a full list of roles that this role inherits
+ * privileges from. This includes its direct subordinate roles as well as the subordinates
+ * of its subordinates, and so on.
+ */
+ RoleNameIterator getIndirectSubordinates(const RoleName& role);
+
+ /**
+ * Returns an iterator that can be used to get a full list of roles (in lexicographical
+ * order) that are defined on the given database.
+ */
+ RoleNameIterator getRolesForDatabase(const std::string& dbname);
+
+ /**
+ * Returns a vector of the privileges that the given role has been directly granted.
+ * Privileges that have been granted transitively through this role's subordinate roles are
+ * not included.
+ */
+ const PrivilegeVector& getDirectPrivileges(const RoleName& role);
+
/**
- * A graph of role and privilege relationships.
+ * Returns a vector of all privileges that the given role contains. This includes both the
+ * privileges that have been granted to this role directly, as well as any privileges
+ * inherited from the role's subordinate roles.
+ */
+ const PrivilegeVector& getAllPrivileges(const RoleName& role);
+
+ /**
+ * Returns whether or not the given role exists in the role graph. Will implicitly
+ * add the role to the graph if it is a built-in role and isn't already in the graph.
+ */
+ bool roleExists(const RoleName& role);
+
+ /**
+ * Returns whether the given role corresponds to a built-in role.
+ */
+ static bool isBuiltinRole(const RoleName& role);
+
+ // Mutation functions
+
+ /**
+ * Puts an entry into the RoleGraph for the given RoleName.
+ * Returns DuplicateKey if the role already exists.
+ */
+ Status createRole(const RoleName& role);
+
+ /**
+ * Deletes the given role by first removing it from the members/subordinates arrays for
+ * all other roles, and then by removing its own entries in the 4 member maps.
+ * Returns RoleNotFound if the role doesn't exist.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status deleteRole(const RoleName& role);
+
+ /**
+ * Grants "role" to "recipient". This leaves "recipient" as a member of "role" and "role"
+ * as a subordinate of "recipient".
+ * Returns RoleNotFound if either of "role" or "recipient" doesn't exist in
+ * the RoleGraph.
+ * Returns InvalidRoleModification if "recipient" is a built-in role.
+ */
+ Status addRoleToRole(const RoleName& recipient, const RoleName& role);
+
+ /**
+ * Revokes "role" from "recipient".
+ * Returns RoleNotFound if either of "role" or "recipient" doesn't exist in
+ * the RoleGraph. Returns RolesNotRelated if "recipient" is not currently a
+ * member of "role".
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status removeRoleFromRole(const RoleName& recipient, const RoleName& role);
+
+ /**
+ * Removes all roles held by "victim".
+ * Returns RoleNotFound if "victim" doesn't exist in the role graph.
+ * Returns InvalidRoleModification if "victim" is a built-in role.
+ */
+ Status removeAllRolesFromRole(const RoleName& victim);
+
+ /**
+ * Grants "privilegeToAdd" to "role".
+ * Returns RoleNotFound if "role" doesn't exist in the role graph.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status addPrivilegeToRole(const RoleName& role, const Privilege& privilegeToAdd);
+
+ /**
+ * Grants Privileges from "privilegesToAdd" to "role".
+ * Returns RoleNotFound if "role" doesn't exist in the role graph.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status addPrivilegesToRole(const RoleName& role, const PrivilegeVector& privilegesToAdd);
+
+ /**
+ * Removes "privilegeToRemove" from "role".
+ * Returns RoleNotFound if "role" doesn't exist in the role graph.
+ * Returns PrivilegeNotFound if "role" doesn't contain the full privilege being removed.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status removePrivilegeFromRole(const RoleName& role, const Privilege& privilegeToRemove);
+
+ /**
+ * Removes all privileges in the "privilegesToRemove" vector from "role".
+ * Returns RoleNotFound if "role" doesn't exist in the role graph.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ * Returns PrivilegeNotFound if "role" is missing any of the privileges being removed. If
+ * PrivilegeNotFound is returned then the graph may be in an inconsistent state and needs to
+ * be abandoned.
+ */
+ Status removePrivilegesFromRole(const RoleName& role,
+ const PrivilegeVector& privilegesToRemove);
+
+ /**
+ * Removes all privileges from "role".
+ * Returns RoleNotFound if "role" doesn't exist in the role graph.
+ * Returns InvalidRoleModification if "role" is a built-in role.
+ */
+ Status removeAllPrivilegesFromRole(const RoleName& role);
+
+ /**
+ * Updates the RoleGraph by adding the role named "roleName", with the given role
+ * memberships and privileges. If the name "roleName" already exists, it is replaced. Any
+ * subordinate roles mentioned in role.roles are created, if needed, with empty privilege
+ * and subordinate role lists.
+ *
+ * Should _only_ fail if the role to replace is a builtin role, in which
+ * case it will return ErrorCodes::InvalidRoleModification.
+ */
+ Status replaceRole(const RoleName& roleName,
+ const std::vector<RoleName>& roles,
+ const PrivilegeVector& privileges);
+
+ /**
+ * Adds the role described in "doc" the role graph.
+ */
+ Status addRoleFromDocument(const BSONObj& doc);
+
+ /**
+ * Applies to the RoleGraph the oplog operation described by the parameters.
*
- * This structure is used to store an in-memory representation of the admin.system.roledata
- * collection, specifically the graph of which roles are members of other roles and what
- * privileges each role has, both directly and transitively through membership in other roles.
- * There are some restrictions on calls to getAllPrivileges(), specifically, one must call
- * recomputePrivilegeData() before calling getAllPrivileges() if any of the mutation methods
- * have been called on the instance since the later of its construction or the last call to
- * recomputePrivilegeData() on the object.
+ * Returns Status::OK() on success, ErrorCodes::OplogOperationUnsupported if the oplog
+ * operation is not supported, and other codes (typically BadValue) if the oplog operation
+ * is ill-described.
*/
- class RoleGraph {
- public:
- /**
- * Adds to "privileges" the privileges associated with the named built-in role, and returns
- * true. Returns false if "role" does not name a built-in role, and does not modify
- * "privileges". Addition of new privileges is done as with
- * Privilege::addPrivilegeToPrivilegeVector.
- */
- static bool addPrivilegesForBuiltinRole(const RoleName& role, PrivilegeVector* privileges);
-
- RoleGraph();
- RoleGraph(const RoleGraph& other);
- ~RoleGraph();
-
- // Built-in roles for backwards compatibility with 2.2 and prior
- static const std::string BUILTIN_ROLE_V0_READ;
- static const std::string BUILTIN_ROLE_V0_READ_WRITE;
- static const std::string BUILTIN_ROLE_V0_ADMIN_READ;
- static const std::string BUILTIN_ROLE_V0_ADMIN_READ_WRITE;
-
- // Swaps the contents of this RoleGraph with those of "other"
- void swap(RoleGraph& other);
-
- /**
- * Adds to "privileges" the necessary privileges to do absolutely anything on the system.
- */
- static void generateUniversalPrivileges(PrivilegeVector* privileges);
-
- /**
- * Returns an iterator over the RoleNames of the "members" of the given role.
- * Members of a role are roles that have been granted this role directly (roles that are
- * members transitively through another role are not included). These are the "parents" of
- * this node in the graph.
- */
- RoleNameIterator getDirectMembers(const RoleName& role);
-
- /**
- * Returns an iterator over the RoleNames of the "subordinates" of the given role.
- * Subordinate roles are the roles that this role has been granted directly (roles
- * that have been granted transitively through another role are not included). These are
- * the "children" of this node in the graph.
- */
- RoleNameIterator getDirectSubordinates(const RoleName& role);
-
- /**
- * Returns an iterator that can be used to get a full list of roles that this role inherits
- * privileges from. This includes its direct subordinate roles as well as the subordinates
- * of its subordinates, and so on.
- */
- RoleNameIterator getIndirectSubordinates(const RoleName& role);
-
- /**
- * Returns an iterator that can be used to get a full list of roles (in lexicographical
- * order) that are defined on the given database.
- */
- RoleNameIterator getRolesForDatabase(const std::string& dbname);
-
- /**
- * Returns a vector of the privileges that the given role has been directly granted.
- * Privileges that have been granted transitively through this role's subordinate roles are
- * not included.
- */
- const PrivilegeVector& getDirectPrivileges(const RoleName& role);
-
- /**
- * Returns a vector of all privileges that the given role contains. This includes both the
- * privileges that have been granted to this role directly, as well as any privileges
- * inherited from the role's subordinate roles.
- */
- const PrivilegeVector& getAllPrivileges(const RoleName& role);
-
- /**
- * Returns whether or not the given role exists in the role graph. Will implicitly
- * add the role to the graph if it is a built-in role and isn't already in the graph.
- */
- bool roleExists(const RoleName& role);
-
- /**
- * Returns whether the given role corresponds to a built-in role.
- */
- static bool isBuiltinRole(const RoleName& role);
-
- // Mutation functions
-
- /**
- * Puts an entry into the RoleGraph for the given RoleName.
- * Returns DuplicateKey if the role already exists.
- */
- Status createRole(const RoleName& role);
-
- /**
- * Deletes the given role by first removing it from the members/subordinates arrays for
- * all other roles, and then by removing its own entries in the 4 member maps.
- * Returns RoleNotFound if the role doesn't exist.
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status deleteRole(const RoleName& role);
-
- /**
- * Grants "role" to "recipient". This leaves "recipient" as a member of "role" and "role"
- * as a subordinate of "recipient".
- * Returns RoleNotFound if either of "role" or "recipient" doesn't exist in
- * the RoleGraph.
- * Returns InvalidRoleModification if "recipient" is a built-in role.
- */
- Status addRoleToRole(const RoleName& recipient, const RoleName& role);
-
- /**
- * Revokes "role" from "recipient".
- * Returns RoleNotFound if either of "role" or "recipient" doesn't exist in
- * the RoleGraph. Returns RolesNotRelated if "recipient" is not currently a
- * member of "role".
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status removeRoleFromRole(const RoleName& recipient, const RoleName& role);
-
- /**
- * Removes all roles held by "victim".
- * Returns RoleNotFound if "victim" doesn't exist in the role graph.
- * Returns InvalidRoleModification if "victim" is a built-in role.
- */
- Status removeAllRolesFromRole(const RoleName& victim);
-
- /**
- * Grants "privilegeToAdd" to "role".
- * Returns RoleNotFound if "role" doesn't exist in the role graph.
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status addPrivilegeToRole(const RoleName& role, const Privilege& privilegeToAdd);
-
- /**
- * Grants Privileges from "privilegesToAdd" to "role".
- * Returns RoleNotFound if "role" doesn't exist in the role graph.
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status addPrivilegesToRole(const RoleName& role, const PrivilegeVector& privilegesToAdd);
-
- /**
- * Removes "privilegeToRemove" from "role".
- * Returns RoleNotFound if "role" doesn't exist in the role graph.
- * Returns PrivilegeNotFound if "role" doesn't contain the full privilege being removed.
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status removePrivilegeFromRole(const RoleName& role,
- const Privilege& privilegeToRemove);
-
- /**
- * Removes all privileges in the "privilegesToRemove" vector from "role".
- * Returns RoleNotFound if "role" doesn't exist in the role graph.
- * Returns InvalidRoleModification if "role" is a built-in role.
- * Returns PrivilegeNotFound if "role" is missing any of the privileges being removed. If
- * PrivilegeNotFound is returned then the graph may be in an inconsistent state and needs to
- * be abandoned.
- */
- Status removePrivilegesFromRole(const RoleName& role,
- const PrivilegeVector& privilegesToRemove);
-
- /**
- * Removes all privileges from "role".
- * Returns RoleNotFound if "role" doesn't exist in the role graph.
- * Returns InvalidRoleModification if "role" is a built-in role.
- */
- Status removeAllPrivilegesFromRole(const RoleName& role);
-
- /**
- * Updates the RoleGraph by adding the role named "roleName", with the given role
- * memberships and privileges. If the name "roleName" already exists, it is replaced. Any
- * subordinate roles mentioned in role.roles are created, if needed, with empty privilege
- * and subordinate role lists.
- *
- * Should _only_ fail if the role to replace is a builtin role, in which
- * case it will return ErrorCodes::InvalidRoleModification.
- */
- Status replaceRole(const RoleName& roleName,
- const std::vector<RoleName>& roles,
- const PrivilegeVector& privileges);
-
- /**
- * Adds the role described in "doc" the role graph.
- */
- Status addRoleFromDocument(const BSONObj& doc);
-
- /**
- * Applies to the RoleGraph the oplog operation described by the parameters.
- *
- * Returns Status::OK() on success, ErrorCodes::OplogOperationUnsupported if the oplog
- * operation is not supported, and other codes (typically BadValue) if the oplog operation
- * is ill-described.
- */
- Status handleLogOp(
- const char* op,
- const NamespaceString& ns,
- const BSONObj& o,
- const BSONObj* o2);
-
- /**
- * Recomputes the indirect (getAllPrivileges) data for this graph.
- *
- * Must be called between calls to any of the mutation functions and calls
- * to getAllPrivileges().
- *
- * Returns Status::OK() on success. If a cycle is detected, returns
- * ErrorCodes::GraphContainsCycle, and the status message reveals the cycle.
- */
- Status recomputePrivilegeData();
-
- private:
- // Helper method doing a topological DFS to compute the indirect privilege
- // data and look for cycles
- Status _recomputePrivilegeDataHelper(const RoleName& currentRole,
- unordered_set<RoleName>& visitedRoles);
-
- /**
- * If the role name given is not a built-in role, or it is but it's already in the role
- * graph, then this does nothing. If it *is* a built-in role and this is the first time
- * this function has been called for this role, it will add the role into the role graph.
- */
- void _createBuiltinRoleIfNeeded(const RoleName& role);
-
- /**
- * Adds the built-in roles for the given database name to the role graph if they aren't
- * already present.
- */
- void _createBuiltinRolesForDBIfNeeded(const std::string& dbname);
-
- /**
- * Returns whether or not the given role exists strictly within the role graph.
- */
- bool _roleExistsDontCreateBuiltin(const RoleName& role);
-
- /**
- * Just creates the role in the role graph, without checking whether or not the role already
- * exists.
- */
- void _createRoleDontCheckIfRoleExists(const RoleName& role);
-
- /**
- * Grants "privilegeToAdd" to "role".
- * Doesn't do any checking as to whether the role exists or is a built-in role.
- */
- void _addPrivilegeToRoleNoChecks(const RoleName& role, const Privilege& privilegeToAdd);
-
-
- // Represents all the outgoing edges to other roles from any given role.
- typedef unordered_map<RoleName, std::vector<RoleName> > EdgeSet;
- // Maps a role name to a list of privileges associated with that role.
- typedef unordered_map<RoleName, PrivilegeVector> RolePrivilegeMap;
-
- EdgeSet _roleToSubordinates;
- unordered_map<RoleName, unordered_set<RoleName> > _roleToIndirectSubordinates;
- EdgeSet _roleToMembers;
- RolePrivilegeMap _directPrivilegesForRole;
- RolePrivilegeMap _allPrivilegesForRole;
- std::set<RoleName> _allRoles;
- };
-
- void swap(RoleGraph& lhs, RoleGraph& rhs);
+ Status handleLogOp(const char* op,
+ const NamespaceString& ns,
+ const BSONObj& o,
+ const BSONObj* o2);
+
+ /**
+ * Recomputes the indirect (getAllPrivileges) data for this graph.
+ *
+ * Must be called between calls to any of the mutation functions and calls
+ * to getAllPrivileges().
+ *
+ * Returns Status::OK() on success. If a cycle is detected, returns
+ * ErrorCodes::GraphContainsCycle, and the status message reveals the cycle.
+ */
+ Status recomputePrivilegeData();
+
+private:
+ // Helper method doing a topological DFS to compute the indirect privilege
+ // data and look for cycles
+ Status _recomputePrivilegeDataHelper(const RoleName& currentRole,
+ unordered_set<RoleName>& visitedRoles);
+
+ /**
+ * If the role name given is not a built-in role, or it is but it's already in the role
+ * graph, then this does nothing. If it *is* a built-in role and this is the first time
+ * this function has been called for this role, it will add the role into the role graph.
+ */
+ void _createBuiltinRoleIfNeeded(const RoleName& role);
+
+ /**
+ * Adds the built-in roles for the given database name to the role graph if they aren't
+ * already present.
+ */
+ void _createBuiltinRolesForDBIfNeeded(const std::string& dbname);
+
+ /**
+ * Returns whether or not the given role exists strictly within the role graph.
+ */
+ bool _roleExistsDontCreateBuiltin(const RoleName& role);
+
+ /**
+ * Just creates the role in the role graph, without checking whether or not the role already
+ * exists.
+ */
+ void _createRoleDontCheckIfRoleExists(const RoleName& role);
+
+ /**
+ * Grants "privilegeToAdd" to "role".
+ * Doesn't do any checking as to whether the role exists or is a built-in role.
+ */
+ void _addPrivilegeToRoleNoChecks(const RoleName& role, const Privilege& privilegeToAdd);
+
+
+ // Represents all the outgoing edges to other roles from any given role.
+ typedef unordered_map<RoleName, std::vector<RoleName>> EdgeSet;
+ // Maps a role name to a list of privileges associated with that role.
+ typedef unordered_map<RoleName, PrivilegeVector> RolePrivilegeMap;
+
+ EdgeSet _roleToSubordinates;
+ unordered_map<RoleName, unordered_set<RoleName>> _roleToIndirectSubordinates;
+ EdgeSet _roleToMembers;
+ RolePrivilegeMap _directPrivilegesForRole;
+ RolePrivilegeMap _allPrivilegesForRole;
+ std::set<RoleName> _allRoles;
+};
+
+void swap(RoleGraph& lhs, RoleGraph& rhs);
} // namespace mongo
diff --git a/src/mongo/db/auth/role_graph_builtin_roles.cpp b/src/mongo/db/auth/role_graph_builtin_roles.cpp
index 479cc0de9fa..6b8a1762bce 100644
--- a/src/mongo/db/auth/role_graph_builtin_roles.cpp
+++ b/src/mongo/db/auth/role_graph_builtin_roles.cpp
@@ -36,784 +36,648 @@
namespace mongo {
- const std::string RoleGraph::BUILTIN_ROLE_V0_READ = "read";
- const std::string RoleGraph::BUILTIN_ROLE_V0_READ_WRITE= "dbOwner";
- const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ = "readAnyDatabase";
- const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE= "root";
+const std::string RoleGraph::BUILTIN_ROLE_V0_READ = "read";
+const std::string RoleGraph::BUILTIN_ROLE_V0_READ_WRITE = "dbOwner";
+const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ = "readAnyDatabase";
+const std::string RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE = "root";
namespace {
- const std::string ADMIN_DBNAME = "admin";
-
- const std::string BUILTIN_ROLE_READ = "read";
- const std::string BUILTIN_ROLE_READ_WRITE = "readWrite";
- const std::string BUILTIN_ROLE_USER_ADMIN = "userAdmin";
- const std::string BUILTIN_ROLE_DB_ADMIN = "dbAdmin";
- const std::string BUILTIN_ROLE_CLUSTER_ADMIN = "clusterAdmin";
- const std::string BUILTIN_ROLE_READ_ANY_DB = "readAnyDatabase";
- const std::string BUILTIN_ROLE_READ_WRITE_ANY_DB = "readWriteAnyDatabase";
- const std::string BUILTIN_ROLE_USER_ADMIN_ANY_DB = "userAdminAnyDatabase";
- const std::string BUILTIN_ROLE_DB_ADMIN_ANY_DB = "dbAdminAnyDatabase";
- const std::string BUILTIN_ROLE_ROOT = "root";
- const std::string BUILTIN_ROLE_INTERNAL = "__system";
- const std::string BUILTIN_ROLE_DB_OWNER = "dbOwner";
- const std::string BUILTIN_ROLE_CLUSTER_MONITOR = "clusterMonitor";
- const std::string BUILTIN_ROLE_HOST_MANAGEMENT = "hostManager";
- const std::string BUILTIN_ROLE_CLUSTER_MANAGEMENT = "clusterManager";
- const std::string BUILTIN_ROLE_BACKUP = "backup";
- const std::string BUILTIN_ROLE_RESTORE = "restore";
-
- /// Actions that the "read" role may perform on a normal resources of a specific database, and
- /// that the "readAnyDatabase" role may perform on normal resources of any database.
- ActionSet readRoleActions;
-
- /// Actions that the "readWrite" role may perform on a normal resources of a specific database,
- /// and that the "readWriteAnyDatabase" role may perform on normal resources of any database.
- ActionSet readWriteRoleActions;
-
- /// Actions that the "userAdmin" role may perform on normal resources of a specific database,
- /// and that the "userAdminAnyDatabase" role may perform on normal resources of any database.
- ActionSet userAdminRoleActions;
-
- /// Actions that the "dbAdmin" role may perform on normal resources of a specific database,
- // and that the "dbAdminAnyDatabase" role may perform on normal resources of any database.
- ActionSet dbAdminRoleActions;
-
- /// Actions that the "clusterMonitor" role may perform on the cluster resource.
- ActionSet clusterMonitorRoleClusterActions;
-
- /// Actions that the "clusterMonitor" role may perform on any database.
- ActionSet clusterMonitorRoleDatabaseActions;
-
- /// Actions that the "hostManager" role may perform on the cluster resource.
- ActionSet hostManagerRoleClusterActions;
-
- /// Actions that the "hostManager" role may perform on any database.
- ActionSet hostManagerRoleDatabaseActions;
-
- /// Actions that the "clusterManager" role may perform on the cluster resource.
- ActionSet clusterManagerRoleClusterActions;
-
- /// Actions that the "clusterManager" role may perform on any database
- ActionSet clusterManagerRoleDatabaseActions;
-
- ActionSet& operator<<(ActionSet& target, ActionType source) {
- target.addAction(source);
- return target;
- }
-
- void operator+=(ActionSet& target, const ActionSet& source) {
- target.addAllActionsFromSet(source);
- }
-
- // This sets up the built-in role ActionSets. This is what determines what actions each role
- // is authorized to perform
- MONGO_INITIALIZER(AuthorizationBuiltinRoles)(InitializerContext* context) {
- // Read role
- readRoleActions
- << ActionType::collStats
- << ActionType::dbHash
- << ActionType::dbStats
- << ActionType::find
- << ActionType::killCursors
- << ActionType::listCollections
- << ActionType::listIndexes
- << ActionType::planCacheRead;
-
- // Read-write role
- readWriteRoleActions += readRoleActions;
- readWriteRoleActions
- << ActionType::convertToCapped // db admin gets this also
- << ActionType::createCollection // db admin gets this also
- << ActionType::dropCollection
- << ActionType::dropIndex
- << ActionType::emptycapped
- << ActionType::createIndex
- << ActionType::insert
- << ActionType::remove
- << ActionType::renameCollectionSameDB // db admin gets this also
- << ActionType::update;
-
- // User admin role
- userAdminRoleActions
- << ActionType::changeCustomData
- << ActionType::changePassword
- << ActionType::createUser
- << ActionType::createRole
- << ActionType::dropUser
- << ActionType::dropRole
- << ActionType::grantRole
- << ActionType::revokeRole
- << ActionType::viewUser
- << ActionType::viewRole;
-
-
- // DB admin role
- dbAdminRoleActions
- << ActionType::bypassDocumentValidation
- << ActionType::collMod
- << ActionType::collStats // clusterMonitor gets this also
- << ActionType::compact
- << ActionType::convertToCapped // read_write gets this also
- << ActionType::createCollection // read_write gets this also
- << ActionType::dbStats // clusterMonitor gets this also
- << ActionType::dropCollection
- << ActionType::dropDatabase // clusterAdmin gets this also TODO(spencer): should readWriteAnyDatabase?
- << ActionType::dropIndex
- << ActionType::createIndex
- << ActionType::indexStats
- << ActionType::enableProfiler
- << ActionType::listCollections
- << ActionType::listIndexes
- << ActionType::planCacheIndexFilter
- << ActionType::planCacheRead
- << ActionType::planCacheWrite
- << ActionType::reIndex
- << ActionType::renameCollectionSameDB // read_write gets this also
- << ActionType::repairDatabase
- << ActionType::storageDetails
- << ActionType::validate;
-
- // clusterMonitor role actions that target the cluster resource
- clusterMonitorRoleClusterActions
- << ActionType::connPoolStats
- << ActionType::getCmdLineOpts
- << ActionType::getLog
- << ActionType::getParameter
- << ActionType::getShardMap
- << ActionType::hostInfo
- << ActionType::listDatabases
- << ActionType::listShards // clusterManager gets this also
- << ActionType::netstat
- << ActionType::replSetGetConfig // clusterManager gets this also
- << ActionType::replSetGetStatus // clusterManager gets this also
- << ActionType::serverStatus
- << ActionType::top
- << ActionType::cursorInfo
- << ActionType::inprog
- << ActionType::shardingState;
-
- // clusterMonitor role actions that target a database (or collection) resource
- clusterMonitorRoleDatabaseActions
- << ActionType::collStats // dbAdmin gets this also
- << ActionType::dbStats // dbAdmin gets this also
- << ActionType::getShardVersion;
-
- // hostManager role actions that target the cluster resource
- hostManagerRoleClusterActions
- << ActionType::applicationMessage // clusterManager gets this also
- << ActionType::connPoolSync
- << ActionType::cpuProfiler
- << ActionType::logRotate
- << ActionType::setParameter
- << ActionType::shutdown
- << ActionType::touch
- << ActionType::unlock
- << ActionType::diagLogging
- << ActionType::flushRouterConfig // clusterManager gets this also
- << ActionType::fsync
- << ActionType::invalidateUserCache // userAdminAnyDatabase gets this also
- << ActionType::killop
- << ActionType::resync; // clusterManager gets this also
-
- // hostManager role actions that target the database resource
- hostManagerRoleDatabaseActions
- << ActionType::killCursors
- << ActionType::repairDatabase;
-
-
- // clusterManager role actions that target the cluster resource
- clusterManagerRoleClusterActions
- << ActionType::appendOplogNote // backup gets this also
- << ActionType::applicationMessage // hostManager gets this also
- << ActionType::replSetConfigure
- << ActionType::replSetGetConfig // clusterMonitor gets this also
- << ActionType::replSetGetStatus // clusterMonitor gets this also
- << ActionType::replSetStateChange
- << ActionType::resync // hostManager gets this also
- << ActionType::addShard
- << ActionType::removeShard
- << ActionType::listShards // clusterMonitor gets this also
- << ActionType::flushRouterConfig // hostManager gets this also
- << ActionType::cleanupOrphaned;
-
- clusterManagerRoleDatabaseActions
- << ActionType::splitChunk
- << ActionType::moveChunk
- << ActionType::enableSharding
- << ActionType::splitVector;
-
- return Status::OK();
- }
-
- void addReadOnlyDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges, Privilege(ResourcePattern::forDatabaseName(dbName), readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString(dbName, "system.indexes")),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString(dbName, "system.namespaces")),
- readRoleActions));
- }
-
- void addReadWriteDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
- addReadOnlyDbPrivileges(privileges, dbName);
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forDatabaseName(dbName), readWriteRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")),
- readWriteRoleActions));
- }
-
- void addUserAdminDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
- privileges->push_back(
- Privilege(ResourcePattern::forDatabaseName(dbName), userAdminRoleActions));
- }
-
- void addDbAdminDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forDatabaseName(dbName), dbAdminRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString(dbName, "system.indexes")),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString(dbName, "system.namespaces")),
- readRoleActions));
-
- ActionSet profileActions = readRoleActions;
- profileActions.addAction(ActionType::convertToCapped);
- profileActions.addAction(ActionType::createCollection);
- profileActions.addAction(ActionType::dropCollection);
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString(dbName, "system.profile")),
- profileActions));
- }
-
- void addDbOwnerPrivileges(PrivilegeVector* privileges, StringData dbName) {
- addReadWriteDbPrivileges(privileges, dbName);
- addDbAdminDbPrivileges(privileges, dbName);
- addUserAdminDbPrivileges(privileges, dbName);
- }
-
-
- void addReadOnlyAnyDbPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.indexes"),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.js"),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.namespaces"),
- readRoleActions));
- }
-
- void addReadWriteAnyDbPrivileges(PrivilegeVector* privileges) {
- addReadOnlyAnyDbPrivileges(privileges);
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), readWriteRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.js"), readWriteRoleActions));
- }
-
- void addUserAdminAnyDbPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), ActionType::invalidateUserCache));
-
-
- ActionSet readRoleAndIndexActions;
- readRoleAndIndexActions += readRoleActions;
- readRoleAndIndexActions << ActionType::createIndex << ActionType::dropIndex;
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.users"),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersCollectionNamespace),
- readRoleAndIndexActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::rolesCollectionNamespace),
- readRoleAndIndexActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::versionCollectionNamespace),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersAltCollectionNamespace),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersBackupCollectionNamespace),
- readRoleActions));
- }
-
- void addDbAdminAnyDbPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), dbAdminRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.indexes"),
- readRoleActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.namespaces"),
- readRoleActions));
- ActionSet profileActions = readRoleActions;
- profileActions.addAction(ActionType::convertToCapped);
- profileActions.addAction(ActionType::createCollection);
- profileActions.addAction(ActionType::dropCollection);
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.profile"),
- profileActions));
- }
-
- void addClusterMonitorPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), clusterMonitorRoleClusterActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(),
- clusterMonitorRoleDatabaseActions));
- addReadOnlyDbPrivileges(privileges, "config");
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- NamespaceString("local.system.replset")),
- ActionType::find));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.profile"), ActionType::find));
- }
-
- void addHostManagerPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), hostManagerRoleClusterActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(),
- hostManagerRoleDatabaseActions));
- }
-
- void addClusterManagerPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forClusterResource(), clusterManagerRoleClusterActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(),
- clusterManagerRoleDatabaseActions));
- addReadOnlyDbPrivileges(privileges, "config");
-
- ActionSet configSettingsActions;
- configSettingsActions << ActionType::insert << ActionType::update << ActionType::remove;
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("config",
- "settings")),
- configSettingsActions));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("local",
- "system.replset")),
- readRoleActions));
- }
+const std::string ADMIN_DBNAME = "admin";
+
+const std::string BUILTIN_ROLE_READ = "read";
+const std::string BUILTIN_ROLE_READ_WRITE = "readWrite";
+const std::string BUILTIN_ROLE_USER_ADMIN = "userAdmin";
+const std::string BUILTIN_ROLE_DB_ADMIN = "dbAdmin";
+const std::string BUILTIN_ROLE_CLUSTER_ADMIN = "clusterAdmin";
+const std::string BUILTIN_ROLE_READ_ANY_DB = "readAnyDatabase";
+const std::string BUILTIN_ROLE_READ_WRITE_ANY_DB = "readWriteAnyDatabase";
+const std::string BUILTIN_ROLE_USER_ADMIN_ANY_DB = "userAdminAnyDatabase";
+const std::string BUILTIN_ROLE_DB_ADMIN_ANY_DB = "dbAdminAnyDatabase";
+const std::string BUILTIN_ROLE_ROOT = "root";
+const std::string BUILTIN_ROLE_INTERNAL = "__system";
+const std::string BUILTIN_ROLE_DB_OWNER = "dbOwner";
+const std::string BUILTIN_ROLE_CLUSTER_MONITOR = "clusterMonitor";
+const std::string BUILTIN_ROLE_HOST_MANAGEMENT = "hostManager";
+const std::string BUILTIN_ROLE_CLUSTER_MANAGEMENT = "clusterManager";
+const std::string BUILTIN_ROLE_BACKUP = "backup";
+const std::string BUILTIN_ROLE_RESTORE = "restore";
+
+/// Actions that the "read" role may perform on a normal resources of a specific database, and
+/// that the "readAnyDatabase" role may perform on normal resources of any database.
+ActionSet readRoleActions;
+
+/// Actions that the "readWrite" role may perform on a normal resources of a specific database,
+/// and that the "readWriteAnyDatabase" role may perform on normal resources of any database.
+ActionSet readWriteRoleActions;
+
+/// Actions that the "userAdmin" role may perform on normal resources of a specific database,
+/// and that the "userAdminAnyDatabase" role may perform on normal resources of any database.
+ActionSet userAdminRoleActions;
+
+/// Actions that the "dbAdmin" role may perform on normal resources of a specific database,
+// and that the "dbAdminAnyDatabase" role may perform on normal resources of any database.
+ActionSet dbAdminRoleActions;
+
+/// Actions that the "clusterMonitor" role may perform on the cluster resource.
+ActionSet clusterMonitorRoleClusterActions;
+
+/// Actions that the "clusterMonitor" role may perform on any database.
+ActionSet clusterMonitorRoleDatabaseActions;
+
+/// Actions that the "hostManager" role may perform on the cluster resource.
+ActionSet hostManagerRoleClusterActions;
+
+/// Actions that the "hostManager" role may perform on any database.
+ActionSet hostManagerRoleDatabaseActions;
+
+/// Actions that the "clusterManager" role may perform on the cluster resource.
+ActionSet clusterManagerRoleClusterActions;
+
+/// Actions that the "clusterManager" role may perform on any database
+ActionSet clusterManagerRoleDatabaseActions;
+
+ActionSet& operator<<(ActionSet& target, ActionType source) {
+ target.addAction(source);
+ return target;
+}
+
+void operator+=(ActionSet& target, const ActionSet& source) {
+ target.addAllActionsFromSet(source);
+}
+
+// This sets up the built-in role ActionSets. This is what determines what actions each role
+// is authorized to perform
+MONGO_INITIALIZER(AuthorizationBuiltinRoles)(InitializerContext* context) {
+ // Read role
+ readRoleActions << ActionType::collStats << ActionType::dbHash << ActionType::dbStats
+ << ActionType::find << ActionType::killCursors << ActionType::listCollections
+ << ActionType::listIndexes << ActionType::planCacheRead;
+
+ // Read-write role
+ readWriteRoleActions += readRoleActions;
+ readWriteRoleActions << ActionType::convertToCapped // db admin gets this also
+ << ActionType::createCollection // db admin gets this also
+ << ActionType::dropCollection << ActionType::dropIndex
+ << ActionType::emptycapped << ActionType::createIndex << ActionType::insert
+ << ActionType::remove
+ << ActionType::renameCollectionSameDB // db admin gets this also
+ << ActionType::update;
+
+ // User admin role
+ userAdminRoleActions << ActionType::changeCustomData << ActionType::changePassword
+ << ActionType::createUser << ActionType::createRole << ActionType::dropUser
+ << ActionType::dropRole << ActionType::grantRole << ActionType::revokeRole
+ << ActionType::viewUser << ActionType::viewRole;
+
+
+ // DB admin role
+ dbAdminRoleActions
+ << ActionType::bypassDocumentValidation << ActionType::collMod
+ << ActionType::collStats // clusterMonitor gets this also
+ << ActionType::compact << ActionType::convertToCapped // read_write gets this also
+ << ActionType::createCollection // read_write gets this also
+ << ActionType::dbStats // clusterMonitor gets this also
+ << ActionType::dropCollection
+ << ActionType::
+ dropDatabase // clusterAdmin gets this also TODO(spencer): should readWriteAnyDatabase?
+ << ActionType::dropIndex << ActionType::createIndex << ActionType::indexStats
+ << ActionType::enableProfiler << ActionType::listCollections << ActionType::listIndexes
+ << ActionType::planCacheIndexFilter << ActionType::planCacheRead
+ << ActionType::planCacheWrite << ActionType::reIndex
+ << ActionType::renameCollectionSameDB // read_write gets this also
+ << ActionType::repairDatabase << ActionType::storageDetails << ActionType::validate;
+
+ // clusterMonitor role actions that target the cluster resource
+ clusterMonitorRoleClusterActions
+ << ActionType::connPoolStats << ActionType::getCmdLineOpts << ActionType::getLog
+ << ActionType::getParameter << ActionType::getShardMap << ActionType::hostInfo
+ << ActionType::listDatabases << ActionType::listShards // clusterManager gets this also
+ << ActionType::netstat << ActionType::replSetGetConfig // clusterManager gets this also
+ << ActionType::replSetGetStatus // clusterManager gets this also
+ << ActionType::serverStatus << ActionType::top << ActionType::cursorInfo
+ << ActionType::inprog << ActionType::shardingState;
+
+ // clusterMonitor role actions that target a database (or collection) resource
+ clusterMonitorRoleDatabaseActions << ActionType::collStats // dbAdmin gets this also
+ << ActionType::dbStats // dbAdmin gets this also
+ << ActionType::getShardVersion;
+
+ // hostManager role actions that target the cluster resource
+ hostManagerRoleClusterActions
+ << ActionType::applicationMessage // clusterManager gets this also
+ << ActionType::connPoolSync << ActionType::cpuProfiler << ActionType::logRotate
+ << ActionType::setParameter << ActionType::shutdown << ActionType::touch
+ << ActionType::unlock << ActionType::diagLogging
+ << ActionType::flushRouterConfig // clusterManager gets this also
+ << ActionType::fsync
+ << ActionType::invalidateUserCache // userAdminAnyDatabase gets this also
+ << ActionType::killop << ActionType::resync; // clusterManager gets this also
+
+ // hostManager role actions that target the database resource
+ hostManagerRoleDatabaseActions << ActionType::killCursors << ActionType::repairDatabase;
+
+
+ // clusterManager role actions that target the cluster resource
+ clusterManagerRoleClusterActions
+ << ActionType::appendOplogNote // backup gets this also
+ << ActionType::applicationMessage // hostManager gets this also
+ << ActionType::replSetConfigure
+ << ActionType::replSetGetConfig // clusterMonitor gets this also
+ << ActionType::replSetGetStatus // clusterMonitor gets this also
+ << ActionType::replSetStateChange << ActionType::resync // hostManager gets this also
+ << ActionType::addShard << ActionType::removeShard
+ << ActionType::listShards // clusterMonitor gets this also
+ << ActionType::flushRouterConfig // hostManager gets this also
+ << ActionType::cleanupOrphaned;
+
+ clusterManagerRoleDatabaseActions << ActionType::splitChunk << ActionType::moveChunk
+ << ActionType::enableSharding << ActionType::splitVector;
+
+ return Status::OK();
+}
+
+void addReadOnlyDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName(dbName), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.indexes")),
+ readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")),
+ readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.namespaces")),
+ readRoleActions));
+}
+
+void addReadWriteDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
+ addReadOnlyDbPrivileges(privileges, dbName);
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName(dbName), readWriteRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.js")),
+ readWriteRoleActions));
+}
+
+void addUserAdminDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
+ privileges->push_back(
+ Privilege(ResourcePattern::forDatabaseName(dbName), userAdminRoleActions));
+}
+
+void addDbAdminDbPrivileges(PrivilegeVector* privileges, StringData dbName) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forDatabaseName(dbName), dbAdminRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.indexes")),
+ readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.namespaces")),
+ readRoleActions));
+
+ ActionSet profileActions = readRoleActions;
+ profileActions.addAction(ActionType::convertToCapped);
+ profileActions.addAction(ActionType::createCollection);
+ profileActions.addAction(ActionType::dropCollection);
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString(dbName, "system.profile")),
+ profileActions));
+}
+
+void addDbOwnerPrivileges(PrivilegeVector* privileges, StringData dbName) {
+ addReadWriteDbPrivileges(privileges, dbName);
+ addDbAdminDbPrivileges(privileges, dbName);
+ addUserAdminDbPrivileges(privileges, dbName);
+}
+
+
+void addReadOnlyAnyDbPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.indexes"), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forCollectionName("system.js"), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.namespaces"), readRoleActions));
+}
+
+void addReadWriteAnyDbPrivileges(PrivilegeVector* privileges) {
+ addReadOnlyAnyDbPrivileges(privileges);
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), readWriteRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.js"), readWriteRoleActions));
+}
+
+void addUserAdminAnyDbPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forClusterResource(), ActionType::invalidateUserCache));
+
+
+ ActionSet readRoleAndIndexActions;
+ readRoleAndIndexActions += readRoleActions;
+ readRoleAndIndexActions << ActionType::createIndex << ActionType::dropIndex;
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forCollectionName("system.users"), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::usersCollectionNamespace),
+ readRoleAndIndexActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::rolesCollectionNamespace),
+ readRoleAndIndexActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::versionCollectionNamespace),
+ readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::usersAltCollectionNamespace),
+ readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(
+ AuthorizationManager::usersBackupCollectionNamespace),
+ readRoleActions));
+}
+
+void addDbAdminAnyDbPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::listDatabases));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), dbAdminRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.indexes"), readRoleActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.namespaces"), readRoleActions));
+ ActionSet profileActions = readRoleActions;
+ profileActions.addAction(ActionType::convertToCapped);
+ profileActions.addAction(ActionType::createCollection);
+ profileActions.addAction(ActionType::dropCollection);
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.profile"), profileActions));
+}
+
+void addClusterMonitorPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forClusterResource(), clusterMonitorRoleClusterActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forAnyNormalResource(), clusterMonitorRoleDatabaseActions));
+ addReadOnlyDbPrivileges(privileges, "config");
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("local.system.replset")),
+ ActionType::find));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.profile"), ActionType::find));
+}
+
+void addHostManagerPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forClusterResource(), hostManagerRoleClusterActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forAnyNormalResource(), hostManagerRoleDatabaseActions));
+}
+
+void addClusterManagerPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forClusterResource(), clusterManagerRoleClusterActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forAnyNormalResource(), clusterManagerRoleDatabaseActions));
+ addReadOnlyDbPrivileges(privileges, "config");
+
+ ActionSet configSettingsActions;
+ configSettingsActions << ActionType::insert << ActionType::update << ActionType::remove;
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("config", "settings")),
+ configSettingsActions));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("local", "system.replset")),
+ readRoleActions));
+}
+
+void addClusterAdminPrivileges(PrivilegeVector* privileges) {
+ addClusterMonitorPrivileges(privileges);
+ addHostManagerPrivileges(privileges);
+ addClusterManagerPrivileges(privileges);
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), ActionType::dropDatabase));
+}
+
+void addBackupPrivileges(PrivilegeVector* privileges) {
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::collStats));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), ActionType::find));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::listIndexes));
+
+ ActionSet clusterActions;
+ clusterActions << ActionType::getParameter // To check authSchemaVersion
+ << ActionType::listDatabases << ActionType::appendOplogNote; // For BRS
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forClusterResource(), clusterActions));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.indexes"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.namespaces"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forCollectionName("system.js"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.users"), ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::usersAltCollectionNamespace),
+ ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(
+ AuthorizationManager::usersBackupCollectionNamespace),
+ ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::rolesCollectionNamespace),
+ ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::versionCollectionNamespace),
+ ActionType::find));
+
+ ActionSet configSettingsActions;
+ configSettingsActions << ActionType::insert << ActionType::update << ActionType::find;
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(NamespaceString("config", "settings")),
+ configSettingsActions));
+}
+
+void addRestorePrivileges(PrivilegeVector* privileges) {
+ ActionSet actions;
+ actions << ActionType::bypassDocumentValidation << ActionType::collMod
+ << ActionType::createCollection << ActionType::createIndex << ActionType::dropCollection
+ << ActionType::insert;
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), actions));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forCollectionName("system.js"), actions));
+
+ // Need to be able to query system.namespaces to check existing collection options.
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forCollectionName("system.namespaces"), ActionType::find));
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
+
+ // Privileges for user/role management
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(
+ AuthorizationManager::defaultTempUsersCollectionNamespace),
+ ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(
+ AuthorizationManager::defaultTempRolesCollectionNamespace),
+ ActionType::find));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::usersAltCollectionNamespace),
+ actions));
+
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(ResourcePattern::forExactNamespace(
+ AuthorizationManager::usersBackupCollectionNamespace),
+ actions));
+
+ actions << ActionType::find;
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::versionCollectionNamespace),
+ actions));
+
+ // Need additional actions on system.users.
+ actions << ActionType::update << ActionType::remove;
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forCollectionName("system.users"), actions));
+
+ // Need to be able to run getParameter to check authSchemaVersion
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges, Privilege(ResourcePattern::forClusterResource(), ActionType::getParameter));
+
+ // Need to be able to create an index on the system.roles collection.
+ Privilege::addPrivilegeToPrivilegeVector(
+ privileges,
+ Privilege(
+ ResourcePattern::forExactNamespace(AuthorizationManager::rolesCollectionNamespace),
+ ActionType::createIndex));
+}
+
+void addRootRolePrivileges(PrivilegeVector* privileges) {
+ addClusterAdminPrivileges(privileges);
+ addUserAdminAnyDbPrivileges(privileges);
+ addDbAdminAnyDbPrivileges(privileges);
+ addReadWriteAnyDbPrivileges(privileges);
+}
+
+void addInternalRolePrivileges(PrivilegeVector* privileges) {
+ RoleGraph::generateUniversalPrivileges(privileges);
+}
- void addClusterAdminPrivileges(PrivilegeVector* privileges) {
- addClusterMonitorPrivileges(privileges);
- addHostManagerPrivileges(privileges);
- addClusterManagerPrivileges(privileges);
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(),
- ActionType::dropDatabase));
- }
+} // namespace
- void addBackupPrivileges(PrivilegeVector* privileges) {
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyResource(), ActionType::collStats));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), ActionType::find));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyResource(), ActionType::listIndexes));
-
- ActionSet clusterActions;
- clusterActions << ActionType::getParameter // To check authSchemaVersion
- << ActionType::listDatabases
- << ActionType::appendOplogNote; // For BRS
- Privilege::addPrivilegeToPrivilegeVector(
- privileges, Privilege(ResourcePattern::forClusterResource(), clusterActions));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.indexes"), ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.namespaces"),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.js"), ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.users"), ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersAltCollectionNamespace),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersBackupCollectionNamespace),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::rolesCollectionNamespace),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::versionCollectionNamespace),
- ActionType::find));
-
- ActionSet configSettingsActions;
- configSettingsActions << ActionType::insert << ActionType::update << ActionType::find;
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(NamespaceString("config",
- "settings")),
- configSettingsActions));
+bool RoleGraph::addPrivilegesForBuiltinRole(const RoleName& roleName, PrivilegeVector* result) {
+ const bool isAdminDB = (roleName.getDB() == ADMIN_DBNAME);
+
+ if (roleName.getRole() == BUILTIN_ROLE_READ) {
+ addReadOnlyDbPrivileges(result, roleName.getDB());
+ } else if (roleName.getRole() == BUILTIN_ROLE_READ_WRITE) {
+ addReadWriteDbPrivileges(result, roleName.getDB());
+ } else if (roleName.getRole() == BUILTIN_ROLE_USER_ADMIN) {
+ addUserAdminDbPrivileges(result, roleName.getDB());
+ } else if (roleName.getRole() == BUILTIN_ROLE_DB_ADMIN) {
+ addDbAdminDbPrivileges(result, roleName.getDB());
+ } else if (roleName.getRole() == BUILTIN_ROLE_DB_OWNER) {
+ addDbOwnerPrivileges(result, roleName.getDB());
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
+ addReadOnlyAnyDbPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
+ addReadWriteAnyDbPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
+ addUserAdminAnyDbPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
+ addDbAdminAnyDbPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
+ addClusterMonitorPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
+ addHostManagerPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
+ addClusterManagerPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
+ addClusterAdminPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_BACKUP) {
+ addBackupPrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_RESTORE) {
+ addRestorePrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_ROOT) {
+ addRootRolePrivileges(result);
+ } else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_INTERNAL) {
+ addInternalRolePrivileges(result);
+ } else {
+ return false;
}
+ return true;
+}
- void addRestorePrivileges(PrivilegeVector* privileges) {
- ActionSet actions;
- actions
- << ActionType::bypassDocumentValidation
- << ActionType::collMod
- << ActionType::createCollection
- << ActionType::createIndex
- << ActionType::dropCollection
- << ActionType::insert
- ;
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), actions));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.js"), actions));
-
- // Need to be able to query system.namespaces to check existing collection options.
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.namespaces"),
- ActionType::find));
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyResource(), ActionType::listCollections));
-
- // Privileges for user/role management
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forAnyNormalResource(), userAdminRoleActions));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::defaultTempUsersCollectionNamespace),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::defaultTempRolesCollectionNamespace),
- ActionType::find));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersAltCollectionNamespace),
- actions));
-
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::usersBackupCollectionNamespace),
- actions));
-
- actions << ActionType::find;
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(
- ResourcePattern::forExactNamespace(
- AuthorizationManager::versionCollectionNamespace),
- actions));
-
- // Need additional actions on system.users.
- actions << ActionType::update << ActionType::remove;
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forCollectionName("system.users"), actions));
-
- // Need to be able to run getParameter to check authSchemaVersion
- Privilege::addPrivilegeToPrivilegeVector(
- privileges, Privilege(ResourcePattern::forClusterResource(),
- ActionType::getParameter));
-
- // Need to be able to create an index on the system.roles collection.
- Privilege::addPrivilegeToPrivilegeVector(
- privileges,
- Privilege(ResourcePattern::forExactNamespace(
- AuthorizationManager::rolesCollectionNamespace),
- ActionType::createIndex));
- }
+void RoleGraph::generateUniversalPrivileges(PrivilegeVector* privileges) {
+ ActionSet allActions;
+ allActions.addAllActions();
+ privileges->push_back(Privilege(ResourcePattern::forAnyResource(), allActions));
+}
- void addRootRolePrivileges(PrivilegeVector* privileges) {
- addClusterAdminPrivileges(privileges);
- addUserAdminAnyDbPrivileges(privileges);
- addDbAdminAnyDbPrivileges(privileges);
- addReadWriteAnyDbPrivileges(privileges);
+bool RoleGraph::isBuiltinRole(const RoleName& role) {
+ if (!NamespaceString::validDBName(role.getDB()) || role.getDB() == "$external") {
+ return false;
}
- void addInternalRolePrivileges(PrivilegeVector* privileges) {
- RoleGraph::generateUniversalPrivileges(privileges);
- }
+ bool isAdminDB = role.getDB() == ADMIN_DBNAME;
-} // namespace
-
- bool RoleGraph::addPrivilegesForBuiltinRole(const RoleName& roleName,
- PrivilegeVector* result) {
- const bool isAdminDB = (roleName.getDB() == ADMIN_DBNAME);
-
- if (roleName.getRole() == BUILTIN_ROLE_READ) {
- addReadOnlyDbPrivileges(result, roleName.getDB());
- }
- else if (roleName.getRole() == BUILTIN_ROLE_READ_WRITE) {
- addReadWriteDbPrivileges(result, roleName.getDB());
- }
- else if (roleName.getRole() == BUILTIN_ROLE_USER_ADMIN) {
- addUserAdminDbPrivileges(result, roleName.getDB());
- }
- else if (roleName.getRole() == BUILTIN_ROLE_DB_ADMIN) {
- addDbAdminDbPrivileges(result, roleName.getDB());
- }
- else if (roleName.getRole() == BUILTIN_ROLE_DB_OWNER) {
- addDbOwnerPrivileges(result, roleName.getDB());
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
- addReadOnlyAnyDbPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
- addReadWriteAnyDbPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
- addUserAdminAnyDbPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
- addDbAdminAnyDbPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
- addClusterMonitorPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
- addHostManagerPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
- addClusterManagerPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
- addClusterAdminPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_BACKUP) {
- addBackupPrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_RESTORE) {
- addRestorePrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_ROOT) {
- addRootRolePrivileges(result);
- }
- else if (isAdminDB && roleName.getRole() == BUILTIN_ROLE_INTERNAL) {
- addInternalRolePrivileges(result);
- }
- else {
- return false;
- }
+ if (role.getRole() == BUILTIN_ROLE_READ) {
+ return true;
+ } else if (role.getRole() == BUILTIN_ROLE_READ_WRITE) {
+ return true;
+ } else if (role.getRole() == BUILTIN_ROLE_USER_ADMIN) {
+ return true;
+ } else if (role.getRole() == BUILTIN_ROLE_DB_ADMIN) {
+ return true;
+ } else if (role.getRole() == BUILTIN_ROLE_DB_OWNER) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_BACKUP) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_RESTORE) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_ROOT) {
+ return true;
+ } else if (isAdminDB && role.getRole() == BUILTIN_ROLE_INTERNAL) {
return true;
}
-
- void RoleGraph::generateUniversalPrivileges(PrivilegeVector* privileges) {
- ActionSet allActions;
- allActions.addAllActions();
- privileges->push_back(Privilege(ResourcePattern::forAnyResource(), allActions));
- }
-
- bool RoleGraph::isBuiltinRole(const RoleName& role) {
- if (!NamespaceString::validDBName(role.getDB()) || role.getDB() == "$external") {
- return false;
- }
-
- bool isAdminDB = role.getDB() == ADMIN_DBNAME;
-
- if (role.getRole() == BUILTIN_ROLE_READ) {
- return true;
- }
- else if (role.getRole() == BUILTIN_ROLE_READ_WRITE) {
- return true;
- }
- else if (role.getRole() == BUILTIN_ROLE_USER_ADMIN) {
- return true;
- }
- else if (role.getRole() == BUILTIN_ROLE_DB_ADMIN) {
- return true;
- }
- else if (role.getRole() == BUILTIN_ROLE_DB_OWNER) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_ANY_DB) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_READ_WRITE_ANY_DB) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_USER_ADMIN_ANY_DB) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_DB_ADMIN_ANY_DB) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MONITOR) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_HOST_MANAGEMENT) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_MANAGEMENT) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_CLUSTER_ADMIN) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_BACKUP) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_RESTORE) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_ROOT) {
- return true;
- }
- else if (isAdminDB && role.getRole() == BUILTIN_ROLE_INTERNAL) {
- return true;
- }
- return false;
+ return false;
+}
+
+void RoleGraph::_createBuiltinRolesForDBIfNeeded(const std::string& dbname) {
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_OWNER, dbname));
+
+ if (dbname == "admin") {
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN_ANY_DB, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MONITOR, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_HOST_MANAGEMENT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MANAGEMENT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_ADMIN, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_BACKUP, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_RESTORE, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_ROOT, dbname));
+ _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_INTERNAL, dbname));
}
+}
- void RoleGraph::_createBuiltinRolesForDBIfNeeded(const std::string& dbname) {
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_OWNER, dbname));
-
- if (dbname == "admin") {
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_READ_WRITE_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_USER_ADMIN_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_DB_ADMIN_ANY_DB, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MONITOR, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_HOST_MANAGEMENT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_MANAGEMENT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_CLUSTER_ADMIN, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_BACKUP, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_RESTORE, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_ROOT, dbname));
- _createBuiltinRoleIfNeeded(RoleName(BUILTIN_ROLE_INTERNAL, dbname));
- }
+void RoleGraph::_createBuiltinRoleIfNeeded(const RoleName& role) {
+ if (!isBuiltinRole(role) || _roleExistsDontCreateBuiltin(role)) {
+ return;
}
- void RoleGraph::_createBuiltinRoleIfNeeded(const RoleName& role) {
- if (!isBuiltinRole(role) || _roleExistsDontCreateBuiltin(role)) {
- return;
- }
-
- _createRoleDontCheckIfRoleExists(role);
- PrivilegeVector privileges;
- fassert(17145, addPrivilegesForBuiltinRole(role, &privileges));
- for (size_t i = 0; i < privileges.size(); ++i) {
- _addPrivilegeToRoleNoChecks(role, privileges[i]);
- _allPrivilegesForRole[role].push_back(privileges[i]);
- }
+ _createRoleDontCheckIfRoleExists(role);
+ PrivilegeVector privileges;
+ fassert(17145, addPrivilegesForBuiltinRole(role, &privileges));
+ for (size_t i = 0; i < privileges.size(); ++i) {
+ _addPrivilegeToRoleNoChecks(role, privileges[i]);
+ _allPrivilegesForRole[role].push_back(privileges[i]);
}
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/role_graph_test.cpp b/src/mongo/db/auth/role_graph_test.cpp
index 4e99f584276..745207c4b6d 100644
--- a/src/mongo/db/auth/role_graph_test.cpp
+++ b/src/mongo/db/auth/role_graph_test.cpp
@@ -40,693 +40,696 @@
namespace mongo {
namespace {
- // Tests adding and removing roles from other roles, the RoleNameIterator, and the
- // getDirectMembers and getDirectSubordinates methods
- TEST(RoleGraphTest, AddRemoveRoles) {
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
- RoleName roleD("readWrite", "dbD"); // built-in role
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
-
- RoleNameIterator it;
- it = graph.getDirectSubordinates(roleA);
- ASSERT_FALSE(it.more());
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
-
- // A -> B
- it = graph.getDirectSubordinates(roleA);
- ASSERT_TRUE(it.more());
- // should not advance the iterator
- ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
- ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
+// Tests adding and removing roles from other roles, the RoleNameIterator, and the
+// getDirectMembers and getDirectSubordinates methods
+TEST(RoleGraphTest, AddRemoveRoles) {
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+ RoleName roleD("readWrite", "dbD"); // built-in role
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+
+ RoleNameIterator it;
+ it = graph.getDirectSubordinates(roleA);
+ ASSERT_FALSE(it.more());
+ it = graph.getDirectMembers(roleA);
+ ASSERT_FALSE(it.more());
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+
+ // A -> B
+ it = graph.getDirectSubordinates(roleA);
+ ASSERT_TRUE(it.more());
+ // should not advance the iterator
+ ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
+ ASSERT_EQUALS(it.get().getFullName(), roleB.getFullName());
+ ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
+ ASSERT_FALSE(it.more());
+
+ it = graph.getDirectMembers(roleA);
+ ASSERT_FALSE(it.more());
+
+ it = graph.getDirectMembers(roleB);
+ ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
+ ASSERT_FALSE(it.more());
+
+ it = graph.getDirectSubordinates(roleB);
+ ASSERT_FALSE(it.more());
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleC));
+ ASSERT_OK(graph.addRoleToRole(roleB, roleC));
+ ASSERT_OK(graph.addRoleToRole(roleB, roleD));
+ // Adding the same role twice should be a no-op, duplicate roles should be de-duped.
+ ASSERT_OK(graph.addRoleToRole(roleB, roleD));
+
+ /*
+ * Graph now looks like:
+ * A
+ * / \
+ * v v
+ * B -> C
+ * |
+ * v
+ * D
+ */
+
+
+ it = graph.getDirectSubordinates(roleA); // should be roleB and roleC, order doesn't matter
+ RoleName cur = it.next();
+ if (cur == roleB) {
+ ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
+ } else if (cur == roleC) {
ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleB);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectSubordinates(roleB);
- ASSERT_FALSE(it.more());
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleC));
- ASSERT_OK(graph.addRoleToRole(roleB, roleC));
- ASSERT_OK(graph.addRoleToRole(roleB, roleD));
- // Adding the same role twice should be a no-op, duplicate roles should be de-duped.
- ASSERT_OK(graph.addRoleToRole(roleB, roleD));
-
- /*
- * Graph now looks like:
- * A
- * / \
- * v v
- * B -> C
- * |
- * v
- * D
- */
-
-
- it = graph.getDirectSubordinates(roleA); // should be roleB and roleC, order doesn't matter
+ } else {
+ FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
+ }
+ ASSERT_FALSE(it.more());
+
+ ASSERT_OK(graph.recomputePrivilegeData());
+ it = graph.getIndirectSubordinates(roleA); // should have roleB, roleC and roleD
+ bool hasB = false;
+ bool hasC = false;
+ bool hasD = false;
+ int num = 0;
+ while (it.more()) {
+ ++num;
RoleName cur = it.next();
if (cur == roleB) {
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
+ hasB = true;
} else if (cur == roleC) {
- ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
- }
- ASSERT_FALSE(it.more());
-
- ASSERT_OK(graph.recomputePrivilegeData());
- it = graph.getIndirectSubordinates(roleA); // should have roleB, roleC and roleD
- bool hasB = false;
- bool hasC = false;
- bool hasD = false;
- int num = 0;
- while (it.more()) {
- ++num;
- RoleName cur = it.next();
- if (cur == roleB) {
- hasB = true;
- } else if (cur == roleC) {
- hasC = true;
- } else if (cur == roleD) {
- hasD = true;
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " <<
- cur.getFullName());
- }
- }
- ASSERT_EQUALS(3, num);
- ASSERT(hasB);
- ASSERT(hasC);
- ASSERT(hasD);
-
- it = graph.getDirectSubordinates(roleB); // should be roleC and roleD, order doesn't matter
- cur = it.next();
- if (cur == roleC) {
- ASSERT_EQUALS(it.next().getFullName(), roleD.getFullName());
+ hasC = true;
} else if (cur == roleD) {
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- } else {
- FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
- }
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectSubordinates(roleC);
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleA);
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleB);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectMembers(roleC); // should be role A and role B, order doesn't matter
- cur = it.next();
- if (cur == roleA) {
- ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
- } else if (cur == roleB) {
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
+ hasD = true;
} else {
FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
}
- ASSERT_FALSE(it.more());
-
- // Now remove roleD from roleB and make sure graph is update correctly
- ASSERT_OK(graph.removeRoleFromRole(roleB, roleD));
-
- /*
- * Graph now looks like:
- * A
- * / \
- * v v
- * B -> C
- */
- it = graph.getDirectSubordinates(roleB); // should be just roleC
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
-
- it = graph.getDirectSubordinates(roleD); // should be empty
- ASSERT_FALSE(it.more());
-
-
- // Now delete roleB entirely and make sure that the other roles are updated properly
- ASSERT_OK(graph.deleteRole(roleB));
- ASSERT_NOT_OK(graph.deleteRole(roleB));
- it = graph.getDirectSubordinates(roleA);
- ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
- it = graph.getDirectMembers(roleC);
- ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
- ASSERT_FALSE(it.more());
}
-
- const ResourcePattern collectionAFooResource(ResourcePattern::forExactNamespace(
- NamespaceString("dbA.foo")));
- const ResourcePattern db1Resource(ResourcePattern::forDatabaseName("db1"));
- const ResourcePattern db2Resource(ResourcePattern::forDatabaseName("db2"));
- const ResourcePattern dbAResource(ResourcePattern::forDatabaseName("dbA"));
- const ResourcePattern dbBResource(ResourcePattern::forDatabaseName("dbB"));
- const ResourcePattern dbCResource(ResourcePattern::forDatabaseName("dbC"));
- const ResourcePattern dbDResource(ResourcePattern::forDatabaseName("dbD"));
- const ResourcePattern dbResource(ResourcePattern::forDatabaseName("db"));
-
- // Tests that adding multiple privileges on the same resource correctly collapses those to one
- // privilege
- TEST(RoleGraphTest, AddPrivileges) {
- RoleName roleA("roleA", "dbA");
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
-
- // Test adding a single privilege
- ActionSet actions;
- actions.addAction(ActionType::find);
- ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
-
- PrivilegeVector privileges = graph.getDirectPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
-
- // Add a privilege on a different resource
- ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(collectionAFooResource, actions)));
- privileges = graph.getDirectPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
- ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern());
- ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString());
-
-
- // Add different privileges on an existing resource and make sure they get de-duped
- actions.removeAllActions();
- actions.addAction(ActionType::insert);
-
- PrivilegeVector privilegesToAdd;
- privilegesToAdd.push_back(Privilege(dbAResource, actions));
-
- actions.removeAllActions();
- actions.addAction(ActionType::update);
- privilegesToAdd.push_back(Privilege(dbAResource, actions));
-
- ASSERT_OK(graph.addPrivilegesToRole(roleA, privilegesToAdd));
-
- privileges = graph.getDirectPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_NOT_EQUALS(actions.toString(), privileges[0].getActions().toString());
- actions.addAction(ActionType::find);
- actions.addAction(ActionType::insert);
- ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
- actions.removeAction(ActionType::insert);
- actions.removeAction(ActionType::update);
- ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern());
- ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString());
- }
-
- // Tests that recomputePrivilegeData correctly detects cycles in the graph.
- TEST(RoleGraphTest, DetectCycles) {
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
- RoleName roleD("roleD", "dbD");
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
- ASSERT_OK(graph.createRole(roleD));
-
- // Add a role to itself
- ASSERT_OK(graph.recomputePrivilegeData());
- ASSERT_OK(graph.addRoleToRole(roleA, roleA));
- ASSERT_NOT_OK(graph.recomputePrivilegeData());
- ASSERT_OK(graph.removeRoleFromRole(roleA, roleA));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
- ASSERT_OK(graph.addRoleToRole(roleA, roleC));
- ASSERT_OK(graph.addRoleToRole(roleB, roleC));
- ASSERT_OK(graph.recomputePrivilegeData());
- /*
- * Graph now looks like:
- * A
- * / \
- * v v
- * B -> C
- */
- ASSERT_OK(graph.addRoleToRole(roleC, roleD));
- ASSERT_OK(graph.addRoleToRole(roleD, roleB)); // Add a cycle
- /*
- * Graph now looks like:
- * A
- * / \
- * v v
- * B -> C
- * ^ /
- * \ v
- * D
- */
- ASSERT_NOT_OK(graph.recomputePrivilegeData());
- ASSERT_OK(graph.removeRoleFromRole(roleD, roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
- }
-
- // Tests that recomputePrivilegeData correctly updates transitive privilege data for all roles.
- TEST(RoleGraphTest, RecomputePrivilegeData) {
- // We create 4 roles and give each of them a unique privilege. After that the direct
- // privileges for all the roles are not touched. The only thing that is changed is the
- // role membership graph, and we test how that affects the set of all transitive privileges
- // for each role.
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
- RoleName roleD("readWrite", "dbD"); // built-in role
-
- ActionSet actions;
- actions.addAllActions();
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
-
- ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
- ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions)));
- ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions)));
-
- ASSERT_OK(graph.recomputePrivilegeData());
-
- PrivilegeVector privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
-
- // At this point we have all 4 roles set up, each with their own privilege, but no
- // roles have been granted to each other.
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- // Role graph: A->B
- ASSERT_OK(graph.recomputePrivilegeData());
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
-
- // Add's roleC's privileges to roleB and make sure roleA gets them as well.
- ASSERT_OK(graph.addRoleToRole(roleB, roleC));
- // Role graph: A->B->C
- ASSERT_OK(graph.recomputePrivilegeData());
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(3), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
- ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
- privileges = graph.getAllPrivileges(roleB);
- ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
- ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern());
-
- // Add's roleD's privileges to roleC and make sure that roleA and roleB get them as well.
- ASSERT_OK(graph.addRoleToRole(roleC, roleD));
- // Role graph: A->B->C->D
- ASSERT_OK(graph.recomputePrivilegeData());
- privileges = graph.getAllPrivileges(roleA);
- const size_t readWriteRolePrivilegeCount = graph.getAllPrivileges(roleD).size();
- ASSERT_EQUALS(readWriteRolePrivilegeCount + 3, privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
- ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
- privileges = graph.getAllPrivileges(roleB);
- ASSERT_EQUALS(readWriteRolePrivilegeCount + 2, privileges.size());
- ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern());
- privileges = graph.getAllPrivileges(roleC);
- ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size());
- ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
-
- // Remove roleC from roleB, make sure that roleA then loses both roleC's and roleD's
- // privileges
- ASSERT_OK(graph.removeRoleFromRole(roleB, roleC));
- // Role graph: A->B C->D
- ASSERT_OK(graph.recomputePrivilegeData());
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
- privileges = graph.getAllPrivileges(roleB);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
- privileges = graph.getAllPrivileges(roleC);
- ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size());
- ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
- privileges = graph.getAllPrivileges(roleD);
- ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size());
-
- // Make sure direct privileges were untouched
- privileges = graph.getDirectPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- privileges = graph.getDirectPrivileges(roleB);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
- privileges = graph.getDirectPrivileges(roleC);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
- privileges = graph.getDirectPrivileges(roleD);
- ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size());
- }
-
- // Test that if you grant 1 role to another, then remove it and change it's privileges, then
- // re-grant it, the receiving role sees the new privileges and not the old ones.
- TEST(RoleGraphTest, ReAddRole) {
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
-
- ActionSet actionsA, actionsB, actionsC;
- actionsA.addAction(ActionType::find);
- actionsB.addAction(ActionType::insert);
- actionsC.addAction(ActionType::update);
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
-
- ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbResource, actionsA)));
- ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB)));
- ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbResource, actionsC)));
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.addRoleToRole(roleB, roleC)); // graph: A <- B <- C
-
- ASSERT_OK(graph.recomputePrivilegeData());
-
- // roleA should have privileges from roleB and roleC
- PrivilegeVector privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::insert));
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update));
-
- // Now remove roleB from roleA. B still is a member of C, but A no longer should have
- // privileges from B or C.
- ASSERT_OK(graph.removeRoleFromRole(roleA, roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- // roleA should no longer have the privileges from roleB or roleC
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
-
- // Change the privileges that roleB grants
- ASSERT_OK(graph.removeAllPrivilegesFromRole(roleB));
- ActionSet newActionsB;
- newActionsB.addAction(ActionType::remove);
- ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, newActionsB)));
-
- // Grant roleB back to roleA, make sure roleA has roleB's new privilege but not its old one.
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update)); // should get roleC's actions again
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::remove)); // roleB should grant this to roleA
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert)); // no roles have this action anymore
-
- // Now delete roleB completely. A should once again lose the privileges from both B and C.
- ASSERT_OK(graph.deleteRole(roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- // roleA should no longer have the privileges from roleB or roleC
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
-
- // Now re-create roleB and give it a new privilege, then grant it back to roleA.
- // RoleA should get its new privilege but not roleC's privilege this time nor either of
- // roleB's old privileges.
- ASSERT_OK(graph.createRole(roleB));
- actionsB.removeAllActions();
- actionsB.addAction(ActionType::shutdown);
- ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB)));
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
- ASSERT_TRUE(privileges[0].getActions().contains(ActionType::shutdown));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove));
- ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
- }
-
- // Tests copy constructor and swap functionality.
- TEST(RoleGraphTest, CopySwap) {
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
-
- RoleGraph graph;
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.createRole(roleC));
-
- ActionSet actions;
- actions.addAction(ActionType::find);
- ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
- ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions)));
- ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions)));
-
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
-
- // Make a copy of the graph to do further modifications on.
- RoleGraph tempGraph(graph);
- ASSERT_OK(tempGraph.addRoleToRole(roleB, roleC));
- tempGraph.recomputePrivilegeData();
-
- // Now swap the copy back with the original graph and make sure the original was updated
- // properly.
- swap(tempGraph, graph);
-
- RoleNameIterator it = graph.getDirectSubordinates(roleB);
- ASSERT_TRUE(it.more());
+ ASSERT_EQUALS(3, num);
+ ASSERT(hasB);
+ ASSERT(hasC);
+ ASSERT(hasD);
+
+ it = graph.getDirectSubordinates(roleB); // should be roleC and roleD, order doesn't matter
+ cur = it.next();
+ if (cur == roleC) {
+ ASSERT_EQUALS(it.next().getFullName(), roleD.getFullName());
+ } else if (cur == roleD) {
ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
- ASSERT_FALSE(it.more());
-
- graph.getAllPrivileges(roleA); // should have privileges from roleB *and* role C
- PrivilegeVector privileges = graph.getAllPrivileges(roleA);
- ASSERT_EQUALS(static_cast<size_t>(3), privileges.size());
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
- ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
- ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
+ } else {
+ FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
}
+ ASSERT_FALSE(it.more());
- // Tests error handling
- TEST(RoleGraphTest, ErrorHandling) {
- RoleName roleA("roleA", "dbA");
- RoleName roleB("roleB", "dbB");
- RoleName roleC("roleC", "dbC");
-
- ActionSet actions;
- actions.addAction(ActionType::find);
- Privilege privilege1(db1Resource, actions);
- Privilege privilege2(db2Resource, actions);
- PrivilegeVector privileges;
- privileges.push_back(privilege1);
- privileges.push_back(privilege2);
-
- RoleGraph graph;
- // None of the roles exist yet.
- ASSERT_NOT_OK(graph.addPrivilegeToRole(roleA, privilege1));
- ASSERT_NOT_OK(graph.addPrivilegesToRole(roleA, privileges));
- ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1));
- ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges));
- ASSERT_NOT_OK(graph.removeAllPrivilegesFromRole(roleA));
- ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB));
-
- // One of the roles exists
- ASSERT_OK(graph.createRole(roleA));
- ASSERT_NOT_OK(graph.createRole(roleA)); // Can't create same role twice
- ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_NOT_OK(graph.addRoleToRole(roleB, roleA));
- ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB));
- ASSERT_NOT_OK(graph.removeRoleFromRole(roleB, roleA));
-
- // Should work now that both exist.
- ASSERT_OK(graph.createRole(roleB));
- ASSERT_OK(graph.addRoleToRole(roleA, roleB));
- ASSERT_OK(graph.removeRoleFromRole(roleA, roleB));
- ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB)); // roleA isn't actually a member of roleB
-
- // Can't remove a privilege from a role that doesn't have it.
- ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1));
- ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
- ASSERT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); // now should work
-
- // Test that removing a vector of privileges fails if *any* of the privileges are missing.
- ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
- ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege2));
- // Removing both privileges should work since it has both
- ASSERT_OK(graph.removePrivilegesFromRole(roleA, privileges));
- // Now add only 1 back and this time removing both should fail
- ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
- ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges));
- }
+ it = graph.getDirectSubordinates(roleC);
+ ASSERT_FALSE(it.more());
+ it = graph.getDirectMembers(roleA);
+ ASSERT_FALSE(it.more());
- TEST(RoleGraphTest, BuiltinRoles) {
- RoleName userRole("userDefined", "dbA");
- RoleName builtinRole("read", "dbA");
-
- ActionSet actions;
- actions.addAction(ActionType::insert);
- Privilege privilege(dbAResource, actions);
-
- RoleGraph graph;
-
- ASSERT(graph.roleExists(builtinRole));
- ASSERT_NOT_OK(graph.createRole(builtinRole));
- ASSERT_NOT_OK(graph.deleteRole(builtinRole));
- ASSERT(graph.roleExists(builtinRole));
- ASSERT(!graph.roleExists(userRole));
- ASSERT_OK(graph.createRole(userRole));
- ASSERT(graph.roleExists(userRole));
-
- ASSERT_NOT_OK(graph.addPrivilegeToRole(builtinRole, privilege));
- ASSERT_NOT_OK(graph.removePrivilegeFromRole(builtinRole, privilege));
- ASSERT_NOT_OK(graph.addRoleToRole(builtinRole, userRole));
- ASSERT_NOT_OK(graph.removeRoleFromRole(builtinRole, userRole));
-
- ASSERT_OK(graph.addPrivilegeToRole(userRole, privilege));
- ASSERT_OK(graph.addRoleToRole(userRole, builtinRole));
- ASSERT_OK(graph.recomputePrivilegeData());
-
- PrivilegeVector privileges = graph.getDirectPrivileges(userRole);
- ASSERT_EQUALS(1U, privileges.size());
- ASSERT(privileges[0].getActions().equals(actions));
- ASSERT(!privileges[0].getActions().contains(ActionType::find));
- ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
-
- privileges = graph.getAllPrivileges(userRole);
- size_t i;
- for (i = 0; i < privileges.size(); ++i) {
- if (dbAResource == privileges[i].getResourcePattern())
- break;
- }
- ASSERT_NOT_EQUALS(privileges.size(), i);
- ASSERT(privileges[i].getActions().isSupersetOf(actions));
- ASSERT(privileges[i].getActions().contains(ActionType::insert));
- ASSERT(privileges[i].getActions().contains(ActionType::find));
+ it = graph.getDirectMembers(roleB);
+ ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
+ ASSERT_FALSE(it.more());
- ASSERT_OK(graph.deleteRole(userRole));
- ASSERT(!graph.roleExists(userRole));
- }
-
- TEST(RoleGraphTest, BuiltinRolesOnlyOnAppropriateDatabases) {
- RoleGraph graph;
- ASSERT(graph.roleExists(RoleName("read", "test")));
- ASSERT(graph.roleExists(RoleName("readWrite", "test")));
- ASSERT(graph.roleExists(RoleName("userAdmin", "test")));
- ASSERT(graph.roleExists(RoleName("dbAdmin", "test")));
- ASSERT(graph.roleExists(RoleName("dbOwner", "test")));
- ASSERT(!graph.roleExists(RoleName("readAnyDatabase", "test")));
- ASSERT(!graph.roleExists(RoleName("readWriteAnyDatabase", "test")));
- ASSERT(!graph.roleExists(RoleName("userAdminAnyDatabase", "test")));
- ASSERT(!graph.roleExists(RoleName("dbAdminAnyDatabase", "test")));
- ASSERT(!graph.roleExists(RoleName("clusterAdmin", "test")));
- ASSERT(!graph.roleExists(RoleName("root", "test")));
- ASSERT(!graph.roleExists(RoleName("__system", "test")));
- ASSERT(!graph.roleExists(RoleName("MyRole", "test")));
-
- ASSERT(graph.roleExists(RoleName("read", "admin")));
- ASSERT(graph.roleExists(RoleName("readWrite", "admin")));
- ASSERT(graph.roleExists(RoleName("userAdmin", "admin")));
- ASSERT(graph.roleExists(RoleName("dbAdmin", "admin")));
- ASSERT(graph.roleExists(RoleName("dbOwner", "admin")));
- ASSERT(graph.roleExists(RoleName("readAnyDatabase", "admin")));
- ASSERT(graph.roleExists(RoleName("readWriteAnyDatabase", "admin")));
- ASSERT(graph.roleExists(RoleName("userAdminAnyDatabase", "admin")));
- ASSERT(graph.roleExists(RoleName("dbAdminAnyDatabase", "admin")));
- ASSERT(graph.roleExists(RoleName("clusterAdmin", "admin")));
- ASSERT(graph.roleExists(RoleName("root", "admin")));
- ASSERT(graph.roleExists(RoleName("__system", "admin")));
- ASSERT(!graph.roleExists(RoleName("MyRole", "admin")));
+ it = graph.getDirectMembers(roleC); // should be role A and role B, order doesn't matter
+ cur = it.next();
+ if (cur == roleA) {
+ ASSERT_EQUALS(it.next().getFullName(), roleB.getFullName());
+ } else if (cur == roleB) {
+ ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
+ } else {
+ FAIL(mongoutils::str::stream() << "unexpected role returned: " << cur.getFullName());
}
-
- TEST(RoleGraphTest, getRolesForDatabase) {
- RoleGraph graph;
- graph.createRole(RoleName("myRole", "test"));
- // Make sure that a role on "test2" doesn't show up in the roles list for "test"
- graph.createRole(RoleName("anotherRole", "test2"));
- graph.createRole(RoleName("myAdminRole", "admin"));
-
- // Non-admin DB with no user-defined roles
- RoleNameIterator it = graph.getRolesForDatabase("fakedb");
- ASSERT_EQUALS(RoleName("dbAdmin", "fakedb"), it.next());
- ASSERT_EQUALS(RoleName("dbOwner", "fakedb"), it.next());
- ASSERT_EQUALS(RoleName("read", "fakedb"), it.next());
- ASSERT_EQUALS(RoleName("readWrite", "fakedb"), it.next());
- ASSERT_EQUALS(RoleName("userAdmin", "fakedb"), it.next());
- ASSERT_FALSE(it.more());
-
- // Non-admin DB with a user-defined role
- it = graph.getRolesForDatabase("test");
- ASSERT_EQUALS(RoleName("dbAdmin", "test"), it.next());
- ASSERT_EQUALS(RoleName("dbOwner", "test"), it.next());
- ASSERT_EQUALS(RoleName("myRole", "test"), it.next());
- ASSERT_EQUALS(RoleName("read", "test"), it.next());
- ASSERT_EQUALS(RoleName("readWrite", "test"), it.next());
- ASSERT_EQUALS(RoleName("userAdmin", "test"), it.next());
- ASSERT_FALSE(it.more());
-
- // Admin DB
- it = graph.getRolesForDatabase("admin");
- ASSERT_EQUALS(RoleName("__system", "admin"), it.next());
- ASSERT_EQUALS(RoleName("backup", "admin"), it.next());
- ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), it.next());
- ASSERT_EQUALS(RoleName("clusterManager", "admin"), it.next());
- ASSERT_EQUALS(RoleName("clusterMonitor", "admin"), it.next());
- ASSERT_EQUALS(RoleName("dbAdmin", "admin"), it.next());
- ASSERT_EQUALS(RoleName("dbAdminAnyDatabase", "admin"), it.next());
- ASSERT_EQUALS(RoleName("dbOwner", "admin"), it.next());
- ASSERT_EQUALS(RoleName("hostManager", "admin"), it.next());
- ASSERT_EQUALS(RoleName("myAdminRole", "admin"), it.next());
- ASSERT_EQUALS(RoleName("read", "admin"), it.next());
- ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), it.next());
- ASSERT_EQUALS(RoleName("readWrite", "admin"), it.next());
- ASSERT_EQUALS(RoleName("readWriteAnyDatabase", "admin"), it.next());
- ASSERT_EQUALS(RoleName("restore", "admin"), it.next());
- ASSERT_EQUALS(RoleName("root", "admin"), it.next());
- ASSERT_EQUALS(RoleName("userAdmin", "admin"), it.next());
- ASSERT_EQUALS(RoleName("userAdminAnyDatabase", "admin"), it.next());
- ASSERT_FALSE(it.more());
+ ASSERT_FALSE(it.more());
+
+ // Now remove roleD from roleB and make sure graph is update correctly
+ ASSERT_OK(graph.removeRoleFromRole(roleB, roleD));
+
+ /*
+ * Graph now looks like:
+ * A
+ * / \
+ * v v
+ * B -> C
+ */
+ it = graph.getDirectSubordinates(roleB); // should be just roleC
+ ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
+ ASSERT_FALSE(it.more());
+
+ it = graph.getDirectSubordinates(roleD); // should be empty
+ ASSERT_FALSE(it.more());
+
+
+ // Now delete roleB entirely and make sure that the other roles are updated properly
+ ASSERT_OK(graph.deleteRole(roleB));
+ ASSERT_NOT_OK(graph.deleteRole(roleB));
+ it = graph.getDirectSubordinates(roleA);
+ ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
+ ASSERT_FALSE(it.more());
+ it = graph.getDirectMembers(roleC);
+ ASSERT_EQUALS(it.next().getFullName(), roleA.getFullName());
+ ASSERT_FALSE(it.more());
+}
+
+const ResourcePattern collectionAFooResource(
+ ResourcePattern::forExactNamespace(NamespaceString("dbA.foo")));
+const ResourcePattern db1Resource(ResourcePattern::forDatabaseName("db1"));
+const ResourcePattern db2Resource(ResourcePattern::forDatabaseName("db2"));
+const ResourcePattern dbAResource(ResourcePattern::forDatabaseName("dbA"));
+const ResourcePattern dbBResource(ResourcePattern::forDatabaseName("dbB"));
+const ResourcePattern dbCResource(ResourcePattern::forDatabaseName("dbC"));
+const ResourcePattern dbDResource(ResourcePattern::forDatabaseName("dbD"));
+const ResourcePattern dbResource(ResourcePattern::forDatabaseName("db"));
+
+// Tests that adding multiple privileges on the same resource correctly collapses those to one
+// privilege
+TEST(RoleGraphTest, AddPrivileges) {
+ RoleName roleA("roleA", "dbA");
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+
+ // Test adding a single privilege
+ ActionSet actions;
+ actions.addAction(ActionType::find);
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
+
+ PrivilegeVector privileges = graph.getDirectPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
+
+ // Add a privilege on a different resource
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(collectionAFooResource, actions)));
+ privileges = graph.getDirectPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
+ ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern());
+ ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString());
+
+
+ // Add different privileges on an existing resource and make sure they get de-duped
+ actions.removeAllActions();
+ actions.addAction(ActionType::insert);
+
+ PrivilegeVector privilegesToAdd;
+ privilegesToAdd.push_back(Privilege(dbAResource, actions));
+
+ actions.removeAllActions();
+ actions.addAction(ActionType::update);
+ privilegesToAdd.push_back(Privilege(dbAResource, actions));
+
+ ASSERT_OK(graph.addPrivilegesToRole(roleA, privilegesToAdd));
+
+ privileges = graph.getDirectPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_NOT_EQUALS(actions.toString(), privileges[0].getActions().toString());
+ actions.addAction(ActionType::find);
+ actions.addAction(ActionType::insert);
+ ASSERT_EQUALS(actions.toString(), privileges[0].getActions().toString());
+ actions.removeAction(ActionType::insert);
+ actions.removeAction(ActionType::update);
+ ASSERT_EQUALS(collectionAFooResource, privileges[1].getResourcePattern());
+ ASSERT_EQUALS(actions.toString(), privileges[1].getActions().toString());
+}
+
+// Tests that recomputePrivilegeData correctly detects cycles in the graph.
+TEST(RoleGraphTest, DetectCycles) {
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+ RoleName roleD("roleD", "dbD");
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+ ASSERT_OK(graph.createRole(roleD));
+
+ // Add a role to itself
+ ASSERT_OK(graph.recomputePrivilegeData());
+ ASSERT_OK(graph.addRoleToRole(roleA, roleA));
+ ASSERT_NOT_OK(graph.recomputePrivilegeData());
+ ASSERT_OK(graph.removeRoleFromRole(roleA, roleA));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+ ASSERT_OK(graph.addRoleToRole(roleA, roleC));
+ ASSERT_OK(graph.addRoleToRole(roleB, roleC));
+ ASSERT_OK(graph.recomputePrivilegeData());
+ /*
+ * Graph now looks like:
+ * A
+ * / \
+ * v v
+ * B -> C
+ */
+ ASSERT_OK(graph.addRoleToRole(roleC, roleD));
+ ASSERT_OK(graph.addRoleToRole(roleD, roleB)); // Add a cycle
+ /*
+ * Graph now looks like:
+ * A
+ * / \
+ * v v
+ * B -> C
+ * ^ /
+ * \ v
+ * D
+ */
+ ASSERT_NOT_OK(graph.recomputePrivilegeData());
+ ASSERT_OK(graph.removeRoleFromRole(roleD, roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+}
+
+// Tests that recomputePrivilegeData correctly updates transitive privilege data for all roles.
+TEST(RoleGraphTest, RecomputePrivilegeData) {
+ // We create 4 roles and give each of them a unique privilege. After that the direct
+ // privileges for all the roles are not touched. The only thing that is changed is the
+ // role membership graph, and we test how that affects the set of all transitive privileges
+ // for each role.
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+ RoleName roleD("readWrite", "dbD"); // built-in role
+
+ ActionSet actions;
+ actions.addAllActions();
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions)));
+
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ PrivilegeVector privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+
+ // At this point we have all 4 roles set up, each with their own privilege, but no
+ // roles have been granted to each other.
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ // Role graph: A->B
+ ASSERT_OK(graph.recomputePrivilegeData());
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
+
+ // Add's roleC's privileges to roleB and make sure roleA gets them as well.
+ ASSERT_OK(graph.addRoleToRole(roleB, roleC));
+ // Role graph: A->B->C
+ ASSERT_OK(graph.recomputePrivilegeData());
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(3), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
+ ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleB);
+ ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
+ ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern());
+
+ // Add's roleD's privileges to roleC and make sure that roleA and roleB get them as well.
+ ASSERT_OK(graph.addRoleToRole(roleC, roleD));
+ // Role graph: A->B->C->D
+ ASSERT_OK(graph.recomputePrivilegeData());
+ privileges = graph.getAllPrivileges(roleA);
+ const size_t readWriteRolePrivilegeCount = graph.getAllPrivileges(roleD).size();
+ ASSERT_EQUALS(readWriteRolePrivilegeCount + 3, privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
+ ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleB);
+ ASSERT_EQUALS(readWriteRolePrivilegeCount + 2, privileges.size());
+ ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbCResource, privileges[1].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleC);
+ ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size());
+ ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
+
+ // Remove roleC from roleB, make sure that roleA then loses both roleC's and roleD's
+ // privileges
+ ASSERT_OK(graph.removeRoleFromRole(roleB, roleC));
+ // Role graph: A->B C->D
+ ASSERT_OK(graph.recomputePrivilegeData());
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(2), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleB);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleC);
+ ASSERT_EQUALS(readWriteRolePrivilegeCount + 1, privileges.size());
+ ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
+ privileges = graph.getAllPrivileges(roleD);
+ ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size());
+
+ // Make sure direct privileges were untouched
+ privileges = graph.getDirectPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ privileges = graph.getDirectPrivileges(roleB);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbBResource, privileges[0].getResourcePattern());
+ privileges = graph.getDirectPrivileges(roleC);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_EQUALS(dbCResource, privileges[0].getResourcePattern());
+ privileges = graph.getDirectPrivileges(roleD);
+ ASSERT_EQUALS(readWriteRolePrivilegeCount, privileges.size());
+}
+
+// Test that if you grant 1 role to another, then remove it and change it's privileges, then
+// re-grant it, the receiving role sees the new privileges and not the old ones.
+TEST(RoleGraphTest, ReAddRole) {
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+
+ ActionSet actionsA, actionsB, actionsC;
+ actionsA.addAction(ActionType::find);
+ actionsB.addAction(ActionType::insert);
+ actionsC.addAction(ActionType::update);
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbResource, actionsA)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbResource, actionsC)));
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.addRoleToRole(roleB, roleC)); // graph: A <- B <- C
+
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ // roleA should have privileges from roleB and roleC
+ PrivilegeVector privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::insert));
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::update));
+
+ // Now remove roleB from roleA. B still is a member of C, but A no longer should have
+ // privileges from B or C.
+ ASSERT_OK(graph.removeRoleFromRole(roleA, roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ // roleA should no longer have the privileges from roleB or roleC
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
+
+ // Change the privileges that roleB grants
+ ASSERT_OK(graph.removeAllPrivilegesFromRole(roleB));
+ ActionSet newActionsB;
+ newActionsB.addAction(ActionType::remove);
+ ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, newActionsB)));
+
+ // Grant roleB back to roleA, make sure roleA has roleB's new privilege but not its old one.
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
+ ASSERT_TRUE(privileges[0].getActions().contains(
+ ActionType::update)); // should get roleC's actions again
+ ASSERT_TRUE(privileges[0].getActions().contains(
+ ActionType::remove)); // roleB should grant this to roleA
+ ASSERT_FALSE(privileges[0].getActions().contains(
+ ActionType::insert)); // no roles have this action anymore
+
+ // Now delete roleB completely. A should once again lose the privileges from both B and C.
+ ASSERT_OK(graph.deleteRole(roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ // roleA should no longer have the privileges from roleB or roleC
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
+
+ // Now re-create roleB and give it a new privilege, then grant it back to roleA.
+ // RoleA should get its new privilege but not roleC's privilege this time nor either of
+ // roleB's old privileges.
+ ASSERT_OK(graph.createRole(roleB));
+ actionsB.removeAllActions();
+ actionsB.addAction(ActionType::shutdown);
+ ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbResource, actionsB)));
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(1), privileges.size());
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::find));
+ ASSERT_TRUE(privileges[0].getActions().contains(ActionType::shutdown));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::update));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::remove));
+ ASSERT_FALSE(privileges[0].getActions().contains(ActionType::insert));
+}
+
+// Tests copy constructor and swap functionality.
+TEST(RoleGraphTest, CopySwap) {
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+
+ RoleGraph graph;
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.createRole(roleC));
+
+ ActionSet actions;
+ actions.addAction(ActionType::find);
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, Privilege(dbAResource, actions)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleB, Privilege(dbBResource, actions)));
+ ASSERT_OK(graph.addPrivilegeToRole(roleC, Privilege(dbCResource, actions)));
+
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+
+ // Make a copy of the graph to do further modifications on.
+ RoleGraph tempGraph(graph);
+ ASSERT_OK(tempGraph.addRoleToRole(roleB, roleC));
+ tempGraph.recomputePrivilegeData();
+
+ // Now swap the copy back with the original graph and make sure the original was updated
+ // properly.
+ swap(tempGraph, graph);
+
+ RoleNameIterator it = graph.getDirectSubordinates(roleB);
+ ASSERT_TRUE(it.more());
+ ASSERT_EQUALS(it.next().getFullName(), roleC.getFullName());
+ ASSERT_FALSE(it.more());
+
+ graph.getAllPrivileges(roleA); // should have privileges from roleB *and* role C
+ PrivilegeVector privileges = graph.getAllPrivileges(roleA);
+ ASSERT_EQUALS(static_cast<size_t>(3), privileges.size());
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+ ASSERT_EQUALS(dbBResource, privileges[1].getResourcePattern());
+ ASSERT_EQUALS(dbCResource, privileges[2].getResourcePattern());
+}
+
+// Tests error handling
+TEST(RoleGraphTest, ErrorHandling) {
+ RoleName roleA("roleA", "dbA");
+ RoleName roleB("roleB", "dbB");
+ RoleName roleC("roleC", "dbC");
+
+ ActionSet actions;
+ actions.addAction(ActionType::find);
+ Privilege privilege1(db1Resource, actions);
+ Privilege privilege2(db2Resource, actions);
+ PrivilegeVector privileges;
+ privileges.push_back(privilege1);
+ privileges.push_back(privilege2);
+
+ RoleGraph graph;
+ // None of the roles exist yet.
+ ASSERT_NOT_OK(graph.addPrivilegeToRole(roleA, privilege1));
+ ASSERT_NOT_OK(graph.addPrivilegesToRole(roleA, privileges));
+ ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1));
+ ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges));
+ ASSERT_NOT_OK(graph.removeAllPrivilegesFromRole(roleA));
+ ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB));
+
+ // One of the roles exists
+ ASSERT_OK(graph.createRole(roleA));
+ ASSERT_NOT_OK(graph.createRole(roleA)); // Can't create same role twice
+ ASSERT_NOT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_NOT_OK(graph.addRoleToRole(roleB, roleA));
+ ASSERT_NOT_OK(graph.removeRoleFromRole(roleA, roleB));
+ ASSERT_NOT_OK(graph.removeRoleFromRole(roleB, roleA));
+
+ // Should work now that both exist.
+ ASSERT_OK(graph.createRole(roleB));
+ ASSERT_OK(graph.addRoleToRole(roleA, roleB));
+ ASSERT_OK(graph.removeRoleFromRole(roleA, roleB));
+ ASSERT_NOT_OK(
+ graph.removeRoleFromRole(roleA, roleB)); // roleA isn't actually a member of roleB
+
+ // Can't remove a privilege from a role that doesn't have it.
+ ASSERT_NOT_OK(graph.removePrivilegeFromRole(roleA, privilege1));
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
+ ASSERT_OK(graph.removePrivilegeFromRole(roleA, privilege1)); // now should work
+
+ // Test that removing a vector of privileges fails if *any* of the privileges are missing.
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege2));
+ // Removing both privileges should work since it has both
+ ASSERT_OK(graph.removePrivilegesFromRole(roleA, privileges));
+ // Now add only 1 back and this time removing both should fail
+ ASSERT_OK(graph.addPrivilegeToRole(roleA, privilege1));
+ ASSERT_NOT_OK(graph.removePrivilegesFromRole(roleA, privileges));
+}
+
+
+TEST(RoleGraphTest, BuiltinRoles) {
+ RoleName userRole("userDefined", "dbA");
+ RoleName builtinRole("read", "dbA");
+
+ ActionSet actions;
+ actions.addAction(ActionType::insert);
+ Privilege privilege(dbAResource, actions);
+
+ RoleGraph graph;
+
+ ASSERT(graph.roleExists(builtinRole));
+ ASSERT_NOT_OK(graph.createRole(builtinRole));
+ ASSERT_NOT_OK(graph.deleteRole(builtinRole));
+ ASSERT(graph.roleExists(builtinRole));
+ ASSERT(!graph.roleExists(userRole));
+ ASSERT_OK(graph.createRole(userRole));
+ ASSERT(graph.roleExists(userRole));
+
+ ASSERT_NOT_OK(graph.addPrivilegeToRole(builtinRole, privilege));
+ ASSERT_NOT_OK(graph.removePrivilegeFromRole(builtinRole, privilege));
+ ASSERT_NOT_OK(graph.addRoleToRole(builtinRole, userRole));
+ ASSERT_NOT_OK(graph.removeRoleFromRole(builtinRole, userRole));
+
+ ASSERT_OK(graph.addPrivilegeToRole(userRole, privilege));
+ ASSERT_OK(graph.addRoleToRole(userRole, builtinRole));
+ ASSERT_OK(graph.recomputePrivilegeData());
+
+ PrivilegeVector privileges = graph.getDirectPrivileges(userRole);
+ ASSERT_EQUALS(1U, privileges.size());
+ ASSERT(privileges[0].getActions().equals(actions));
+ ASSERT(!privileges[0].getActions().contains(ActionType::find));
+ ASSERT_EQUALS(dbAResource, privileges[0].getResourcePattern());
+
+ privileges = graph.getAllPrivileges(userRole);
+ size_t i;
+ for (i = 0; i < privileges.size(); ++i) {
+ if (dbAResource == privileges[i].getResourcePattern())
+ break;
}
+ ASSERT_NOT_EQUALS(privileges.size(), i);
+ ASSERT(privileges[i].getActions().isSupersetOf(actions));
+ ASSERT(privileges[i].getActions().contains(ActionType::insert));
+ ASSERT(privileges[i].getActions().contains(ActionType::find));
+
+ ASSERT_OK(graph.deleteRole(userRole));
+ ASSERT(!graph.roleExists(userRole));
+}
+
+TEST(RoleGraphTest, BuiltinRolesOnlyOnAppropriateDatabases) {
+ RoleGraph graph;
+ ASSERT(graph.roleExists(RoleName("read", "test")));
+ ASSERT(graph.roleExists(RoleName("readWrite", "test")));
+ ASSERT(graph.roleExists(RoleName("userAdmin", "test")));
+ ASSERT(graph.roleExists(RoleName("dbAdmin", "test")));
+ ASSERT(graph.roleExists(RoleName("dbOwner", "test")));
+ ASSERT(!graph.roleExists(RoleName("readAnyDatabase", "test")));
+ ASSERT(!graph.roleExists(RoleName("readWriteAnyDatabase", "test")));
+ ASSERT(!graph.roleExists(RoleName("userAdminAnyDatabase", "test")));
+ ASSERT(!graph.roleExists(RoleName("dbAdminAnyDatabase", "test")));
+ ASSERT(!graph.roleExists(RoleName("clusterAdmin", "test")));
+ ASSERT(!graph.roleExists(RoleName("root", "test")));
+ ASSERT(!graph.roleExists(RoleName("__system", "test")));
+ ASSERT(!graph.roleExists(RoleName("MyRole", "test")));
+
+ ASSERT(graph.roleExists(RoleName("read", "admin")));
+ ASSERT(graph.roleExists(RoleName("readWrite", "admin")));
+ ASSERT(graph.roleExists(RoleName("userAdmin", "admin")));
+ ASSERT(graph.roleExists(RoleName("dbAdmin", "admin")));
+ ASSERT(graph.roleExists(RoleName("dbOwner", "admin")));
+ ASSERT(graph.roleExists(RoleName("readAnyDatabase", "admin")));
+ ASSERT(graph.roleExists(RoleName("readWriteAnyDatabase", "admin")));
+ ASSERT(graph.roleExists(RoleName("userAdminAnyDatabase", "admin")));
+ ASSERT(graph.roleExists(RoleName("dbAdminAnyDatabase", "admin")));
+ ASSERT(graph.roleExists(RoleName("clusterAdmin", "admin")));
+ ASSERT(graph.roleExists(RoleName("root", "admin")));
+ ASSERT(graph.roleExists(RoleName("__system", "admin")));
+ ASSERT(!graph.roleExists(RoleName("MyRole", "admin")));
+}
+
+TEST(RoleGraphTest, getRolesForDatabase) {
+ RoleGraph graph;
+ graph.createRole(RoleName("myRole", "test"));
+ // Make sure that a role on "test2" doesn't show up in the roles list for "test"
+ graph.createRole(RoleName("anotherRole", "test2"));
+ graph.createRole(RoleName("myAdminRole", "admin"));
+
+ // Non-admin DB with no user-defined roles
+ RoleNameIterator it = graph.getRolesForDatabase("fakedb");
+ ASSERT_EQUALS(RoleName("dbAdmin", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("read", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "fakedb"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "fakedb"), it.next());
+ ASSERT_FALSE(it.more());
+
+ // Non-admin DB with a user-defined role
+ it = graph.getRolesForDatabase("test");
+ ASSERT_EQUALS(RoleName("dbAdmin", "test"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "test"), it.next());
+ ASSERT_EQUALS(RoleName("myRole", "test"), it.next());
+ ASSERT_EQUALS(RoleName("read", "test"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "test"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "test"), it.next());
+ ASSERT_FALSE(it.more());
+
+ // Admin DB
+ it = graph.getRolesForDatabase("admin");
+ ASSERT_EQUALS(RoleName("__system", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("backup", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterManager", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("clusterMonitor", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbAdminAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("dbOwner", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("hostManager", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("myAdminRole", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("read", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readWrite", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("readWriteAnyDatabase", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("restore", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("root", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("userAdmin", "admin"), it.next());
+ ASSERT_EQUALS(RoleName("userAdminAnyDatabase", "admin"), it.next());
+ ASSERT_FALSE(it.more());
+}
} // namespace
} // namespace mongo
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
diff --git a/src/mongo/db/auth/role_name.cpp b/src/mongo/db/auth/role_name.cpp
index f38bcedeb59..d5f6aecfa48 100644
--- a/src/mongo/db/auth/role_name.cpp
+++ b/src/mongo/db/auth/role_name.cpp
@@ -36,20 +36,19 @@
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();
- }
+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();
- }
+std::ostream& operator<<(std::ostream& os, const RoleName& name) {
+ return os << name.getFullName();
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/role_name.h b/src/mongo/db/auth/role_name.h
index 05b5d30e80b..30ef4d50412 100644
--- a/src/mongo/db/auth/role_name.h
+++ b/src/mongo/db/auth/role_name.h
@@ -40,146 +40,176 @@
namespace mongo {
+/**
+ * Representation of a name of a role in a MongoDB system.
+ *
+ * Consists of a "role name" part and a "datbase name" part.
+ */
+class RoleName {
+public:
+ RoleName() : _splitPoint(0) {}
+ RoleName(StringData role, StringData dbname);
+
/**
- * Representation of a name of a role in a MongoDB system.
- *
- * Consists of a "role name" part and a "datbase name" part.
+ * Gets the name of the role excluding the "@dbname" component.
*/
- class RoleName {
- public:
- RoleName() : _splitPoint(0) {}
- RoleName(StringData role, StringData dbname);
+ StringData getRole() const {
+ return StringData(_fullName).substr(0, _splitPoint);
+ }
- /**
- * Gets the name of the role excluding the "@dbname" component.
- */
- StringData getRole() const { return StringData(_fullName).substr(0, _splitPoint); }
+ /**
+ * Gets the database name part of a role name.
+ */
+ StringData getDB() const {
+ return StringData(_fullName).substr(_splitPoint + 1);
+ }
- /**
- * Gets the database name part of a role name.
- */
- StringData getDB() const { return StringData(_fullName).substr(_splitPoint + 1); }
+ bool empty() const {
+ return _fullName.empty();
+ }
- bool empty() const { return _fullName.empty(); }
+ /**
+ * Gets the full name of a role as a string, formatted as "role@db".
+ *
+ * Allowed for keys in non-persistent data structures, such as std::map.
+ */
+ const std::string& getFullName() const {
+ return _fullName;
+ }
- /**
- * Gets the full name of a role as a string, formatted as "role@db".
- *
- * Allowed for keys in non-persistent data structures, such as std::map.
- */
- const std::string& getFullName() const { return _fullName; }
+ /**
+ * Stringifies the object, for logging/debugging.
+ */
+ const std::string& toString() const {
+ return getFullName();
+ }
- /**
- * Stringifies the object, for logging/debugging.
- */
- const std::string& toString() const { return getFullName(); }
+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.
+};
- 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.
- };
+static inline bool operator==(const RoleName& lhs, const RoleName& rhs) {
+ return lhs.getFullName() == rhs.getFullName();
+}
- static inline bool operator==(const RoleName& lhs, const RoleName& rhs) {
- return lhs.getFullName() == rhs.getFullName();
- }
+static inline bool operator!=(const RoleName& lhs, const RoleName& rhs) {
+ return lhs.getFullName() != rhs.getFullName();
+}
- static inline bool operator!=(const RoleName& lhs, const RoleName& rhs) {
- return lhs.getFullName() != rhs.getFullName();
+static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
+ if (lhs.getDB() == rhs.getDB()) {
+ return lhs.getRole() < rhs.getRole();
}
+ return lhs.getDB() < rhs.getDB();
+}
- static inline bool operator<(const RoleName& lhs, const RoleName& rhs) {
- if (lhs.getDB() == rhs.getDB()) {
- return lhs.getRole() < rhs.getRole();
- }
- return lhs.getDB() < rhs.getDB();
- }
+std::ostream& operator<<(std::ostream& os, const RoleName& name);
- std::ostream& operator<<(std::ostream& os, const RoleName& name);
+/**
+ * Iterator over an unspecified container of RoleName objects.
+ */
+class RoleNameIterator {
+public:
+ class Impl {
+ MONGO_DISALLOW_COPYING(Impl);
- /**
- * Iterator over an unspecified container of RoleName objects.
- */
- class RoleNameIterator {
public:
- class Impl {
- MONGO_DISALLOW_COPYING(Impl);
- public:
- Impl() {};
- virtual ~Impl() {};
- static Impl* clone(Impl* orig) { return orig ? orig->doClone(): NULL; }
- virtual bool more() const = 0;
- virtual const RoleName& get() const = 0;
-
- virtual const RoleName& next() = 0;
-
- private:
- virtual Impl* doClone() const = 0;
- };
-
- RoleNameIterator() : _impl(nullptr) {}
- RoleNameIterator(const RoleNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
- explicit RoleNameIterator(Impl* impl) : _impl(impl) {}
-
- RoleNameIterator& operator=(const RoleNameIterator& other) {
- _impl.reset(Impl::clone(other._impl.get()));
- return *this;
+ Impl(){};
+ virtual ~Impl(){};
+ static Impl* clone(Impl* orig) {
+ return orig ? orig->doClone() : NULL;
}
+ virtual bool more() const = 0;
+ virtual const RoleName& get() const = 0;
- bool more() const { return _impl.get() && _impl->more(); }
- const RoleName& get() const { return _impl->get(); }
-
- const RoleName& next() { return _impl->next(); }
-
- const RoleName& operator*() const { return get(); }
- const RoleName* operator->() const { return &get(); }
+ virtual const RoleName& next() = 0;
private:
- std::unique_ptr<Impl> _impl;
+ virtual Impl* doClone() const = 0;
};
-} // namespace mongo
+ RoleNameIterator() : _impl(nullptr) {}
+ RoleNameIterator(const RoleNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
+ explicit RoleNameIterator(Impl* impl) : _impl(impl) {}
+
+ RoleNameIterator& operator=(const RoleNameIterator& other) {
+ _impl.reset(Impl::clone(other._impl.get()));
+ return *this;
+ }
+
+ bool more() const {
+ return _impl.get() && _impl->more();
+ }
+ const RoleName& get() const {
+ return _impl->get();
+ }
+
+ const RoleName& next() {
+ return _impl->next();
+ }
+
+ const RoleName& operator*() const {
+ return get();
+ }
+ const RoleName* operator->() const {
+ return &get();
+ }
+
+private:
+ std::unique_ptr<Impl> _impl;
+};
+
+} // namespace mongo
// Define hash function for RoleNames so they can be keys in std::unordered_map
MONGO_HASH_NAMESPACE_START
- template <> struct hash<mongo::RoleName> {
- size_t operator()(const mongo::RoleName& rname) const {
- return hash<std::string>()(rname.getFullName());
- }
- };
+template <>
+struct hash<mongo::RoleName> {
+ size_t operator()(const mongo::RoleName& rname) const {
+ return hash<std::string>()(rname.getFullName());
+ }
+};
MONGO_HASH_NAMESPACE_END
namespace mongo {
- template <typename ContainerIterator>
- class RoleNameContainerIteratorImpl : public RoleNameIterator::Impl {
- MONGO_DISALLOW_COPYING(RoleNameContainerIteratorImpl);
- public:
- RoleNameContainerIteratorImpl(const ContainerIterator& begin,
- const ContainerIterator& end) :
- _curr(begin), _end(end) {}
- virtual ~RoleNameContainerIteratorImpl() {}
- virtual bool more() const { return _curr != _end; }
- virtual const RoleName& next() { return *(_curr++); }
- virtual const RoleName& get() const { return *_curr; }
- virtual RoleNameIterator::Impl* doClone() const {
- return new RoleNameContainerIteratorImpl(_curr, _end);
- }
+template <typename ContainerIterator>
+class RoleNameContainerIteratorImpl : public RoleNameIterator::Impl {
+ MONGO_DISALLOW_COPYING(RoleNameContainerIteratorImpl);
- private:
- ContainerIterator _curr;
- ContainerIterator _end;
- };
-
- template <typename ContainerIterator>
- RoleNameIterator makeRoleNameIterator(const ContainerIterator& begin,
- const ContainerIterator& end) {
- return RoleNameIterator( new RoleNameContainerIteratorImpl<ContainerIterator>(begin, end));
+public:
+ RoleNameContainerIteratorImpl(const ContainerIterator& begin, const ContainerIterator& end)
+ : _curr(begin), _end(end) {}
+ virtual ~RoleNameContainerIteratorImpl() {}
+ virtual bool more() const {
+ return _curr != _end;
}
-
- template <typename Container>
- RoleNameIterator makeRoleNameIteratorForContainer(const Container& container) {
- return makeRoleNameIterator(container.begin(), container.end());
+ virtual const RoleName& next() {
+ return *(_curr++);
}
+ virtual const RoleName& get() const {
+ return *_curr;
+ }
+ virtual RoleNameIterator::Impl* doClone() const {
+ return new RoleNameContainerIteratorImpl(_curr, _end);
+ }
+
+private:
+ ContainerIterator _curr;
+ ContainerIterator _end;
+};
+
+template <typename ContainerIterator>
+RoleNameIterator makeRoleNameIterator(const ContainerIterator& begin,
+ const ContainerIterator& end) {
+ return RoleNameIterator(new RoleNameContainerIteratorImpl<ContainerIterator>(begin, end));
+}
+
+template <typename Container>
+RoleNameIterator makeRoleNameIteratorForContainer(const Container& container) {
+ return makeRoleNameIterator(container.begin(), container.end());
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_authentication_session.cpp b/src/mongo/db/auth/sasl_authentication_session.cpp
index 62abdc8a284..c74bba6fadb 100644
--- a/src/mongo/db/auth/sasl_authentication_session.cpp
+++ b/src/mongo/db/auth/sasl_authentication_session.cpp
@@ -46,51 +46,47 @@
#include "mongo/util/mongoutils/str.h"
namespace mongo {
- SaslAuthenticationSession::SaslAuthenticationSessionFactoryFn
- SaslAuthenticationSession::create;
+SaslAuthenticationSession::SaslAuthenticationSessionFactoryFn SaslAuthenticationSession::create;
- // Mechanism name constants.
- const char SaslAuthenticationSession::mechanismCRAMMD5[] = "CRAM-MD5";
- const char SaslAuthenticationSession::mechanismDIGESTMD5[] = "DIGEST-MD5";
- const char SaslAuthenticationSession::mechanismSCRAMSHA1[] = "SCRAM-SHA-1";
- const char SaslAuthenticationSession::mechanismGSSAPI[] = "GSSAPI";
- const char SaslAuthenticationSession::mechanismPLAIN[] = "PLAIN";
+// Mechanism name constants.
+const char SaslAuthenticationSession::mechanismCRAMMD5[] = "CRAM-MD5";
+const char SaslAuthenticationSession::mechanismDIGESTMD5[] = "DIGEST-MD5";
+const char SaslAuthenticationSession::mechanismSCRAMSHA1[] = "SCRAM-SHA-1";
+const char SaslAuthenticationSession::mechanismGSSAPI[] = "GSSAPI";
+const char SaslAuthenticationSession::mechanismPLAIN[] = "PLAIN";
- /**
- * Standard method in mongodb for determining if "authenticatedUser" may act as "requestedUser."
- *
- * The standard rule in MongoDB is simple. The authenticated user name must be the same as the
- * requested user name.
- */
- bool isAuthorizedCommon(SaslAuthenticationSession* session,
- StringData requestedUser,
- StringData authenticatedUser) {
-
- return requestedUser == authenticatedUser;
- }
+/**
+ * Standard method in mongodb for determining if "authenticatedUser" may act as "requestedUser."
+ *
+ * The standard rule in MongoDB is simple. The authenticated user name must be the same as the
+ * requested user name.
+ */
+bool isAuthorizedCommon(SaslAuthenticationSession* session,
+ StringData requestedUser,
+ StringData authenticatedUser) {
+ return requestedUser == authenticatedUser;
+}
- SaslAuthenticationSession::SaslAuthenticationSession(AuthorizationSession* authzSession) :
- AuthenticationSession(AuthenticationSession::SESSION_TYPE_SASL),
- _authzSession(authzSession),
- _saslStep(0),
- _conversationId(0),
- _autoAuthorize(false),
- _done(false) {
- }
+SaslAuthenticationSession::SaslAuthenticationSession(AuthorizationSession* authzSession)
+ : AuthenticationSession(AuthenticationSession::SESSION_TYPE_SASL),
+ _authzSession(authzSession),
+ _saslStep(0),
+ _conversationId(0),
+ _autoAuthorize(false),
+ _done(false) {}
- SaslAuthenticationSession::~SaslAuthenticationSession() {};
+SaslAuthenticationSession::~SaslAuthenticationSession(){};
- StringData SaslAuthenticationSession::getAuthenticationDatabase() const {
- if (Command::testCommandsEnabled &&
- _authenticationDatabase == "admin" &&
- getPrincipalId() == internalSecurity.user->getName().getUser()) {
- // Allows authenticating as the internal user against the admin database. This is to
- // support the auth passthrough test framework on mongos (since you can't use the local
- // database on a mongos, so you can't auth as the internal user without this).
- return internalSecurity.user->getName().getDB();
- } else {
- return _authenticationDatabase;
- }
+StringData SaslAuthenticationSession::getAuthenticationDatabase() const {
+ if (Command::testCommandsEnabled && _authenticationDatabase == "admin" &&
+ getPrincipalId() == internalSecurity.user->getName().getUser()) {
+ // Allows authenticating as the internal user against the admin database. This is to
+ // support the auth passthrough test framework on mongos (since you can't use the local
+ // database on a mongos, so you can't auth as the internal user without this).
+ return internalSecurity.user->getName().getDB();
+ } else {
+ return _authenticationDatabase;
}
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_authentication_session.h b/src/mongo/db/auth/sasl_authentication_session.h
index e1bc7cf7551..ac298dbede6 100644
--- a/src/mongo/db/auth/sasl_authentication_session.h
+++ b/src/mongo/db/auth/sasl_authentication_session.h
@@ -41,128 +41,140 @@
namespace mongo {
- class AuthorizationSession;
- class OperationContext;
+class AuthorizationSession;
+class OperationContext;
+
+/**
+ * Authentication session data for the server side of SASL authentication.
+ */
+class SaslAuthenticationSession : public AuthenticationSession {
+ MONGO_DISALLOW_COPYING(SaslAuthenticationSession);
+
+public:
+ typedef stdx::function<SaslAuthenticationSession*(AuthorizationSession*, const std::string&)>
+ SaslAuthenticationSessionFactoryFn;
+ static SaslAuthenticationSessionFactoryFn create;
+
+ // Mechanism name constants.
+ static const char mechanismCRAMMD5[];
+ static const char mechanismDIGESTMD5[];
+ static const char mechanismSCRAMSHA1[];
+ static const char mechanismGSSAPI[];
+ static const char mechanismPLAIN[];
+
+ explicit SaslAuthenticationSession(AuthorizationSession* authSession);
+ virtual ~SaslAuthenticationSession();
+
+ /**
+ * Start the server side of a SASL authentication.
+ *
+ * "authenticationDatabase" is the database against which the user is authenticating.
+ * "mechanism" is the SASL mechanism to use.
+ * "serviceName" is the SASL service name to use.
+ * "serviceHostname" is the FQDN of this server.
+ * "conversationId" is the conversation identifier to use for this session.
+ *
+ * If "autoAuthorize" is set to true, the server will automatically acquire all privileges
+ * for a successfully authenticated user. If it is false, the client will need to
+ * explicilty acquire privileges on resources it wishes to access.
+ *
+ * Must be called only once on an instance.
+ */
+ virtual Status start(StringData authenticationDatabase,
+ StringData mechanism,
+ StringData serviceName,
+ StringData serviceHostname,
+ int64_t conversationId,
+ bool autoAuthorize) = 0;
+
+ /**
+ * Perform one step of the server side of the authentication session,
+ * consuming "inputData" and producing "*outputData".
+ *
+ * A return of Status::OK() indiciates succesful progress towards authentication.
+ * Any other return code indicates that authentication has failed.
+ *
+ * Must not be called before start().
+ */
+ virtual Status step(StringData inputData, std::string* outputData) = 0;
+
+ /**
+ * Returns the the operation context associated with the currently executing command.
+ * Authentication commands must set this on their associated
+ * SaslAuthenticationSession.
+ */
+ OperationContext* getOpCtxt() const {
+ return _txn;
+ }
+ void setOpCtxt(OperationContext* txn) {
+ _txn = txn;
+ }
+
+ /**
+ * Gets the name of the database against which this authentication conversation is running.
+ *
+ * Not meaningful before a successful call to start().
+ */
+ StringData getAuthenticationDatabase() const;
+
+ /**
+ * Get the conversation id for this authentication session.
+ *
+ * Must not be called before start().
+ */
+ int64_t getConversationId() const {
+ return _conversationId;
+ }
+
+ /**
+ * If the last call to step() returned Status::OK(), this method returns true if the
+ * authentication conversation has completed, from the server's perspective. If it returns
+ * false, the server expects more input from the client. If the last call to step() did not
+ * return Status::OK(), returns true.
+ *
+ * Behavior is undefined if step() has not been called.
+ */
+ bool isDone() const {
+ return _done;
+ }
+
+ /**
+ * Gets the string identifier of the principal being authenticated.
+ *
+ * Returns the empty string if the session does not yet know the identity being
+ * authenticated.
+ */
+ virtual std::string getPrincipalId() const = 0;
+
+ /**
+ * Gets the name of the SASL mechanism in use.
+ *
+ * Returns "" if start() has not been called or if start() did not return Status::OK().
+ */
+ virtual const char* getMechanism() const = 0;
/**
- * Authentication session data for the server side of SASL authentication.
+ * Returns true if automatic privilege acquisition should be used for this principal, after
+ * authentication. Not meaningful before a successful call to start().
*/
- class SaslAuthenticationSession : public AuthenticationSession {
- MONGO_DISALLOW_COPYING(SaslAuthenticationSession);
- public:
- typedef stdx::function<SaslAuthenticationSession* (AuthorizationSession*,
- const std::string&)>
- SaslAuthenticationSessionFactoryFn;
- static SaslAuthenticationSessionFactoryFn create;
-
- // Mechanism name constants.
- static const char mechanismCRAMMD5[];
- static const char mechanismDIGESTMD5[];
- static const char mechanismSCRAMSHA1[];
- static const char mechanismGSSAPI[];
- static const char mechanismPLAIN[];
-
- explicit SaslAuthenticationSession(AuthorizationSession* authSession);
- virtual ~SaslAuthenticationSession();
-
- /**
- * Start the server side of a SASL authentication.
- *
- * "authenticationDatabase" is the database against which the user is authenticating.
- * "mechanism" is the SASL mechanism to use.
- * "serviceName" is the SASL service name to use.
- * "serviceHostname" is the FQDN of this server.
- * "conversationId" is the conversation identifier to use for this session.
- *
- * If "autoAuthorize" is set to true, the server will automatically acquire all privileges
- * for a successfully authenticated user. If it is false, the client will need to
- * explicilty acquire privileges on resources it wishes to access.
- *
- * Must be called only once on an instance.
- */
- virtual Status start(StringData authenticationDatabase,
- StringData mechanism,
- StringData serviceName,
- StringData serviceHostname,
- int64_t conversationId,
- bool autoAuthorize) = 0;
-
- /**
- * Perform one step of the server side of the authentication session,
- * consuming "inputData" and producing "*outputData".
- *
- * A return of Status::OK() indiciates succesful progress towards authentication.
- * Any other return code indicates that authentication has failed.
- *
- * Must not be called before start().
- */
- virtual Status step(StringData inputData, std::string* outputData) = 0;
-
- /**
- * Returns the the operation context associated with the currently executing command.
- * Authentication commands must set this on their associated
- * SaslAuthenticationSession.
- */
- OperationContext* getOpCtxt() const { return _txn; }
- void setOpCtxt(OperationContext* txn) { _txn = txn; }
-
- /**
- * Gets the name of the database against which this authentication conversation is running.
- *
- * Not meaningful before a successful call to start().
- */
- StringData getAuthenticationDatabase() const;
-
- /**
- * Get the conversation id for this authentication session.
- *
- * Must not be called before start().
- */
- int64_t getConversationId() const { return _conversationId; }
-
- /**
- * If the last call to step() returned Status::OK(), this method returns true if the
- * authentication conversation has completed, from the server's perspective. If it returns
- * false, the server expects more input from the client. If the last call to step() did not
- * return Status::OK(), returns true.
- *
- * Behavior is undefined if step() has not been called.
- */
- bool isDone() const { return _done; }
-
- /**
- * Gets the string identifier of the principal being authenticated.
- *
- * Returns the empty string if the session does not yet know the identity being
- * authenticated.
- */
- virtual std::string getPrincipalId() const = 0;
-
- /**
- * Gets the name of the SASL mechanism in use.
- *
- * Returns "" if start() has not been called or if start() did not return Status::OK().
- */
- virtual const char* getMechanism() const = 0;
-
- /**
- * Returns true if automatic privilege acquisition should be used for this principal, after
- * authentication. Not meaningful before a successful call to start().
- */
- bool shouldAutoAuthorize() const { return _autoAuthorize; }
-
- AuthorizationSession* getAuthorizationSession() { return _authzSession; }
-
- protected:
- OperationContext* _txn;
- AuthorizationSession* _authzSession;
- std::string _authenticationDatabase;
- std::string _serviceName;
- std::string _serviceHostname;
- int _saslStep;
- int64_t _conversationId;
- bool _autoAuthorize;
- bool _done;
- };
+ bool shouldAutoAuthorize() const {
+ return _autoAuthorize;
+ }
+
+ AuthorizationSession* getAuthorizationSession() {
+ return _authzSession;
+ }
+
+protected:
+ OperationContext* _txn;
+ AuthorizationSession* _authzSession;
+ std::string _authenticationDatabase;
+ std::string _serviceName;
+ std::string _serviceHostname;
+ int _saslStep;
+ int64_t _conversationId;
+ bool _autoAuthorize;
+ bool _done;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_commands.cpp b/src/mongo/db/auth/sasl_commands.cpp
index 6747a35771c..c05eec78539 100644
--- a/src/mongo/db/auth/sasl_commands.cpp
+++ b/src/mongo/db/auth/sasl_commands.cpp
@@ -56,327 +56,320 @@
namespace mongo {
namespace {
- using std::stringstream;
-
- const bool autoAuthorizeDefault = true;
-
- class CmdSaslStart : public Command {
- public:
- CmdSaslStart();
- virtual ~CmdSaslStart();
-
- virtual void addRequiredPrivileges(
- const std::string&, const BSONObj&, std::vector<Privilege>*) {}
-
- virtual bool run(OperationContext* txn,
- const std::string& db,
- BSONObj& cmdObj,
- int options,
- std::string& ignored,
- BSONObjBuilder& result);
-
- virtual void help(stringstream& help) const;
- virtual bool isWriteCommandForConfigServer() const { return false; }
- virtual bool slaveOk() const { return true; }
- virtual bool requiresAuth() { return false; }
-
- };
-
- class CmdSaslContinue : public Command {
- public:
- CmdSaslContinue();
- virtual ~CmdSaslContinue();
-
- virtual void addRequiredPrivileges(
- const std::string&, const BSONObj&, std::vector<Privilege>*) {}
-
- virtual bool run(OperationContext* txn,
- const std::string& db,
- BSONObj& cmdObj,
- int options,
- std::string& ignored,
- BSONObjBuilder& result);
-
- virtual void help(stringstream& help) const;
- virtual bool isWriteCommandForConfigServer() const { return false; }
- virtual bool slaveOk() const { return true; }
- virtual bool requiresAuth() { return false; }
- };
-
- CmdSaslStart cmdSaslStart;
- CmdSaslContinue cmdSaslContinue;
- Status buildResponse(const SaslAuthenticationSession* session,
- const std::string& responsePayload,
- BSONType responsePayloadType,
- BSONObjBuilder* result) {
- result->appendIntOrLL(saslCommandConversationIdFieldName, session->getConversationId());
- result->appendBool(saslCommandDoneFieldName, session->isDone());
-
- if (responsePayload.size() > size_t(std::numeric_limits<int>::max())) {
- return Status(ErrorCodes::InvalidLength, "Response payload too long");
- }
- if (responsePayloadType == BinData) {
- result->appendBinData(saslCommandPayloadFieldName,
- int(responsePayload.size()),
- BinDataGeneral,
- responsePayload.data());
- }
- else if (responsePayloadType == String) {
- result->append(saslCommandPayloadFieldName, base64::encode(responsePayload));
- }
- else {
- fassertFailed(4003);
- }
+using std::stringstream;
- return Status::OK();
- }
+const bool autoAuthorizeDefault = true;
- Status extractConversationId(const BSONObj& cmdObj, int64_t* conversationId) {
- BSONElement element;
- Status status = bsonExtractField(cmdObj, saslCommandConversationIdFieldName, &element);
- if (!status.isOK())
- return status;
+class CmdSaslStart : public Command {
+public:
+ CmdSaslStart();
+ virtual ~CmdSaslStart();
- if (!element.isNumber()) {
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "Wrong type for field; expected number for " << element);
- }
- *conversationId = element.numberLong();
- return Status::OK();
- }
+ virtual void addRequiredPrivileges(const std::string&,
+ const BSONObj&,
+ std::vector<Privilege>*) {}
- Status extractMechanism(const BSONObj& cmdObj, std::string* mechanism) {
- return bsonExtractStringField(cmdObj, saslCommandMechanismFieldName, mechanism);
- }
+ virtual bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result);
- void addStatus(const Status& status, BSONObjBuilder* builder) {
- builder->append("ok", status.isOK() ? 1.0: 0.0);
- if (!status.isOK())
- builder->append(saslCommandCodeFieldName, status.code());
- if (!status.reason().empty())
- builder->append(saslCommandErrmsgFieldName, status.reason());
+ virtual void help(stringstream& help) const;
+ virtual bool isWriteCommandForConfigServer() const {
+ return false;
}
-
- Status doSaslStep(const ClientBasic* client,
- SaslAuthenticationSession* session,
- const BSONObj& cmdObj,
- BSONObjBuilder* result) {
-
- std::string payload;
- BSONType type = EOO;
- Status status = saslExtractPayload(cmdObj, &payload, &type);
- if (!status.isOK())
- return status;
-
- std::string responsePayload;
- // Passing in a payload and extracting a responsePayload
- status = session->step(payload, &responsePayload);
-
- if (!status.isOK()) {
- const SockAddr clientAddr = client->port()->localAddr();
- log() << session->getMechanism() << " authentication failed for " <<
- session->getPrincipalId() << " on " <<
- session->getAuthenticationDatabase() << " from client " << clientAddr.getAddr() <<
- " ; " << status.toString() << std::endl;
- // All the client needs to know is that authentication has failed.
- return Status(ErrorCodes::AuthenticationFailed, "Authentication failed.");
- }
-
- status = buildResponse(session, responsePayload, type, result);
- if (!status.isOK())
- return status;
-
- if (session->isDone()) {
- UserName userName(session->getPrincipalId(), session->getAuthenticationDatabase());
- status = session->getAuthorizationSession()->addAndAuthorizeUser(
- session->getOpCtxt(), userName);
- if (!status.isOK()) {
- return status;
- }
-
- if (!serverGlobalParams.quiet) {
- log() << "Successfully authenticated as principal " << session->getPrincipalId()
- << " on " << session->getAuthenticationDatabase();
- }
- }
- return Status::OK();
+ virtual bool slaveOk() const {
+ return true;
+ }
+ virtual bool requiresAuth() {
+ return false;
+ }
+};
+
+class CmdSaslContinue : public Command {
+public:
+ CmdSaslContinue();
+ virtual ~CmdSaslContinue();
+
+ virtual void addRequiredPrivileges(const std::string&,
+ const BSONObj&,
+ std::vector<Privilege>*) {}
+
+ virtual bool run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result);
+
+ virtual void help(stringstream& help) const;
+ virtual bool isWriteCommandForConfigServer() const {
+ return false;
+ }
+ virtual bool slaveOk() const {
+ return true;
+ }
+ virtual bool requiresAuth() {
+ return false;
+ }
+};
+
+CmdSaslStart cmdSaslStart;
+CmdSaslContinue cmdSaslContinue;
+Status buildResponse(const SaslAuthenticationSession* session,
+ const std::string& responsePayload,
+ BSONType responsePayloadType,
+ BSONObjBuilder* result) {
+ result->appendIntOrLL(saslCommandConversationIdFieldName, session->getConversationId());
+ result->appendBool(saslCommandDoneFieldName, session->isDone());
+
+ if (responsePayload.size() > size_t(std::numeric_limits<int>::max())) {
+ return Status(ErrorCodes::InvalidLength, "Response payload too long");
+ }
+ if (responsePayloadType == BinData) {
+ result->appendBinData(saslCommandPayloadFieldName,
+ int(responsePayload.size()),
+ BinDataGeneral,
+ responsePayload.data());
+ } else if (responsePayloadType == String) {
+ result->append(saslCommandPayloadFieldName, base64::encode(responsePayload));
+ } else {
+ fassertFailed(4003);
}
- Status doSaslStart(const ClientBasic* client,
- SaslAuthenticationSession* session,
- const std::string& db,
- const BSONObj& cmdObj,
- BSONObjBuilder* result) {
-
- bool autoAuthorize = false;
- Status status = bsonExtractBooleanFieldWithDefault(cmdObj,
- saslCommandAutoAuthorizeFieldName,
- autoAuthorizeDefault,
- &autoAuthorize);
- if (!status.isOK())
- return status;
-
- std::string mechanism;
- status = extractMechanism(cmdObj, &mechanism);
- if (!status.isOK())
- return status;
-
- if (!sequenceContains(saslGlobalParams.authenticationMechanisms, mechanism) &&
- mechanism != "SCRAM-SHA-1") {
- // Always allow SCRAM-SHA-1 to pass to the first sasl step since we need to
- // handle internal user authentication, SERVER-16534
- result->append(saslCommandMechanismListFieldName,
- saslGlobalParams.authenticationMechanisms);
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "Unsupported mechanism " << mechanism);
- }
+ return Status::OK();
+}
- status = session->start(db,
- mechanism,
- saslGlobalParams.serviceName,
- saslGlobalParams.hostName,
- 1,
- autoAuthorize);
- if (!status.isOK())
- return status;
+Status extractConversationId(const BSONObj& cmdObj, int64_t* conversationId) {
+ BSONElement element;
+ Status status = bsonExtractField(cmdObj, saslCommandConversationIdFieldName, &element);
+ if (!status.isOK())
+ return status;
- return doSaslStep(client, session, cmdObj, result);
+ if (!element.isNumber()) {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "Wrong type for field; expected number for " << element);
+ }
+ *conversationId = element.numberLong();
+ return Status::OK();
+}
+
+Status extractMechanism(const BSONObj& cmdObj, std::string* mechanism) {
+ return bsonExtractStringField(cmdObj, saslCommandMechanismFieldName, mechanism);
+}
+
+void addStatus(const Status& status, BSONObjBuilder* builder) {
+ builder->append("ok", status.isOK() ? 1.0 : 0.0);
+ if (!status.isOK())
+ builder->append(saslCommandCodeFieldName, status.code());
+ if (!status.reason().empty())
+ builder->append(saslCommandErrmsgFieldName, status.reason());
+}
+
+Status doSaslStep(const ClientBasic* client,
+ SaslAuthenticationSession* session,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ std::string payload;
+ BSONType type = EOO;
+ Status status = saslExtractPayload(cmdObj, &payload, &type);
+ if (!status.isOK())
+ return status;
+
+ std::string responsePayload;
+ // Passing in a payload and extracting a responsePayload
+ status = session->step(payload, &responsePayload);
+
+ if (!status.isOK()) {
+ const SockAddr clientAddr = client->port()->localAddr();
+ log() << session->getMechanism() << " authentication failed for "
+ << session->getPrincipalId() << " on " << session->getAuthenticationDatabase()
+ << " from client " << clientAddr.getAddr() << " ; " << status.toString() << std::endl;
+ // All the client needs to know is that authentication has failed.
+ return Status(ErrorCodes::AuthenticationFailed, "Authentication failed.");
}
- Status doSaslContinue(const ClientBasic* client,
- SaslAuthenticationSession* session,
- const BSONObj& cmdObj,
- BSONObjBuilder* result) {
+ status = buildResponse(session, responsePayload, type, result);
+ if (!status.isOK())
+ return status;
- int64_t conversationId = 0;
- Status status = extractConversationId(cmdObj, &conversationId);
- if (!status.isOK())
+ if (session->isDone()) {
+ UserName userName(session->getPrincipalId(), session->getAuthenticationDatabase());
+ status =
+ session->getAuthorizationSession()->addAndAuthorizeUser(session->getOpCtxt(), userName);
+ if (!status.isOK()) {
return status;
- if (conversationId != session->getConversationId())
- return Status(ErrorCodes::ProtocolError, "sasl: Mismatched conversation id");
+ }
- return doSaslStep(client, session, cmdObj, result);
+ if (!serverGlobalParams.quiet) {
+ log() << "Successfully authenticated as principal " << session->getPrincipalId()
+ << " on " << session->getAuthenticationDatabase();
+ }
}
-
- CmdSaslStart::CmdSaslStart() : Command(saslStartCommandName) {}
- CmdSaslStart::~CmdSaslStart() {}
-
- void CmdSaslStart::help(std::stringstream& os) const {
- os << "First step in a SASL authentication conversation.";
+ return Status::OK();
+}
+
+Status doSaslStart(const ClientBasic* client,
+ SaslAuthenticationSession* session,
+ const std::string& db,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ bool autoAuthorize = false;
+ Status status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, saslCommandAutoAuthorizeFieldName, autoAuthorizeDefault, &autoAuthorize);
+ if (!status.isOK())
+ return status;
+
+ std::string mechanism;
+ status = extractMechanism(cmdObj, &mechanism);
+ if (!status.isOK())
+ return status;
+
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, mechanism) &&
+ mechanism != "SCRAM-SHA-1") {
+ // Always allow SCRAM-SHA-1 to pass to the first sasl step since we need to
+ // handle internal user authentication, SERVER-16534
+ result->append(saslCommandMechanismListFieldName,
+ saslGlobalParams.authenticationMechanisms);
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "Unsupported mechanism " << mechanism);
}
- bool CmdSaslStart::run(OperationContext* txn,
- const std::string& db,
- BSONObj& cmdObj,
- int options,
- std::string& ignored,
- BSONObjBuilder& result) {
+ status = session->start(
+ db, mechanism, saslGlobalParams.serviceName, saslGlobalParams.hostName, 1, autoAuthorize);
+ if (!status.isOK())
+ return status;
- ClientBasic* client = ClientBasic::getCurrent();
- AuthenticationSession::set(client, std::unique_ptr<AuthenticationSession>());
+ return doSaslStep(client, session, cmdObj, result);
+}
- std::string mechanism;
- if (!extractMechanism(cmdObj, &mechanism).isOK()) {
- return false;
- }
+Status doSaslContinue(const ClientBasic* client,
+ SaslAuthenticationSession* session,
+ const BSONObj& cmdObj,
+ BSONObjBuilder* result) {
+ int64_t conversationId = 0;
+ Status status = extractConversationId(cmdObj, &conversationId);
+ if (!status.isOK())
+ return status;
+ if (conversationId != session->getConversationId())
+ return Status(ErrorCodes::ProtocolError, "sasl: Mismatched conversation id");
+
+ return doSaslStep(client, session, cmdObj, result);
+}
+
+CmdSaslStart::CmdSaslStart() : Command(saslStartCommandName) {}
+CmdSaslStart::~CmdSaslStart() {}
+
+void CmdSaslStart::help(std::stringstream& os) const {
+ os << "First step in a SASL authentication conversation.";
+}
+
+bool CmdSaslStart::run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result) {
+ ClientBasic* client = ClientBasic::getCurrent();
+ AuthenticationSession::set(client, std::unique_ptr<AuthenticationSession>());
+
+ std::string mechanism;
+ if (!extractMechanism(cmdObj, &mechanism).isOK()) {
+ return false;
+ }
- SaslAuthenticationSession* session =
- SaslAuthenticationSession::create(AuthorizationSession::get(client), mechanism);
+ SaslAuthenticationSession* session =
+ SaslAuthenticationSession::create(AuthorizationSession::get(client), mechanism);
- std::unique_ptr<AuthenticationSession> sessionGuard(session);
+ std::unique_ptr<AuthenticationSession> sessionGuard(session);
- session->setOpCtxt(txn);
+ session->setOpCtxt(txn);
- Status status = doSaslStart(client, session, db, cmdObj, &result);
- addStatus(status, &result);
+ Status status = doSaslStart(client, session, db, cmdObj, &result);
+ addStatus(status, &result);
- if (session->isDone()) {
- audit::logAuthentication(
- client,
- session->getMechanism(),
- UserName(session->getPrincipalId(), db),
- status.code());
- }
- else {
- AuthenticationSession::swap(client, sessionGuard);
- }
- return status.isOK();
+ if (session->isDone()) {
+ audit::logAuthentication(client,
+ session->getMechanism(),
+ UserName(session->getPrincipalId(), db),
+ status.code());
+ } else {
+ AuthenticationSession::swap(client, sessionGuard);
}
-
- CmdSaslContinue::CmdSaslContinue() : Command(saslContinueCommandName) {}
- CmdSaslContinue::~CmdSaslContinue() {}
-
- void CmdSaslContinue::help(std::stringstream& os) const {
- os << "Subsequent steps in a SASL authentication conversation.";
+ return status.isOK();
+}
+
+CmdSaslContinue::CmdSaslContinue() : Command(saslContinueCommandName) {}
+CmdSaslContinue::~CmdSaslContinue() {}
+
+void CmdSaslContinue::help(std::stringstream& os) const {
+ os << "Subsequent steps in a SASL authentication conversation.";
+}
+
+bool CmdSaslContinue::run(OperationContext* txn,
+ const std::string& db,
+ BSONObj& cmdObj,
+ int options,
+ std::string& ignored,
+ BSONObjBuilder& result) {
+ ClientBasic* client = ClientBasic::getCurrent();
+ std::unique_ptr<AuthenticationSession> sessionGuard;
+ AuthenticationSession::swap(client, sessionGuard);
+
+ if (!sessionGuard || sessionGuard->getType() != AuthenticationSession::SESSION_TYPE_SASL) {
+ addStatus(Status(ErrorCodes::ProtocolError, "No SASL session state found"), &result);
+ return false;
}
- bool CmdSaslContinue::run(OperationContext* txn,
- const std::string& db,
- BSONObj& cmdObj,
- int options,
- std::string& ignored,
- BSONObjBuilder& result) {
-
- ClientBasic* client = ClientBasic::getCurrent();
- std::unique_ptr<AuthenticationSession> sessionGuard;
- AuthenticationSession::swap(client, sessionGuard);
-
- if (!sessionGuard || sessionGuard->getType() != AuthenticationSession::SESSION_TYPE_SASL) {
- addStatus(Status(ErrorCodes::ProtocolError, "No SASL session state found"), &result);
- return false;
- }
-
- SaslAuthenticationSession* session =
- static_cast<SaslAuthenticationSession*>(sessionGuard.get());
+ SaslAuthenticationSession* session =
+ static_cast<SaslAuthenticationSession*>(sessionGuard.get());
- // Authenticating the __system@local user to the admin database on mongos is required
- // by the auth passthrough test suite.
- if (session->getAuthenticationDatabase() != db && !Command::testCommandsEnabled) {
- addStatus(Status(ErrorCodes::ProtocolError,
- "Attempt to switch database target during SASL authentication."),
- &result);
- return false;
- }
+ // Authenticating the __system@local user to the admin database on mongos is required
+ // by the auth passthrough test suite.
+ if (session->getAuthenticationDatabase() != db && !Command::testCommandsEnabled) {
+ addStatus(Status(ErrorCodes::ProtocolError,
+ "Attempt to switch database target during SASL authentication."),
+ &result);
+ return false;
+ }
- session->setOpCtxt(txn);
+ session->setOpCtxt(txn);
- Status status = doSaslContinue(client, session, cmdObj, &result);
- addStatus(status, &result);
+ Status status = doSaslContinue(client, session, cmdObj, &result);
+ addStatus(status, &result);
- if (session->isDone()) {
- audit::logAuthentication(
- client,
- session->getMechanism(),
- UserName(session->getPrincipalId(), db),
- status.code());
- }
- else {
- AuthenticationSession::swap(client, sessionGuard);
- }
-
- return status.isOK();
+ if (session->isDone()) {
+ audit::logAuthentication(client,
+ session->getMechanism(),
+ UserName(session->getPrincipalId(), db),
+ status.code());
+ } else {
+ AuthenticationSession::swap(client, sessionGuard);
}
- // The CyrusSaslCommands Enterprise initializer is dependent on PreSaslCommands
- MONGO_INITIALIZER_WITH_PREREQUISITES(PreSaslCommands,
- ("NativeSaslServerCore"))
- (InitializerContext*) {
+ return status.isOK();
+}
- if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-CR"))
- CmdAuthenticate::disableAuthMechanism("MONGODB-CR");
+// The CyrusSaslCommands Enterprise initializer is dependent on PreSaslCommands
+MONGO_INITIALIZER_WITH_PREREQUISITES(PreSaslCommands, ("NativeSaslServerCore"))
+(InitializerContext*) {
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-CR"))
+ CmdAuthenticate::disableAuthMechanism("MONGODB-CR");
- if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-X509"))
- CmdAuthenticate::disableAuthMechanism("MONGODB-X509");
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-X509"))
+ CmdAuthenticate::disableAuthMechanism("MONGODB-X509");
- // For backwards compatibility, in 3.0 we are letting MONGODB-CR imply general
- // challenge-response auth and hence SCRAM-SHA-1 is enabled by either specifying
- // SCRAM-SHA-1 or MONGODB-CR in the authenticationMechanism server parameter.
- if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "SCRAM-SHA-1") &&
- sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-CR"))
- saslGlobalParams.authenticationMechanisms.push_back("SCRAM-SHA-1");
+ // For backwards compatibility, in 3.0 we are letting MONGODB-CR imply general
+ // challenge-response auth and hence SCRAM-SHA-1 is enabled by either specifying
+ // SCRAM-SHA-1 or MONGODB-CR in the authenticationMechanism server parameter.
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "SCRAM-SHA-1") &&
+ sequenceContains(saslGlobalParams.authenticationMechanisms, "MONGODB-CR"))
+ saslGlobalParams.authenticationMechanisms.push_back("SCRAM-SHA-1");
- return Status::OK();
- }
+ return Status::OK();
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.cpp b/src/mongo/db/auth/sasl_options.cpp
index 7261d8b49f0..8ca9e38dd63 100644
--- a/src/mongo/db/auth/sasl_options.cpp
+++ b/src/mongo/db/auth/sasl_options.cpp
@@ -39,162 +39,160 @@
namespace mongo {
- SASLGlobalParams saslGlobalParams;
-
- const int defaultScramIterationCount = 10000;
- const int minimumScramIterationCount = 5000;
-
- SASLGlobalParams::SASLGlobalParams() {
- // Authentication mechanisms supported by default.
- authenticationMechanisms.push_back("MONGODB-CR");
- authenticationMechanisms.push_back("MONGODB-X509");
- authenticationMechanisms.push_back("SCRAM-SHA-1");
- // Default iteration count for SCRAM authentication.
- scramIterationCount = defaultScramIterationCount;
+SASLGlobalParams saslGlobalParams;
+
+const int defaultScramIterationCount = 10000;
+const int minimumScramIterationCount = 5000;
+
+SASLGlobalParams::SASLGlobalParams() {
+ // Authentication mechanisms supported by default.
+ authenticationMechanisms.push_back("MONGODB-CR");
+ authenticationMechanisms.push_back("MONGODB-X509");
+ authenticationMechanisms.push_back("SCRAM-SHA-1");
+ // Default iteration count for SCRAM authentication.
+ scramIterationCount = defaultScramIterationCount;
+}
+
+Status addSASLOptions(moe::OptionSection* options) {
+ moe::OptionSection saslOptions("SASL Options");
+
+ saslOptions.addOptionChaining("security.authenticationMechanisms",
+ "",
+ moe::StringVector,
+ "List of supported authentication mechanisms. "
+ "Default is MONGODB-CR, SCRAM-SHA-1 and MONGODB-X509.")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining(
+ "security.sasl.hostName", "", moe::String, "Fully qualified server domain name")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining("security.sasl.serviceName",
+ "",
+ moe::String,
+ "Registered name of the service using SASL")
+ .setSources(moe::SourceYAMLConfig);
+
+ saslOptions.addOptionChaining("security.sasl.saslauthdSocketPath",
+ "",
+ moe::String,
+ "Path to Unix domain socket file for saslauthd")
+ .setSources(moe::SourceYAMLConfig);
+
+ Status ret = options->addSection(saslOptions);
+ if (!ret.isOK()) {
+ log() << "Failed to add sasl option section: " << ret.toString();
+ return ret;
}
- Status addSASLOptions(moe::OptionSection* options) {
-
- moe::OptionSection saslOptions("SASL Options");
-
- saslOptions.addOptionChaining("security.authenticationMechanisms", "",
- moe::StringVector, "List of supported authentication mechanisms. "
- "Default is MONGODB-CR, SCRAM-SHA-1 and MONGODB-X509.")
- .setSources(moe::SourceYAMLConfig);
-
- saslOptions.addOptionChaining("security.sasl.hostName", "", moe::String,
- "Fully qualified server domain name")
- .setSources(moe::SourceYAMLConfig);
-
- saslOptions.addOptionChaining("security.sasl.serviceName", "", moe::String,
- "Registered name of the service using SASL")
- .setSources(moe::SourceYAMLConfig);
-
- saslOptions.addOptionChaining("security.sasl.saslauthdSocketPath", "", moe::String,
- "Path to Unix domain socket file for saslauthd")
- .setSources(moe::SourceYAMLConfig);
-
- Status ret = options->addSection(saslOptions);
- if (!ret.isOK()) {
- log() << "Failed to add sasl option section: " << ret.toString();
- return ret;
- }
-
- return Status::OK();
- }
-
- Status storeSASLOptions(const moe::Environment& params) {
-
- bool haveAuthenticationMechanisms = false;
- bool haveHostName = false;
- bool haveServiceName = false;
- bool haveAuthdPath = false;
- bool haveScramIterationCount = false;
-
- // Check our setParameter options first so that these values can be properly overridden via
- // the command line even though the options have different names.
- if (params.count("setParameter")) {
- std::map<std::string, std::string> parameters =
- params["setParameter"].as<std::map<std::string, std::string> >();
- for (std::map<std::string, std::string>::iterator parametersIt = parameters.begin();
- parametersIt != parameters.end(); parametersIt++) {
- if (parametersIt->first == "authenticationMechanisms") {
- haveAuthenticationMechanisms = true;
- }
- else if (parametersIt->first == "saslHostName") {
- haveHostName = true;
- }
- else if (parametersIt->first == "saslServiceName") {
- haveServiceName = true;
- }
- else if (parametersIt->first == "saslauthdPath") {
- haveAuthdPath = true;
- }
- else if (parametersIt->first == "scramIterationCount") {
- haveScramIterationCount = true;
- }
+ return Status::OK();
+}
+
+Status storeSASLOptions(const moe::Environment& params) {
+ bool haveAuthenticationMechanisms = false;
+ bool haveHostName = false;
+ bool haveServiceName = false;
+ bool haveAuthdPath = false;
+ bool haveScramIterationCount = false;
+
+ // Check our setParameter options first so that these values can be properly overridden via
+ // the command line even though the options have different names.
+ if (params.count("setParameter")) {
+ std::map<std::string, std::string> parameters =
+ params["setParameter"].as<std::map<std::string, std::string>>();
+ for (std::map<std::string, std::string>::iterator parametersIt = parameters.begin();
+ parametersIt != parameters.end();
+ parametersIt++) {
+ if (parametersIt->first == "authenticationMechanisms") {
+ haveAuthenticationMechanisms = true;
+ } else if (parametersIt->first == "saslHostName") {
+ haveHostName = true;
+ } else if (parametersIt->first == "saslServiceName") {
+ haveServiceName = true;
+ } else if (parametersIt->first == "saslauthdPath") {
+ haveAuthdPath = true;
+ } else if (parametersIt->first == "scramIterationCount") {
+ haveScramIterationCount = true;
}
}
-
- if (params.count("security.authenticationMechanisms") &&
- !haveAuthenticationMechanisms) {
- saslGlobalParams.authenticationMechanisms =
- params["security.authenticationMechanisms"].as<std::vector<std::string> >();
- }
- if (params.count("security.sasl.hostName") && !haveHostName) {
- saslGlobalParams.hostName =
- params["security.sasl.hostName"].as<std::string>();
- }
- if (params.count("security.sasl.serviceName") && !haveServiceName) {
- saslGlobalParams.serviceName =
- params["security.sasl.serviceName"].as<std::string>();
- }
- if (params.count("security.sasl.saslauthdSocketPath") && !haveAuthdPath) {
- saslGlobalParams.authdPath =
- params["security.sasl.saslauthdSocketPath"].as<std::string>();
- }
- if (params.count("security.sasl.scramIterationCount") && !haveScramIterationCount) {
- saslGlobalParams.scramIterationCount =
- params["security.sasl.scramIterationCount"].as<int>();
- }
-
- return Status::OK();
}
- MONGO_MODULE_STARTUP_OPTIONS_REGISTER(SASLOptions)(InitializerContext* context) {
- return addSASLOptions(&moe::startupOptions);
+ if (params.count("security.authenticationMechanisms") && !haveAuthenticationMechanisms) {
+ saslGlobalParams.authenticationMechanisms =
+ params["security.authenticationMechanisms"].as<std::vector<std::string>>();
}
-
- MONGO_STARTUP_OPTIONS_STORE(SASLOptions)(InitializerContext* context) {
- return storeSASLOptions(moe::startupOptionsParsed);
+ if (params.count("security.sasl.hostName") && !haveHostName) {
+ saslGlobalParams.hostName = params["security.sasl.hostName"].as<std::string>();
+ }
+ if (params.count("security.sasl.serviceName") && !haveServiceName) {
+ saslGlobalParams.serviceName = params["security.sasl.serviceName"].as<std::string>();
+ }
+ if (params.count("security.sasl.saslauthdSocketPath") && !haveAuthdPath) {
+ saslGlobalParams.authdPath = params["security.sasl.saslauthdSocketPath"].as<std::string>();
+ }
+ if (params.count("security.sasl.scramIterationCount") && !haveScramIterationCount) {
+ saslGlobalParams.scramIterationCount =
+ params["security.sasl.scramIterationCount"].as<int>();
}
- // SASL Startup Parameters, making them settable via setParameter on the command line or in the
- // legacy INI config file. None of these parameters are modifiable at runtime.
- ExportedServerParameter<std::vector<std::string> > SASLAuthenticationMechanismsSetting(
- ServerParameterSet::getGlobal(),
- "authenticationMechanisms",
- &saslGlobalParams.authenticationMechanisms,
- true, // Change at startup
- false); // Change at runtime
-
- ExportedServerParameter<std::string> SASLHostNameSetting(ServerParameterSet::getGlobal(),
- "saslHostName",
- &saslGlobalParams.hostName,
- true, // Change at startup
- false); // Change at runtime
-
- ExportedServerParameter<std::string> SASLServiceNameSetting(ServerParameterSet::getGlobal(),
- "saslServiceName",
- &saslGlobalParams.serviceName,
- true, // Change at startup
- false); // Change at runtime
-
- ExportedServerParameter<std::string> SASLAuthdPathSetting(ServerParameterSet::getGlobal(),
- "saslauthdPath",
- &saslGlobalParams.authdPath,
- true, // Change at startup
- false); // Change at runtime
-
- const std::string scramIterationCountServerParameter = "scramIterationCount";
- class ExportedScramIterationCountParameter : public ExportedServerParameter<int> {
- public:
- ExportedScramIterationCountParameter():
- ExportedServerParameter<int>(ServerParameterSet::getGlobal(),
- scramIterationCountServerParameter,
- &saslGlobalParams.scramIterationCount,
- true, // Change at startup
- true) {} // Change at runtime
-
- virtual Status validate(const int& newValue) {
- if (newValue < minimumScramIterationCount) {
- return Status(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Invalid value for SCRAM iteration count: " << newValue <<
- " is less than the minimum SCRAM iteration count, " <<
- minimumScramIterationCount);
- }
- return Status::OK();
+ return Status::OK();
+}
+
+MONGO_MODULE_STARTUP_OPTIONS_REGISTER(SASLOptions)(InitializerContext* context) {
+ return addSASLOptions(&moe::startupOptions);
+}
+
+MONGO_STARTUP_OPTIONS_STORE(SASLOptions)(InitializerContext* context) {
+ return storeSASLOptions(moe::startupOptionsParsed);
+}
+
+// SASL Startup Parameters, making them settable via setParameter on the command line or in the
+// legacy INI config file. None of these parameters are modifiable at runtime.
+ExportedServerParameter<std::vector<std::string>> SASLAuthenticationMechanismsSetting(
+ ServerParameterSet::getGlobal(),
+ "authenticationMechanisms",
+ &saslGlobalParams.authenticationMechanisms,
+ true, // Change at startup
+ false); // Change at runtime
+
+ExportedServerParameter<std::string> SASLHostNameSetting(ServerParameterSet::getGlobal(),
+ "saslHostName",
+ &saslGlobalParams.hostName,
+ true, // Change at startup
+ false); // Change at runtime
+
+ExportedServerParameter<std::string> SASLServiceNameSetting(ServerParameterSet::getGlobal(),
+ "saslServiceName",
+ &saslGlobalParams.serviceName,
+ true, // Change at startup
+ false); // Change at runtime
+
+ExportedServerParameter<std::string> SASLAuthdPathSetting(ServerParameterSet::getGlobal(),
+ "saslauthdPath",
+ &saslGlobalParams.authdPath,
+ true, // Change at startup
+ false); // Change at runtime
+
+const std::string scramIterationCountServerParameter = "scramIterationCount";
+class ExportedScramIterationCountParameter : public ExportedServerParameter<int> {
+public:
+ ExportedScramIterationCountParameter()
+ : ExportedServerParameter<int>(ServerParameterSet::getGlobal(),
+ scramIterationCountServerParameter,
+ &saslGlobalParams.scramIterationCount,
+ true, // Change at startup
+ true) {} // Change at runtime
+
+ virtual Status validate(const int& newValue) {
+ if (newValue < minimumScramIterationCount) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Invalid value for SCRAM iteration count: " << newValue
+ << " is less than the minimum SCRAM iteration count, "
+ << minimumScramIterationCount);
}
- } scramIterationCountParam;
+ return Status::OK();
+ }
+} scramIterationCountParam;
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_options.h b/src/mongo/db/auth/sasl_options.h
index cc649adeeba..f9a4bb15efd 100644
--- a/src/mongo/db/auth/sasl_options.h
+++ b/src/mongo/db/auth/sasl_options.h
@@ -36,27 +36,26 @@
namespace mongo {
namespace optionenvironment {
- class OptionSection;
- class Environment;
-} // namespace optionenvironment
+class OptionSection;
+class Environment;
+} // namespace optionenvironment
- namespace moe = optionenvironment;
+namespace moe = optionenvironment;
- struct SASLGlobalParams {
+struct SASLGlobalParams {
+ std::vector<std::string> authenticationMechanisms;
+ std::string hostName;
+ std::string serviceName;
+ std::string authdPath;
+ int scramIterationCount;
- std::vector<std::string> authenticationMechanisms;
- std::string hostName;
- std::string serviceName;
- std::string authdPath;
- int scramIterationCount;
+ SASLGlobalParams();
+};
- SASLGlobalParams();
- };
+extern SASLGlobalParams saslGlobalParams;
- extern SASLGlobalParams saslGlobalParams;
+Status addSASLOptions(moe::OptionSection* options);
- Status addSASLOptions(moe::OptionSection* options);
+Status storeSASLOptions(const moe::Environment& params);
- Status storeSASLOptions(const moe::Environment& params);
-
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
index ef38762e3a5..b5f0b9e3c8f 100644
--- a/src/mongo/db/auth/sasl_plain_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
@@ -36,73 +36,70 @@
namespace mongo {
- SaslPLAINServerConversation::SaslPLAINServerConversation(
- SaslAuthenticationSession* saslAuthSession) :
- SaslServerConversation(saslAuthSession) {
+SaslPLAINServerConversation::SaslPLAINServerConversation(SaslAuthenticationSession* saslAuthSession)
+ : SaslServerConversation(saslAuthSession) {}
+
+SaslPLAINServerConversation::~SaslPLAINServerConversation(){};
+
+StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::string* outputData) {
+ // Expecting user input on the form: user\0user\0pwd
+ std::string input = inputData.toString();
+ std::string pwd = "";
+
+ try {
+ _user = input.substr(0, inputData.find('\0'));
+ pwd = input.substr(inputData.find('\0', _user.size() + 1) + 1);
+ } catch (std::out_of_range& exception) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream()
+ << "Incorrectly formatted PLAIN client message");
}
- SaslPLAINServerConversation::~SaslPLAINServerConversation() {};
+ User* userObj;
+ // The authentication database is also the source database for the user.
+ Status status =
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser(
+ _saslAuthSession->getOpCtxt(),
+ UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
+ &userObj);
- StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData,
- std::string* outputData) {
- // Expecting user input on the form: user\0user\0pwd
- std::string input = inputData.toString();
- std::string pwd = "";
-
- try {
- _user = input.substr(0, inputData.find('\0'));
- pwd = input.substr(inputData.find('\0', _user.size()+1)+1);
- }
- catch (std::out_of_range& exception) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() << "Incorrectly formatted PLAIN client message");
- }
-
- User* userObj;
- // The authentication database is also the source database for the user.
- Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
- acquireUser(_saslAuthSession->getOpCtxt(),
- UserName(_user, _saslAuthSession->getAuthenticationDatabase()),
- &userObj);
-
- if (!status.isOK()) {
- return StatusWith<bool>(status);
- }
+ if (!status.isOK()) {
+ return StatusWith<bool>(status);
+ }
- const User::CredentialData creds = userObj->getCredentials();
- _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
- releaseUser(userObj);
+ const User::CredentialData creds = userObj->getCredentials();
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);
- std::string authDigest = createPasswordDigest(_user, pwd);
+ std::string authDigest = createPasswordDigest(_user, pwd);
- if (!creds.password.empty()) {
- // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode)
- if (authDigest != creds.password) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() << "Incorrect user name or password");
- }
+ if (!creds.password.empty()) {
+ // Handle schemaVersion26Final (MONGODB-CR/SCRAM mixed mode)
+ if (authDigest != creds.password) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() << "Incorrect user name or password");
}
- else {
- // Handle schemaVersion28SCRAM (SCRAM only mode)
- unsigned char storedKey[scram::hashSize];
- unsigned char serverKey[scram::hashSize];
-
- scram::generateSecrets(authDigest,
- reinterpret_cast<const unsigned char*>(base64::decode(creds.scram.salt).c_str()),
- 16,
- creds.scram.iterationCount,
- storedKey,
- serverKey);
- if (creds.scram.storedKey != base64::encode(reinterpret_cast<const char*>(storedKey),
- scram::hashSize)){
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() << "Incorrect user name or password");
- }
+ } else {
+ // Handle schemaVersion28SCRAM (SCRAM only mode)
+ unsigned char storedKey[scram::hashSize];
+ unsigned char serverKey[scram::hashSize];
+
+ scram::generateSecrets(
+ authDigest,
+ reinterpret_cast<const unsigned char*>(base64::decode(creds.scram.salt).c_str()),
+ 16,
+ creds.scram.iterationCount,
+ storedKey,
+ serverKey);
+ if (creds.scram.storedKey !=
+ base64::encode(reinterpret_cast<const char*>(storedKey), scram::hashSize)) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream() << "Incorrect user name or password");
}
+ }
- *outputData = "";
+ *outputData = "";
- return StatusWith<bool>(true);
- }
+ return StatusWith<bool>(true);
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.h b/src/mongo/db/auth/sasl_plain_server_conversation.h
index d8e33e99905..5d3b57ffa89 100644
--- a/src/mongo/db/auth/sasl_plain_server_conversation.h
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.h
@@ -36,21 +36,22 @@
#include "mongo/db/auth/sasl_server_conversation.h"
namespace mongo {
+/**
+ * Server side authentication session for SASL PLAIN.
+ */
+class SaslPLAINServerConversation : public SaslServerConversation {
+ MONGO_DISALLOW_COPYING(SaslPLAINServerConversation);
+
+public:
/**
- * Server side authentication session for SASL PLAIN.
- */
- class SaslPLAINServerConversation : public SaslServerConversation {
- MONGO_DISALLOW_COPYING(SaslPLAINServerConversation);
- public:
- /**
- * Implements the server side of a SASL PLAIN mechanism session.
- *
- **/
- explicit SaslPLAINServerConversation(SaslAuthenticationSession* saslAuthSession);
-
- virtual ~SaslPLAINServerConversation();
-
- virtual StatusWith<bool> step(StringData inputData, std::string* outputData);
- };
+ * Implements the server side of a SASL PLAIN mechanism session.
+ *
+ **/
+ explicit SaslPLAINServerConversation(SaslAuthenticationSession* saslAuthSession);
+
+ virtual ~SaslPLAINServerConversation();
+
+ virtual StatusWith<bool> step(StringData inputData, std::string* outputData);
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
index 83137a8bd99..9fd8496b7bc 100644
--- a/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.cpp
@@ -48,292 +48,289 @@
namespace mongo {
- using std::unique_ptr;
- using std::string;
-
- SaslSCRAMSHA1ServerConversation::SaslSCRAMSHA1ServerConversation(
- SaslAuthenticationSession* saslAuthSession) :
- SaslServerConversation(saslAuthSession),
- _step(0),
- _authMessage(""),
- _nonce("") {
+using std::unique_ptr;
+using std::string;
+
+SaslSCRAMSHA1ServerConversation::SaslSCRAMSHA1ServerConversation(
+ SaslAuthenticationSession* saslAuthSession)
+ : SaslServerConversation(saslAuthSession), _step(0), _authMessage(""), _nonce("") {}
+
+StatusWith<bool> SaslSCRAMSHA1ServerConversation::step(StringData inputData,
+ std::string* outputData) {
+ std::vector<std::string> input = StringSplitter::split(inputData.toString(), ",");
+ _step++;
+
+ if (_step > 3 || _step <= 0) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream()
+ << "Invalid SCRAM-SHA-1 authentication step: " << _step);
}
-
- StatusWith<bool> SaslSCRAMSHA1ServerConversation::step(StringData inputData,
- std::string* outputData) {
-
- std::vector<std::string> input = StringSplitter::split(inputData.toString(), ",");
- _step++;
-
- if (_step > 3 || _step <= 0) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() << "Invalid SCRAM-SHA-1 authentication step: " << _step);
- }
- if (_step == 1) {
- return _firstStep(input, outputData);
- }
- if (_step == 2) {
- return _secondStep(input, outputData);
- }
-
- *outputData = "";
-
- return StatusWith<bool>(true);
+ if (_step == 1) {
+ return _firstStep(input, outputData);
}
-
- /*
- * RFC 5802 specifies that in SCRAM user names characters ',' and '=' are encoded as
- * =2C and =3D respectively.
- */
- static void decodeSCRAMUsername(std::string& user) {
- boost::replace_all(user, "=2C", ",");
- boost::replace_all(user, "=3D", "=");
+ if (_step == 2) {
+ return _secondStep(input, outputData);
}
- /*
- * Parse client-first-message of the form:
- * n,a=authzid,n=encoded-username,r=client-nonce
- *
- * Generate server-first-message on the form:
- * r=client-nonce|server-nonce,s=user-salt,i=iteration-count
- *
- * NOTE: we are ignoring the authorization ID part of the message
- */
- StatusWith<bool> SaslSCRAMSHA1ServerConversation::_firstStep(std::vector<string>& input,
- std::string* outputData) {
- std::string authzId = "";
-
- if (input.size() == 4) {
- /* The second entry a=authzid is optional. If provided it will be
- * validated against the encoded username.
- *
- * The two allowed input forms are:
- * n,,n=encoded-username,r=client-nonce
- * n,a=authzid,n=encoded-username,r=client-nonce
- */
- if (!str::startsWith(input[1], "a=") || input[1].size() < 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 authzid: " << input[1]);
- }
- authzId = input[1].substr(2);
- input.erase(input.begin() + 1);
- }
-
- if (input.size() != 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect number of arguments for first SCRAM-SHA-1 client message, got " <<
- input.size() << " expected 4");
- }
- else if (input[0] != "n") {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 client message prefix: " << input[0]);
- }
- else if (!str::startsWith(input[1], "n=") || input[1].size() < 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 user name: " << input[1]);
- }
- else if(!str::startsWith(input[2], "r=") || input[2].size() < 6) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 client nonce: " << input[2]);
- }
+ *outputData = "";
- _user = input[1].substr(2);
- if (!authzId.empty() && _user != authzId) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "SCRAM-SHA-1 user name " << _user << " does not match authzid " << authzId);
- }
+ return StatusWith<bool>(true);
+}
- decodeSCRAMUsername(_user);
+/*
+ * RFC 5802 specifies that in SCRAM user names characters ',' and '=' are encoded as
+ * =2C and =3D respectively.
+ */
+static void decodeSCRAMUsername(std::string& user) {
+ boost::replace_all(user, "=2C", ",");
+ boost::replace_all(user, "=3D", "=");
+}
- // SERVER-16534, SCRAM-SHA-1 must be enabled for authenticating the internal user, so that
- // cluster members may communicate with each other. Hence ignore disabled auth mechanism
- // for the internal user.
- UserName user(_user, _saslAuthSession->getAuthenticationDatabase());
- if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "SCRAM-SHA-1") &&
- user != internalSecurity.user->getName()) {
+/*
+ * Parse client-first-message of the form:
+ * n,a=authzid,n=encoded-username,r=client-nonce
+ *
+ * Generate server-first-message on the form:
+ * r=client-nonce|server-nonce,s=user-salt,i=iteration-count
+ *
+ * NOTE: we are ignoring the authorization ID part of the message
+ */
+StatusWith<bool> SaslSCRAMSHA1ServerConversation::_firstStep(std::vector<string>& input,
+ std::string* outputData) {
+ std::string authzId = "";
+
+ if (input.size() == 4) {
+ /* The second entry a=authzid is optional. If provided it will be
+ * validated against the encoded username.
+ *
+ * The two allowed input forms are:
+ * n,,n=encoded-username,r=client-nonce
+ * n,a=authzid,n=encoded-username,r=client-nonce
+ */
+ if (!str::startsWith(input[1], "a=") || input[1].size() < 3) {
return StatusWith<bool>(ErrorCodes::BadValue,
- "SCRAM-SHA-1 authentication is disabled");
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 authzid: " << input[1]);
}
+ authzId = input[1].substr(2);
+ input.erase(input.begin() + 1);
+ }
- // add client-first-message-bare to _authMessage
- _authMessage += input[1] + "," + input[2] + ",";
-
- std::string clientNonce = input[2].substr(2);
-
- // The authentication database is also the source database for the user.
- User* userObj;
- Status status = _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
- acquireUser(_saslAuthSession->getOpCtxt(),
- user,
- &userObj);
+ if (input.size() != 3) {
+ return StatusWith<bool>(
+ ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect number of arguments for first SCRAM-SHA-1 client message, got "
+ << input.size() << " expected 4");
+ } else if (input[0] != "n") {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 client message prefix: " << input[0]);
+ } else if (!str::startsWith(input[1], "n=") || input[1].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 user name: " << input[1]);
+ } else if (!str::startsWith(input[2], "r=") || input[2].size() < 6) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 client nonce: " << input[2]);
+ }
- if (!status.isOK()) {
- return StatusWith<bool>(status);
- }
+ _user = input[1].substr(2);
+ if (!authzId.empty() && _user != authzId) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "SCRAM-SHA-1 user name " << _user
+ << " does not match authzid " << authzId);
+ }
- _creds = userObj->getCredentials();
- UserName userName = userObj->getName();
+ decodeSCRAMUsername(_user);
- _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().
- releaseUser(userObj);
+ // SERVER-16534, SCRAM-SHA-1 must be enabled for authenticating the internal user, so that
+ // cluster members may communicate with each other. Hence ignore disabled auth mechanism
+ // for the internal user.
+ UserName user(_user, _saslAuthSession->getAuthenticationDatabase());
+ if (!sequenceContains(saslGlobalParams.authenticationMechanisms, "SCRAM-SHA-1") &&
+ user != internalSecurity.user->getName()) {
+ return StatusWith<bool>(ErrorCodes::BadValue, "SCRAM-SHA-1 authentication is disabled");
+ }
- // Check for authentication attempts of the __system user on
- // systems started without a keyfile.
- if (userName == internalSecurity.user->getName() &&
- _creds.scram.salt.empty()) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- "It is not possible to authenticate as the __system user "
- "on servers started without a --keyFile parameter");
- }
+ // add client-first-message-bare to _authMessage
+ _authMessage += input[1] + "," + input[2] + ",";
- // Generate SCRAM credentials on the fly for mixed MONGODB-CR/SCRAM mode.
- if (_creds.scram.salt.empty() && !_creds.password.empty()) {
- // Use a default value of 5000 for the scramIterationCount when in mixed mode,
- // overriding the default value (10000) used for SCRAM mode or the user-given value.
- const int mixedModeScramIterationCount = 5000;
- BSONObj scramCreds = scram::generateCredentials(_creds.password,
- mixedModeScramIterationCount);
- _creds.scram.iterationCount = scramCreds[scram::iterationCountFieldName].Int();
- _creds.scram.salt = scramCreds[scram::saltFieldName].String();
- _creds.scram.storedKey = scramCreds[scram::storedKeyFieldName].String();
- _creds.scram.serverKey = scramCreds[scram::serverKeyFieldName].String();
- }
+ std::string clientNonce = input[2].substr(2);
- // Generate server-first-message
- // Create text-based nonce as base64 encoding of a binary blob of length multiple of 3
- const int nonceLenQWords = 3;
- uint64_t binaryNonce[nonceLenQWords];
+ // The authentication database is also the source database for the user.
+ User* userObj;
+ Status status =
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().acquireUser(
+ _saslAuthSession->getOpCtxt(), user, &userObj);
- unique_ptr<SecureRandom> sr(SecureRandom::create());
+ if (!status.isOK()) {
+ return StatusWith<bool>(status);
+ }
- binaryNonce[0] = sr->nextInt64();
- binaryNonce[1] = sr->nextInt64();
- binaryNonce[2] = sr->nextInt64();
+ _creds = userObj->getCredentials();
+ UserName userName = userObj->getName();
- _nonce = clientNonce +
- base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce));
- StringBuilder sb;
- sb << "r=" << _nonce <<
- ",s=" << _creds.scram.salt <<
- ",i=" << _creds.scram.iterationCount;
- *outputData = sb.str();
+ _saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);
- // add server-first-message to authMessage
- _authMessage += *outputData + ",";
+ // Check for authentication attempts of the __system user on
+ // systems started without a keyfile.
+ if (userName == internalSecurity.user->getName() && _creds.scram.salt.empty()) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ "It is not possible to authenticate as the __system user "
+ "on servers started without a --keyFile parameter");
+ }
- return StatusWith<bool>(false);
+ // Generate SCRAM credentials on the fly for mixed MONGODB-CR/SCRAM mode.
+ if (_creds.scram.salt.empty() && !_creds.password.empty()) {
+ // Use a default value of 5000 for the scramIterationCount when in mixed mode,
+ // overriding the default value (10000) used for SCRAM mode or the user-given value.
+ const int mixedModeScramIterationCount = 5000;
+ BSONObj scramCreds =
+ scram::generateCredentials(_creds.password, mixedModeScramIterationCount);
+ _creds.scram.iterationCount = scramCreds[scram::iterationCountFieldName].Int();
+ _creds.scram.salt = scramCreds[scram::saltFieldName].String();
+ _creds.scram.storedKey = scramCreds[scram::storedKeyFieldName].String();
+ _creds.scram.serverKey = scramCreds[scram::serverKeyFieldName].String();
}
- /**
- * Parse client-final-message of the form:
- * c=channel-binding(base64),r=client-nonce|server-nonce,p=ClientProof
- *
- * Generate successful authentication server-final-message on the form:
- * v=ServerSignature
- *
- * or failed authentication server-final-message on the form:
- * e=message
- *
- * NOTE: we are ignoring the channel binding part of the message
- **/
- StatusWith<bool> SaslSCRAMSHA1ServerConversation::_secondStep(const std::vector<string>& input,
- std::string* outputData) {
- if (input.size() != 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect number of arguments for second SCRAM-SHA-1 client message, got " <<
- input.size() << " expected 3");
- }
- else if (!str::startsWith(input[0], "c=") || input[0].size() < 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 channel binding: " << input[0]);
- }
- else if (!str::startsWith(input[1], "r=") || input[1].size() < 6) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 client|server nonce: " << input[1]);
- }
- else if(!str::startsWith(input[2], "p=") || input[2].size() < 3) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Incorrect SCRAM-SHA-1 ClientProof: " << input[2]);
- }
+ // Generate server-first-message
+ // Create text-based nonce as base64 encoding of a binary blob of length multiple of 3
+ const int nonceLenQWords = 3;
+ uint64_t binaryNonce[nonceLenQWords];
- // add client-final-message-without-proof to authMessage
- _authMessage += input[0] + "," + input[1];
+ unique_ptr<SecureRandom> sr(SecureRandom::create());
- // Concatenated nonce sent by client should equal the one in server-first-message
- std::string nonce = input[1].substr(2);
- if (nonce != _nonce) {
- return StatusWith<bool>(ErrorCodes::BadValue, mongoutils::str::stream() <<
- "Unmatched SCRAM-SHA-1 nonce received from client in second step, expected " <<
- _nonce << " but received " << nonce);
- }
+ binaryNonce[0] = sr->nextInt64();
+ binaryNonce[1] = sr->nextInt64();
+ binaryNonce[2] = sr->nextInt64();
- std::string clientProof = input[2].substr(2);
-
- // Do server side computations, compare storedKeys and generate client-final-message
- // AuthMessage := client-first-message-bare + "," +
- // server-first-message + "," +
- // client-final-message-without-proof
- // ClientSignature := HMAC(StoredKey, AuthMessage)
- // ClientKey := ClientSignature XOR ClientProof
- // ServerSignature := HMAC(ServerKey, AuthMessage)
-
- unsigned int hashLen = 0;
- unsigned char clientSignature[scram::hashSize];
-
- std::string decodedStoredKey = base64::decode(_creds.scram.storedKey);
- // ClientSignature := HMAC(StoredKey, AuthMessage)
- fassert(18662, crypto::hmacSha1(
- reinterpret_cast<const unsigned char*>(decodedStoredKey.c_str()),
- scram::hashSize,
- reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
- _authMessage.size(),
- clientSignature,
- &hashLen));
-
- fassert(18658, hashLen == scram::hashSize);
-
- try {
- clientProof = base64::decode(clientProof);
- }
- catch (const DBException& ex) {
- return StatusWith<bool>(ex.toStatus());
- }
- const unsigned char *decodedClientProof =
- reinterpret_cast<const unsigned char*>(clientProof.c_str());
+ _nonce =
+ clientNonce + base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce));
+ StringBuilder sb;
+ sb << "r=" << _nonce << ",s=" << _creds.scram.salt << ",i=" << _creds.scram.iterationCount;
+ *outputData = sb.str();
- // ClientKey := ClientSignature XOR ClientProof
- unsigned char clientKey[scram::hashSize];
- for(size_t i=0; i<scram::hashSize; i++) {
- clientKey[i] = clientSignature[i]^decodedClientProof[i];
- }
+ // add server-first-message to authMessage
+ _authMessage += *outputData + ",";
- // StoredKey := H(ClientKey)
- unsigned char computedStoredKey[scram::hashSize];
- fassert(18659, crypto::sha1(clientKey, scram::hashSize, computedStoredKey));
+ return StatusWith<bool>(false);
+}
- if (memcmp(decodedStoredKey.c_str(), computedStoredKey, scram::hashSize) != 0) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() <<
- "SCRAM-SHA-1 authentication failed, storedKey mismatch");
- }
+/**
+ * Parse client-final-message of the form:
+ * c=channel-binding(base64),r=client-nonce|server-nonce,p=ClientProof
+ *
+ * Generate successful authentication server-final-message on the form:
+ * v=ServerSignature
+ *
+ * or failed authentication server-final-message on the form:
+ * e=message
+ *
+ * NOTE: we are ignoring the channel binding part of the message
+**/
+StatusWith<bool> SaslSCRAMSHA1ServerConversation::_secondStep(const std::vector<string>& input,
+ std::string* outputData) {
+ if (input.size() != 3) {
+ return StatusWith<bool>(
+ ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect number of arguments for second SCRAM-SHA-1 client message, got "
+ << input.size() << " expected 3");
+ } else if (!str::startsWith(input[0], "c=") || input[0].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 channel binding: " << input[0]);
+ } else if (!str::startsWith(input[1], "r=") || input[1].size() < 6) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 client|server nonce: " << input[1]);
+ } else if (!str::startsWith(input[2], "p=") || input[2].size() < 3) {
+ return StatusWith<bool>(ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Incorrect SCRAM-SHA-1 ClientProof: " << input[2]);
+ }
+
+ // add client-final-message-without-proof to authMessage
+ _authMessage += input[0] + "," + input[1];
+
+ // Concatenated nonce sent by client should equal the one in server-first-message
+ std::string nonce = input[1].substr(2);
+ if (nonce != _nonce) {
+ return StatusWith<bool>(
+ ErrorCodes::BadValue,
+ mongoutils::str::stream()
+ << "Unmatched SCRAM-SHA-1 nonce received from client in second step, expected "
+ << _nonce << " but received " << nonce);
+ }
- // ServerSignature := HMAC(ServerKey, AuthMessage)
- unsigned char serverSignature[scram::hashSize];
- std::string decodedServerKey = base64::decode(_creds.scram.serverKey);
- fassert(18660, crypto::hmacSha1(
- reinterpret_cast<const unsigned char*>(decodedServerKey.c_str()),
- scram::hashSize,
- reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
- _authMessage.size(),
- serverSignature,
- &hashLen));
+ std::string clientProof = input[2].substr(2);
+
+ // Do server side computations, compare storedKeys and generate client-final-message
+ // AuthMessage := client-first-message-bare + "," +
+ // server-first-message + "," +
+ // client-final-message-without-proof
+ // ClientSignature := HMAC(StoredKey, AuthMessage)
+ // ClientKey := ClientSignature XOR ClientProof
+ // ServerSignature := HMAC(ServerKey, AuthMessage)
+
+ unsigned int hashLen = 0;
+ unsigned char clientSignature[scram::hashSize];
+
+ std::string decodedStoredKey = base64::decode(_creds.scram.storedKey);
+ // ClientSignature := HMAC(StoredKey, AuthMessage)
+ fassert(18662,
+ crypto::hmacSha1(reinterpret_cast<const unsigned char*>(decodedStoredKey.c_str()),
+ scram::hashSize,
+ reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
+ _authMessage.size(),
+ clientSignature,
+ &hashLen));
+
+ fassert(18658, hashLen == scram::hashSize);
+
+ try {
+ clientProof = base64::decode(clientProof);
+ } catch (const DBException& ex) {
+ return StatusWith<bool>(ex.toStatus());
+ }
+ const unsigned char* decodedClientProof =
+ reinterpret_cast<const unsigned char*>(clientProof.c_str());
- fassert(18661, hashLen == scram::hashSize);
+ // ClientKey := ClientSignature XOR ClientProof
+ unsigned char clientKey[scram::hashSize];
+ for (size_t i = 0; i < scram::hashSize; i++) {
+ clientKey[i] = clientSignature[i] ^ decodedClientProof[i];
+ }
- StringBuilder sb;
- sb << "v=" << base64::encode(reinterpret_cast<char*>(serverSignature), scram::hashSize);
- *outputData = sb.str();
+ // StoredKey := H(ClientKey)
+ unsigned char computedStoredKey[scram::hashSize];
+ fassert(18659, crypto::sha1(clientKey, scram::hashSize, computedStoredKey));
- return StatusWith<bool>(false);
+ if (memcmp(decodedStoredKey.c_str(), computedStoredKey, scram::hashSize) != 0) {
+ return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
+ mongoutils::str::stream()
+ << "SCRAM-SHA-1 authentication failed, storedKey mismatch");
}
+
+ // ServerSignature := HMAC(ServerKey, AuthMessage)
+ unsigned char serverSignature[scram::hashSize];
+ std::string decodedServerKey = base64::decode(_creds.scram.serverKey);
+ fassert(18660,
+ crypto::hmacSha1(reinterpret_cast<const unsigned char*>(decodedServerKey.c_str()),
+ scram::hashSize,
+ reinterpret_cast<const unsigned char*>(_authMessage.c_str()),
+ _authMessage.size(),
+ serverSignature,
+ &hashLen));
+
+ fassert(18661, hashLen == scram::hashSize);
+
+ StringBuilder sb;
+ sb << "v=" << base64::encode(reinterpret_cast<char*>(serverSignature), scram::hashSize);
+ *outputData = sb.str();
+
+ return StatusWith<bool>(false);
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_scramsha1_server_conversation.h b/src/mongo/db/auth/sasl_scramsha1_server_conversation.h
index 1a2e1ad8fbd..68b3e226168 100644
--- a/src/mongo/db/auth/sasl_scramsha1_server_conversation.h
+++ b/src/mongo/db/auth/sasl_scramsha1_server_conversation.h
@@ -37,45 +37,46 @@
#include "mongo/db/auth/sasl_server_conversation.h"
namespace mongo {
+/**
+ * Server side authentication session for SASL SCRAM-SHA-1.
+ */
+class SaslSCRAMSHA1ServerConversation : public SaslServerConversation {
+ MONGO_DISALLOW_COPYING(SaslSCRAMSHA1ServerConversation);
+
+public:
/**
- * Server side authentication session for SASL SCRAM-SHA-1.
- */
- class SaslSCRAMSHA1ServerConversation : public SaslServerConversation {
- MONGO_DISALLOW_COPYING(SaslSCRAMSHA1ServerConversation);
- public:
- /**
- * Implements the server side of a SASL SCRAM-SHA-1 mechanism session.
- **/
- explicit SaslSCRAMSHA1ServerConversation(SaslAuthenticationSession* saslAuthSession);
+ * Implements the server side of a SASL SCRAM-SHA-1 mechanism session.
+ **/
+ explicit SaslSCRAMSHA1ServerConversation(SaslAuthenticationSession* saslAuthSession);
- virtual ~SaslSCRAMSHA1ServerConversation() {};
+ virtual ~SaslSCRAMSHA1ServerConversation(){};
- /**
- * Take one step in a SCRAM-SHA-1 conversation.
- *
- * @return !Status::OK() if auth failed. The boolean part indicates if the
- * authentication conversation is finished or not.
- *
- **/
- virtual StatusWith<bool> step(StringData inputData, std::string* outputData);
+ /**
+ * Take one step in a SCRAM-SHA-1 conversation.
+ *
+ * @return !Status::OK() if auth failed. The boolean part indicates if the
+ * authentication conversation is finished or not.
+ *
+ **/
+ virtual StatusWith<bool> step(StringData inputData, std::string* outputData);
- private:
- /**
- * Parse client-first-message and generate server-first-message
- **/
- StatusWith<bool> _firstStep(std::vector<std::string>& input, std::string* outputData);
+private:
+ /**
+ * Parse client-first-message and generate server-first-message
+ **/
+ StatusWith<bool> _firstStep(std::vector<std::string>& input, std::string* outputData);
- /**
- * Parse client-final-message and generate server-final-message
- **/
- StatusWith<bool> _secondStep(const std::vector<std::string>& input, std::string* outputData);
+ /**
+ * Parse client-final-message and generate server-final-message
+ **/
+ StatusWith<bool> _secondStep(const std::vector<std::string>& input, std::string* outputData);
- int _step;
- std::string _authMessage;
- User::CredentialData _creds;
+ int _step;
+ std::string _authMessage;
+ User::CredentialData _creds;
- // client and server nonce concatenated
- std::string _nonce;
- };
+ // client and server nonce concatenated
+ std::string _nonce;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_server_conversation.cpp b/src/mongo/db/auth/sasl_server_conversation.cpp
index aa7f662535d..75f680e9a0d 100644
--- a/src/mongo/db/auth/sasl_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_server_conversation.cpp
@@ -31,11 +31,11 @@
#include <string>
namespace mongo {
-
- SaslServerConversation::~SaslServerConversation() {};
-
- std::string SaslServerConversation::getPrincipalId() {
- return _user;
- }
+
+SaslServerConversation::~SaslServerConversation(){};
+
+std::string SaslServerConversation::getPrincipalId() {
+ return _user;
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_server_conversation.h b/src/mongo/db/auth/sasl_server_conversation.h
index 005e5fc67f1..5d0ce497523 100644
--- a/src/mongo/db/auth/sasl_server_conversation.h
+++ b/src/mongo/db/auth/sasl_server_conversation.h
@@ -37,52 +37,53 @@
#include "mongo/db/auth/user.h"
namespace mongo {
-
- class SaslAuthenticationSession;
- template <typename T> class StatusWith;
-
+
+class SaslAuthenticationSession;
+template <typename T>
+class StatusWith;
+
+/**
+ * Abstract class for implementing the server-side
+ * of a SASL mechanism conversation.
+ */
+class SaslServerConversation {
+ MONGO_DISALLOW_COPYING(SaslServerConversation);
+
+public:
/**
- * Abstract class for implementing the server-side
- * of a SASL mechanism conversation.
- */
- class SaslServerConversation {
- MONGO_DISALLOW_COPYING(SaslServerConversation);
- public:
- /**
- * Implements the server side of a SASL authentication mechanism.
- *
- * "saslAuthSession" is the corresponding SASLAuthenticationSession.
- * "saslAuthSession" must stay in scope until the SaslServerConversation's
- * destructor completes.
- *
- **/
- explicit SaslServerConversation(SaslAuthenticationSession* saslAuthSession) :
- _saslAuthSession(saslAuthSession),
- _user("") {}
+ * Implements the server side of a SASL authentication mechanism.
+ *
+ * "saslAuthSession" is the corresponding SASLAuthenticationSession.
+ * "saslAuthSession" must stay in scope until the SaslServerConversation's
+ * destructor completes.
+ *
+ **/
+ explicit SaslServerConversation(SaslAuthenticationSession* saslAuthSession)
+ : _saslAuthSession(saslAuthSession), _user("") {}
- virtual ~SaslServerConversation();
+ virtual ~SaslServerConversation();
- /**
- * Performs one step of the server side of the authentication session,
- * consuming "inputData" and producing "*outputData".
- *
- * A return of Status::OK() indicates successful progress towards authentication.
- * A return of !Status::OK() indicates failed authentication
- *
- * A return of true means that the authentication process has finished.
- * A return of false means that the authentication process has more steps.
- *
- */
- virtual StatusWith<bool> step(StringData inputData, std::string* outputData) = 0;
+ /**
+ * Performs one step of the server side of the authentication session,
+ * consuming "inputData" and producing "*outputData".
+ *
+ * A return of Status::OK() indicates successful progress towards authentication.
+ * A return of !Status::OK() indicates failed authentication
+ *
+ * A return of true means that the authentication process has finished.
+ * A return of false means that the authentication process has more steps.
+ *
+ */
+ virtual StatusWith<bool> step(StringData inputData, std::string* outputData) = 0;
+
+ /**
+ * Gets the SASL principal id (user name) for the conversation
+ **/
+ std::string getPrincipalId();
- /**
- * Gets the SASL principal id (user name) for the conversation
- **/
- std::string getPrincipalId();
-
- protected:
- SaslAuthenticationSession* _saslAuthSession;
- std::string _user;
- };
+protected:
+ SaslAuthenticationSession* _saslAuthSession;
+ std::string _user;
+};
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_test_crutch.cpp b/src/mongo/db/auth/sasl_test_crutch.cpp
index da6eae4d332..d64877492aa 100644
--- a/src/mongo/db/auth/sasl_test_crutch.cpp
+++ b/src/mongo/db/auth/sasl_test_crutch.cpp
@@ -30,5 +30,5 @@
#include "mongo/db/commands.h"
namespace mongo {
- int Command::testCommandsEnabled = 0;
+int Command::testCommandsEnabled = 0;
}
diff --git a/src/mongo/db/auth/security_key.cpp b/src/mongo/db/auth/security_key.cpp
index 8edbc6ef140..d7d7c96410e 100644
--- a/src/mongo/db/auth/security_key.cpp
+++ b/src/mongo/db/auth/security_key.cpp
@@ -51,97 +51,97 @@
namespace mongo {
- using std::endl;
- using std::string;
+using std::endl;
+using std::string;
- bool setUpSecurityKey(const string& filename) {
- struct stat stats;
+bool setUpSecurityKey(const string& filename) {
+ struct stat stats;
- // check obvious file errors
- if (stat(filename.c_str(), &stats) == -1) {
- log() << "error getting file " << filename << ": " << strerror(errno) << endl;
- return false;
- }
+ // check obvious file errors
+ if (stat(filename.c_str(), &stats) == -1) {
+ log() << "error getting file " << filename << ": " << strerror(errno) << endl;
+ return false;
+ }
#if !defined(_WIN32)
- // check permissions: must be X00, where X is >= 4
- if ((stats.st_mode & (S_IRWXG|S_IRWXO)) != 0) {
- log() << "permissions on " << filename << " are too open" << endl;
- return false;
- }
+ // check permissions: must be X00, where X is >= 4
+ if ((stats.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ log() << "permissions on " << filename << " are too open" << endl;
+ return false;
+ }
#endif
- FILE* file = fopen( filename.c_str(), "rb" );
- if (!file) {
- log() << "error opening file: " << filename << ": " << strerror(errno) << endl;
+ FILE* file = fopen(filename.c_str(), "rb");
+ if (!file) {
+ log() << "error opening file: " << filename << ": " << strerror(errno) << endl;
+ return false;
+ }
+
+ string str = "";
+
+ // strip key file
+ const unsigned long long fileLength = stats.st_size;
+ unsigned long long read = 0;
+ while (read < fileLength) {
+ char buf;
+ int readLength = fread(&buf, 1, 1, file);
+ if (readLength < 1) {
+ log() << "error reading file " << filename << endl;
+ fclose(file);
return false;
}
+ read++;
- string str = "";
-
- // strip key file
- const unsigned long long fileLength = stats.st_size;
- unsigned long long read = 0;
- while (read < fileLength) {
- char buf;
- int readLength = fread(&buf, 1, 1, file);
- if (readLength < 1) {
- log() << "error reading file " << filename << endl;
- fclose( file );
- return false;
- }
- read++;
-
- // check for whitespace
- if ((buf >= '\x09' && buf <= '\x0D') || buf == ' ') {
- continue;
- }
-
- // check valid base64
- if ((buf < 'A' || buf > 'Z') && (buf < 'a' || buf > 'z') && (buf < '0' || buf > '9') && buf != '+' && buf != '/') {
- log() << "invalid char in key file " << filename << ": " << buf << endl;
- fclose( file );
- return false;
- }
-
- str += buf;
+ // check for whitespace
+ if ((buf >= '\x09' && buf <= '\x0D') || buf == ' ') {
+ continue;
}
- fclose( file );
-
- const unsigned long long keyLength = str.size();
- if (keyLength < 6 || keyLength > 1024) {
- log() << " security key in " << filename << " has length " << keyLength
- << ", must be between 6 and 1024 chars" << endl;
+ // check valid base64
+ if ((buf < 'A' || buf > 'Z') && (buf < 'a' || buf > 'z') && (buf < '0' || buf > '9') &&
+ buf != '+' && buf != '/') {
+ log() << "invalid char in key file " << filename << ": " << buf << endl;
+ fclose(file);
return false;
}
- // Generate MONGODB-CR and SCRAM credentials for the internal user based on the keyfile.
- User::CredentialData credentials;
- credentials.password = mongo::createPasswordDigest(
- internalSecurity.user->getName().getUser().toString(), str);
-
- BSONObj creds = scram::generateCredentials(credentials.password,
- saslGlobalParams.scramIterationCount);
- credentials.scram.iterationCount = creds[scram::iterationCountFieldName].Int();
- credentials.scram.salt = creds[scram::saltFieldName].String();
- credentials.scram.storedKey = creds[scram::storedKeyFieldName].String();
- credentials.scram.serverKey = creds[scram::serverKeyFieldName].String();
-
- internalSecurity.user->setCredentials(credentials);
-
- int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
- if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile ||
- clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile) {
- setInternalUserAuthParams(
- BSON(saslCommandMechanismFieldName << "SCRAM-SHA-1" <<
- saslCommandUserDBFieldName <<
- internalSecurity.user->getName().getDB() <<
- saslCommandUserFieldName << internalSecurity.user->getName().getUser() <<
- saslCommandPasswordFieldName << credentials.password <<
- saslCommandDigestPasswordFieldName << false));
- }
- return true;
+ str += buf;
+ }
+
+ fclose(file);
+
+ const unsigned long long keyLength = str.size();
+ if (keyLength < 6 || keyLength > 1024) {
+ log() << " security key in " << filename << " has length " << keyLength
+ << ", must be between 6 and 1024 chars" << endl;
+ return false;
+ }
+
+ // Generate MONGODB-CR and SCRAM credentials for the internal user based on the keyfile.
+ User::CredentialData credentials;
+ credentials.password =
+ mongo::createPasswordDigest(internalSecurity.user->getName().getUser().toString(), str);
+
+ BSONObj creds =
+ scram::generateCredentials(credentials.password, saslGlobalParams.scramIterationCount);
+ credentials.scram.iterationCount = creds[scram::iterationCountFieldName].Int();
+ credentials.scram.salt = creds[scram::saltFieldName].String();
+ credentials.scram.storedKey = creds[scram::storedKeyFieldName].String();
+ credentials.scram.serverKey = creds[scram::serverKeyFieldName].String();
+
+ internalSecurity.user->setCredentials(credentials);
+
+ int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
+ if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile ||
+ clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile) {
+ setInternalUserAuthParams(
+ BSON(saslCommandMechanismFieldName
+ << "SCRAM-SHA-1" << saslCommandUserDBFieldName
+ << internalSecurity.user->getName().getDB() << saslCommandUserFieldName
+ << internalSecurity.user->getName().getUser() << saslCommandPasswordFieldName
+ << credentials.password << saslCommandDigestPasswordFieldName << false));
}
+ return true;
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/security_key.h b/src/mongo/db/auth/security_key.h
index a250b34a029..898efdf9e8a 100644
--- a/src/mongo/db/auth/security_key.h
+++ b/src/mongo/db/auth/security_key.h
@@ -31,13 +31,13 @@
#include <string>
namespace mongo {
- /**
- * This method checks the validity of filename as a security key, hashes its
- * contents, and stores it in the internalSecurity variable. Prints an
- * error message to the logs if there's an error.
- * @param filename the file containing the key
- * @return if the key was successfully stored
- */
- bool setUpSecurityKey(const std::string& filename);
+/**
+ * This method checks the validity of filename as a security key, hashes its
+ * contents, and stores it in the internalSecurity variable. Prints an
+ * error message to the logs if there's an error.
+ * @param filename the file containing the key
+ * @return if the key was successfully stored
+ */
+bool setUpSecurityKey(const std::string& filename);
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp
index a2afa97a4e3..dd4dd3196a9 100644
--- a/src/mongo/db/auth/user.cpp
+++ b/src/mongo/db/auth/user.cpp
@@ -41,123 +41,119 @@
namespace mongo {
- User::User(const UserName& name) :
- _name(name),
- _refCount(0),
- _isValid(1) {}
+User::User(const UserName& name) : _name(name), _refCount(0), _isValid(1) {}
- User::~User() {
- dassert(_refCount == 0);
- }
-
- const UserName& User::getName() const {
- return _name;
- }
-
- RoleNameIterator User::getRoles() const {
- return makeRoleNameIteratorForContainer(_roles);
- }
-
- RoleNameIterator User::getIndirectRoles() const {
- return makeRoleNameIteratorForContainer(_indirectRoles);
- }
-
- bool User::hasRole(const RoleName& roleName) const {
- return _roles.count(roleName);
- }
-
- const User::CredentialData& User::getCredentials() const {
- return _credentials;
- }
-
- bool User::isValid() const {
- return _isValid.loadRelaxed() == 1;
- }
-
- uint32_t User::getRefCount() const {
- return _refCount;
- }
-
- const ActionSet User::getActionsForResource(const ResourcePattern& resource) const {
- unordered_map<ResourcePattern, Privilege>::const_iterator it = _privileges.find(resource);
- if (it == _privileges.end()) {
- return ActionSet();
- }
- return it->second.getActions();
- }
-
- User* User::clone() const {
- std::unique_ptr<User> result(new User(_name));
- result->_privileges = _privileges;
- result->_roles = _roles;
- result->_credentials = _credentials;
- return result.release();
- }
+User::~User() {
+ dassert(_refCount == 0);
+}
- void User::setCredentials(const CredentialData& credentials) {
- _credentials = credentials;
- }
-
- void User::setRoles(RoleNameIterator roles) {
- _roles.clear();
- while (roles.more()) {
- _roles.insert(roles.next());
- }
- }
-
- void User::setIndirectRoles(RoleNameIterator indirectRoles) {
- _indirectRoles.clear();
- while (indirectRoles.more()) {
- _indirectRoles.push_back(indirectRoles.next());
- }
- }
+const UserName& User::getName() const {
+ return _name;
+}
- void User::setPrivileges(const PrivilegeVector& privileges) {
- _privileges.clear();
- for (size_t i = 0; i < privileges.size(); ++i) {
- const Privilege& privilege = privileges[i];
- _privileges[privilege.getResourcePattern()] = privilege;
- }
- }
-
- void User::addRole(const RoleName& roleName) {
- _roles.insert(roleName);
- }
-
- void User::addRoles(const std::vector<RoleName>& roles) {
- for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
- addRole(*it);
- }
- }
+RoleNameIterator User::getRoles() const {
+ return makeRoleNameIteratorForContainer(_roles);
+}
- void User::addPrivilege(const Privilege& privilegeToAdd) {
- ResourcePrivilegeMap::iterator it = _privileges.find(privilegeToAdd.getResourcePattern());
- if (it == _privileges.end()) {
- // No privilege exists yet for this resource
- _privileges.insert(std::make_pair(privilegeToAdd.getResourcePattern(), privilegeToAdd));
- } else {
- dassert(it->first == privilegeToAdd.getResourcePattern());
- it->second.addActions(privilegeToAdd.getActions());
- }
- }
-
- void User::addPrivileges(const PrivilegeVector& privileges) {
- for (PrivilegeVector::const_iterator it = privileges.begin();
- it != privileges.end(); ++it) {
- addPrivilege(*it);
- }
- }
-
- void User::invalidate() {
- _isValid.store(0);
- }
-
- void User::incrementRefCount() {
- ++_refCount;
- }
-
- void User::decrementRefCount() {
- dassert(_refCount > 0);
- --_refCount;
- }
-} // namespace mongo
+RoleNameIterator User::getIndirectRoles() const {
+ return makeRoleNameIteratorForContainer(_indirectRoles);
+}
+
+bool User::hasRole(const RoleName& roleName) const {
+ return _roles.count(roleName);
+}
+
+const User::CredentialData& User::getCredentials() const {
+ return _credentials;
+}
+
+bool User::isValid() const {
+ return _isValid.loadRelaxed() == 1;
+}
+
+uint32_t User::getRefCount() const {
+ return _refCount;
+}
+
+const ActionSet User::getActionsForResource(const ResourcePattern& resource) const {
+ unordered_map<ResourcePattern, Privilege>::const_iterator it = _privileges.find(resource);
+ if (it == _privileges.end()) {
+ return ActionSet();
+ }
+ return it->second.getActions();
+}
+
+User* User::clone() const {
+ std::unique_ptr<User> result(new User(_name));
+ result->_privileges = _privileges;
+ result->_roles = _roles;
+ result->_credentials = _credentials;
+ return result.release();
+}
+
+void User::setCredentials(const CredentialData& credentials) {
+ _credentials = credentials;
+}
+
+void User::setRoles(RoleNameIterator roles) {
+ _roles.clear();
+ while (roles.more()) {
+ _roles.insert(roles.next());
+ }
+}
+
+void User::setIndirectRoles(RoleNameIterator indirectRoles) {
+ _indirectRoles.clear();
+ while (indirectRoles.more()) {
+ _indirectRoles.push_back(indirectRoles.next());
+ }
+}
+
+void User::setPrivileges(const PrivilegeVector& privileges) {
+ _privileges.clear();
+ for (size_t i = 0; i < privileges.size(); ++i) {
+ const Privilege& privilege = privileges[i];
+ _privileges[privilege.getResourcePattern()] = privilege;
+ }
+}
+
+void User::addRole(const RoleName& roleName) {
+ _roles.insert(roleName);
+}
+
+void User::addRoles(const std::vector<RoleName>& roles) {
+ for (std::vector<RoleName>::const_iterator it = roles.begin(); it != roles.end(); ++it) {
+ addRole(*it);
+ }
+}
+
+void User::addPrivilege(const Privilege& privilegeToAdd) {
+ ResourcePrivilegeMap::iterator it = _privileges.find(privilegeToAdd.getResourcePattern());
+ if (it == _privileges.end()) {
+ // No privilege exists yet for this resource
+ _privileges.insert(std::make_pair(privilegeToAdd.getResourcePattern(), privilegeToAdd));
+ } else {
+ dassert(it->first == privilegeToAdd.getResourcePattern());
+ it->second.addActions(privilegeToAdd.getActions());
+ }
+}
+
+void User::addPrivileges(const PrivilegeVector& privileges) {
+ for (PrivilegeVector::const_iterator it = privileges.begin(); it != privileges.end(); ++it) {
+ addPrivilege(*it);
+ }
+}
+
+void User::invalidate() {
+ _isValid.store(0);
+}
+
+void User::incrementRefCount() {
+ ++_refCount;
+}
+
+void User::decrementRefCount() {
+ dassert(_refCount > 0);
+ --_refCount;
+}
+} // namespace mongo
diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h
index d920abdda9d..d4aea7e442b 100644
--- a/src/mongo/db/auth/user.h
+++ b/src/mongo/db/auth/user.h
@@ -41,193 +41,188 @@
namespace mongo {
+/**
+ * Represents a MongoDB user. Stores information about the user necessary for access control
+ * checks and authentications, such as what privileges this user has, as well as what roles
+ * the user belongs to.
+ *
+ * Every User object is owned by an AuthorizationManager. The AuthorizationManager is the only
+ * one that should construct, modify, or delete a User object. All other consumers of User must
+ * use only the const methods. The AuthorizationManager is responsible for maintaining the
+ * reference count on all User objects it gives out and must not mutate any User objects with
+ * a non-zero reference count (except to call invalidate()). Any consumer of a User object
+ * should check isInvalidated() before using it, and if it has been invalidated, it should
+ * return the object to the AuthorizationManager and fetch a new User object instance for this
+ * user from the AuthorizationManager.
+ */
+class User {
+ MONGO_DISALLOW_COPYING(User);
+
+public:
+ struct SCRAMCredentials {
+ SCRAMCredentials() : iterationCount(0), salt(""), serverKey(""), storedKey("") {}
+
+ int iterationCount;
+ std::string salt;
+ std::string serverKey;
+ std::string storedKey;
+ };
+ struct CredentialData {
+ CredentialData() : password(""), scram(), isExternal(false) {}
+
+ std::string password;
+ SCRAMCredentials scram;
+ bool isExternal;
+ };
+
+ typedef unordered_map<ResourcePattern, Privilege> ResourcePrivilegeMap;
+
+ explicit User(const UserName& name);
+ ~User();
+
+ /**
+ * Returns the user name for this user.
+ */
+ const UserName& getName() const;
+
+ /**
+ * Returns an iterator over the names of the user's direct roles
+ */
+ RoleNameIterator getRoles() const;
+
+ /**
+ * Returns an iterator over the names of the user's indirect roles
+ */
+ RoleNameIterator getIndirectRoles() const;
+
+ /**
+ * Returns true if this user is a member of the given role.
+ */
+ bool hasRole(const RoleName& roleName) const;
+
+ /**
+ * Returns a reference to the information about the user's privileges.
+ */
+ const ResourcePrivilegeMap& getPrivileges() const {
+ return _privileges;
+ }
+
+ /**
+ * Returns the CredentialData for this user.
+ */
+ const CredentialData& getCredentials() const;
+
+ /**
+ * Gets the set of actions this user is allowed to perform on the given resource.
+ */
+ const ActionSet getActionsForResource(const ResourcePattern& resource) const;
+
+ /**
+ * Returns true if this copy of information about this user is still valid. If this returns
+ * false, this object should no longer be used and should be returned to the
+ * AuthorizationManager and a new User object for this user should be requested.
+ */
+ bool isValid() const;
+
+ /**
+ * This returns the reference count for this User. The AuthorizationManager should be the
+ * only caller of this.
+ */
+ uint32_t getRefCount() const;
+
+ /**
+ * Clones this user into a new, valid User object with refcount of 0.
+ */
+ User* clone() const;
+
+ // Mutators below. Mutation functions should *only* be called by the AuthorizationManager
+
/**
- * Represents a MongoDB user. Stores information about the user necessary for access control
- * checks and authentications, such as what privileges this user has, as well as what roles
- * the user belongs to.
+ * Sets this user's authentication credentials.
+ */
+ void setCredentials(const CredentialData& credentials);
+
+ /**
+ * Replaces any existing user role membership information with the roles from "roles".
+ */
+ void setRoles(RoleNameIterator roles);
+
+ /**
+ * Replaces any existing indirect user role membership information with the roles from
+ * "indirectRoles".
+ */
+ void setIndirectRoles(RoleNameIterator indirectRoles);
+
+ /**
+ * Replaces any existing user privilege information with "privileges".
+ */
+ void setPrivileges(const PrivilegeVector& privileges);
+
+ /**
+ * Adds the given role name to the list of roles of which this user is a member.
+ */
+ void addRole(const RoleName& role);
+
+ /**
+ * Adds the given role names to the list of roles that this user belongs to.
+ */
+ void addRoles(const std::vector<RoleName>& roles);
+
+ /**
+ * Adds the given privilege to the list of privileges this user is authorized for.
+ */
+ void addPrivilege(const Privilege& privilege);
+
+ /**
+ * Adds the given privileges to the list of privileges this user is authorized for.
+ */
+ void addPrivileges(const PrivilegeVector& privileges);
+
+ /**
+ * Marks this instance of the User object as invalid, most likely because information about
+ * the user has been updated and needs to be reloaded from the AuthorizationManager.
*
- * Every User object is owned by an AuthorizationManager. The AuthorizationManager is the only
- * one that should construct, modify, or delete a User object. All other consumers of User must
- * use only the const methods. The AuthorizationManager is responsible for maintaining the
- * reference count on all User objects it gives out and must not mutate any User objects with
- * a non-zero reference count (except to call invalidate()). Any consumer of a User object
- * should check isInvalidated() before using it, and if it has been invalidated, it should
- * return the object to the AuthorizationManager and fetch a new User object instance for this
- * user from the AuthorizationManager.
- */
- class User {
- MONGO_DISALLOW_COPYING(User);
- public:
- struct SCRAMCredentials {
- SCRAMCredentials() :
- iterationCount(0),
- salt(""),
- serverKey(""),
- storedKey("") {}
-
- int iterationCount;
- std::string salt;
- std::string serverKey;
- std::string storedKey;
- };
- struct CredentialData {
- CredentialData() :
- password(""),
- scram(),
- isExternal(false) {}
-
- std::string password;
- SCRAMCredentials scram;
- bool isExternal;
- };
-
- typedef unordered_map<ResourcePattern, Privilege> ResourcePrivilegeMap;
-
- explicit User(const UserName& name);
- ~User();
-
- /**
- * Returns the user name for this user.
- */
- const UserName& getName() const;
-
- /**
- * Returns an iterator over the names of the user's direct roles
- */
- RoleNameIterator getRoles() const;
-
- /**
- * Returns an iterator over the names of the user's indirect roles
- */
- RoleNameIterator getIndirectRoles() const;
-
- /**
- * Returns true if this user is a member of the given role.
- */
- bool hasRole(const RoleName& roleName) const;
-
- /**
- * Returns a reference to the information about the user's privileges.
- */
- const ResourcePrivilegeMap& getPrivileges() const { return _privileges; }
-
- /**
- * Returns the CredentialData for this user.
- */
- const CredentialData& getCredentials() const;
-
- /**
- * Gets the set of actions this user is allowed to perform on the given resource.
- */
- const ActionSet getActionsForResource(const ResourcePattern& resource) const;
-
- /**
- * Returns true if this copy of information about this user is still valid. If this returns
- * false, this object should no longer be used and should be returned to the
- * AuthorizationManager and a new User object for this user should be requested.
- */
- bool isValid() const;
-
- /**
- * This returns the reference count for this User. The AuthorizationManager should be the
- * only caller of this.
- */
- uint32_t getRefCount() const;
-
- /**
- * Clones this user into a new, valid User object with refcount of 0.
- */
- User* clone() const;
-
- // Mutators below. Mutation functions should *only* be called by the AuthorizationManager
-
- /**
- * Sets this user's authentication credentials.
- */
- void setCredentials(const CredentialData& credentials);
-
- /**
- * Replaces any existing user role membership information with the roles from "roles".
- */
- void setRoles(RoleNameIterator roles);
-
- /**
- * Replaces any existing indirect user role membership information with the roles from
- * "indirectRoles".
- */
- void setIndirectRoles(RoleNameIterator indirectRoles);
-
- /**
- * Replaces any existing user privilege information with "privileges".
- */
- void setPrivileges(const PrivilegeVector& privileges);
-
- /**
- * Adds the given role name to the list of roles of which this user is a member.
- */
- void addRole(const RoleName& role);
-
- /**
- * Adds the given role names to the list of roles that this user belongs to.
- */
- void addRoles(const std::vector<RoleName>& roles);
-
- /**
- * Adds the given privilege to the list of privileges this user is authorized for.
- */
- void addPrivilege(const Privilege& privilege);
-
- /**
- * Adds the given privileges to the list of privileges this user is authorized for.
- */
- void addPrivileges(const PrivilegeVector& privileges);
-
- /**
- * Marks this instance of the User object as invalid, most likely because information about
- * the user has been updated and needs to be reloaded from the AuthorizationManager.
- *
- * This method should *only* be called by the AuthorizationManager.
- */
- void invalidate();
-
- /**
- * Increments the reference count for this User object, which records how many threads have
- * a reference to it.
- *
- * This method should *only* be called by the AuthorizationManager.
- */
- void incrementRefCount();
-
- /**
- * Decrements the reference count for this User object, which records how many threads have
- * a reference to it. Once the reference count goes to zero, the AuthorizationManager is
- * allowed to destroy this instance.
- *
- * This method should *only* be called by the AuthorizationManager.
- */
- void decrementRefCount();
-
- private:
-
- UserName _name;
-
- // Maps resource name to privilege on that resource
- ResourcePrivilegeMap _privileges;
-
- // Roles the user has privileges from
- unordered_set<RoleName> _roles;
-
- // Roles that the user indirectly has privileges from, due to role inheritance.
- std::vector<RoleName> _indirectRoles;
-
- // Credential information.
- CredentialData _credentials;
-
- // _refCount and _isInvalidated are modified exclusively by the AuthorizationManager
- // _isInvalidated can be read by any consumer of User, but _refCount can only be
- // meaningfully read by the AuthorizationManager, as _refCount is guarded by the AM's _lock
- uint32_t _refCount;
- AtomicUInt32 _isValid; // Using as a boolean
- };
+ * This method should *only* be called by the AuthorizationManager.
+ */
+ void invalidate();
+
+ /**
+ * Increments the reference count for this User object, which records how many threads have
+ * a reference to it.
+ *
+ * This method should *only* be called by the AuthorizationManager.
+ */
+ void incrementRefCount();
+
+ /**
+ * Decrements the reference count for this User object, which records how many threads have
+ * a reference to it. Once the reference count goes to zero, the AuthorizationManager is
+ * allowed to destroy this instance.
+ *
+ * This method should *only* be called by the AuthorizationManager.
+ */
+ void decrementRefCount();
+
+private:
+ UserName _name;
+
+ // Maps resource name to privilege on that resource
+ ResourcePrivilegeMap _privileges;
+
+ // Roles the user has privileges from
+ unordered_set<RoleName> _roles;
+
+ // Roles that the user indirectly has privileges from, due to role inheritance.
+ std::vector<RoleName> _indirectRoles;
+
+ // Credential information.
+ CredentialData _credentials;
+
+ // _refCount and _isInvalidated are modified exclusively by the AuthorizationManager
+ // _isInvalidated can be read by any consumer of User, but _refCount can only be
+ // meaningfully read by the AuthorizationManager, as _refCount is guarded by the AM's _lock
+ uint32_t _refCount;
+ AtomicUInt32 _isValid; // Using as a boolean
+};
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_cache_invalidator_job.cpp b/src/mongo/db/auth/user_cache_invalidator_job.cpp
index 0faaaa84bc6..dd44b200f60 100644
--- a/src/mongo/db/auth/user_cache_invalidator_job.cpp
+++ b/src/mongo/db/auth/user_cache_invalidator_job.cpp
@@ -51,132 +51,127 @@
namespace mongo {
namespace {
- // How often to check with the config servers whether authorization information has changed.
- int userCacheInvalidationIntervalSecs = 30; // 30 second default
- stdx::mutex invalidationIntervalMutex;
- stdx::condition_variable invalidationIntervalChangedCondition;
- Date_t lastInvalidationTime;
-
- class ExportedInvalidationIntervalParameter : public ExportedServerParameter<int> {
- public:
- ExportedInvalidationIntervalParameter() :
- ExportedServerParameter<int>(ServerParameterSet::getGlobal(),
- "userCacheInvalidationIntervalSecs",
- &userCacheInvalidationIntervalSecs,
- true,
- true) {}
-
- virtual Status validate( const int& potentialNewValue )
- {
- if (potentialNewValue < 1 || potentialNewValue > 86400) {
- return Status(ErrorCodes::BadValue,
- "userCacheInvalidationIntervalSecs must be between 1 "
- "and 86400 (24 hours)");
- }
- return Status::OK();
+// How often to check with the config servers whether authorization information has changed.
+int userCacheInvalidationIntervalSecs = 30; // 30 second default
+stdx::mutex invalidationIntervalMutex;
+stdx::condition_variable invalidationIntervalChangedCondition;
+Date_t lastInvalidationTime;
+
+class ExportedInvalidationIntervalParameter : public ExportedServerParameter<int> {
+public:
+ ExportedInvalidationIntervalParameter()
+ : ExportedServerParameter<int>(ServerParameterSet::getGlobal(),
+ "userCacheInvalidationIntervalSecs",
+ &userCacheInvalidationIntervalSecs,
+ true,
+ true) {}
+
+ virtual Status validate(const int& potentialNewValue) {
+ if (potentialNewValue < 1 || potentialNewValue > 86400) {
+ return Status(ErrorCodes::BadValue,
+ "userCacheInvalidationIntervalSecs must be between 1 "
+ "and 86400 (24 hours)");
}
+ return Status::OK();
+ }
- // Without this the compiler complains that defining set(const int&)
- // hides set(const BSONElement&)
- using ExportedServerParameter<int>::set;
+ // Without this the compiler complains that defining set(const int&)
+ // hides set(const BSONElement&)
+ using ExportedServerParameter<int>::set;
- virtual Status set( const int& newValue ) {
- stdx::unique_lock<stdx::mutex> lock(invalidationIntervalMutex);
- Status status = ExportedServerParameter<int>::set(newValue);
- invalidationIntervalChangedCondition.notify_all();
- return status;
- }
+ virtual Status set(const int& newValue) {
+ stdx::unique_lock<stdx::mutex> lock(invalidationIntervalMutex);
+ Status status = ExportedServerParameter<int>::set(newValue);
+ invalidationIntervalChangedCondition.notify_all();
+ return status;
+ }
- } exportedIntervalParam;
-
- StatusWith<OID> getCurrentCacheGeneration() {
- try {
- BSONObjBuilder result;
- const bool ok = grid.catalogManager()->runUserManagementReadCommand(
- "admin",
- BSON("_getUserCacheGeneration" << 1),
- &result);
- if (!ok) {
- return Command::getStatusFromCommandResult(result.obj());
- }
- return result.obj()["cacheGeneration"].OID();
- } catch (const DBException& e) {
- return StatusWith<OID>(e.toStatus());
- } catch (const std::exception& e) {
- return StatusWith<OID>(ErrorCodes::UnknownError, e.what());
+} exportedIntervalParam;
+
+StatusWith<OID> getCurrentCacheGeneration() {
+ try {
+ BSONObjBuilder result;
+ const bool ok = grid.catalogManager()->runUserManagementReadCommand(
+ "admin", BSON("_getUserCacheGeneration" << 1), &result);
+ if (!ok) {
+ return Command::getStatusFromCommandResult(result.obj());
}
+ return result.obj()["cacheGeneration"].OID();
+ } catch (const DBException& e) {
+ return StatusWith<OID>(e.toStatus());
+ } catch (const std::exception& e) {
+ return StatusWith<OID>(ErrorCodes::UnknownError, e.what());
}
+}
-} // namespace
+} // namespace
- UserCacheInvalidator::UserCacheInvalidator(AuthorizationManager* authzManager) :
- _authzManager(authzManager) {
+UserCacheInvalidator::UserCacheInvalidator(AuthorizationManager* authzManager)
+ : _authzManager(authzManager) {
+ StatusWith<OID> currentGeneration = getCurrentCacheGeneration();
+ if (currentGeneration.isOK()) {
+ _previousCacheGeneration = currentGeneration.getValue();
+ return;
+ }
- StatusWith<OID> currentGeneration = getCurrentCacheGeneration();
- if (currentGeneration.isOK()) {
- _previousCacheGeneration = currentGeneration.getValue();
- return;
+ if (currentGeneration.getStatus().code() == ErrorCodes::CommandNotFound) {
+ warning() << "_getUserCacheGeneration command not found while fetching initial user "
+ "cache generation from the config server(s). This most likely means you are "
+ "running an outdated version of mongod on the config servers";
+ } else {
+ warning() << "An error occurred while fetching initial user cache generation from "
+ "config servers: " << currentGeneration.getStatus();
+ }
+ _previousCacheGeneration = OID();
+}
+
+void UserCacheInvalidator::run() {
+ Client::initThread("UserCacheInvalidator");
+ lastInvalidationTime = Date_t::now();
+
+ while (true) {
+ stdx::unique_lock<stdx::mutex> lock(invalidationIntervalMutex);
+ Date_t sleepUntil = lastInvalidationTime + Seconds(userCacheInvalidationIntervalSecs);
+ Date_t now = Date_t::now();
+ while (now < sleepUntil) {
+ invalidationIntervalChangedCondition.wait_for(lock, sleepUntil - now);
+ sleepUntil = lastInvalidationTime + Seconds(userCacheInvalidationIntervalSecs);
+ now = Date_t::now();
}
+ lastInvalidationTime = now;
+ lock.unlock();
- if (currentGeneration.getStatus().code() == ErrorCodes::CommandNotFound) {
- warning() << "_getUserCacheGeneration command not found while fetching initial user "
- "cache generation from the config server(s). This most likely means you are "
- "running an outdated version of mongod on the config servers";
- } else {
- warning() << "An error occurred while fetching initial user cache generation from "
- "config servers: " << currentGeneration.getStatus();
+ if (inShutdown()) {
+ break;
}
- _previousCacheGeneration = OID();
- }
-
- void UserCacheInvalidator::run() {
- Client::initThread("UserCacheInvalidator");
- lastInvalidationTime = Date_t::now();
-
- while (true) {
- stdx::unique_lock<stdx::mutex> lock(invalidationIntervalMutex);
- Date_t sleepUntil = lastInvalidationTime + Seconds(userCacheInvalidationIntervalSecs);
- Date_t now = Date_t::now();
- while (now < sleepUntil) {
- invalidationIntervalChangedCondition.wait_for(lock, sleepUntil - now);
- sleepUntil = lastInvalidationTime + Seconds(userCacheInvalidationIntervalSecs);
- now = Date_t::now();
- }
- lastInvalidationTime = now;
- lock.unlock();
-
- if (inShutdown()) {
- break;
- }
- StatusWith<OID> currentGeneration = getCurrentCacheGeneration();
- if (!currentGeneration.isOK()) {
- if (currentGeneration.getStatus().code() == ErrorCodes::CommandNotFound) {
- warning() << "_getUserCacheGeneration command not found on config server(s), "
- "this most likely means you are running an outdated version of mongod "
- "on the config servers" << std::endl;
- } else {
- warning() << "An error occurred while fetching current user cache generation "
- "to check if user cache needs invalidation: " <<
- currentGeneration.getStatus() << std::endl;
- }
- // When in doubt, invalidate the cache
- _authzManager->invalidateUserCache();
- continue;
+ StatusWith<OID> currentGeneration = getCurrentCacheGeneration();
+ if (!currentGeneration.isOK()) {
+ if (currentGeneration.getStatus().code() == ErrorCodes::CommandNotFound) {
+ warning() << "_getUserCacheGeneration command not found on config server(s), "
+ "this most likely means you are running an outdated version of mongod "
+ "on the config servers" << std::endl;
+ } else {
+ warning() << "An error occurred while fetching current user cache generation "
+ "to check if user cache needs invalidation: "
+ << currentGeneration.getStatus() << std::endl;
}
+ // When in doubt, invalidate the cache
+ _authzManager->invalidateUserCache();
+ continue;
+ }
- if (currentGeneration.getValue() != _previousCacheGeneration) {
- log() << "User cache generation changed from " << _previousCacheGeneration <<
- " to " << currentGeneration.getValue() << "; invalidating user cache" <<
- std::endl;
- _authzManager->invalidateUserCache();
- _previousCacheGeneration = currentGeneration.getValue();
- }
+ if (currentGeneration.getValue() != _previousCacheGeneration) {
+ log() << "User cache generation changed from " << _previousCacheGeneration << " to "
+ << currentGeneration.getValue() << "; invalidating user cache" << std::endl;
+ _authzManager->invalidateUserCache();
+ _previousCacheGeneration = currentGeneration.getValue();
}
}
+}
- std::string UserCacheInvalidator::name() const {
- return "UserCacheInvalidatorThread";
- }
+std::string UserCacheInvalidator::name() const {
+ return "UserCacheInvalidatorThread";
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_cache_invalidator_job.h b/src/mongo/db/auth/user_cache_invalidator_job.h
index 8742a28c79c..3eb173b0a56 100644
--- a/src/mongo/db/auth/user_cache_invalidator_job.h
+++ b/src/mongo/db/auth/user_cache_invalidator_job.h
@@ -32,25 +32,25 @@
namespace mongo {
- class AuthorizationManager;
-
- /**
- * Background job that runs only in mongos and periodically checks in with the config servers
- * to determine whether any authorization information has changed, and if so causes the
- * AuthorizationManager to throw out its in-memory cache of User objects (which contains the
- * users' credentials, roles, privileges, etc).
- */
- class UserCacheInvalidator : public BackgroundJob {
- public:
- explicit UserCacheInvalidator(AuthorizationManager* authzManager);
-
- protected:
- virtual std::string name() const;
- virtual void run();
-
- private:
- AuthorizationManager* _authzManager;
- OID _previousCacheGeneration;
- };
-
-} // namespace mongo
+class AuthorizationManager;
+
+/**
+ * Background job that runs only in mongos and periodically checks in with the config servers
+ * to determine whether any authorization information has changed, and if so causes the
+ * AuthorizationManager to throw out its in-memory cache of User objects (which contains the
+ * users' credentials, roles, privileges, etc).
+ */
+class UserCacheInvalidator : public BackgroundJob {
+public:
+ explicit UserCacheInvalidator(AuthorizationManager* authzManager);
+
+protected:
+ virtual std::string name() const;
+ virtual void run();
+
+private:
+ AuthorizationManager* _authzManager;
+ OID _previousCacheGeneration;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_document_parser.cpp b/src/mongo/db/auth/user_document_parser.cpp
index d4a095c661d..058829622f5 100644
--- a/src/mongo/db/auth/user_document_parser.cpp
+++ b/src/mongo/db/auth/user_document_parser.cpp
@@ -44,142 +44,135 @@
namespace mongo {
namespace {
- const std::string ADMIN_DBNAME = "admin";
-
- const std::string ROLES_FIELD_NAME = "roles";
- const std::string PRIVILEGES_FIELD_NAME = "inheritedPrivileges";
- const std::string INHERITED_ROLES_FIELD_NAME = "inheritedRoles";
- const std::string OTHER_DB_ROLES_FIELD_NAME = "otherDBRoles";
- const std::string READONLY_FIELD_NAME = "readOnly";
- const std::string CREDENTIALS_FIELD_NAME = "credentials";
- const std::string ROLE_NAME_FIELD_NAME = "role";
- const std::string ROLE_DB_FIELD_NAME = "db";
- const std::string MONGODB_CR_CREDENTIAL_FIELD_NAME = "MONGODB-CR";
- const std::string SCRAM_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-1";
- const std::string MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME = "external";
-
- inline Status _badValue(const char* reason, int location) {
- return Status(ErrorCodes::BadValue, reason, location);
+const std::string ADMIN_DBNAME = "admin";
+
+const std::string ROLES_FIELD_NAME = "roles";
+const std::string PRIVILEGES_FIELD_NAME = "inheritedPrivileges";
+const std::string INHERITED_ROLES_FIELD_NAME = "inheritedRoles";
+const std::string OTHER_DB_ROLES_FIELD_NAME = "otherDBRoles";
+const std::string READONLY_FIELD_NAME = "readOnly";
+const std::string CREDENTIALS_FIELD_NAME = "credentials";
+const std::string ROLE_NAME_FIELD_NAME = "role";
+const std::string ROLE_DB_FIELD_NAME = "db";
+const std::string MONGODB_CR_CREDENTIAL_FIELD_NAME = "MONGODB-CR";
+const std::string SCRAM_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-1";
+const std::string MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME = "external";
+
+inline Status _badValue(const char* reason, int location) {
+ return Status(ErrorCodes::BadValue, reason, location);
+}
+
+inline Status _badValue(const std::string& reason, int location) {
+ return Status(ErrorCodes::BadValue, reason, location);
+}
+
+Status _checkV1RolesArray(const BSONElement& rolesElement) {
+ if (rolesElement.type() != Array) {
+ return _badValue("Role fields must be an array when present in system.users entries", 0);
}
-
- inline Status _badValue(const std::string& reason, int location) {
- return Status(ErrorCodes::BadValue, reason, location);
- }
-
- Status _checkV1RolesArray(const BSONElement& rolesElement) {
- if (rolesElement.type() != Array) {
- return _badValue("Role fields must be an array when present in system.users entries",
- 0);
- }
- for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
- BSONElement element = *iter;
- if (element.type() != String || element.valueStringData().empty()) {
- return _badValue("Roles must be non-empty strings.", 0);
- }
+ for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
+ BSONElement element = *iter;
+ if (element.type() != String || element.valueStringData().empty()) {
+ return _badValue("Roles must be non-empty strings.", 0);
}
- return Status::OK();
}
+ return Status::OK();
+}
} // namespace
- std::string V1UserDocumentParser::extractUserNameFromUserDocument(
- const BSONObj& doc) const {
- return doc[AuthorizationManager::V1_USER_NAME_FIELD_NAME].str();
- }
-
- Status V1UserDocumentParser::initializeUserCredentialsFromUserDocument(
- User* user, const BSONObj& privDoc) const {
- User::CredentialData credentials;
- if (privDoc.hasField(AuthorizationManager::PASSWORD_FIELD_NAME)) {
- credentials.password = privDoc[AuthorizationManager::PASSWORD_FIELD_NAME].String();
- credentials.isExternal = false;
- }
- else if (privDoc.hasField(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME)) {
- std::string userSource = privDoc[AuthorizationManager::V1_USER_SOURCE_FIELD_NAME].String();
- if (userSource != "$external") {
- return Status(ErrorCodes::UnsupportedFormat,
- "Cannot extract credentials from user documents without a password "
- "and with userSource != \"$external\"");
- } else {
- credentials.isExternal = true;
- }
- }
- else {
+std::string V1UserDocumentParser::extractUserNameFromUserDocument(const BSONObj& doc) const {
+ return doc[AuthorizationManager::V1_USER_NAME_FIELD_NAME].str();
+}
+
+Status V1UserDocumentParser::initializeUserCredentialsFromUserDocument(
+ User* user, const BSONObj& privDoc) const {
+ User::CredentialData credentials;
+ if (privDoc.hasField(AuthorizationManager::PASSWORD_FIELD_NAME)) {
+ credentials.password = privDoc[AuthorizationManager::PASSWORD_FIELD_NAME].String();
+ credentials.isExternal = false;
+ } else if (privDoc.hasField(AuthorizationManager::V1_USER_SOURCE_FIELD_NAME)) {
+ std::string userSource = privDoc[AuthorizationManager::V1_USER_SOURCE_FIELD_NAME].String();
+ if (userSource != "$external") {
return Status(ErrorCodes::UnsupportedFormat,
- "Invalid user document: must have one of \"pwd\" and \"userSource\"");
+ "Cannot extract credentials from user documents without a password "
+ "and with userSource != \"$external\"");
+ } else {
+ credentials.isExternal = true;
}
-
- user->setCredentials(credentials);
- return Status::OK();
+ } else {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "Invalid user document: must have one of \"pwd\" and \"userSource\"");
}
- static void _initializeUserRolesFromV0UserDocument(
- User* user, const BSONObj& privDoc, StringData dbname) {
- bool readOnly = privDoc["readOnly"].trueValue();
- if (dbname == "admin") {
- if (readOnly) {
- user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ, "admin"));
- } else {
- user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE, "admin"));
- }
+ user->setCredentials(credentials);
+ return Status::OK();
+}
+
+static void _initializeUserRolesFromV0UserDocument(User* user,
+ const BSONObj& privDoc,
+ StringData dbname) {
+ bool readOnly = privDoc["readOnly"].trueValue();
+ if (dbname == "admin") {
+ if (readOnly) {
+ user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ, "admin"));
} else {
- if (readOnly) {
- user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_READ, dbname));
- } else {
- user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_READ_WRITE, dbname));
- }
+ user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_ADMIN_READ_WRITE, "admin"));
+ }
+ } else {
+ if (readOnly) {
+ user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_READ, dbname));
+ } else {
+ user->addRole(RoleName(RoleGraph::BUILTIN_ROLE_V0_READ_WRITE, dbname));
}
}
+}
- Status _initializeUserRolesFromV1RolesArray(User* user,
- const BSONElement& rolesElement,
- StringData dbname) {
- static const char privilegesTypeMismatchMessage[] =
- "Roles in V1 user documents must be enumerated in an array of strings.";
+Status _initializeUserRolesFromV1RolesArray(User* user,
+ const BSONElement& rolesElement,
+ StringData dbname) {
+ static const char privilegesTypeMismatchMessage[] =
+ "Roles in V1 user documents must be enumerated in an array of strings.";
- if (rolesElement.type() != Array)
- return Status(ErrorCodes::TypeMismatch, privilegesTypeMismatchMessage);
+ if (rolesElement.type() != Array)
+ return Status(ErrorCodes::TypeMismatch, privilegesTypeMismatchMessage);
- for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
- BSONElement roleElement = *iter;
- if (roleElement.type() != String)
- return Status(ErrorCodes::TypeMismatch, privilegesTypeMismatchMessage);
+ for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
+ BSONElement roleElement = *iter;
+ if (roleElement.type() != String)
+ return Status(ErrorCodes::TypeMismatch, privilegesTypeMismatchMessage);
- user->addRole(RoleName(roleElement.String(), dbname));
- }
- return Status::OK();
+ user->addRole(RoleName(roleElement.String(), dbname));
+ }
+ return Status::OK();
+}
+
+static Status _initializeUserRolesFromV1UserDocument(User* user,
+ const BSONObj& privDoc,
+ StringData dbname) {
+ if (!privDoc[READONLY_FIELD_NAME].eoo()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User documents may not contain both \"readonly\" and "
+ "\"roles\" fields");
}
- static Status _initializeUserRolesFromV1UserDocument(
- User* user, const BSONObj& privDoc, StringData dbname) {
-
- if (!privDoc[READONLY_FIELD_NAME].eoo()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User documents may not contain both \"readonly\" and "
- "\"roles\" fields");
- }
-
- Status status = _initializeUserRolesFromV1RolesArray(user,
- privDoc[ROLES_FIELD_NAME],
- dbname);
- if (!status.isOK()) {
- return status;
- }
+ Status status = _initializeUserRolesFromV1RolesArray(user, privDoc[ROLES_FIELD_NAME], dbname);
+ if (!status.isOK()) {
+ return status;
+ }
- // If "dbname" is the admin database, handle the otherDBPrivileges field, which
- // grants privileges on databases other than "dbname".
- BSONElement otherDbPrivileges = privDoc[OTHER_DB_ROLES_FIELD_NAME];
- if (dbname == ADMIN_DBNAME) {
- switch (otherDbPrivileges.type()) {
+ // If "dbname" is the admin database, handle the otherDBPrivileges field, which
+ // grants privileges on databases other than "dbname".
+ BSONElement otherDbPrivileges = privDoc[OTHER_DB_ROLES_FIELD_NAME];
+ if (dbname == ADMIN_DBNAME) {
+ switch (otherDbPrivileges.type()) {
case EOO:
break;
case Object: {
- for (BSONObjIterator iter(otherDbPrivileges.embeddedObject());
- iter.more(); iter.next()) {
-
+ for (BSONObjIterator iter(otherDbPrivileges.embeddedObject()); iter.more();
+ iter.next()) {
BSONElement rolesElement = *iter;
- status = _initializeUserRolesFromV1RolesArray(user,
- rolesElement,
- rolesElement.fieldName());
+ status = _initializeUserRolesFromV1RolesArray(
+ user, rolesElement, rolesElement.fieldName());
if (!status.isOK())
return status;
}
@@ -188,359 +181,337 @@ namespace {
default:
return Status(ErrorCodes::TypeMismatch,
"Field \"otherDBRoles\" must be an object, if present.");
- }
- }
- else if (!otherDbPrivileges.eoo()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "Only the admin database may contain a field called \"otherDBRoles\"");
}
+ } else if (!otherDbPrivileges.eoo()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "Only the admin database may contain a field called \"otherDBRoles\"");
+ }
- return Status::OK();
+ return Status::OK();
+}
+
+Status V1UserDocumentParser::initializeUserRolesFromUserDocument(User* user,
+ const BSONObj& privDoc,
+ StringData dbname) const {
+ if (!privDoc.hasField("roles")) {
+ _initializeUserRolesFromV0UserDocument(user, privDoc, dbname);
+ } else {
+ return _initializeUserRolesFromV1UserDocument(user, privDoc, dbname);
}
+ return Status::OK();
+}
- Status V1UserDocumentParser::initializeUserRolesFromUserDocument(
- User* user, const BSONObj& privDoc, StringData dbname) const {
- if (!privDoc.hasField("roles")) {
- _initializeUserRolesFromV0UserDocument(user, privDoc, dbname);
- } else {
- return _initializeUserRolesFromV1UserDocument(user, privDoc, dbname);
+
+Status _checkV2RolesArray(const BSONElement& rolesElement) {
+ if (rolesElement.eoo()) {
+ return _badValue("User document needs 'roles' field to be provided", 0);
+ }
+ if (rolesElement.type() != Array) {
+ return _badValue("'roles' field must be an array", 0);
+ }
+ for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
+ if ((*iter).type() != Object) {
+ return _badValue("Elements in 'roles' array must objects", 0);
}
- return Status::OK();
+ Status status = V2UserDocumentParser::checkValidRoleObject((*iter).Obj());
+ if (!status.isOK())
+ return status;
+ }
+ return Status::OK();
+}
+
+Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const {
+ BSONElement userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME];
+ BSONElement userDBElement = doc[AuthorizationManager::USER_DB_FIELD_NAME];
+ BSONElement credentialsElement = doc[CREDENTIALS_FIELD_NAME];
+ BSONElement rolesElement = doc[ROLES_FIELD_NAME];
+
+ // Validate the "user" element.
+ if (userElement.type() != String)
+ return _badValue("User document needs 'user' field to be a string", 0);
+ if (userElement.valueStringData().empty())
+ return _badValue("User document needs 'user' field to be non-empty", 0);
+
+ // Validate the "db" element
+ if (userDBElement.type() != String || userDBElement.valueStringData().empty()) {
+ return _badValue("User document needs 'db' field to be a non-empty string", 0);
+ }
+ StringData userDBStr = userDBElement.valueStringData();
+ if (!NamespaceString::validDBName(userDBStr) && userDBStr != "$external") {
+ return _badValue(mongoutils::str::stream() << "'" << userDBStr
+ << "' is not a valid value for the db field.",
+ 0);
}
+ // Validate the "credentials" element
+ if (credentialsElement.eoo()) {
+ return _badValue("User document needs 'credentials' object", 0);
+ }
+ if (credentialsElement.type() != Object) {
+ return _badValue("User document needs 'credentials' field to be an object", 0);
+ }
- Status _checkV2RolesArray(const BSONElement& rolesElement) {
- if (rolesElement.eoo()) {
- return _badValue("User document needs 'roles' field to be provided", 0);
- }
- if (rolesElement.type() != Array) {
- return _badValue("'roles' field must be an array", 0);
+ BSONObj credentialsObj = credentialsElement.Obj();
+ if (credentialsObj.isEmpty()) {
+ return _badValue("User document needs 'credentials' field to be a non-empty object", 0);
+ }
+ if (userDBStr == "$external") {
+ BSONElement externalElement = credentialsObj[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME];
+ if (externalElement.eoo() || externalElement.type() != Bool || !externalElement.Bool()) {
+ return _badValue(
+ "User documents for users defined on '$external' must have "
+ "'credentials' field set to {external: true}",
+ 0);
}
- for (BSONObjIterator iter(rolesElement.embeddedObject()); iter.more(); iter.next()) {
- if ((*iter).type() != Object) {
- return _badValue("Elements in 'roles' array must objects", 0);
+ } else {
+ BSONElement scramElement = credentialsObj[SCRAM_CREDENTIAL_FIELD_NAME];
+ BSONElement mongoCRElement = credentialsObj[MONGODB_CR_CREDENTIAL_FIELD_NAME];
+
+ if (!mongoCRElement.eoo()) {
+ if (mongoCRElement.type() != String || mongoCRElement.valueStringData().empty()) {
+ return _badValue(
+ "MONGODB-CR credential must to be a non-empty string"
+ ", if present",
+ 0);
+ }
+ } else if (!scramElement.eoo()) {
+ if (scramElement.type() != Object) {
+ return _badValue("SCRAM credential must be an object, if present", 0);
}
- Status status = V2UserDocumentParser::checkValidRoleObject((*iter).Obj());
- if (!status.isOK())
- return status;
+ } else {
+ return _badValue(
+ "User document must provide credentials for all "
+ "non-external users",
+ 0);
}
- return Status::OK();
}
- Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const {
- BSONElement userElement = doc[AuthorizationManager::USER_NAME_FIELD_NAME];
- BSONElement userDBElement = doc[AuthorizationManager::USER_DB_FIELD_NAME];
- BSONElement credentialsElement = doc[CREDENTIALS_FIELD_NAME];
- BSONElement rolesElement = doc[ROLES_FIELD_NAME];
-
- // Validate the "user" element.
- if (userElement.type() != String)
- return _badValue("User document needs 'user' field to be a string", 0);
- if (userElement.valueStringData().empty())
- return _badValue("User document needs 'user' field to be non-empty", 0);
-
- // Validate the "db" element
- if (userDBElement.type() != String || userDBElement.valueStringData().empty()) {
- return _badValue("User document needs 'db' field to be a non-empty string", 0);
- }
- StringData userDBStr = userDBElement.valueStringData();
- if (!NamespaceString::validDBName(userDBStr) && userDBStr != "$external") {
- return _badValue(mongoutils::str::stream() << "'" << userDBStr <<
- "' is not a valid value for the db field.",
- 0);
- }
+ // Validate the "roles" element.
+ Status status = _checkV2RolesArray(rolesElement);
+ if (!status.isOK())
+ return status;
- // Validate the "credentials" element
- if (credentialsElement.eoo()) {
- return _badValue("User document needs 'credentials' object",
- 0);
- }
- if (credentialsElement.type() != Object) {
- return _badValue("User document needs 'credentials' field to be an object", 0);
- }
+ return Status::OK();
+}
+
+std::string V2UserDocumentParser::extractUserNameFromUserDocument(const BSONObj& doc) const {
+ return doc[AuthorizationManager::USER_NAME_FIELD_NAME].str();
+}
- BSONObj credentialsObj = credentialsElement.Obj();
- if (credentialsObj.isEmpty()) {
- return _badValue("User document needs 'credentials' field to be a non-empty object",
- 0);
+Status V2UserDocumentParser::initializeUserCredentialsFromUserDocument(
+ User* user, const BSONObj& privDoc) const {
+ User::CredentialData credentials;
+ std::string userDB = privDoc[AuthorizationManager::USER_DB_FIELD_NAME].String();
+ BSONElement credentialsElement = privDoc[CREDENTIALS_FIELD_NAME];
+ if (!credentialsElement.eoo()) {
+ if (credentialsElement.type() != Object) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "'credentials' field in user documents must be an object");
}
- if (userDBStr == "$external") {
- BSONElement externalElement = credentialsObj[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME];
- if (externalElement.eoo() || externalElement.type() != Bool ||
- !externalElement.Bool()) {
- return _badValue("User documents for users defined on '$external' must have "
- "'credentials' field set to {external: true}", 0);
- }
- }
- else {
- BSONElement scramElement = credentialsObj[SCRAM_CREDENTIAL_FIELD_NAME];
- BSONElement mongoCRElement = credentialsObj[MONGODB_CR_CREDENTIAL_FIELD_NAME];
-
- if (!mongoCRElement.eoo()) {
- if (mongoCRElement.type() != String || mongoCRElement.valueStringData().empty()) {
- return _badValue("MONGODB-CR credential must to be a non-empty string"
- ", if present", 0);
- }
- }
- else if (!scramElement.eoo()) {
- if (scramElement.type() != Object) {
- return _badValue("SCRAM credential must be an object, if present", 0);
+ if (userDB == "$external") {
+ BSONElement externalCredentialElement =
+ credentialsElement.Obj()[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME];
+ if (!externalCredentialElement.eoo()) {
+ if (externalCredentialElement.type() != Bool || !externalCredentialElement.Bool()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "'external' field in credentials object must be set to true");
+ } else {
+ credentials.isExternal = true;
}
+ } else {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User documents defined on '$external' must provide set "
+ "credentials to {external:true}");
}
- else {
- return _badValue("User document must provide credentials for all "
- "non-external users", 0);
+ } else {
+ BSONElement scramElement = credentialsElement.Obj()[SCRAM_CREDENTIAL_FIELD_NAME];
+ BSONElement mongoCRCredentialElement =
+ credentialsElement.Obj()[MONGODB_CR_CREDENTIAL_FIELD_NAME];
+
+ if (scramElement.eoo() && mongoCRCredentialElement.eoo()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User documents must provide credentials for SCRAM-SHA-1 "
+ "or MONGODB-CR authentication");
}
- }
- // Validate the "roles" element.
- Status status = _checkV2RolesArray(rolesElement);
- if (!status.isOK())
- return status;
+ if (!scramElement.eoo()) {
+ // We are asserting rather then returning errors since these
+ // fields should have been prepopulated by the calling code.
+ credentials.scram.iterationCount = scramElement.Obj()["iterationCount"].numberInt();
+ uassert(17501,
+ "Invalid or missing SCRAM iteration count",
+ credentials.scram.iterationCount > 0);
- return Status::OK();
- }
+ credentials.scram.salt = scramElement.Obj()["salt"].str();
+ uassert(17502, "Missing SCRAM salt", !credentials.scram.salt.empty());
- std::string V2UserDocumentParser::extractUserNameFromUserDocument(
- const BSONObj& doc) const {
- return doc[AuthorizationManager::USER_NAME_FIELD_NAME].str();
- }
+ credentials.scram.serverKey = scramElement["serverKey"].str();
+ uassert(17503, "Missing SCRAM serverKey", !credentials.scram.serverKey.empty());
- Status V2UserDocumentParser::initializeUserCredentialsFromUserDocument(
- User* user, const BSONObj& privDoc) const {
- User::CredentialData credentials;
- std::string userDB = privDoc[AuthorizationManager::USER_DB_FIELD_NAME].String();
- BSONElement credentialsElement = privDoc[CREDENTIALS_FIELD_NAME];
- if (!credentialsElement.eoo()) {
- if (credentialsElement.type() != Object) {
- return Status(ErrorCodes::UnsupportedFormat,
- "'credentials' field in user documents must be an object");
+ credentials.scram.storedKey = scramElement["storedKey"].str();
+ uassert(17504, "Missing SCRAM storedKey", !credentials.scram.storedKey.empty());
}
- if (userDB == "$external") {
- BSONElement externalCredentialElement =
- credentialsElement.Obj()[MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME];
- if (!externalCredentialElement.eoo()) {
- if (externalCredentialElement.type() != Bool ||
- !externalCredentialElement.Bool()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "'external' field in credentials object must be set to true");
- } else {
- credentials.isExternal = true;
- }
- } else {
- return Status(ErrorCodes::UnsupportedFormat,
- "User documents defined on '$external' must provide set "
- "credentials to {external:true}");
- }
- } else {
-
- BSONElement scramElement =
- credentialsElement.Obj()[SCRAM_CREDENTIAL_FIELD_NAME];
- BSONElement mongoCRCredentialElement =
- credentialsElement.Obj()[MONGODB_CR_CREDENTIAL_FIELD_NAME];
-
- if (scramElement.eoo() && mongoCRCredentialElement.eoo()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User documents must provide credentials for SCRAM-SHA-1 "
- "or MONGODB-CR authentication");
- }
- if (!scramElement.eoo()) {
- // We are asserting rather then returning errors since these
- // fields should have been prepopulated by the calling code.
- credentials.scram.iterationCount =
- scramElement.Obj()["iterationCount"].numberInt();
- uassert(17501, "Invalid or missing SCRAM iteration count",
- credentials.scram.iterationCount > 0);
-
- credentials.scram.salt =
- scramElement.Obj()["salt"].str();
- uassert(17502, "Missing SCRAM salt",
- !credentials.scram.salt.empty());
-
- credentials.scram.serverKey =
- scramElement["serverKey"].str();
- uassert(17503, "Missing SCRAM serverKey",
- !credentials.scram.serverKey.empty());
-
- credentials.scram.storedKey =
- scramElement["storedKey"].str();
- uassert(17504, "Missing SCRAM storedKey",
- !credentials.scram.storedKey.empty());
- }
-
- if (!mongoCRCredentialElement.eoo()) {
- if (mongoCRCredentialElement.type() != String ||
- mongoCRCredentialElement.valueStringData().empty()) {
+ if (!mongoCRCredentialElement.eoo()) {
+ if (mongoCRCredentialElement.type() != String ||
+ mongoCRCredentialElement.valueStringData().empty()) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "MONGODB-CR credentials must be non-empty strings");
+ } else {
+ credentials.password = mongoCRCredentialElement.String();
+ if (credentials.password.empty()) {
return Status(ErrorCodes::UnsupportedFormat,
- "MONGODB-CR credentials must be non-empty strings");
- } else {
- credentials.password = mongoCRCredentialElement.String();
- if (credentials.password.empty()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User documents must provide authentication credentials");
- }
+ "User documents must provide authentication credentials");
}
}
- credentials.isExternal = false;
}
- } else {
- return Status(ErrorCodes::UnsupportedFormat,
- "Cannot extract credentials from user documents without a "
- "'credentials' field");
+ credentials.isExternal = false;
}
-
- user->setCredentials(credentials);
- return Status::OK();
+ } else {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "Cannot extract credentials from user documents without a "
+ "'credentials' field");
}
- static Status _extractRoleDocumentElements(
- const BSONObj& roleObject,
- BSONElement* roleNameElement,
- BSONElement* roleSourceElement) {
+ user->setCredentials(credentials);
+ return Status::OK();
+}
- *roleNameElement = roleObject[ROLE_NAME_FIELD_NAME];
- *roleSourceElement = roleObject[ROLE_DB_FIELD_NAME];
-
- if (roleNameElement->type() != String || roleNameElement->valueStringData().empty()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "Role names must be non-empty strings");
- }
- if (roleSourceElement->type() != String || roleSourceElement->valueStringData().empty()) {
- return Status(ErrorCodes::UnsupportedFormat, "Role db must be non-empty strings");
- }
+static Status _extractRoleDocumentElements(const BSONObj& roleObject,
+ BSONElement* roleNameElement,
+ BSONElement* roleSourceElement) {
+ *roleNameElement = roleObject[ROLE_NAME_FIELD_NAME];
+ *roleSourceElement = roleObject[ROLE_DB_FIELD_NAME];
- return Status::OK();
+ if (roleNameElement->type() != String || roleNameElement->valueStringData().empty()) {
+ return Status(ErrorCodes::UnsupportedFormat, "Role names must be non-empty strings");
}
-
- Status V2UserDocumentParser::checkValidRoleObject(const BSONObj& roleObject) {
- BSONElement roleNameElement;
- BSONElement roleSourceElement;
- return _extractRoleDocumentElements(
- roleObject,
- &roleNameElement,
- &roleSourceElement);
+ if (roleSourceElement->type() != String || roleSourceElement->valueStringData().empty()) {
+ return Status(ErrorCodes::UnsupportedFormat, "Role db must be non-empty strings");
}
- Status V2UserDocumentParser::parseRoleName(const BSONObj& roleObject, RoleName* result) {
- BSONElement roleNameElement;
- BSONElement roleSourceElement;
- Status status = _extractRoleDocumentElements(
- roleObject,
- &roleNameElement,
- &roleSourceElement);
- if (!status.isOK())
- return status;
- *result = RoleName(roleNameElement.str(), roleSourceElement.str());
- return status;
- }
+ return Status::OK();
+}
- Status V2UserDocumentParser::parseRoleVector(const BSONArray& rolesArray,
- std::vector<RoleName>* result) {
- std::vector<RoleName> roles;
- for (BSONObjIterator it(rolesArray); it.more(); it.next()) {
- if ((*it).type() != Object) {
- return Status(ErrorCodes::TypeMismatch, "Roles must be objects.");
- }
- RoleName role;
- Status status = parseRoleName((*it).Obj(), &role);
- if (!status.isOK())
- return status;
- roles.push_back(role);
+Status V2UserDocumentParser::checkValidRoleObject(const BSONObj& roleObject) {
+ BSONElement roleNameElement;
+ BSONElement roleSourceElement;
+ return _extractRoleDocumentElements(roleObject, &roleNameElement, &roleSourceElement);
+}
+
+Status V2UserDocumentParser::parseRoleName(const BSONObj& roleObject, RoleName* result) {
+ BSONElement roleNameElement;
+ BSONElement roleSourceElement;
+ Status status = _extractRoleDocumentElements(roleObject, &roleNameElement, &roleSourceElement);
+ if (!status.isOK())
+ return status;
+ *result = RoleName(roleNameElement.str(), roleSourceElement.str());
+ return status;
+}
+
+Status V2UserDocumentParser::parseRoleVector(const BSONArray& rolesArray,
+ std::vector<RoleName>* result) {
+ std::vector<RoleName> roles;
+ for (BSONObjIterator it(rolesArray); it.more(); it.next()) {
+ if ((*it).type() != Object) {
+ return Status(ErrorCodes::TypeMismatch, "Roles must be objects.");
}
- std::swap(*result, roles);
- return Status::OK();
+ RoleName role;
+ Status status = parseRoleName((*it).Obj(), &role);
+ if (!status.isOK())
+ return status;
+ roles.push_back(role);
}
+ std::swap(*result, roles);
+ return Status::OK();
+}
- Status V2UserDocumentParser::initializeUserRolesFromUserDocument(
- const BSONObj& privDoc, User* user) const {
+Status V2UserDocumentParser::initializeUserRolesFromUserDocument(const BSONObj& privDoc,
+ User* user) const {
+ BSONElement rolesElement = privDoc[ROLES_FIELD_NAME];
- BSONElement rolesElement = privDoc[ROLES_FIELD_NAME];
+ if (rolesElement.type() != Array) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User document needs 'roles' field to be an array");
+ }
- if (rolesElement.type() != Array) {
+ std::vector<RoleName> roles;
+ for (BSONObjIterator it(rolesElement.Obj()); it.more(); it.next()) {
+ if ((*it).type() != Object) {
return Status(ErrorCodes::UnsupportedFormat,
- "User document needs 'roles' field to be an array");
+ "User document needs values in 'roles' array to be a sub-documents");
}
+ BSONObj roleObject = (*it).Obj();
- std::vector<RoleName> roles;
- for (BSONObjIterator it(rolesElement.Obj()); it.more(); it.next()) {
- if ((*it).type() != Object) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User document needs values in 'roles' array to be a sub-documents");
- }
- BSONObj roleObject = (*it).Obj();
-
- RoleName role;
- Status status = parseRoleName(roleObject, &role);
- if (!status.isOK()) {
- return status;
- }
- roles.push_back(role);
+ RoleName role;
+ Status status = parseRoleName(roleObject, &role);
+ if (!status.isOK()) {
+ return status;
}
- user->setRoles(makeRoleNameIteratorForContainer(roles));
- return Status::OK();
+ roles.push_back(role);
}
+ user->setRoles(makeRoleNameIteratorForContainer(roles));
+ return Status::OK();
+}
- Status V2UserDocumentParser::initializeUserIndirectRolesFromUserDocument(
- const BSONObj& privDoc, User* user) const {
+Status V2UserDocumentParser::initializeUserIndirectRolesFromUserDocument(const BSONObj& privDoc,
+ User* user) const {
+ BSONElement indirectRolesElement = privDoc[INHERITED_ROLES_FIELD_NAME];
- BSONElement indirectRolesElement = privDoc[INHERITED_ROLES_FIELD_NAME];
+ if (indirectRolesElement.type() != Array) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User document needs 'inheritedRoles' field to be an array");
+ }
- if (indirectRolesElement.type() != Array) {
+ std::vector<RoleName> indirectRoles;
+ for (BSONObjIterator it(indirectRolesElement.Obj()); it.more(); it.next()) {
+ if ((*it).type() != Object) {
return Status(ErrorCodes::UnsupportedFormat,
- "User document needs 'inheritedRoles' field to be an array");
+ "User document needs values in 'inheritedRoles'"
+ " array to be a sub-documents");
}
+ BSONObj indirectRoleObject = (*it).Obj();
- std::vector<RoleName> indirectRoles;
- for (BSONObjIterator it(indirectRolesElement.Obj()); it.more(); it.next()) {
- if ((*it).type() != Object) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User document needs values in 'inheritedRoles'"
- " array to be a sub-documents");
- }
- BSONObj indirectRoleObject = (*it).Obj();
-
- RoleName indirectRole;
- Status status = parseRoleName(indirectRoleObject, &indirectRole);
- if (!status.isOK()) {
- return status;
- }
- indirectRoles.push_back(indirectRole);
+ RoleName indirectRole;
+ Status status = parseRoleName(indirectRoleObject, &indirectRole);
+ if (!status.isOK()) {
+ return status;
}
- user->setIndirectRoles(makeRoleNameIteratorForContainer(indirectRoles));
+ indirectRoles.push_back(indirectRole);
+ }
+ user->setIndirectRoles(makeRoleNameIteratorForContainer(indirectRoles));
+ return Status::OK();
+}
+
+Status V2UserDocumentParser::initializeUserPrivilegesFromUserDocument(const BSONObj& doc,
+ User* user) const {
+ BSONElement privilegesElement = doc[PRIVILEGES_FIELD_NAME];
+ if (privilegesElement.eoo())
return Status::OK();
+ if (privilegesElement.type() != Array) {
+ return Status(ErrorCodes::UnsupportedFormat,
+ "User document 'inheritedPrivileges' element must be Array if present.");
}
-
- Status V2UserDocumentParser::initializeUserPrivilegesFromUserDocument(const BSONObj& doc,
- User* user) const {
- BSONElement privilegesElement = doc[PRIVILEGES_FIELD_NAME];
- if (privilegesElement.eoo())
- return Status::OK();
- if (privilegesElement.type() != Array) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User document 'inheritedPrivileges' element must be Array if present.");
+ PrivilegeVector privileges;
+ std::string errmsg;
+ for (BSONObjIterator it(privilegesElement.Obj()); it.more(); it.next()) {
+ if ((*it).type() != Object) {
+ warning() << "Wrong type of element in inheritedPrivileges array for "
+ << user->getName() << ": " << *it;
+ continue;
}
- PrivilegeVector privileges;
- std::string errmsg;
- for (BSONObjIterator it(privilegesElement.Obj()); it.more(); it.next()) {
- if ((*it).type() != Object) {
- warning() << "Wrong type of element in inheritedPrivileges array for " <<
- user->getName() << ": " << *it;
- continue;
- }
- Privilege privilege;
- ParsedPrivilege pp;
- if (!pp.parseBSON((*it).Obj(), &errmsg) ||
- !ParsedPrivilege::parsedPrivilegeToPrivilege(pp, &privilege, &errmsg)) {
-
- warning() << "Could not parse privilege element in user document for " <<
- user->getName() << ": " << errmsg;
- continue;
- }
- privileges.push_back(privilege);
+ Privilege privilege;
+ ParsedPrivilege pp;
+ if (!pp.parseBSON((*it).Obj(), &errmsg) ||
+ !ParsedPrivilege::parsedPrivilegeToPrivilege(pp, &privilege, &errmsg)) {
+ warning() << "Could not parse privilege element in user document for "
+ << user->getName() << ": " << errmsg;
+ continue;
}
- user->setPrivileges(privileges);
- return Status::OK();
+ privileges.push_back(privilege);
}
+ user->setPrivileges(privileges);
+ return Status::OK();
+}
-} // namespace mongo
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_document_parser.h b/src/mongo/db/auth/user_document_parser.h
index 28b93ee5673..502a3f349ea 100644
--- a/src/mongo/db/auth/user_document_parser.h
+++ b/src/mongo/db/auth/user_document_parser.h
@@ -36,41 +36,43 @@
namespace mongo {
- class V1UserDocumentParser {
- MONGO_DISALLOW_COPYING(V1UserDocumentParser);
- public:
- V1UserDocumentParser() {}
- std::string extractUserNameFromUserDocument(const BSONObj& doc) const;
+class V1UserDocumentParser {
+ MONGO_DISALLOW_COPYING(V1UserDocumentParser);
- Status initializeUserCredentialsFromUserDocument(User* user,
- const BSONObj& privDoc) const;
+public:
+ V1UserDocumentParser() {}
+ std::string extractUserNameFromUserDocument(const BSONObj& doc) const;
- Status initializeUserRolesFromUserDocument(
- User* user, const BSONObj& privDoc, StringData dbname) const;
- };
+ Status initializeUserCredentialsFromUserDocument(User* user, const BSONObj& privDoc) const;
- class V2UserDocumentParser {
- MONGO_DISALLOW_COPYING(V2UserDocumentParser);
- public:
- V2UserDocumentParser() {}
- Status checkValidUserDocument(const BSONObj& doc) const;
+ Status initializeUserRolesFromUserDocument(User* user,
+ const BSONObj& privDoc,
+ StringData dbname) const;
+};
- /**
- * Returns Status::OK() iff the given BSONObj describes a valid element from a roles array.
- */
- static Status checkValidRoleObject(const BSONObj& roleObject);
+class V2UserDocumentParser {
+ MONGO_DISALLOW_COPYING(V2UserDocumentParser);
- static Status parseRoleName(const BSONObj& roleObject, RoleName* result);
+public:
+ V2UserDocumentParser() {}
+ Status checkValidUserDocument(const BSONObj& doc) const;
- static Status parseRoleVector(const BSONArray& rolesArray, std::vector<RoleName>* result);
+ /**
+ * Returns Status::OK() iff the given BSONObj describes a valid element from a roles array.
+ */
+ static Status checkValidRoleObject(const BSONObj& roleObject);
- std::string extractUserNameFromUserDocument(const BSONObj& doc) const;
+ static Status parseRoleName(const BSONObj& roleObject, RoleName* result);
- Status initializeUserCredentialsFromUserDocument(User* user, const BSONObj& privDoc) const;
+ static Status parseRoleVector(const BSONArray& rolesArray, std::vector<RoleName>* result);
- Status initializeUserRolesFromUserDocument(const BSONObj& doc, User* user) const;
- Status initializeUserIndirectRolesFromUserDocument(const BSONObj& doc, User* user) const;
- Status initializeUserPrivilegesFromUserDocument(const BSONObj& doc, User* user) const;
- };
+ std::string extractUserNameFromUserDocument(const BSONObj& doc) const;
-} // namespace mongo
+ Status initializeUserCredentialsFromUserDocument(User* user, const BSONObj& privDoc) const;
+
+ Status initializeUserRolesFromUserDocument(const BSONObj& doc, User* user) const;
+ Status initializeUserIndirectRolesFromUserDocument(const BSONObj& doc, User* user) const;
+ Status initializeUserPrivilegesFromUserDocument(const BSONObj& doc, User* user) const;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_document_parser_test.cpp b/src/mongo/db/auth/user_document_parser_test.cpp
index d2dff197b12..ae6c566d109 100644
--- a/src/mongo/db/auth/user_document_parser_test.cpp
+++ b/src/mongo/db/auth/user_document_parser_test.cpp
@@ -44,360 +44,450 @@
namespace mongo {
namespace {
- using std::unique_ptr;
-
- class V1UserDocumentParsing : public ::mongo::unittest::Test {
- public:
- V1UserDocumentParsing() {}
-
- unique_ptr<User> user;
- unique_ptr<User> adminUser;
- V1UserDocumentParser v1parser;
-
- void setUp() {
- resetUsers();
- }
-
- void resetUsers() {
- user.reset(new User(UserName("spencer", "test")));
- adminUser.reset(new User(UserName("admin", "admin")));
- }
- };
-
- TEST_F(V1UserDocumentParsing, testParsingV0UserDocuments) {
- BSONObj readWrite = BSON("user" << "spencer" << "pwd" << "passwordHash");
- BSONObj readOnly = BSON("user" << "spencer" << "pwd" << "passwordHash" <<
- "readOnly" << true);
- BSONObj readWriteAdmin = BSON("user" << "admin" << "pwd" << "passwordHash");
- BSONObj readOnlyAdmin = BSON("user" << "admin" << "pwd" << "passwordHash" <<
- "readOnly" << true);
-
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(), readOnly, "test"));
- RoleNameIterator roles = user->getRoles();
- ASSERT_EQUALS(RoleName("read", "test"), roles.next());
- ASSERT_FALSE(roles.more());
+using std::unique_ptr;
- resetUsers();
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(), readWrite, "test"));
- roles = user->getRoles();
- ASSERT_EQUALS(RoleName("dbOwner", "test"), roles.next());
- ASSERT_FALSE(roles.more());
+class V1UserDocumentParsing : public ::mongo::unittest::Test {
+public:
+ V1UserDocumentParsing() {}
- resetUsers();
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- adminUser.get(), readOnlyAdmin, "admin"));
- roles = adminUser->getRoles();
- ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), roles.next());
- ASSERT_FALSE(roles.more());
+ unique_ptr<User> user;
+ unique_ptr<User> adminUser;
+ V1UserDocumentParser v1parser;
+ void setUp() {
resetUsers();
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- adminUser.get(), readWriteAdmin, "admin"));
- roles = adminUser->getRoles();
- ASSERT_EQUALS(RoleName("root", "admin"), roles.next());
- ASSERT_FALSE(roles.more());
- }
-
- TEST_F(V1UserDocumentParsing, VerifyRolesFieldMustBeAnArray) {
- ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(),
- BSON("user" << "spencer" << "pwd" << "" << "roles" << "read"),
- "test"));
- ASSERT_FALSE(user->getRoles().more());
- }
-
- TEST_F(V1UserDocumentParsing, VerifySemanticallyInvalidRolesStillParse) {
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "pwd" << "" <<
- "roles" << BSON_ARRAY("read" << "frim")),
- "test"));
- RoleNameIterator roles = user->getRoles();
- RoleName role = roles.next();
- if (role == RoleName("read", "test")) {
- ASSERT_EQUALS(RoleName("frim", "test"), roles.next());
- } else {
- ASSERT_EQUALS(RoleName("frim", "test"), role);
- ASSERT_EQUALS(RoleName("read", "test"), roles.next());
- }
- ASSERT_FALSE(roles.more());
- }
-
- TEST_F(V1UserDocumentParsing, VerifyOtherDBRolesMustBeAnObjectOfArraysOfStrings) {
- ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(
- adminUser.get(),
- BSON("user" << "admin" <<
- "pwd" << "" <<
- "roles" << BSON_ARRAY("read") <<
- "otherDBRoles" << BSON_ARRAY("read")),
- "admin"));
-
- ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(
- adminUser.get(),
- BSON("user" << "admin" <<
- "pwd" << "" <<
- "roles" << BSON_ARRAY("read") <<
- "otherDBRoles" << BSON("test2" << "read")),
- "admin"));
}
- TEST_F(V1UserDocumentParsing, VerifyCannotGrantPrivilegesOnOtherDatabasesNormally) {
- // Cannot grant roles on other databases, except from admin database.
- ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "pwd" << "" <<
- "roles" << BSONArrayBuilder().arr() <<
- "otherDBRoles" << BSON("test2" << BSON_ARRAY("read"))),
- "test"));
- ASSERT_FALSE(user->getRoles().more());
+ void resetUsers() {
+ user.reset(new User(UserName("spencer", "test")));
+ adminUser.reset(new User(UserName("admin", "admin")));
}
-
- TEST_F(V1UserDocumentParsing, GrantUserAdminOnTestViaAdmin) {
- // Grant userAdmin on test via admin.
- ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
- adminUser.get(),
- BSON("user" << "admin" <<
- "pwd" << "" <<
- "roles" << BSONArrayBuilder().arr() <<
- "otherDBRoles" << BSON("test" << BSON_ARRAY("userAdmin"))),
- "admin"));
- RoleNameIterator roles = adminUser->getRoles();
- ASSERT_EQUALS(RoleName("userAdmin", "test"), roles.next());
- ASSERT_FALSE(roles.more());
- }
-
- TEST_F(V1UserDocumentParsing, MixedV0V1UserDocumentsAreInvalid) {
- // Try to mix fields from V0 and V1 user documents and make sure it fails.
- ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "pwd" << "passwordHash" <<
- "readOnly" << false <<
- "roles" << BSON_ARRAY("read")),
- "test"));
- ASSERT_FALSE(user->getRoles().more());
- }
-
- class V2UserDocumentParsing : public ::mongo::unittest::Test {
- public:
- V2UserDocumentParsing() {}
-
- unique_ptr<User> user;
- unique_ptr<User> adminUser;
- V2UserDocumentParser v2parser;
-
- void setUp() {
- user.reset(new User(UserName("spencer", "test")));
- adminUser.reset(new User(UserName("admin", "admin")));
- }
- };
-
-
- TEST_F(V2UserDocumentParsing, V2DocumentValidation) {
- BSONArray emptyArray = BSONArrayBuilder().arr();
-
- // V1 documents don't work
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" << "pwd" << "a" <<
- "roles" << BSON_ARRAY("read"))));
-
- // Need name field
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << emptyArray)));
-
- // Need source field
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << emptyArray)));
-
- // Need credentials field
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "roles" << emptyArray)));
-
- // Need roles field
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a"))));
-
- // Empty roles arrays are OK
- ASSERT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << emptyArray)));
-
- // Need credentials of {external: true} if user's db is $external
- ASSERT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "$external" <<
- "credentials" << BSON("external" << true) <<
- "roles" << emptyArray)));
-
- // Roles must be objects
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY("read"))));
-
- // Role needs name
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("db" << "dbA")))));
-
- // Role needs source
- ASSERT_NOT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA")))));
-
-
- // Basic valid user document
- ASSERT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA" <<
- "db" << "dbA")))));
-
- // Multiple roles OK
- ASSERT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA" <<
- "db" << "dbA") <<
- BSON("role" << "roleB" <<
- "db" << "dbB")))));
-
- // Optional extraData field OK
- ASSERT_OK(v2parser.checkValidUserDocument(
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a") <<
- "extraData" << BSON("foo" << "bar") <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA" <<
- "db" << "dbA")))));
+};
+
+TEST_F(V1UserDocumentParsing, testParsingV0UserDocuments) {
+ BSONObj readWrite = BSON("user"
+ << "spencer"
+ << "pwd"
+ << "passwordHash");
+ BSONObj readOnly = BSON("user"
+ << "spencer"
+ << "pwd"
+ << "passwordHash"
+ << "readOnly" << true);
+ BSONObj readWriteAdmin = BSON("user"
+ << "admin"
+ << "pwd"
+ << "passwordHash");
+ BSONObj readOnlyAdmin = BSON("user"
+ << "admin"
+ << "pwd"
+ << "passwordHash"
+ << "readOnly" << true);
+
+ ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(user.get(), readOnly, "test"));
+ RoleNameIterator roles = user->getRoles();
+ ASSERT_EQUALS(RoleName("read", "test"), roles.next());
+ ASSERT_FALSE(roles.more());
+
+ resetUsers();
+ ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(user.get(), readWrite, "test"));
+ roles = user->getRoles();
+ ASSERT_EQUALS(RoleName("dbOwner", "test"), roles.next());
+ ASSERT_FALSE(roles.more());
+
+ resetUsers();
+ ASSERT_OK(
+ v1parser.initializeUserRolesFromUserDocument(adminUser.get(), readOnlyAdmin, "admin"));
+ roles = adminUser->getRoles();
+ ASSERT_EQUALS(RoleName("readAnyDatabase", "admin"), roles.next());
+ ASSERT_FALSE(roles.more());
+
+ resetUsers();
+ ASSERT_OK(
+ v1parser.initializeUserRolesFromUserDocument(adminUser.get(), readWriteAdmin, "admin"));
+ roles = adminUser->getRoles();
+ ASSERT_EQUALS(RoleName("root", "admin"), roles.next());
+ ASSERT_FALSE(roles.more());
+}
+
+TEST_F(V1UserDocumentParsing, VerifyRolesFieldMustBeAnArray) {
+ ASSERT_NOT_OK(v1parser.initializeUserRolesFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "pwd"
+ << ""
+ << "roles"
+ << "read"),
+ "test"));
+ ASSERT_FALSE(user->getRoles().more());
+}
+
+TEST_F(V1UserDocumentParsing, VerifySemanticallyInvalidRolesStillParse) {
+ ASSERT_OK(
+ v1parser.initializeUserRolesFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "pwd"
+ << ""
+ << "roles" << BSON_ARRAY("read"
+ << "frim")),
+ "test"));
+ RoleNameIterator roles = user->getRoles();
+ RoleName role = roles.next();
+ if (role == RoleName("read", "test")) {
+ ASSERT_EQUALS(RoleName("frim", "test"), roles.next());
+ } else {
+ ASSERT_EQUALS(RoleName("frim", "test"), role);
+ ASSERT_EQUALS(RoleName("read", "test"), roles.next());
}
-
- TEST_F(V2UserDocumentParsing, V2CredentialExtraction) {
- // Old "pwd" field not valid
- ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "pwd" << "")));
-
- // Credentials must be provided
- ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "test")));
-
- // Credentials must be object
- ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << "a")));
-
- // Must specify credentials for MONGODB-CR
- ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("foo" << "bar"))));
-
- // Make sure extracting valid credentials works
- ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "test" <<
- "credentials" << BSON("MONGODB-CR" << "a"))));
- ASSERT(user->getCredentials().password == "a");
- ASSERT(!user->getCredentials().isExternal);
-
- // Credentials are {external:true if users's db is $external
- ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(
- user.get(),
- BSON("user" << "spencer" <<
- "db" << "$external" <<
- "credentials" << BSON("external" << true))));
- ASSERT(user->getCredentials().password.empty());
- ASSERT(user->getCredentials().isExternal);
-
+ ASSERT_FALSE(roles.more());
+}
+
+TEST_F(V1UserDocumentParsing, VerifyOtherDBRolesMustBeAnObjectOfArraysOfStrings) {
+ ASSERT_NOT_OK(
+ v1parser.initializeUserRolesFromUserDocument(adminUser.get(),
+ BSON("user"
+ << "admin"
+ << "pwd"
+ << ""
+ << "roles" << BSON_ARRAY("read")
+ << "otherDBRoles" << BSON_ARRAY("read")),
+ "admin"));
+
+ ASSERT_NOT_OK(
+ v1parser.initializeUserRolesFromUserDocument(adminUser.get(),
+ BSON("user"
+ << "admin"
+ << "pwd"
+ << ""
+ << "roles" << BSON_ARRAY("read")
+ << "otherDBRoles" << BSON("test2"
+ << "read")),
+ "admin"));
+}
+
+TEST_F(V1UserDocumentParsing, VerifyCannotGrantPrivilegesOnOtherDatabasesNormally) {
+ // Cannot grant roles on other databases, except from admin database.
+ ASSERT_NOT_OK(
+ v1parser.initializeUserRolesFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "pwd"
+ << ""
+ << "roles" << BSONArrayBuilder().arr()
+ << "otherDBRoles"
+ << BSON("test2" << BSON_ARRAY("read"))),
+ "test"));
+ ASSERT_FALSE(user->getRoles().more());
+}
+
+TEST_F(V1UserDocumentParsing, GrantUserAdminOnTestViaAdmin) {
+ // Grant userAdmin on test via admin.
+ ASSERT_OK(v1parser.initializeUserRolesFromUserDocument(
+ adminUser.get(),
+ BSON("user"
+ << "admin"
+ << "pwd"
+ << ""
+ << "roles" << BSONArrayBuilder().arr() << "otherDBRoles"
+ << BSON("test" << BSON_ARRAY("userAdmin"))),
+ "admin"));
+ RoleNameIterator roles = adminUser->getRoles();
+ ASSERT_EQUALS(RoleName("userAdmin", "test"), roles.next());
+ ASSERT_FALSE(roles.more());
+}
+
+TEST_F(V1UserDocumentParsing, MixedV0V1UserDocumentsAreInvalid) {
+ // Try to mix fields from V0 and V1 user documents and make sure it fails.
+ ASSERT_NOT_OK(
+ v1parser.initializeUserRolesFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "pwd"
+ << "passwordHash"
+ << "readOnly" << false << "roles"
+ << BSON_ARRAY("read")),
+ "test"));
+ ASSERT_FALSE(user->getRoles().more());
+}
+
+class V2UserDocumentParsing : public ::mongo::unittest::Test {
+public:
+ V2UserDocumentParsing() {}
+
+ unique_ptr<User> user;
+ unique_ptr<User> adminUser;
+ V2UserDocumentParser v2parser;
+
+ void setUp() {
+ user.reset(new User(UserName("spencer", "test")));
+ adminUser.reset(new User(UserName("admin", "admin")));
}
-
- TEST_F(V2UserDocumentParsing, V2RoleExtraction) {
- // "roles" field must be provided
- ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer"),
- user.get()));
-
- // V1-style roles arrays no longer work
- ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY("read")),
- user.get()));
-
- // Roles must have "db" field
- ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY(BSONObj())),
- user.get()));
-
- ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA"))),
- user.get()));
-
- ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY(BSON("user" << "roleA" <<
- "db" << "dbA"))),
- user.get()));
-
- // Valid role names are extracted successfully
- ASSERT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA" <<
- "db" << "dbA"))),
- user.get()));
- RoleNameIterator roles = user->getRoles();
+};
+
+
+TEST_F(V2UserDocumentParsing, V2DocumentValidation) {
+ BSONArray emptyArray = BSONArrayBuilder().arr();
+
+ // V1 documents don't work
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "pwd"
+ << "a"
+ << "roles" << BSON_ARRAY("read"))));
+
+ // Need name field
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << emptyArray)));
+
+ // Need source field
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << emptyArray)));
+
+ // Need credentials field
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "roles" << emptyArray)));
+
+ // Need roles field
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a"))));
+
+ // Empty roles arrays are OK
+ ASSERT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << emptyArray)));
+
+ // Need credentials of {external: true} if user's db is $external
+ ASSERT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "$external"
+ << "credentials" << BSON("external" << true)
+ << "roles" << emptyArray)));
+
+ // Roles must be objects
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY("read"))));
+
+ // Role needs name
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("db"
+ << "dbA")))));
+
+ // Role needs source
+ ASSERT_NOT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "roleA")))));
+
+
+ // Basic valid user document
+ ASSERT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "roleA"
+ << "db"
+ << "dbA")))));
+
+ // Multiple roles OK
+ ASSERT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "roleA"
+ << "db"
+ << "dbA")
+ << BSON("role"
+ << "roleB"
+ << "db"
+ << "dbB")))));
+
+ // Optional extraData field OK
+ ASSERT_OK(v2parser.checkValidUserDocument(BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials" << BSON("MONGODB-CR"
+ << "a") << "extraData"
+ << BSON("foo"
+ << "bar") << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "roleA"
+ << "db"
+ << "dbA")))));
+}
+
+TEST_F(V2UserDocumentParsing, V2CredentialExtraction) {
+ // Old "pwd" field not valid
+ ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "pwd"
+ << "")));
+
+ // Credentials must be provided
+ ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test")));
+
+ // Credentials must be object
+ ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << "a")));
+
+ // Must specify credentials for MONGODB-CR
+ ASSERT_NOT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("foo"
+ << "bar"))));
+
+ // Make sure extracting valid credentials works
+ ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("MONGODB-CR"
+ << "a"))));
+ ASSERT(user->getCredentials().password == "a");
+ ASSERT(!user->getCredentials().isExternal);
+
+ // Credentials are {external:true if users's db is $external
+ ASSERT_OK(
+ v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "$external"
+ << "credentials"
+ << BSON("external" << true))));
+ ASSERT(user->getCredentials().password.empty());
+ ASSERT(user->getCredentials().isExternal);
+}
+
+TEST_F(V2UserDocumentParsing, V2RoleExtraction) {
+ // "roles" field must be provided
+ ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"),
+ user.get()));
+
+ // V1-style roles arrays no longer work
+ ASSERT_NOT_OK(
+ v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles" << BSON_ARRAY("read")),
+ user.get()));
+
+ // Roles must have "db" field
+ ASSERT_NOT_OK(
+ v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles" << BSON_ARRAY(BSONObj())),
+ user.get()));
+
+ ASSERT_NOT_OK(v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles" << BSON_ARRAY(BSON(
+ "role"
+ << "roleA"))),
+ user.get()));
+
+ ASSERT_NOT_OK(
+ v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles" << BSON_ARRAY(BSON("user"
+ << "roleA"
+ << "db"
+ << "dbA"))),
+ user.get()));
+
+ // Valid role names are extracted successfully
+ ASSERT_OK(
+ v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles" << BSON_ARRAY(BSON("role"
+ << "roleA"
+ << "db"
+ << "dbA"))),
+ user.get()));
+ RoleNameIterator roles = user->getRoles();
+ ASSERT_EQUALS(RoleName("roleA", "dbA"), roles.next());
+ ASSERT_FALSE(roles.more());
+
+ // Multiple roles OK
+ ASSERT_OK(v2parser.initializeUserRolesFromUserDocument(BSON("user"
+ << "spencer"
+ << "roles"
+ << BSON_ARRAY(BSON("role"
+ << "roleA"
+ << "db"
+ << "dbA")
+ << BSON("role"
+ << "roleB"
+ << "db"
+ << "dbB"))),
+ user.get()));
+ roles = user->getRoles();
+ RoleName role = roles.next();
+ if (role == RoleName("roleA", "dbA")) {
+ ASSERT_EQUALS(RoleName("roleB", "dbB"), roles.next());
+ } else {
+ ASSERT_EQUALS(RoleName("roleB", "dbB"), role);
ASSERT_EQUALS(RoleName("roleA", "dbA"), roles.next());
- ASSERT_FALSE(roles.more());
-
- // Multiple roles OK
- ASSERT_OK(v2parser.initializeUserRolesFromUserDocument(
- BSON("user" << "spencer" <<
- "roles" << BSON_ARRAY(BSON("role" << "roleA" <<
- "db" << "dbA") <<
- BSON("role" << "roleB" <<
- "db" << "dbB"))),
- user.get()));
- roles = user->getRoles();
- RoleName role = roles.next();
- if (role == RoleName("roleA", "dbA")) {
- ASSERT_EQUALS(RoleName("roleB", "dbB"), roles.next());
- } else {
- ASSERT_EQUALS(RoleName("roleB", "dbB"), role);
- ASSERT_EQUALS(RoleName("roleA", "dbA"), roles.next());
- }
- ASSERT_FALSE(roles.more());
}
+ ASSERT_FALSE(roles.more());
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/auth/user_management_commands_parser.cpp b/src/mongo/db/auth/user_management_commands_parser.cpp
index eecef9d9301..82130e2a530 100644
--- a/src/mongo/db/auth/user_management_commands_parser.cpp
+++ b/src/mongo/db/auth/user_management_commands_parser.cpp
@@ -47,652 +47,620 @@
namespace mongo {
namespace auth {
- using std::vector;
-
- /**
- * Writes into *writeConcern a BSONObj describing the parameters to getLastError to use for
- * the write confirmation.
- */
- Status _extractWriteConcern(const BSONObj& cmdObj, BSONObj* writeConcern) {
- BSONElement writeConcernElement;
- Status status = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement);
- if (!status.isOK()) {
- if (status.code() == ErrorCodes::NoSuchKey) {
- *writeConcern = BSONObj();
- return Status::OK();
- }
- return status;
- }
- *writeConcern = writeConcernElement.Obj().getOwned();;
- return Status::OK();
- }
-
- Status _checkNoExtraFields(const BSONObj& cmdObj,
- StringData cmdName,
- const unordered_set<std::string>& validFieldNames) {
- // Iterate through all fields in command object and make sure there are no unexpected
- // ones.
- for (BSONObjIterator iter(cmdObj); iter.more(); iter.next()) {
- StringData fieldName = (*iter).fieldNameStringData();
- if (!validFieldNames.count(fieldName.toString())) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "\"" << fieldName << "\" is not "
- "a valid argument to " << cmdName);
- }
- }
- return Status::OK();
- }
-
- // Extracts a UserName or RoleName object from a BSONElement.
- template <typename Name>
- Status _parseNameFromBSONElement(const BSONElement& element,
- StringData dbname,
- StringData nameFieldName,
- StringData sourceFieldName,
- Name* parsedName) {
- if (element.type() == String) {
- *parsedName = Name(element.String(), dbname);
- }
- else if (element.type() == Object) {
- BSONObj obj = element.Obj();
-
- std::string name;
- std::string source;
- Status status = bsonExtractStringField(obj, nameFieldName, &name);
- if (!status.isOK()) {
- return status;
- }
- status = bsonExtractStringField(obj, sourceFieldName, &source);
- if (!status.isOK()) {
- return status;
- }
-
- *parsedName = Name(name, source);
- }
- else {
+using std::vector;
+
+/**
+ * Writes into *writeConcern a BSONObj describing the parameters to getLastError to use for
+ * the write confirmation.
+ */
+Status _extractWriteConcern(const BSONObj& cmdObj, BSONObj* writeConcern) {
+ BSONElement writeConcernElement;
+ Status status = bsonExtractTypedField(cmdObj, "writeConcern", Object, &writeConcernElement);
+ if (!status.isOK()) {
+ if (status.code() == ErrorCodes::NoSuchKey) {
+ *writeConcern = BSONObj();
+ return Status::OK();
+ }
+ return status;
+ }
+ *writeConcern = writeConcernElement.Obj().getOwned();
+ ;
+ return Status::OK();
+}
+
+Status _checkNoExtraFields(const BSONObj& cmdObj,
+ StringData cmdName,
+ const unordered_set<std::string>& validFieldNames) {
+ // Iterate through all fields in command object and make sure there are no unexpected
+ // ones.
+ for (BSONObjIterator iter(cmdObj); iter.more(); iter.next()) {
+ StringData fieldName = (*iter).fieldNameStringData();
+ if (!validFieldNames.count(fieldName.toString())) {
return Status(ErrorCodes::BadValue,
- "User and role names must be either strings or objects");
- }
- return Status::OK();
- }
-
- // Extracts UserName or RoleName objects from a BSONArray of role/user names.
- template <typename Name>
- Status _parseNamesFromBSONArray(const BSONArray& array,
- StringData dbname,
- StringData nameFieldName,
- StringData sourceFieldName,
- std::vector<Name>* parsedNames) {
- for (BSONObjIterator it(array); it.more(); it.next()) {
- BSONElement element = *it;
- Name name;
- Status status = _parseNameFromBSONElement(element,
- dbname,
- nameFieldName,
- sourceFieldName,
- &name);
- if (!status.isOK()) {
- return status;
- }
- parsedNames->push_back(name);
- }
- return Status::OK();
- }
-
- Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
- StringData dbname,
- std::vector<UserName>* parsedUserNames) {
- return _parseNamesFromBSONArray(usersArray,
- dbname,
- AuthorizationManager::USER_NAME_FIELD_NAME,
- AuthorizationManager::USER_DB_FIELD_NAME,
- parsedUserNames);
- }
-
- Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray,
- StringData dbname,
- std::vector<RoleName>* parsedRoleNames) {
- return _parseNamesFromBSONArray(rolesArray,
- dbname,
- AuthorizationManager::ROLE_NAME_FIELD_NAME,
- AuthorizationManager::ROLE_DB_FIELD_NAME,
- parsedRoleNames);
- }
-
- Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- std::string* parsedName,
- vector<RoleName>* parsedRoleNames,
- BSONObj* parsedWriteConcern) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert(cmdName.toString());
- validFieldNames.insert("roles");
- validFieldNames.insert("writeConcern");
-
- Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
- if (!status.isOK()) {
- return status;
+ mongoutils::str::stream() << "\"" << fieldName << "\" is not "
+ "a valid argument to "
+ << cmdName);
}
+ }
+ return Status::OK();
+}
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
+// Extracts a UserName or RoleName object from a BSONElement.
+template <typename Name>
+Status _parseNameFromBSONElement(const BSONElement& element,
+ StringData dbname,
+ StringData nameFieldName,
+ StringData sourceFieldName,
+ Name* parsedName) {
+ if (element.type() == String) {
+ *parsedName = Name(element.String(), dbname);
+ } else if (element.type() == Object) {
+ BSONObj obj = element.Obj();
- status = bsonExtractStringField(cmdObj, cmdName, parsedName);
+ std::string name;
+ std::string source;
+ Status status = bsonExtractStringField(obj, nameFieldName, &name);
if (!status.isOK()) {
return status;
}
-
- BSONElement rolesElement;
- status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
+ status = bsonExtractStringField(obj, sourceFieldName, &source);
if (!status.isOK()) {
return status;
}
- status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()),
- dbname,
- parsedRoleNames);
- if (!status.isOK()) {
- return status;
- }
+ *parsedName = Name(name, source);
+ } else {
+ return Status(ErrorCodes::BadValue,
+ "User and role names must be either strings or objects");
+ }
+ return Status::OK();
+}
- if (!parsedRoleNames->size()) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << cmdName << " command requires a non-empty "
- "\"roles\" array");
- }
- return Status::OK();
- }
-
- Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- CreateOrUpdateUserArgs* parsedArgs) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert(cmdName.toString());
- validFieldNames.insert("customData");
- validFieldNames.insert("digestPassword");
- validFieldNames.insert("pwd");
- validFieldNames.insert("roles");
- validFieldNames.insert("writeConcern");
-
- Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
+// Extracts UserName or RoleName objects from a BSONArray of role/user names.
+template <typename Name>
+Status _parseNamesFromBSONArray(const BSONArray& array,
+ StringData dbname,
+ StringData nameFieldName,
+ StringData sourceFieldName,
+ std::vector<Name>* parsedNames) {
+ for (BSONObjIterator it(array); it.more(); it.next()) {
+ BSONElement element = *it;
+ Name name;
+ Status status =
+ _parseNameFromBSONElement(element, dbname, nameFieldName, sourceFieldName, &name);
if (!status.isOK()) {
return status;
}
+ parsedNames->push_back(name);
+ }
+ return Status::OK();
+}
+
+Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
+ StringData dbname,
+ std::vector<UserName>* parsedUserNames) {
+ return _parseNamesFromBSONArray(usersArray,
+ dbname,
+ AuthorizationManager::USER_NAME_FIELD_NAME,
+ AuthorizationManager::USER_DB_FIELD_NAME,
+ parsedUserNames);
+}
+
+Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray,
+ StringData dbname,
+ std::vector<RoleName>* parsedRoleNames) {
+ return _parseNamesFromBSONArray(rolesArray,
+ dbname,
+ AuthorizationManager::ROLE_NAME_FIELD_NAME,
+ AuthorizationManager::ROLE_DB_FIELD_NAME,
+ parsedRoleNames);
+}
+
+Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ std::string* parsedName,
+ vector<RoleName>* parsedRoleNames,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert(cmdName.toString());
+ validFieldNames.insert("roles");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
- if (!status.isOK()) {
- return status;
- }
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
- BSONObjBuilder userObjBuilder;
+ status = bsonExtractStringField(cmdObj, cmdName, parsedName);
+ if (!status.isOK()) {
+ return status;
+ }
- // Parse user name
- std::string userName;
- status = bsonExtractStringField(cmdObj, cmdName, &userName);
- if (!status.isOK()) {
- return status;
- }
+ BSONElement rolesElement;
+ status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
+ if (!status.isOK()) {
+ return status;
+ }
- parsedArgs->userName = UserName(userName, dbname);
-
- // Parse password
- if (cmdObj.hasField("pwd")) {
- std::string password;
- status = bsonExtractStringField(cmdObj, "pwd", &password);
- if (!status.isOK()) {
- return status;
- }
- if (password.empty()) {
- return Status(ErrorCodes::BadValue, "User passwords must not be empty");
- }
-
- bool digestPassword; // True if the server should digest the password
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "digestPassword",
- true,
- &digestPassword);
- if (!status.isOK()) {
- return status;
- }
-
- if (digestPassword) {
- parsedArgs->hashedPassword = mongo::createPasswordDigest(
- userName, password);
- } else {
- parsedArgs->hashedPassword = password;
- }
- parsedArgs->hasHashedPassword = true;
- }
+ status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()), dbname, parsedRoleNames);
+ if (!status.isOK()) {
+ return status;
+ }
- // Parse custom data
- if (cmdObj.hasField("customData")) {
- BSONElement element;
- status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->customData = element.Obj();
- parsedArgs->hasCustomData = true;
- }
+ if (!parsedRoleNames->size()) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << cmdName << " command requires a non-empty "
+ "\"roles\" array");
+ }
+ return Status::OK();
+}
+
+Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ CreateOrUpdateUserArgs* parsedArgs) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert(cmdName.toString());
+ validFieldNames.insert("customData");
+ validFieldNames.insert("digestPassword");
+ validFieldNames.insert("pwd");
+ validFieldNames.insert("roles");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- // Parse roles
- if (cmdObj.hasField("roles")) {
- BSONElement rolesElement;
- status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
- if (!status.isOK()) {
- return status;
- }
- status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()),
- dbname,
- &parsedArgs->roles);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->hasRoles = true;
- }
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ BSONObjBuilder userObjBuilder;
- return Status::OK();
+ // Parse user name
+ std::string userName;
+ status = bsonExtractStringField(cmdObj, cmdName, &userName);
+ if (!status.isOK()) {
+ return status;
}
- Status parseAndValidateDropUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- UserName* parsedUserName,
- BSONObj* parsedWriteConcern) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("dropUser");
- validFieldNames.insert("writeConcern");
+ parsedArgs->userName = UserName(userName, dbname);
- Status status = _checkNoExtraFields(cmdObj, "dropUser", validFieldNames);
+ // Parse password
+ if (cmdObj.hasField("pwd")) {
+ std::string password;
+ status = bsonExtractStringField(cmdObj, "pwd", &password);
if (!status.isOK()) {
return status;
}
+ if (password.empty()) {
+ return Status(ErrorCodes::BadValue, "User passwords must not be empty");
+ }
- std::string user;
- status = bsonExtractStringField(cmdObj, "dropUser", &user);
+ bool digestPassword; // True if the server should digest the password
+ status =
+ bsonExtractBooleanFieldWithDefault(cmdObj, "digestPassword", true, &digestPassword);
if (!status.isOK()) {
return status;
}
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (digestPassword) {
+ parsedArgs->hashedPassword = mongo::createPasswordDigest(userName, password);
+ } else {
+ parsedArgs->hashedPassword = password;
+ }
+ parsedArgs->hasHashedPassword = true;
+ }
+
+ // Parse custom data
+ if (cmdObj.hasField("customData")) {
+ BSONElement element;
+ status = bsonExtractTypedField(cmdObj, "customData", Object, &element);
if (!status.isOK()) {
return status;
}
-
- *parsedUserName = UserName(user, dbname);
- return Status::OK();
+ parsedArgs->customData = element.Obj();
+ parsedArgs->hasCustomData = true;
}
- Status parseFromDatabaseCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- BSONObj* parsedWriteConcern,
- std::string command) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert(command);
- validFieldNames.insert("writeConcern");
-
- Status status = _checkNoExtraFields(cmdObj, command, validFieldNames);
+ // Parse roles
+ if (cmdObj.hasField("roles")) {
+ BSONElement rolesElement;
+ status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
if (!status.isOK()) {
return status;
}
-
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ status =
+ parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()), dbname, &parsedArgs->roles);
if (!status.isOK()) {
return status;
}
+ parsedArgs->hasRoles = true;
+ }
+
+ return Status::OK();
+}
+
+Status parseAndValidateDropUserCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ UserName* parsedUserName,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("dropUser");
+ validFieldNames.insert("writeConcern");
- return Status::OK();
+ Status status = _checkNoExtraFields(cmdObj, "dropUser", validFieldNames);
+ if (!status.isOK()) {
+ return status;
}
- Status parseAndValidateDropAllUsersFromDatabaseCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- BSONObj* parsedWriteConcern) {
- return parseFromDatabaseCommand(cmdObj, dbname, parsedWriteConcern, "dropAllUsersFromDatabase");
+
+ std::string user;
+ status = bsonExtractStringField(cmdObj, "dropUser", &user);
+ if (!status.isOK()) {
+ return status;
}
- Status parseUsersInfoCommand(const BSONObj& cmdObj,
- StringData dbname,
- UsersInfoArgs* parsedArgs) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("usersInfo");
- validFieldNames.insert("showPrivileges");
- validFieldNames.insert("showCredentials");
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
- Status status = _checkNoExtraFields(cmdObj, "usersInfo", validFieldNames);
- if (!status.isOK()) {
- return status;
- }
+ *parsedUserName = UserName(user, dbname);
+ return Status::OK();
+}
- if (cmdObj["usersInfo"].numberInt() == 1) {
- parsedArgs->allForDB = true;
- } else if (cmdObj["usersInfo"].type() == Array) {
- status = parseUserNamesFromBSONArray(BSONArray(cmdObj["usersInfo"].Obj()),
- dbname,
- &parsedArgs->userNames);
- if (!status.isOK()) {
- return status;
- }
- } else {
- UserName name;
- status = _parseNameFromBSONElement(cmdObj["usersInfo"],
- dbname,
- AuthorizationManager::USER_NAME_FIELD_NAME,
- AuthorizationManager::USER_DB_FIELD_NAME,
- &name);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->userNames.push_back(name);
- }
+Status parseFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern,
+ std::string command) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert(command);
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, command, validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "showPrivileges",
- false,
- &parsedArgs->showPrivileges);
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ return Status::OK();
+}
+Status parseAndValidateDropAllUsersFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern) {
+ return parseFromDatabaseCommand(cmdObj, dbname, parsedWriteConcern, "dropAllUsersFromDatabase");
+}
+
+Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfoArgs* parsedArgs) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("usersInfo");
+ validFieldNames.insert("showPrivileges");
+ validFieldNames.insert("showCredentials");
+
+ Status status = _checkNoExtraFields(cmdObj, "usersInfo", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ if (cmdObj["usersInfo"].numberInt() == 1) {
+ parsedArgs->allForDB = true;
+ } else if (cmdObj["usersInfo"].type() == Array) {
+ status = parseUserNamesFromBSONArray(
+ BSONArray(cmdObj["usersInfo"].Obj()), dbname, &parsedArgs->userNames);
if (!status.isOK()) {
return status;
}
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "showCredentials",
- false,
- &parsedArgs->showCredentials);
+ } else {
+ UserName name;
+ status = _parseNameFromBSONElement(cmdObj["usersInfo"],
+ dbname,
+ AuthorizationManager::USER_NAME_FIELD_NAME,
+ AuthorizationManager::USER_DB_FIELD_NAME,
+ &name);
if (!status.isOK()) {
return status;
}
+ parsedArgs->userNames.push_back(name);
+ }
- return Status::OK();
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "showPrivileges", false, &parsedArgs->showPrivileges);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "showCredentials", false, &parsedArgs->showCredentials);
+ if (!status.isOK()) {
+ return status;
}
- Status parseRolesInfoCommand(const BSONObj& cmdObj,
- StringData dbname,
- RolesInfoArgs* parsedArgs) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("rolesInfo");
- validFieldNames.insert("showPrivileges");
- validFieldNames.insert("showBuiltinRoles");
+ return Status::OK();
+}
- Status status = _checkNoExtraFields(cmdObj, "rolesInfo", validFieldNames);
- if (!status.isOK()) {
- return status;
- }
+Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfoArgs* parsedArgs) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("rolesInfo");
+ validFieldNames.insert("showPrivileges");
+ validFieldNames.insert("showBuiltinRoles");
- if (cmdObj["rolesInfo"].numberInt() == 1) {
- parsedArgs->allForDB = true;
- } else if (cmdObj["rolesInfo"].type() == Array) {
- status = parseRoleNamesFromBSONArray(BSONArray(cmdObj["rolesInfo"].Obj()),
- dbname,
- &parsedArgs->roleNames);
- if (!status.isOK()) {
- return status;
- }
- } else {
- RoleName name;
- status = _parseNameFromBSONElement(cmdObj["rolesInfo"],
- dbname,
- AuthorizationManager::ROLE_NAME_FIELD_NAME,
- AuthorizationManager::ROLE_DB_FIELD_NAME,
- &name);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->roleNames.push_back(name);
- }
+ Status status = _checkNoExtraFields(cmdObj, "rolesInfo", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "showPrivileges",
- false,
- &parsedArgs->showPrivileges);
+ if (cmdObj["rolesInfo"].numberInt() == 1) {
+ parsedArgs->allForDB = true;
+ } else if (cmdObj["rolesInfo"].type() == Array) {
+ status = parseRoleNamesFromBSONArray(
+ BSONArray(cmdObj["rolesInfo"].Obj()), dbname, &parsedArgs->roleNames);
if (!status.isOK()) {
return status;
}
-
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "showBuiltinRoles",
- false,
- &parsedArgs->showBuiltinRoles);
+ } else {
+ RoleName name;
+ status = _parseNameFromBSONElement(cmdObj["rolesInfo"],
+ dbname,
+ AuthorizationManager::ROLE_NAME_FIELD_NAME,
+ AuthorizationManager::ROLE_DB_FIELD_NAME,
+ &name);
if (!status.isOK()) {
return status;
}
+ parsedArgs->roleNames.push_back(name);
+ }
- return Status::OK();
- }
-
- /*
- * Validates that the given privilege BSONArray is valid.
- * If parsedPrivileges is not NULL, adds to it the privileges parsed out of the input BSONArray.
- */
- Status parseAndValidatePrivilegeArray(const BSONArray& privileges,
- PrivilegeVector* parsedPrivileges) {
- for (BSONObjIterator it(privileges); it.more(); it.next()) {
- BSONElement element = *it;
- if (element.type() != Object) {
- return Status(ErrorCodes::FailedToParse,
- "Elements in privilege arrays must be objects");
- }
-
- ParsedPrivilege parsedPrivilege;
- std::string errmsg;
- if (!parsedPrivilege.parseBSON(element.Obj(), &errmsg)) {
- return Status(ErrorCodes::FailedToParse, errmsg);
- }
- if (!parsedPrivilege.isValid(&errmsg)) {
- return Status(ErrorCodes::FailedToParse, errmsg);
- }
-
- Privilege privilege;
- if (!ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg)) {
- return Status(ErrorCodes::FailedToParse, errmsg);
- }
-
- parsedPrivileges->push_back(privilege);
- }
- return Status::OK();
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "showPrivileges", false, &parsedArgs->showPrivileges);
+ if (!status.isOK()) {
+ return status;
}
- Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- CreateOrUpdateRoleArgs* parsedArgs) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert(cmdName.toString());
- validFieldNames.insert("privileges");
- validFieldNames.insert("roles");
- validFieldNames.insert("writeConcern");
+ status = bsonExtractBooleanFieldWithDefault(
+ cmdObj, "showBuiltinRoles", false, &parsedArgs->showBuiltinRoles);
+ if (!status.isOK()) {
+ return status;
+ }
- Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
- if (!status.isOK()) {
- return status;
- }
+ return Status::OK();
+}
- status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
- if (!status.isOK()) {
- return status;
+/*
+ * Validates that the given privilege BSONArray is valid.
+ * If parsedPrivileges is not NULL, adds to it the privileges parsed out of the input BSONArray.
+ */
+Status parseAndValidatePrivilegeArray(const BSONArray& privileges,
+ PrivilegeVector* parsedPrivileges) {
+ for (BSONObjIterator it(privileges); it.more(); it.next()) {
+ BSONElement element = *it;
+ if (element.type() != Object) {
+ return Status(ErrorCodes::FailedToParse,
+ "Elements in privilege arrays must be objects");
}
- std::string roleName;
- status = bsonExtractStringField(cmdObj, cmdName, &roleName);
- if (!status.isOK()) {
- return status;
+ ParsedPrivilege parsedPrivilege;
+ std::string errmsg;
+ if (!parsedPrivilege.parseBSON(element.Obj(), &errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
}
- parsedArgs->roleName = RoleName(roleName, dbname);
-
- // Parse privileges
- if (cmdObj.hasField("privileges")) {
- BSONElement privilegesElement;
- status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
- if (!status.isOK()) {
- return status;
- }
- status = parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()),
- &parsedArgs->privileges);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->hasPrivileges = true;
+ if (!parsedPrivilege.isValid(&errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
}
- // Parse roles
- if (cmdObj.hasField("roles")) {
- BSONElement rolesElement;
- status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
- if (!status.isOK()) {
- return status;
- }
- status = parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()),
- dbname,
- &parsedArgs->roles);
- if (!status.isOK()) {
- return status;
- }
- parsedArgs->hasRoles = true;
+ Privilege privilege;
+ if (!ParsedPrivilege::parsedPrivilegeToPrivilege(parsedPrivilege, &privilege, &errmsg)) {
+ return Status(ErrorCodes::FailedToParse, errmsg);
}
- return Status::OK();
- }
-
- Status parseAndValidateRolePrivilegeManipulationCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- RoleName* parsedRoleName,
- PrivilegeVector* parsedPrivileges,
- BSONObj* parsedWriteConcern) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert(cmdName.toString());
- validFieldNames.insert("privileges");
- validFieldNames.insert("writeConcern");
- Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
- if (!status.isOK()) {
- return status;
- }
-
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
+ parsedPrivileges->push_back(privilege);
+ }
+ return Status::OK();
+}
+
+Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ CreateOrUpdateRoleArgs* parsedArgs) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert(cmdName.toString());
+ validFieldNames.insert("privileges");
+ validFieldNames.insert("roles");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- BSONObjBuilder roleObjBuilder;
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
+ if (!status.isOK()) {
+ return status;
+ }
- // Parse role name
- std::string roleName;
- status = bsonExtractStringField(cmdObj, cmdName, &roleName);
- if (!status.isOK()) {
- return status;
- }
- *parsedRoleName = RoleName(roleName, dbname);
+ std::string roleName;
+ status = bsonExtractStringField(cmdObj, cmdName, &roleName);
+ if (!status.isOK()) {
+ return status;
+ }
+ parsedArgs->roleName = RoleName(roleName, dbname);
- // Parse privileges
+ // Parse privileges
+ if (cmdObj.hasField("privileges")) {
BSONElement privilegesElement;
status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
if (!status.isOK()) {
return status;
}
status = parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()),
- parsedPrivileges);
+ &parsedArgs->privileges);
if (!status.isOK()) {
return status;
}
- if (!parsedPrivileges->size()) {
- return Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << cmdName << " command requires a non-empty "
- "\"privileges\" array");
- }
-
- return Status::OK();
+ parsedArgs->hasPrivileges = true;
}
- Status parseDropRoleCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- RoleName* parsedRoleName,
- BSONObj* parsedWriteConcern) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("dropRole");
- validFieldNames.insert("writeConcern");
-
- Status status = _checkNoExtraFields(cmdObj, "dropRole", validFieldNames);
+ // Parse roles
+ if (cmdObj.hasField("roles")) {
+ BSONElement rolesElement;
+ status = bsonExtractTypedField(cmdObj, "roles", Array, &rolesElement);
if (!status.isOK()) {
return status;
}
-
- std::string user;
- status = bsonExtractStringField(cmdObj, "dropRole", &user);
+ status =
+ parseRoleNamesFromBSONArray(BSONArray(rolesElement.Obj()), dbname, &parsedArgs->roles);
if (!status.isOK()) {
return status;
}
+ parsedArgs->hasRoles = true;
+ }
+ return Status::OK();
+}
+
+Status parseAndValidateRolePrivilegeManipulationCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ PrivilegeVector* parsedPrivileges,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert(cmdName.toString());
+ validFieldNames.insert("privileges");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, cmdName, validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = _extractWriteConcern(cmdObj, parsedWriteConcern);
- if (!status.isOK()) {
- return status;
- }
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ BSONObjBuilder roleObjBuilder;
- *parsedRoleName = RoleName(user, dbname);
- return Status::OK();
+ // Parse role name
+ std::string roleName;
+ status = bsonExtractStringField(cmdObj, cmdName, &roleName);
+ if (!status.isOK()) {
+ return status;
}
+ *parsedRoleName = RoleName(roleName, dbname);
- Status parseDropAllRolesFromDatabaseCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- BSONObj* parsedWriteConcern) {
- return parseFromDatabaseCommand(cmdObj, dbname, parsedWriteConcern, "dropAllRolesFromDatabase");
+ // Parse privileges
+ BSONElement privilegesElement;
+ status = bsonExtractTypedField(cmdObj, "privileges", Array, &privilegesElement);
+ if (!status.isOK()) {
+ return status;
+ }
+ status = parseAndValidatePrivilegeArray(BSONArray(privilegesElement.Obj()), parsedPrivileges);
+ if (!status.isOK()) {
+ return status;
+ }
+ if (!parsedPrivileges->size()) {
+ return Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << cmdName << " command requires a non-empty "
+ "\"privileges\" array");
}
- Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
- MergeAuthzCollectionsArgs* parsedArgs) {
- unordered_set<std::string> validFieldNames;
- validFieldNames.insert("_mergeAuthzCollections");
- validFieldNames.insert("tempUsersCollection");
- validFieldNames.insert("tempRolesCollection");
- validFieldNames.insert("db");
- validFieldNames.insert("drop");
- validFieldNames.insert("writeConcern");
+ return Status::OK();
+}
- Status status = _checkNoExtraFields(cmdObj, "_mergeAuthzCollections", validFieldNames);
- if (!status.isOK()) {
- return status;
- }
+Status parseDropRoleCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ BSONObj* parsedWriteConcern) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("dropRole");
+ validFieldNames.insert("writeConcern");
- status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
- if (!status.isOK()) {
- return status;
- }
+ Status status = _checkNoExtraFields(cmdObj, "dropRole", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractStringFieldWithDefault(cmdObj,
- "tempUsersCollection",
- "",
- &parsedArgs->usersCollName);
- if (!status.isOK()) {
- return status;
- }
+ std::string user;
+ status = bsonExtractStringField(cmdObj, "dropRole", &user);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractStringFieldWithDefault(cmdObj,
- "tempRolesCollection",
- "",
- &parsedArgs->rolesCollName);
- if (!status.isOK()) {
- return status;
- }
+ status = _extractWriteConcern(cmdObj, parsedWriteConcern);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractStringField(cmdObj, "db", &parsedArgs->db);
- if (!status.isOK()) {
- if (status == ErrorCodes::NoSuchKey) {
- return Status(ErrorCodes::OutdatedClient,
- "Missing \"db\" field for _mergeAuthzCollections command. This is "
- "most likely due to running an outdated (pre-2.6.4) version of "
- "mongorestore.");
- }
- return status;
- }
+ *parsedRoleName = RoleName(user, dbname);
+ return Status::OK();
+}
+
+Status parseDropAllRolesFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern) {
+ return parseFromDatabaseCommand(cmdObj, dbname, parsedWriteConcern, "dropAllRolesFromDatabase");
+}
+
+Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
+ MergeAuthzCollectionsArgs* parsedArgs) {
+ unordered_set<std::string> validFieldNames;
+ validFieldNames.insert("_mergeAuthzCollections");
+ validFieldNames.insert("tempUsersCollection");
+ validFieldNames.insert("tempRolesCollection");
+ validFieldNames.insert("db");
+ validFieldNames.insert("drop");
+ validFieldNames.insert("writeConcern");
+
+ Status status = _checkNoExtraFields(cmdObj, "_mergeAuthzCollections", validFieldNames);
+ if (!status.isOK()) {
+ return status;
+ }
- status = bsonExtractBooleanFieldWithDefault(cmdObj,
- "drop",
- false,
- &parsedArgs->drop);
- if (!status.isOK()) {
- return status;
+ status = _extractWriteConcern(cmdObj, &parsedArgs->writeConcern);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractStringFieldWithDefault(
+ cmdObj, "tempUsersCollection", "", &parsedArgs->usersCollName);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractStringFieldWithDefault(
+ cmdObj, "tempRolesCollection", "", &parsedArgs->rolesCollName);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ status = bsonExtractStringField(cmdObj, "db", &parsedArgs->db);
+ if (!status.isOK()) {
+ if (status == ErrorCodes::NoSuchKey) {
+ return Status(ErrorCodes::OutdatedClient,
+ "Missing \"db\" field for _mergeAuthzCollections command. This is "
+ "most likely due to running an outdated (pre-2.6.4) version of "
+ "mongorestore.");
}
+ return status;
+ }
- return Status::OK();
+ status = bsonExtractBooleanFieldWithDefault(cmdObj, "drop", false, &parsedArgs->drop);
+ if (!status.isOK()) {
+ return status;
}
-} // namespace auth
-} // namespace mongo
+ return Status::OK();
+}
+
+} // namespace auth
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_management_commands_parser.h b/src/mongo/db/auth/user_management_commands_parser.h
index 68ef26ce894..c55210e8978 100644
--- a/src/mongo/db/auth/user_management_commands_parser.h
+++ b/src/mongo/db/auth/user_management_commands_parser.h
@@ -43,190 +43,185 @@
namespace mongo {
namespace auth {
- struct CreateOrUpdateUserArgs {
- UserName userName;
- bool hasHashedPassword;
- std::string hashedPassword;
- bool hasCustomData;
- BSONObj customData;
- bool hasRoles;
- std::vector<RoleName> roles;
- BSONObj writeConcern;
-
- CreateOrUpdateUserArgs() :
- hasHashedPassword(false), hasCustomData(false), hasRoles(false) {}
- };
-
- /**
- * Takes a command object describing an invocation of the "createUser" or "updateUser" commands
- * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
- * the arguments into the "parsedArgs" output param.
- */
- Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- CreateOrUpdateUserArgs* parsedArgs);
-
- /**
- * Takes a command object describing an invocation of one of "grantRolesToUser",
- * "revokeRolesFromUser", "grantDelegateRolesToUser", "revokeDelegateRolesFromUser",
- * "grantRolesToRole", and "revokeRolesFromRoles" (which command it is is specified in the
- * "cmdName" argument), and parses out (into the parsedName out param) the user/role name of
- * the user/roles being modified, the roles being granted or revoked, and the write concern to
- * use.
- */
- Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- std::string* parsedName,
- std::vector<RoleName>* parsedRoleNames,
- BSONObj* parsedWriteConcern);
-
- /**
- * Takes a command object describing an invocation of the "dropUser" command and parses out
- * the UserName of the user to be removed and the writeConcern.
- * Also validates the input and returns a non-ok Status if there is anything wrong.
- */
- Status parseAndValidateDropUserCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- UserName* parsedUserName,
- BSONObj* parsedWriteConcern);
-
- /**
- * Takes a command object describing an invocation of the "dropAllUsersFromDatabase" command and
- * parses out the write concern.
- * Also validates the input and returns a non-ok Status if there is anything wrong.
- */
- Status parseAndValidateDropAllUsersFromDatabaseCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- BSONObj* parsedWriteConcern);
-
- struct UsersInfoArgs {
- std::vector<UserName> userNames;
- bool allForDB;
- bool showPrivileges;
- bool showCredentials;
- UsersInfoArgs() : allForDB(false), showPrivileges(false), showCredentials(false) {}
- };
-
- /**
- * Takes a command object describing an invocation of the "usersInfo" command and parses out
- * all the arguments into the "parsedArgs" output param.
- */
- Status parseUsersInfoCommand(const BSONObj& cmdObj,
- StringData dbname,
- UsersInfoArgs* parsedArgs);
-
- struct RolesInfoArgs {
- std::vector<RoleName> roleNames;
- bool allForDB;
- bool showPrivileges;
- bool showBuiltinRoles;
- RolesInfoArgs() : allForDB(false), showPrivileges(false), showBuiltinRoles(false) {}
- };
-
- /**
- * Takes a command object describing an invocation of the "rolesInfo" command and parses out
- * the arguments into the "parsedArgs" output param.
- */
- Status parseRolesInfoCommand(const BSONObj& cmdObj,
- StringData dbname,
- RolesInfoArgs* parsedArgs);
-
- struct CreateOrUpdateRoleArgs {
- RoleName roleName;
- bool hasRoles;
- std::vector<RoleName> roles;
- bool hasPrivileges;
- PrivilegeVector privileges;
- BSONObj writeConcern;
- CreateOrUpdateRoleArgs() : hasRoles(false), hasPrivileges(false) {}
- };
-
- /**
- * Takes a command object describing an invocation of the "createRole" or "updateRole" commands
- * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
- * the arguments into the "parsedArgs" output param.
- */
- Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- CreateOrUpdateRoleArgs* parsedArgs);
-
- /**
- * Takes a command object describing an invocation of the "grantPrivilegesToRole" or
- * "revokePrivilegesFromRole" commands, and parses out the role name of the
- * role being modified, the privileges being granted or revoked, and the write concern to use.
- */
- Status parseAndValidateRolePrivilegeManipulationCommands(const BSONObj& cmdObj,
- StringData cmdName,
- const std::string& dbname,
- RoleName* parsedRoleName,
- PrivilegeVector* parsedPrivileges,
- BSONObj* parsedWriteConcern);
-
- /**
- * Takes a command object describing an invocation of the "dropRole" command and parses out
- * the RoleName of the role to be removed and the writeConcern.
- */
- Status parseDropRoleCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- RoleName* parsedRoleName,
- BSONObj* parsedWriteConcern);
-
- /**
- * Takes a command object describing an invocation of the "dropAllRolesFromDatabase" command and
- * parses out the write concern.
- */
- Status parseDropAllRolesFromDatabaseCommand(const BSONObj& cmdObj,
- const std::string& dbname,
- BSONObj* parsedWriteConcern);
-
- /**
- * Parses the privileges described in "privileges" into a vector of Privilege objects.
- * Returns Status::OK() upon successfully parsing all the elements of "privileges".
- */
- Status parseAndValidatePrivilegeArray(const BSONArray& privileges,
- PrivilegeVector* parsedPrivileges);
-
- /**
- * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
- * output param parsedRoleNames) a list of the role names in the input array.
- * Performs syntactic validation of "rolesArray", only.
- */
- Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray,
- StringData dbname,
- std::vector<RoleName>* parsedRoleNames);
-
- /**
- * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
- * output param parsedUserNames) a list of the usernames in the input array.
- * Performs syntactic validation of "usersArray", only.
- */
- Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
- StringData dbname,
- std::vector<UserName>* parsedUserNames);
-
- struct MergeAuthzCollectionsArgs {
- std::string usersCollName;
- std::string rolesCollName;
- std::string db;
- bool drop;
- BSONObj writeConcern;
- MergeAuthzCollectionsArgs() : drop(false) {}
- };
-
- /**
- * Takes a command object describing an invocation of the "_mergeAuthzCollections" command and
- * parses out the name of the temporary collections to use for user and role data, whether or
- * not to drop the existing users/roles, the database if this is a for a db-specific restore,
- * and the writeConcern.
- * Returns ErrorCodes::OutdatedClient if the "db" field is missing, as that likely indicates
- * the command was sent by an outdated (pre 2.6.4) version of mongorestore.
- * Returns other codes indicating missing or incorrectly typed fields.
- */
- Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
- MergeAuthzCollectionsArgs* parsedArgs);
-
-} // namespace auth
-} // namespace mongo
+struct CreateOrUpdateUserArgs {
+ UserName userName;
+ bool hasHashedPassword;
+ std::string hashedPassword;
+ bool hasCustomData;
+ BSONObj customData;
+ bool hasRoles;
+ std::vector<RoleName> roles;
+ BSONObj writeConcern;
+
+ CreateOrUpdateUserArgs() : hasHashedPassword(false), hasCustomData(false), hasRoles(false) {}
+};
+
+/**
+ * Takes a command object describing an invocation of the "createUser" or "updateUser" commands
+ * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
+ * the arguments into the "parsedArgs" output param.
+ */
+Status parseCreateOrUpdateUserCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ CreateOrUpdateUserArgs* parsedArgs);
+
+/**
+ * Takes a command object describing an invocation of one of "grantRolesToUser",
+ * "revokeRolesFromUser", "grantDelegateRolesToUser", "revokeDelegateRolesFromUser",
+ * "grantRolesToRole", and "revokeRolesFromRoles" (which command it is is specified in the
+ * "cmdName" argument), and parses out (into the parsedName out param) the user/role name of
+ * the user/roles being modified, the roles being granted or revoked, and the write concern to
+ * use.
+ */
+Status parseRolePossessionManipulationCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ std::string* parsedName,
+ std::vector<RoleName>* parsedRoleNames,
+ BSONObj* parsedWriteConcern);
+
+/**
+ * Takes a command object describing an invocation of the "dropUser" command and parses out
+ * the UserName of the user to be removed and the writeConcern.
+ * Also validates the input and returns a non-ok Status if there is anything wrong.
+ */
+Status parseAndValidateDropUserCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ UserName* parsedUserName,
+ BSONObj* parsedWriteConcern);
+
+/**
+ * Takes a command object describing an invocation of the "dropAllUsersFromDatabase" command and
+ * parses out the write concern.
+ * Also validates the input and returns a non-ok Status if there is anything wrong.
+ */
+Status parseAndValidateDropAllUsersFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern);
+
+struct UsersInfoArgs {
+ std::vector<UserName> userNames;
+ bool allForDB;
+ bool showPrivileges;
+ bool showCredentials;
+ UsersInfoArgs() : allForDB(false), showPrivileges(false), showCredentials(false) {}
+};
+
+/**
+ * Takes a command object describing an invocation of the "usersInfo" command and parses out
+ * all the arguments into the "parsedArgs" output param.
+ */
+Status parseUsersInfoCommand(const BSONObj& cmdObj, StringData dbname, UsersInfoArgs* parsedArgs);
+
+struct RolesInfoArgs {
+ std::vector<RoleName> roleNames;
+ bool allForDB;
+ bool showPrivileges;
+ bool showBuiltinRoles;
+ RolesInfoArgs() : allForDB(false), showPrivileges(false), showBuiltinRoles(false) {}
+};
+
+/**
+ * Takes a command object describing an invocation of the "rolesInfo" command and parses out
+ * the arguments into the "parsedArgs" output param.
+ */
+Status parseRolesInfoCommand(const BSONObj& cmdObj, StringData dbname, RolesInfoArgs* parsedArgs);
+
+struct CreateOrUpdateRoleArgs {
+ RoleName roleName;
+ bool hasRoles;
+ std::vector<RoleName> roles;
+ bool hasPrivileges;
+ PrivilegeVector privileges;
+ BSONObj writeConcern;
+ CreateOrUpdateRoleArgs() : hasRoles(false), hasPrivileges(false) {}
+};
+
+/**
+ * Takes a command object describing an invocation of the "createRole" or "updateRole" commands
+ * (which command it is is specified in "cmdName") on the database "dbname", and parses out all
+ * the arguments into the "parsedArgs" output param.
+ */
+Status parseCreateOrUpdateRoleCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ CreateOrUpdateRoleArgs* parsedArgs);
+
+/**
+ * Takes a command object describing an invocation of the "grantPrivilegesToRole" or
+ * "revokePrivilegesFromRole" commands, and parses out the role name of the
+ * role being modified, the privileges being granted or revoked, and the write concern to use.
+ */
+Status parseAndValidateRolePrivilegeManipulationCommands(const BSONObj& cmdObj,
+ StringData cmdName,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ PrivilegeVector* parsedPrivileges,
+ BSONObj* parsedWriteConcern);
+
+/**
+ * Takes a command object describing an invocation of the "dropRole" command and parses out
+ * the RoleName of the role to be removed and the writeConcern.
+ */
+Status parseDropRoleCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ RoleName* parsedRoleName,
+ BSONObj* parsedWriteConcern);
+
+/**
+ * Takes a command object describing an invocation of the "dropAllRolesFromDatabase" command and
+ * parses out the write concern.
+ */
+Status parseDropAllRolesFromDatabaseCommand(const BSONObj& cmdObj,
+ const std::string& dbname,
+ BSONObj* parsedWriteConcern);
+
+/**
+ * Parses the privileges described in "privileges" into a vector of Privilege objects.
+ * Returns Status::OK() upon successfully parsing all the elements of "privileges".
+ */
+Status parseAndValidatePrivilegeArray(const BSONArray& privileges,
+ PrivilegeVector* parsedPrivileges);
+
+/**
+ * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
+ * output param parsedRoleNames) a list of the role names in the input array.
+ * Performs syntactic validation of "rolesArray", only.
+ */
+Status parseRoleNamesFromBSONArray(const BSONArray& rolesArray,
+ StringData dbname,
+ std::vector<RoleName>* parsedRoleNames);
+
+/**
+ * Takes a BSONArray of name,db pair documents, parses that array and returns (via the
+ * output param parsedUserNames) a list of the usernames in the input array.
+ * Performs syntactic validation of "usersArray", only.
+ */
+Status parseUserNamesFromBSONArray(const BSONArray& usersArray,
+ StringData dbname,
+ std::vector<UserName>* parsedUserNames);
+
+struct MergeAuthzCollectionsArgs {
+ std::string usersCollName;
+ std::string rolesCollName;
+ std::string db;
+ bool drop;
+ BSONObj writeConcern;
+ MergeAuthzCollectionsArgs() : drop(false) {}
+};
+
+/**
+ * Takes a command object describing an invocation of the "_mergeAuthzCollections" command and
+ * parses out the name of the temporary collections to use for user and role data, whether or
+ * not to drop the existing users/roles, the database if this is a for a db-specific restore,
+ * and the writeConcern.
+ * Returns ErrorCodes::OutdatedClient if the "db" field is missing, as that likely indicates
+ * the command was sent by an outdated (pre 2.6.4) version of mongorestore.
+ * Returns other codes indicating missing or incorrectly typed fields.
+ */
+Status parseMergeAuthzCollectionsCommand(const BSONObj& cmdObj,
+ MergeAuthzCollectionsArgs* parsedArgs);
+
+} // namespace auth
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_name.cpp b/src/mongo/db/auth/user_name.cpp
index 422f415d478..c2e768edf95 100644
--- a/src/mongo/db/auth/user_name.cpp
+++ b/src/mongo/db/auth/user_name.cpp
@@ -35,20 +35,19 @@
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();
- }
+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();
+}
- std::ostream& operator<<(std::ostream& os, const UserName& name) {
- return os << name.getFullName();
- }
+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 55220f33e10..03f39fa42d6 100644
--- a/src/mongo/db/auth/user_name.h
+++ b/src/mongo/db/auth/user_name.h
@@ -37,126 +37,153 @@
namespace mongo {
+/**
+ * Representation of a name of a principal (authenticatable user) in a MongoDB system.
+ *
+ * Consists of a "user name" part, and a "database name" part.
+ */
+class UserName {
+public:
+ UserName() : _splitPoint(0) {}
+ UserName(StringData user, StringData dbname);
+
/**
- * Representation of a name of a principal (authenticatable user) in a MongoDB system.
- *
- * Consists of a "user name" part, and a "database name" part.
+ * Gets the user part of a UserName.
*/
- class UserName {
- public:
- UserName() : _splitPoint(0) {}
- UserName(StringData user, StringData dbname);
-
- /**
- * Gets the user part of a UserName.
- */
- StringData getUser() const { return StringData(_fullName).substr(0, _splitPoint); }
-
- /**
- * Gets the database name part of a UserName.
- */
- StringData getDB() const { return StringData(_fullName).substr(_splitPoint + 1); }
-
- /**
- * Gets the full unique name of a user as a string, formatted as "user@db".
- */
- const std::string& getFullName() const { return _fullName; }
-
- /**
- * Stringifies the object, for logging/debugging.
- */
- std::string toString() const { return getFullName(); }
-
- 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.
- };
-
- static inline bool operator==(const UserName& lhs, const UserName& rhs) {
- return lhs.getFullName() == rhs.getFullName();
+ StringData getUser() const {
+ return StringData(_fullName).substr(0, _splitPoint);
}
- static inline bool operator!=(const UserName& lhs, const UserName& rhs) {
- return lhs.getFullName() != rhs.getFullName();
+ /**
+ * Gets the database name part of a UserName.
+ */
+ StringData getDB() const {
+ return StringData(_fullName).substr(_splitPoint + 1);
}
- static inline bool operator<(const UserName& lhs, const UserName& rhs) {
- return lhs.getFullName() < rhs.getFullName();
+ /**
+ * Gets the full unique name of a user as a string, formatted as "user@db".
+ */
+ const std::string& getFullName() const {
+ return _fullName;
}
- std::ostream& operator<<(std::ostream& os, const UserName& name);
-
/**
- * Iterator over an unspecified container of UserName objects.
+ * Stringifies the object, for logging/debugging.
*/
- class UserNameIterator {
- public:
- class Impl {
- MONGO_DISALLOW_COPYING(Impl);
- public:
- Impl() {};
- virtual ~Impl() {};
- static Impl* clone(Impl* orig) { return orig ? orig->doClone(): NULL; }
- virtual bool more() const = 0;
- virtual const UserName& get() const = 0;
-
- virtual const UserName& next() = 0;
-
- private:
- virtual Impl* doClone() const = 0;
- };
-
- UserNameIterator() : _impl(nullptr) {}
- UserNameIterator(const UserNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
- explicit UserNameIterator(Impl* impl) : _impl(impl) {}
-
- UserNameIterator& operator=(const UserNameIterator& other) {
- _impl.reset(Impl::clone(other._impl.get()));
- return *this;
- }
+ std::string toString() const {
+ return getFullName();
+ }
- bool more() const { return _impl.get() && _impl->more(); }
- const UserName& get() const { return _impl->get(); }
+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.
+};
- const UserName& next() { return _impl->next(); }
+static inline bool operator==(const UserName& lhs, const UserName& rhs) {
+ return lhs.getFullName() == rhs.getFullName();
+}
- const UserName& operator*() const { return get(); }
- const UserName* operator->() const { return &get(); }
+static inline bool operator!=(const UserName& lhs, const UserName& rhs) {
+ return lhs.getFullName() != rhs.getFullName();
+}
- private:
- std::unique_ptr<Impl> _impl;
- };
+static inline bool operator<(const UserName& lhs, const UserName& rhs) {
+ return lhs.getFullName() < rhs.getFullName();
+}
+
+std::ostream& operator<<(std::ostream& os, const UserName& name);
+/**
+ * Iterator over an unspecified container of UserName objects.
+ */
+class UserNameIterator {
+public:
+ class Impl {
+ MONGO_DISALLOW_COPYING(Impl);
- template <typename ContainerIterator>
- class UserNameContainerIteratorImpl : public UserNameIterator::Impl {
- MONGO_DISALLOW_COPYING(UserNameContainerIteratorImpl);
public:
- UserNameContainerIteratorImpl(const ContainerIterator& begin,
- const ContainerIterator& end) :
- _curr(begin), _end(end) {}
- virtual ~UserNameContainerIteratorImpl() {}
- virtual bool more() const { return _curr != _end; }
- virtual const UserName& next() { return *(_curr++); }
- virtual const UserName& get() const { return *_curr; }
- virtual UserNameIterator::Impl* doClone() const {
- return new UserNameContainerIteratorImpl(_curr, _end);
+ Impl(){};
+ virtual ~Impl(){};
+ static Impl* clone(Impl* orig) {
+ return orig ? orig->doClone() : NULL;
}
+ virtual bool more() const = 0;
+ virtual const UserName& get() const = 0;
+
+ virtual const UserName& next() = 0;
private:
- ContainerIterator _curr;
- ContainerIterator _end;
+ virtual Impl* doClone() const = 0;
};
- template <typename ContainerIterator>
- UserNameIterator makeUserNameIterator(const ContainerIterator& begin,
- const ContainerIterator& end) {
- return UserNameIterator( new UserNameContainerIteratorImpl<ContainerIterator>(begin, end));
+ UserNameIterator() : _impl(nullptr) {}
+ UserNameIterator(const UserNameIterator& other) : _impl(Impl::clone(other._impl.get())) {}
+ explicit UserNameIterator(Impl* impl) : _impl(impl) {}
+
+ UserNameIterator& operator=(const UserNameIterator& other) {
+ _impl.reset(Impl::clone(other._impl.get()));
+ return *this;
+ }
+
+ bool more() const {
+ return _impl.get() && _impl->more();
+ }
+ const UserName& get() const {
+ return _impl->get();
+ }
+
+ const UserName& next() {
+ return _impl->next();
}
- template <typename Container>
- UserNameIterator makeUserNameIteratorForContainer(const Container& container) {
- return makeUserNameIterator(container.begin(), container.end());
+ const UserName& operator*() const {
+ return get();
}
+ const UserName* operator->() const {
+ return &get();
+ }
+
+private:
+ std::unique_ptr<Impl> _impl;
+};
+
+
+template <typename ContainerIterator>
+class UserNameContainerIteratorImpl : public UserNameIterator::Impl {
+ MONGO_DISALLOW_COPYING(UserNameContainerIteratorImpl);
+
+public:
+ UserNameContainerIteratorImpl(const ContainerIterator& begin, const ContainerIterator& end)
+ : _curr(begin), _end(end) {}
+ virtual ~UserNameContainerIteratorImpl() {}
+ virtual bool more() const {
+ return _curr != _end;
+ }
+ virtual const UserName& next() {
+ return *(_curr++);
+ }
+ virtual const UserName& get() const {
+ return *_curr;
+ }
+ virtual UserNameIterator::Impl* doClone() const {
+ return new UserNameContainerIteratorImpl(_curr, _end);
+ }
+
+private:
+ ContainerIterator _curr;
+ ContainerIterator _end;
+};
+
+template <typename ContainerIterator>
+UserNameIterator makeUserNameIterator(const ContainerIterator& begin,
+ const ContainerIterator& end) {
+ return UserNameIterator(new UserNameContainerIteratorImpl<ContainerIterator>(begin, end));
+}
+
+template <typename Container>
+UserNameIterator makeUserNameIteratorForContainer(const Container& container) {
+ return makeUserNameIterator(container.begin(), container.end());
+}
} // namespace mongo
diff --git a/src/mongo/db/auth/user_name_hash.h b/src/mongo/db/auth/user_name_hash.h
index f60a897f590..6f38d9ccfda 100644
--- a/src/mongo/db/auth/user_name_hash.h
+++ b/src/mongo/db/auth/user_name_hash.h
@@ -35,9 +35,10 @@
// Define hash function for UserNames so they can be keys in std::unordered_map
MONGO_HASH_NAMESPACE_START
- template <> struct hash<mongo::UserName> {
- size_t operator()(const mongo::UserName& pname) const {
- return hash<std::string>()(pname.getFullName());
- }
- };
+template <>
+struct hash<mongo::UserName> {
+ size_t operator()(const mongo::UserName& pname) const {
+ return hash<std::string>()(pname.getFullName());
+ }
+};
MONGO_HASH_NAMESPACE_END
diff --git a/src/mongo/db/auth/user_set.cpp b/src/mongo/db/auth/user_set.cpp
index c81869a8ad5..2616b5cc697 100644
--- a/src/mongo/db/auth/user_set.cpp
+++ b/src/mongo/db/auth/user_set.cpp
@@ -36,94 +36,99 @@
namespace mongo {
namespace {
- class UserSetNameIteratorImpl : public UserNameIterator::Impl {
- MONGO_DISALLOW_COPYING(UserSetNameIteratorImpl);
- public:
- UserSetNameIteratorImpl(const UserSet::iterator& begin,
- const UserSet::iterator& end) :
- _curr(begin), _end(end) {}
- virtual ~UserSetNameIteratorImpl() {}
- virtual bool more() const { return _curr != _end; }
- virtual const UserName& next() { return (*(_curr++))->getName(); }
- virtual const UserName& get() const { return (*_curr)->getName(); }
- virtual UserNameIterator::Impl* doClone() const {
- return new UserSetNameIteratorImpl(_curr, _end);
- }
+class UserSetNameIteratorImpl : public UserNameIterator::Impl {
+ MONGO_DISALLOW_COPYING(UserSetNameIteratorImpl);
- private:
- UserSet::iterator _curr;
- UserSet::iterator _end;
- };
-} // namespace
+public:
+ UserSetNameIteratorImpl(const UserSet::iterator& begin, const UserSet::iterator& end)
+ : _curr(begin), _end(end) {}
+ virtual ~UserSetNameIteratorImpl() {}
+ virtual bool more() const {
+ return _curr != _end;
+ }
+ virtual const UserName& next() {
+ return (*(_curr++))->getName();
+ }
+ virtual const UserName& get() const {
+ return (*_curr)->getName();
+ }
+ virtual UserNameIterator::Impl* doClone() const {
+ return new UserSetNameIteratorImpl(_curr, _end);
+ }
- UserSet::UserSet() : _users(), _usersEnd(_users.end()) {}
- UserSet::~UserSet() {}
+private:
+ UserSet::iterator _curr;
+ UserSet::iterator _end;
+};
+} // namespace
- User* UserSet::add(User* user) {
- for (mutable_iterator it = mbegin(); it != mend(); ++it) {
- User* current = *it;
- if (current->getName().getDB() == user->getName().getDB()) {
- // There can be only one user per database.
- *it = user;
- return current;
- }
- }
- if (_usersEnd == _users.end()) {
- _users.push_back(user);
- _usersEnd = _users.end();
- }
- else {
- *_usersEnd = user;
- ++_usersEnd;
+UserSet::UserSet() : _users(), _usersEnd(_users.end()) {}
+UserSet::~UserSet() {}
+
+User* UserSet::add(User* user) {
+ for (mutable_iterator it = mbegin(); it != mend(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == user->getName().getDB()) {
+ // There can be only one user per database.
+ *it = user;
+ return current;
}
- return NULL;
}
+ if (_usersEnd == _users.end()) {
+ _users.push_back(user);
+ _usersEnd = _users.end();
+ } else {
+ *_usersEnd = user;
+ ++_usersEnd;
+ }
+ return NULL;
+}
- User* UserSet::removeByDBName(StringData dbname) {
- for (iterator it = begin(); it != end(); ++it) {
- User* current = *it;
- if (current->getName().getDB() == dbname) {
- return removeAt(it);
- }
+User* UserSet::removeByDBName(StringData dbname) {
+ for (iterator it = begin(); it != end(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == dbname) {
+ return removeAt(it);
}
- return NULL;
}
+ return NULL;
+}
- User* UserSet::replaceAt(iterator it, User* replacement) {
- size_t offset = it - begin();
- User* old = _users[offset];
- _users[offset] = replacement;
- return old;
- }
+User* UserSet::replaceAt(iterator it, User* replacement) {
+ size_t offset = it - begin();
+ User* old = _users[offset];
+ _users[offset] = replacement;
+ return old;
+}
- User* UserSet::removeAt(iterator it) {
- size_t offset = it - begin();
- User* old = _users[offset];
- --_usersEnd;
- _users[offset] = *_usersEnd;
- *_usersEnd = NULL;
- return old;
- }
+User* UserSet::removeAt(iterator it) {
+ size_t offset = it - begin();
+ User* old = _users[offset];
+ --_usersEnd;
+ _users[offset] = *_usersEnd;
+ *_usersEnd = NULL;
+ return old;
+}
- User* UserSet::lookup(const UserName& name) const {
- User* user = lookupByDBName(name.getDB());
- if (user && user->getName() == name) {
- return user;
- }
- return NULL;
+User* UserSet::lookup(const UserName& name) const {
+ User* user = lookupByDBName(name.getDB());
+ if (user && user->getName() == name) {
+ return user;
}
+ return NULL;
+}
- User* UserSet::lookupByDBName(StringData dbname) const {
- for (iterator it = begin(); it != end(); ++it) {
- User* current = *it;
- if (current->getName().getDB() == dbname) {
- return current;
- }
+User* UserSet::lookupByDBName(StringData dbname) const {
+ for (iterator it = begin(); it != end(); ++it) {
+ User* current = *it;
+ if (current->getName().getDB() == dbname) {
+ return current;
}
- return NULL;
}
+ return NULL;
+}
- UserNameIterator UserSet::getNames() const {
- return UserNameIterator(new UserSetNameIteratorImpl(begin(), end()));
- }
-} // namespace mongo
+UserNameIterator UserSet::getNames() const {
+ return UserNameIterator(new UserSetNameIteratorImpl(begin(), end()));
+}
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_set.h b/src/mongo/db/auth/user_set.h
index 8d2bc630d2e..9ad0ab03202 100644
--- a/src/mongo/db/auth/user_set.h
+++ b/src/mongo/db/auth/user_set.h
@@ -39,81 +39,90 @@
namespace mongo {
+/**
+ * A collection of authenticated users.
+ * This class does not do any locking/synchronization, the consumer will be responsible for
+ * synchronizing access.
+ */
+class UserSet {
+ MONGO_DISALLOW_COPYING(UserSet);
+
+public:
+ typedef std::vector<User*>::const_iterator iterator;
+
+ UserSet();
+ ~UserSet();
+
+ /**
+ * Adds a User to the UserSet.
+ *
+ * The UserSet does not take ownership of the User.
+ *
+ * As there can only be one user per database in the UserSet, if a User already exists for
+ * the new User's database, the old user will be removed from the set and returned. It is
+ * the caller's responsibility to then release that user. If no user already exists for the
+ * new user's database, returns NULL.
+ *
+ * Invalidates any outstanding iterators or NameIterators.
+ */
+ User* add(User* user);
+
+ /**
+ * Replaces the user at "it" with "replacement." Does not take ownership of the User.
+ * Returns a pointer to the old user referenced by "it". Does _not_ invalidate "iterator"
+ * instances.
+ */
+ User* replaceAt(iterator it, User* replacement);
+
+ /**
+ * Removes the user at "it", and returns a pointer to it. After this call, "it" remains
+ * valid. It will either equal "end()", or refer to some user between the values of "it"
+ * and "end()" before this call was made.
+ */
+ User* removeAt(iterator it);
+
/**
- * A collection of authenticated users.
- * This class does not do any locking/synchronization, the consumer will be responsible for
- * synchronizing access.
+ * Removes the User whose authentication credentials came from dbname, and returns that
+ * user. It is the caller's responsibility to then release that user back to the
+ * authorizationManger. If no user exists for the given database, returns NULL;
*/
- class UserSet {
- MONGO_DISALLOW_COPYING(UserSet);
- public:
- typedef std::vector<User*>::const_iterator iterator;
-
- UserSet();
- ~UserSet();
-
- /**
- * Adds a User to the UserSet.
- *
- * The UserSet does not take ownership of the User.
- *
- * As there can only be one user per database in the UserSet, if a User already exists for
- * the new User's database, the old user will be removed from the set and returned. It is
- * the caller's responsibility to then release that user. If no user already exists for the
- * new user's database, returns NULL.
- *
- * Invalidates any outstanding iterators or NameIterators.
- */
- User* add(User* user);
-
- /**
- * Replaces the user at "it" with "replacement." Does not take ownership of the User.
- * Returns a pointer to the old user referenced by "it". Does _not_ invalidate "iterator"
- * instances.
- */
- User* replaceAt(iterator it, User* replacement);
-
- /**
- * Removes the user at "it", and returns a pointer to it. After this call, "it" remains
- * valid. It will either equal "end()", or refer to some user between the values of "it"
- * and "end()" before this call was made.
- */
- User* removeAt(iterator it);
-
- /**
- * Removes the User whose authentication credentials came from dbname, and returns that
- * user. It is the caller's responsibility to then release that user back to the
- * authorizationManger. If no user exists for the given database, returns NULL;
- */
- User* removeByDBName(StringData dbname);
-
- // Returns the User with the given name, or NULL if not found.
- // Ownership of the returned User remains with the UserSet. The pointer
- // returned is only guaranteed to remain valid until the next non-const method is called
- // on the UserSet.
- User* lookup(const UserName& name) const;
-
- // Gets the user whose authentication credentials came from dbname, or NULL if none
- // exist. There should be at most one such user.
- User* lookupByDBName(StringData dbname) const;
-
- // Gets an iterator over the names of the users stored in the set. The iterator is
- // valid until the next non-const method is called on the UserSet.
- UserNameIterator getNames() const;
-
- iterator begin() const { return _users.begin(); }
- iterator end() const { return _usersEnd; }
-
- private:
- typedef std::vector<User*>::iterator mutable_iterator;
-
- mutable_iterator mbegin() { return _users.begin(); }
- mutable_iterator mend() { return _usersEnd; }
-
- // The UserSet maintains ownership of the Users in it, and is responsible for
- // returning them to the AuthorizationManager when done with them.
- std::vector<User*> _users;
- std::vector<User*>::iterator _usersEnd;
- };
-
-} // namespace mongo
+ User* removeByDBName(StringData dbname);
+
+ // Returns the User with the given name, or NULL if not found.
+ // Ownership of the returned User remains with the UserSet. The pointer
+ // returned is only guaranteed to remain valid until the next non-const method is called
+ // on the UserSet.
+ User* lookup(const UserName& name) const;
+
+ // Gets the user whose authentication credentials came from dbname, or NULL if none
+ // exist. There should be at most one such user.
+ User* lookupByDBName(StringData dbname) const;
+
+ // Gets an iterator over the names of the users stored in the set. The iterator is
+ // valid until the next non-const method is called on the UserSet.
+ UserNameIterator getNames() const;
+
+ iterator begin() const {
+ return _users.begin();
+ }
+ iterator end() const {
+ return _usersEnd;
+ }
+
+private:
+ typedef std::vector<User*>::iterator mutable_iterator;
+
+ mutable_iterator mbegin() {
+ return _users.begin();
+ }
+ mutable_iterator mend() {
+ return _usersEnd;
+ }
+
+ // The UserSet maintains ownership of the Users in it, and is responsible for
+ // returning them to the AuthorizationManager when done with them.
+ std::vector<User*> _users;
+ std::vector<User*>::iterator _usersEnd;
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/auth/user_set_test.cpp b/src/mongo/db/auth/user_set_test.cpp
index c1f036ae289..d8c4290d7c8 100644
--- a/src/mongo/db/auth/user_set_test.cpp
+++ b/src/mongo/db/auth/user_set_test.cpp
@@ -39,78 +39,78 @@
namespace mongo {
namespace {
- TEST(UserSetTest, BasicTest) {
- UserSet set;
-
- User* p1 = new User(UserName("Bob", "test"));
- User* p2 = new User(UserName("George", "test"));
- User* p3 = new User(UserName("Bob", "test2"));
-
- const std::unique_ptr<User> delp1(p1);
- const std::unique_ptr<User> delp2(p2);
- const std::unique_ptr<User> delp3(p3);
-
- ASSERT_NULL(set.lookup(UserName("Bob", "test")));
- ASSERT_NULL(set.lookup(UserName("George", "test")));
- ASSERT_NULL(set.lookup(UserName("Bob", "test2")));
- ASSERT_NULL(set.lookupByDBName("test"));
- ASSERT_NULL(set.lookupByDBName("test2"));
-
- ASSERT_NULL(set.add(p1));
-
- ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
- ASSERT_EQUALS(p1, set.lookupByDBName("test"));
- ASSERT_NULL(set.lookup(UserName("George", "test")));
- ASSERT_NULL(set.lookup(UserName("Bob", "test2")));
- ASSERT_NULL(set.lookupByDBName("test2"));
-
- // This should not replace the existing user "Bob" because they are different databases
- ASSERT_NULL(set.add(p3));
-
- ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
- ASSERT_EQUALS(p1, set.lookupByDBName("test"));
- ASSERT_NULL(set.lookup(UserName("George", "test")));
- ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
- ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
-
- User* replaced = set.add(p2); // This should replace Bob since they're on the same database
-
- ASSERT_EQUALS(replaced, p1);
- ASSERT_NULL(set.lookup(UserName("Bob", "test")));
- ASSERT_EQUALS(p2, set.lookup(UserName("George", "test")));
- ASSERT_EQUALS(p2, set.lookupByDBName("test"));
- ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
- ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
-
- User* removed = set.removeByDBName("test");
-
- ASSERT_EQUALS(removed, p2);
- ASSERT_NULL(set.lookup(UserName("Bob", "test")));
- ASSERT_NULL(set.lookup(UserName("George", "test")));
- ASSERT_NULL(set.lookupByDBName("test"));
- ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
- ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
-
- UserNameIterator iter = set.getNames();
- ASSERT_TRUE(iter.more());
- ASSERT_EQUALS(iter.next(), UserName("Bob", "test2"));
- ASSERT_FALSE(iter.more());
- }
-
- TEST(UserSetTest, IterateNames) {
- UserSet pset;
- UserNameIterator iter = pset.getNames();
- ASSERT(!iter.more());
-
- std::unique_ptr<User> user(new User(UserName("bob", "test")));
- ASSERT_NULL(pset.add(user.get()));
-
- iter = pset.getNames();
- ASSERT(iter.more());
- ASSERT_EQUALS(*iter, UserName("bob", "test"));
- ASSERT_EQUALS(iter.next(), UserName("bob", "test"));
- ASSERT(!iter.more());
- }
+TEST(UserSetTest, BasicTest) {
+ UserSet set;
+
+ User* p1 = new User(UserName("Bob", "test"));
+ User* p2 = new User(UserName("George", "test"));
+ User* p3 = new User(UserName("Bob", "test2"));
+
+ const std::unique_ptr<User> delp1(p1);
+ const std::unique_ptr<User> delp2(p2);
+ const std::unique_ptr<User> delp3(p3);
+
+ ASSERT_NULL(set.lookup(UserName("Bob", "test")));
+ ASSERT_NULL(set.lookup(UserName("George", "test")));
+ ASSERT_NULL(set.lookup(UserName("Bob", "test2")));
+ ASSERT_NULL(set.lookupByDBName("test"));
+ ASSERT_NULL(set.lookupByDBName("test2"));
+
+ ASSERT_NULL(set.add(p1));
+
+ ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
+ ASSERT_EQUALS(p1, set.lookupByDBName("test"));
+ ASSERT_NULL(set.lookup(UserName("George", "test")));
+ ASSERT_NULL(set.lookup(UserName("Bob", "test2")));
+ ASSERT_NULL(set.lookupByDBName("test2"));
+
+ // This should not replace the existing user "Bob" because they are different databases
+ ASSERT_NULL(set.add(p3));
+
+ ASSERT_EQUALS(p1, set.lookup(UserName("Bob", "test")));
+ ASSERT_EQUALS(p1, set.lookupByDBName("test"));
+ ASSERT_NULL(set.lookup(UserName("George", "test")));
+ ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
+ ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
+
+ User* replaced = set.add(p2); // This should replace Bob since they're on the same database
+
+ ASSERT_EQUALS(replaced, p1);
+ ASSERT_NULL(set.lookup(UserName("Bob", "test")));
+ ASSERT_EQUALS(p2, set.lookup(UserName("George", "test")));
+ ASSERT_EQUALS(p2, set.lookupByDBName("test"));
+ ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
+ ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
+
+ User* removed = set.removeByDBName("test");
+
+ ASSERT_EQUALS(removed, p2);
+ ASSERT_NULL(set.lookup(UserName("Bob", "test")));
+ ASSERT_NULL(set.lookup(UserName("George", "test")));
+ ASSERT_NULL(set.lookupByDBName("test"));
+ ASSERT_EQUALS(p3, set.lookup(UserName("Bob", "test2")));
+ ASSERT_EQUALS(p3, set.lookupByDBName("test2"));
+
+ UserNameIterator iter = set.getNames();
+ ASSERT_TRUE(iter.more());
+ ASSERT_EQUALS(iter.next(), UserName("Bob", "test2"));
+ ASSERT_FALSE(iter.more());
+}
+
+TEST(UserSetTest, IterateNames) {
+ UserSet pset;
+ UserNameIterator iter = pset.getNames();
+ ASSERT(!iter.more());
+
+ std::unique_ptr<User> user(new User(UserName("bob", "test")));
+ ASSERT_NULL(pset.add(user.get()));
+
+ iter = pset.getNames();
+ ASSERT(iter.more());
+ ASSERT_EQUALS(*iter, UserName("bob", "test"));
+ ASSERT_EQUALS(iter.next(), UserName("bob", "test"));
+ ASSERT(!iter.more());
+}
} // namespace
} // namespace mongo