/** * Copyright (C) 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include #include #include #include "mongo/base/disallow_copying.h" #include "mongo/base/status.h" #include "mongo/bson/mutable/element.h" #include "mongo/bson/oid.h" #include "mongo/db/auth/action_set.h" #include "mongo/db/auth/resource_pattern.h" #include "mongo/db/auth/role_graph.h" #include "mongo/db/auth/user.h" #include "mongo/db/auth/user_name.h" #include "mongo/db/auth/user_name_hash.h" #include "mongo/db/jsobj.h" #include "mongo/db/namespace_string.h" #include "mongo/platform/unordered_map.h" #include "mongo/stdx/functional.h" namespace mongo { class AuthzManagerExternalState; class UserDocumentParser; class OperationContext; /** * 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: // The newly constructed AuthorizationManager takes ownership of "externalState" explicit AuthorizationManager(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_SOURCE_FIELD_NAME; // TODO: rename to 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. Users are stored in admin.system.users, * roles in admin.system.roles. */ static const int schemaVersion26Final = 3; // 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); /** * 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. bool hasAnyPrivilegeDocuments(OperationContext* txn) const; /** * Updates the auth schema version document to reflect that the system is upgraded to * schemaVersion26Final. * * Do not call if getAuthorizationVersion() reports a value other than schemaVersion26Final. */ Status writeAuthSchemaVersionIfNeeded(); /** * Creates the given user object in the given database. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status insertPrivilegeDocument(const std::string& dbname, const BSONObj& userObj, const BSONObj& writeConcern) const; /** * Updates the given user object with the given update modifier. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status updatePrivilegeDocument(const UserName& user, const BSONObj& updateObj, const BSONObj& writeConcern) const; /* * Removes users for the given database matching the given query. * Writes into *numRemoved the number of user documents that were modified. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status removePrivilegeDocuments(const BSONObj& query, const BSONObj& writeConcern, int* numRemoved) const; /** * Creates the given role object in the given database. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status insertRoleDocument(const BSONObj& roleObj, const BSONObj& writeConcern) const; /** * Updates the given role object with the given update modifier. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status updateRoleDocument(const RoleName& role, const BSONObj& updateObj, const BSONObj& writeConcern) const; /** * Updates documents matching "query" according to "updatePattern" in "collectionName". * Should only be called on collections with authorization documents in them * (ie admin.system.users and admin.system.roles). */ Status updateAuthzDocuments(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& updatePattern, bool upsert, bool multi, const BSONObj& writeConcern, int* nMatched) const; /* * Removes roles matching the given query. * Writes into *numRemoved the number of role documents that were modified. * 'writeConcern' contains the arguments to be passed to getLastError to block for * successful completion of the write. */ Status removeRoleDocuments(const BSONObj& query, const BSONObj& writeConcern, int* numRemoved) const; /** * Finds all documents matching "query" in "collectionName". For each document returned, * calls the function resultProcessor on it. * Should only be called on collections with authorization documents in them * (ie admin.system.users and admin.system.roles). */ Status queryAuthzDocument(const NamespaceString& collectionName, const BSONObj& query, const BSONObj& projection, const stdx::function& resultProcessor); // 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(const 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* 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(); /** * 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); /** * Tries to acquire the global lock guarding modifications to all persistent data related * to authorization, namely the admin.system.users, admin.system.roles, and * admin.system.version collections. This serializes all writers to the authorization * documents, but does not impact readers. */ bool tryAcquireAuthzUpdateLock(const StringData& why); /** * Releases the lock guarding modifications to persistent authorization data, which must * already be held. */ void releaseAuthzUpdateLock(); /** * Performs one step in the process of upgrading the stored authorization data to the * newest schema. * * On success, returns Status::OK(), and *isDone will indicate whether there are more * steps to perform. * * If the authorization data is already fully upgraded, returns Status::OK and sets *isDone * to true, so this is safe to call on a fully upgraded system. * * On failure, returns a status other than Status::OK(). In this case, is is typically safe * to try again. */ Status upgradeSchemaStep( OperationContext* txn, const BSONObj& writeConcern, bool* isDone); /** * Performs up to maxSteps steps in the process of upgrading the stored authorization data * to the newest schema. Behaves as if by repeatedly calling upgradeSchemaStep up to * maxSteps times until either it completes the upgrade or returns a non-OK status. * * Invalidates the user cache before the first step and after each attempted step. * * Returns Status::OK() to indicate that the upgrade process has completed successfully. * Returns ErrorCodes::OperationIncomplete to indicate that progress was made, but that more * steps must be taken to complete the process. Other returns indicate a failure to make * progress performing the upgrade, and the specific code and message in the returned status * may provide additional information. */ Status upgradeSchema(OperationContext* txn, int maxSteps, const BSONObj& writeConcern); /** * Hook called by replication code to let the AuthorizationManager observe changes * to relevant collections. */ void logOp(const char* opstr, const char* ns, const BSONObj& obj, BSONObj* patt, bool* b); 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::auto_ptr* 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; scoped_ptr _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 _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. */ boost::mutex _cacheMutex; /** * Condition used to signal that it is OK for another CacheGuard to enter a fetch phase. * Manipulated via CacheGuard. */ boost::condition_variable _fetchPhaseIsReady; }; } // namespace mongo