1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
/**
* Copyright (C) 2018-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* 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 Server Side 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 <memory>
#include <string>
#include <vector>
#include "mongo/base/status.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/authz_session_external_state.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/user_name.h"
#include "mongo/db/namespace_string.h"
#include "mongo/db/operation_context.h"
namespace mongo {
class Client;
class AuthorizationContract;
/**
* 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::Client 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 {
AuthorizationSession(const AuthorizationSession&) = delete;
AuthorizationSession& operator=(const AuthorizationSession&) = delete;
public:
static std::unique_ptr<AuthorizationSession> create(AuthorizationManager*);
AuthorizationSession() = default;
/**
* Provides a way to swap out impersonate data for the duration of the ScopedImpersonate's
* lifetime.
*/
class ScopedImpersonate {
public:
ScopedImpersonate(AuthorizationSession* authSession,
boost::optional<UserName>* user,
std::vector<RoleName>* roles)
: _authSession(*authSession), _user(*user), _roles(*roles) {
swap();
}
~ScopedImpersonate() {
this->swap();
}
private:
void swap();
AuthorizationSession& _authSession;
boost::optional<UserName>& _user;
std::vector<RoleName>& _roles;
};
friend class ScopedImpersonate;
/**
* Gets the AuthorizationSession associated with the given "client", or nullptr.
*
* The "client" object continues to own the returned AuthorizationSession.
*/
static AuthorizationSession* get(Client* client);
/**
* Gets the AuthorizationSession associated with the given "client", or nullptr.
*
* The "client" object continues to own the returned AuthorizationSession.
*/
static AuthorizationSession* get(Client& client);
/**
* Returns false if AuthorizationSession::get(client) would return nullptr.
*/
static bool exists(Client* 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(Client* client, std::unique_ptr<AuthorizationSession> session);
// Takes ownership of the externalState.
virtual ~AuthorizationSession() = 0;
virtual AuthorizationManager& getAuthorizationManager() = 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.
// TODO: try to eliminate the need for this call.
virtual void startRequest(OperationContext* opCtx) = 0;
/**
* Start tracking permissions and privileges in the authorization contract.
*/
virtual void startContractTracking() = 0;
/**
* Adds the User identified by "UserName" to the authorization session, acquiring privileges
* for it in the process.
*/
virtual Status addAndAuthorizeUser(OperationContext* opCtx, const UserName& userName) = 0;
// 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
virtual User* lookupUser(const UserName& name) = 0;
// Get the authenticated user's object handle, if any.
virtual boost::optional<UserHandle> getAuthenticatedUser() = 0;
// Is auth disabled? Returns true if auth is disabled.
virtual bool shouldIgnoreAuthChecks() = 0;
// Is authenticated as at least one user.
virtual bool isAuthenticated() = 0;
// Gets the name of the currently authenticated user (if any).
virtual boost::optional<UserName> getAuthenticatedUserName() = 0;
// Gets an iterator over the roles of all authenticated users stored in this manager.
virtual RoleNameIterator getAuthenticatedRoleNames() = 0;
// Removes all authenticated principals while in kSecurityToken authentication mode.
virtual void logoutSecurityTokenUser(Client* client) = 0;
// Removes any authenticated principals and revokes any privileges that were granted via those
// principals. This function modifies state. Synchronizes with the Client lock.
virtual void logoutAllDatabases(Client* client, StringData reason) = 0;
// Removes any authenticated principals whose authorization credentials came from the given
// database, and revokes any privileges that were granted via that principal. This function
// modifies state. Synchronizes with the Client lock.
virtual void logoutDatabase(Client* client, StringData dbname, StringData reason) = 0;
// How the active session is authenticated.
enum class AuthenticationMode {
kNone, // Not authenticated.
kConnection, // For the duration of the connection, or until logged out.
kSecurityToken, // By operation scoped security token.
};
virtual AuthenticationMode getAuthenticationMode() const = 0;
// Adds the internalSecurity user to the set of authenticated users.
// Used to grant internal threads full access. Takes in the Client
// as a parameter so it can take out a lock on the client.
virtual void grantInternalAuthorization(Client* client) = 0;
virtual void grantInternalAuthorization(OperationContext* opCtx) = 0;
// Checks if the current session is authorized to list the collections in the given
// database. If it is, return a privilegeVector containing the privileges used to authorize
// this command.
virtual StatusWith<PrivilegeVector> checkAuthorizedToListCollections(StringData dbname,
const BSONObj& cmdObj) = 0;
// Checks if this connection is using the localhost bypass
virtual bool isUsingLocalhostBypass() = 0;
// Checks if this connection has the privileges necessary to parse a namespace from a
// given BSONElement.
virtual bool isAuthorizedToParseNamespaceElement(const BSONElement& elem) = 0;
// Checks if this connection has the privileges necessary to parse a namespace from a
// given NamespaceOrUUID object.
virtual bool isAuthorizedToParseNamespaceElement(const NamespaceStringOrUUID& nss) = 0;
// Checks if this connection has the privileges necessary to create a new role
virtual bool isAuthorizedToCreateRole(const RoleName& roleName) = 0;
// Utility function for isAuthorizedToChangeOwnPasswordAsUser and
// isAuthorizedToChangeOwnCustomDataAsUser
virtual bool isAuthorizedToChangeAsUser(const UserName& userName, ActionType actionType) = 0;
// 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.
virtual bool isAuthenticatedAsUserWithRole(const RoleName& roleName) = 0;
// Returns true if this session is authorized for the given Privilege.
//
// Contains all the authorization logic including handling things like the localhost
// exception.
virtual bool isAuthorizedForPrivilege(const Privilege& privilege) = 0;
// Like isAuthorizedForPrivilege, above, except returns true if the session is authorized
// for all of the listed privileges.
virtual bool isAuthorizedForPrivileges(const std::vector<Privilege>& privileges) = 0;
// Utility function for isAuthorizedForPrivilege(Privilege(resource, action)).
virtual bool isAuthorizedForActionsOnResource(const ResourcePattern& resource,
ActionType action) = 0;
// Utility function for isAuthorizedForPrivilege(Privilege(resource, actions)).
virtual bool isAuthorizedForActionsOnResource(const ResourcePattern& resource,
const ActionSet& actions) = 0;
// Utility function for
// isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), action).
virtual bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
ActionType action) = 0;
// Utility function for
// isAuthorizedForActionsOnResource(ResourcePattern::forExactNamespace(ns), actions).
virtual bool isAuthorizedForActionsOnNamespace(const NamespaceString& ns,
const ActionSet& actions) = 0;
// Returns true if the current session possesses a privilege which could apply to the
// database resource, or a specific or arbitrary resource within the database.
virtual bool isAuthorizedForAnyActionOnAnyResourceInDB(StringData dbname) = 0;
// Returns true if the current session possesses a privilege which applies to the resource.
virtual bool isAuthorizedForAnyActionOnResource(const ResourcePattern& resource) = 0;
// Replaces the data for the user that a system user is impersonating with new data.
// The auditing system adds this user and their roles to each audit record in the log.
virtual void setImpersonatedUserData(const UserName& username,
const std::vector<RoleName>& roles) = 0;
// Gets the name of the user, if any, that the system user is impersonating.
virtual boost::optional<UserName> getImpersonatedUserName() = 0;
// Gets an iterator over the roles of all users that the system user is impersonating.
virtual RoleNameIterator getImpersonatedRoleNames() = 0;
// Clears the data for impersonated users.
virtual void clearImpersonatedUserData() = 0;
// Returns true if the session and 'opClient's AuthorizationSession share an
// authenticated user. If either object has impersonated users,
// those users will be considered as 'authenticated' for the purpose of this check.
//
// The existence of 'opClient' must be guaranteed through locks taken by the caller,
// as demonstrated by opClientLock which must be a lock taken on opClient.
//
// Returns true if the current auth session and the opClient's auth session have users
// in common.
virtual bool isCoauthorizedWithClient(Client* opClient, WithLock opClientLock) = 0;
// Returns true if the specified userName is the currently authenticated user,
// or if the session is unauthenticated and `boost::none` is specified.
// Impersonated users are not considered as 'authenticated' for the purpose of this check.
// This always returns true if auth is not enabled.
virtual bool isCoauthorizedWith(const boost::optional<UserName>& userName) = 0;
// Tells whether impersonation is active or not. This state is set when
// setImpersonatedUserData is called and cleared when clearImpersonatedUserData is
// called.
virtual bool isImpersonating() const = 0;
// Returns a status encoding whether the current session in the specified `opCtx` has privilege
// to access a cursor in the specified `cursorSessionId` parameter. Returns `Status::OK()`,
// when the session is accessible. Returns a `mongo::Status` with information regarding the
// nature of session inaccessibility when the session is not accessible.
virtual Status checkCursorSessionPrivilege(
OperationContext* opCtx, boost::optional<LogicalSessionId> cursorSessionId) = 0;
// Verify the authorization contract. If contract == nullptr, no check is performed.
virtual void verifyContract(const AuthorizationContract* contract) const = 0;
// Returns true if any user has the privilege to bypass write blocking mode for the cluster
// resource.
virtual bool mayBypassWriteBlockingMode() const = 0;
protected:
virtual std::tuple<boost::optional<UserName>*, std::vector<RoleName>*> _getImpersonations() = 0;
};
// Returns a status encoding whether the current session in the specified `opCtx` has privilege to
// access a cursor in the specified `cursorSessionId` parameter. Returns `Status::OK()`, when the
// session is accessible. Returns a `mongo::Status` with information regarding the nature of
// session inaccessibility when the session is not accessible.
inline Status checkCursorSessionPrivilege(OperationContext* const opCtx,
const boost::optional<LogicalSessionId> cursorSessionId) {
if (!AuthorizationSession::exists(opCtx->getClient())) {
return Status::OK();
}
auto* const authSession = AuthorizationSession::get(opCtx->getClient());
return authSession->checkCursorSessionPrivilege(opCtx, cursorSessionId);
}
} // namespace mongo
|