/**
* 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 "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/privilege_format.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/condition_variable.h"
#include "mongo/stdx/functional.h"
#include "mongo/stdx/mutex.h"
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 authzManager);
// The newly constructed AuthorizationManager takes ownership of "externalState"
explicit AuthorizationManager(std::unique_ptr 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 makeAuthorizationSession();
/**
* Sets whether or not startup AuthSchema validation checks should be applied in this manager.
*/
void setShouldValidateAuthSchemaOnStartup(bool validate);
/**
* Returns true if startup AuthSchema validation checks should be applied in this manager.
*/
bool shouldValidateAuthSchemaOnStartup();
/**
* 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* opCtx, 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* opCtx);
// 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;
/**
* Delegates method call to the underlying AuthzManagerExternalState.
*/
Status getUserDescription(OperationContext* opCtx, const UserName& userName, BSONObj* result);
/**
* Delegates method call to the underlying AuthzManagerExternalState.
*/
Status getRoleDescription(OperationContext* opCtx,
const RoleName& roleName,
PrivilegeFormat privilegeFormat,
BSONObj* result);
/**
* Delegates method call to the underlying AuthzManagerExternalState.
*/
Status getRolesDescription(OperationContext* opCtx,
const std::vector& roleName,
PrivilegeFormat privilegeFormat,
BSONObj* result);
/**
* Delegates method call to the underlying AuthzManagerExternalState.
*/
Status getRoleDescriptionsForDB(OperationContext* opCtx,
const std::string dbname,
PrivilegeFormat privilegeFormat,
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* opCtx, 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* opCtx);
/**
* 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* opCtx,
const char* opstr,
const char* ns,
const BSONObj& obj,
const 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* opCtx,
const UserName& userName,
std::unique_ptr* acquiredUser);
/**
* True if AuthSchema startup checks should be applied in this AuthorizationManager.
*
* Defaults to true. Changes to its value are not synchronized, so it should only be set
* at initalization-time.
*/
bool _startupAuthSchemaValidation;
/**
* 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 _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.
*/
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