1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kAccessControl
32
33 #include "mongo/db/auth/authz_manager_external_state_local.h"
34
35 #include "mongo/base/status.h"
36 #include "mongo/bson/mutable/algorithm.h"
37 #include "mongo/bson/mutable/document.h"
38 #include "mongo/bson/mutable/element.h"
39 #include "mongo/bson/util/bson_extract.h"
40 #include "mongo/db/auth/authorization_manager.h"
41 #include "mongo/db/auth/privilege_parser.h"
42 #include "mongo/db/auth/user_document_parser.h"
43 #include "mongo/db/operation_context.h"
44 #include "mongo/db/server_options.h"
45 #include "mongo/util/log.h"
46 #include "mongo/util/mongoutils/str.h"
47
48 namespace mongo {
49
50 using std::vector;
51
initialize(OperationContext * opCtx)52 Status AuthzManagerExternalStateLocal::initialize(OperationContext* opCtx) {
53 Status status = _initializeRoleGraph(opCtx);
54 if (!status.isOK()) {
55 if (status == ErrorCodes::GraphContainsCycle) {
56 error() << "Cycle detected in admin.system.roles; role inheritance disabled. "
57 "Remove the listed cycle and any others to re-enable role inheritance. "
58 << redact(status);
59 } else {
60 error() << "Could not generate role graph from admin.system.roles; "
61 "only system roles available: "
62 << redact(status);
63 }
64 }
65
66 return Status::OK();
67 }
68
getStoredAuthorizationVersion(OperationContext * opCtx,int * outVersion)69 Status AuthzManagerExternalStateLocal::getStoredAuthorizationVersion(OperationContext* opCtx,
70 int* outVersion) {
71 BSONObj versionDoc;
72 Status status = findOne(opCtx,
73 AuthorizationManager::versionCollectionNamespace,
74 AuthorizationManager::versionDocumentQuery,
75 &versionDoc);
76 if (status.isOK()) {
77 BSONElement versionElement = versionDoc[AuthorizationManager::schemaVersionFieldName];
78 if (versionElement.isNumber()) {
79 *outVersion = versionElement.numberInt();
80 return Status::OK();
81 } else if (versionElement.eoo()) {
82 return Status(ErrorCodes::NoSuchKey,
83 mongoutils::str::stream() << "No "
84 << AuthorizationManager::schemaVersionFieldName
85 << " field in version document.");
86 } else {
87 return Status(ErrorCodes::TypeMismatch,
88 mongoutils::str::stream()
89 << "Could not determine schema version of authorization data. "
90 "Bad (non-numeric) type "
91 << typeName(versionElement.type())
92 << " ("
93 << versionElement.type()
94 << ") for "
95 << AuthorizationManager::schemaVersionFieldName
96 << " field in version document");
97 }
98 } else if (status == ErrorCodes::NoMatchingDocument) {
99 *outVersion = AuthorizationManager::schemaVersion28SCRAM;
100 return Status::OK();
101 } else {
102 return status;
103 }
104 }
105
106 namespace {
addRoleNameToObjectElement(mutablebson::Element object,const RoleName & role)107 void addRoleNameToObjectElement(mutablebson::Element object, const RoleName& role) {
108 fassert(17153, object.appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME, role.getRole()));
109 fassert(17154, object.appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, role.getDB()));
110 }
111
addRoleNameObjectsToArrayElement(mutablebson::Element array,RoleNameIterator roles)112 void addRoleNameObjectsToArrayElement(mutablebson::Element array, RoleNameIterator roles) {
113 for (; roles.more(); roles.next()) {
114 mutablebson::Element roleElement = array.getDocument().makeElementObject("");
115 addRoleNameToObjectElement(roleElement, roles.get());
116 fassert(17155, array.pushBack(roleElement));
117 }
118 }
119
addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,mutablebson::Element warningsElement,const PrivilegeVector & privileges)120 void addPrivilegeObjectsOrWarningsToArrayElement(mutablebson::Element privilegesElement,
121 mutablebson::Element warningsElement,
122 const PrivilegeVector& privileges) {
123 std::string errmsg;
124 for (size_t i = 0; i < privileges.size(); ++i) {
125 ParsedPrivilege pp;
126 if (ParsedPrivilege::privilegeToParsedPrivilege(privileges[i], &pp, &errmsg)) {
127 fassert(17156, privilegesElement.appendObject("", pp.toBSON()));
128 } else {
129 fassert(17157,
130 warningsElement.appendString(
131 "",
132 std::string(mongoutils::str::stream()
133 << "Skipped privileges on resource "
134 << privileges[i].getResourcePattern().toString()
135 << ". Reason: "
136 << errmsg)));
137 }
138 }
139 }
140
addAuthenticationRestrictionObjectsToArrayElement(mutablebson::Element restrictionsElement,const std::vector<SharedRestrictionDocument> & restrictions)141 void addAuthenticationRestrictionObjectsToArrayElement(
142 mutablebson::Element restrictionsElement,
143 const std::vector<SharedRestrictionDocument>& restrictions) {
144 for (const auto& r : restrictions) {
145 fassert(40560, restrictionsElement.appendArray("", r->toBSON()));
146 }
147 }
148 } // namespace
149
hasAnyPrivilegeDocuments(OperationContext * opCtx)150 bool AuthzManagerExternalStateLocal::hasAnyPrivilegeDocuments(OperationContext* opCtx) {
151 BSONObj userBSONObj;
152 Status statusFindUsers =
153 findOne(opCtx, AuthorizationManager::usersCollectionNamespace, BSONObj(), &userBSONObj);
154
155 // If we were unable to complete the query,
156 // it's best to assume that there _are_ privilege documents.
157 if (statusFindUsers != ErrorCodes::NoMatchingDocument) {
158 return true;
159 }
160 Status statusFindRoles =
161 findOne(opCtx, AuthorizationManager::rolesCollectionNamespace, BSONObj(), &userBSONObj);
162 return statusFindRoles != ErrorCodes::NoMatchingDocument;
163 }
164
getUserDescription(OperationContext * opCtx,const UserName & userName,BSONObj * result)165 Status AuthzManagerExternalStateLocal::getUserDescription(OperationContext* opCtx,
166 const UserName& userName,
167 BSONObj* result) {
168 Status status = Status::OK();
169
170 if (!shouldUseRolesFromConnection(opCtx, userName)) {
171 status = _getUserDocument(opCtx, userName, result);
172 if (!status.isOK())
173 return status;
174 } else {
175 // We are able to artifically construct the external user from the request
176 BSONArrayBuilder userRoles;
177 auto& sslPeerInfo = SSLPeerInfo::forSession(opCtx->getClient()->session());
178 for (const RoleName& role : sslPeerInfo.roles) {
179 userRoles << BSON("role" << role.getRole() << "db" << role.getDB());
180 }
181 *result = BSON("_id" << userName.getUser() << "user" << userName.getUser() << "db"
182 << userName.getDB()
183 << "credentials"
184 << BSON("external" << true)
185 << "roles"
186 << userRoles.arr());
187 }
188
189 BSONElement directRolesElement;
190 status = bsonExtractTypedField(*result, "roles", Array, &directRolesElement);
191 if (!status.isOK())
192 return status;
193 std::vector<RoleName> directRoles;
194 status =
195 V2UserDocumentParser::parseRoleVector(BSONArray(directRolesElement.Obj()), &directRoles);
196 if (!status.isOK())
197 return status;
198
199 mutablebson::Document resultDoc(*result, mutablebson::Document::kInPlaceDisabled);
200 resolveUserRoles(&resultDoc, directRoles);
201 *result = resultDoc.getObject();
202
203 return Status::OK();
204 }
205
resolveUserRoles(mutablebson::Document * userDoc,const std::vector<RoleName> & directRoles)206 void AuthzManagerExternalStateLocal::resolveUserRoles(mutablebson::Document* userDoc,
207 const std::vector<RoleName>& directRoles) {
208 unordered_set<RoleName> indirectRoles;
209 PrivilegeVector allPrivileges;
210 std::vector<SharedRestrictionDocument> allAuthenticationRestrictions;
211 bool isRoleGraphConsistent = false;
212
213 {
214 stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
215 isRoleGraphConsistent = _roleGraphState == roleGraphStateConsistent;
216 for (const auto& role : directRoles) {
217 indirectRoles.insert(role);
218 if (isRoleGraphConsistent) {
219 for (RoleNameIterator subordinates = _roleGraph.getIndirectSubordinates(role);
220 subordinates.more();
221 subordinates.next()) {
222 indirectRoles.insert(subordinates.get());
223 }
224 }
225
226 const auto& currentPrivileges = isRoleGraphConsistent
227 ? _roleGraph.getAllPrivileges(role)
228 : _roleGraph.getDirectPrivileges(role);
229 for (const auto& priv : currentPrivileges) {
230 Privilege::addPrivilegeToPrivilegeVector(&allPrivileges, priv);
231 }
232
233 if (isRoleGraphConsistent) {
234 const auto& currentAuthenticationRestrictions =
235 _roleGraph.getAllAuthenticationRestrictions(role);
236 allAuthenticationRestrictions.insert(allAuthenticationRestrictions.end(),
237 currentAuthenticationRestrictions.begin(),
238 currentAuthenticationRestrictions.end());
239 } else {
240 const auto& dar = _roleGraph.getDirectAuthenticationRestrictions(role);
241 if (dar.get()) {
242 allAuthenticationRestrictions.push_back(dar);
243 }
244 }
245 }
246 }
247
248 auto warningsElement = userDoc->makeElementArray("warnings");
249
250 auto inheritedRolesElement = userDoc->makeElementArray("inheritedRoles");
251 fassert(17159, userDoc->root().pushBack(inheritedRolesElement));
252 addRoleNameObjectsToArrayElement(inheritedRolesElement,
253 makeRoleNameIteratorForContainer(indirectRoles));
254
255 auto privilegesElement = userDoc->makeElementArray("inheritedPrivileges");
256 fassert(17158, userDoc->root().pushBack(privilegesElement));
257 addPrivilegeObjectsOrWarningsToArrayElement(privilegesElement, warningsElement, allPrivileges);
258
259 auto inheritedAuthenticationRestrictionsElement =
260 userDoc->makeElementArray("inheritedAuthenticationRestrictions");
261 fassert(40558, userDoc->root().pushBack(inheritedAuthenticationRestrictionsElement));
262 addAuthenticationRestrictionObjectsToArrayElement(inheritedAuthenticationRestrictionsElement,
263 allAuthenticationRestrictions);
264
265 if (!mutablebson::findFirstChildNamed(userDoc->root(), "authenticationRestrictions").ok()) {
266 auto authenticationRestrictionsElement =
267 userDoc->makeElementArray("authenticationRestrictions");
268 fassert(40572, userDoc->root().pushBack(authenticationRestrictionsElement));
269 }
270
271 if (!isRoleGraphConsistent) {
272 fassert(17160,
273 warningsElement.appendString(
274 "", "Role graph inconsistent, only direct privileges available."));
275 }
276
277 if (warningsElement.hasChildren()) {
278 fassert(17161, userDoc->root().pushBack(warningsElement));
279 }
280 }
281
_getUserDocument(OperationContext * opCtx,const UserName & userName,BSONObj * userDoc)282 Status AuthzManagerExternalStateLocal::_getUserDocument(OperationContext* opCtx,
283 const UserName& userName,
284 BSONObj* userDoc) {
285 Status status = findOne(opCtx,
286 AuthorizationManager::usersCollectionNamespace,
287 BSON(AuthorizationManager::USER_NAME_FIELD_NAME
288 << userName.getUser()
289 << AuthorizationManager::USER_DB_FIELD_NAME
290 << userName.getDB()),
291 userDoc);
292
293 if (status == ErrorCodes::NoMatchingDocument) {
294 status =
295 Status(ErrorCodes::UserNotFound,
296 mongoutils::str::stream() << "Could not find user " << userName.getFullName());
297 }
298 return status;
299 }
300
getRoleDescription(OperationContext * opCtx,const RoleName & roleName,PrivilegeFormat showPrivileges,AuthenticationRestrictionsFormat showRestrictions,BSONObj * result)301 Status AuthzManagerExternalStateLocal::getRoleDescription(
302 OperationContext* opCtx,
303 const RoleName& roleName,
304 PrivilegeFormat showPrivileges,
305 AuthenticationRestrictionsFormat showRestrictions,
306 BSONObj* result) {
307 if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
308 mutablebson::Document resultDoc;
309 mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
310 fassert(40273, resultDoc.root().pushBack(rolesElement));
311 addRoleNameObjectsToArrayElement(
312 rolesElement, makeRoleNameIteratorForContainer(std::vector<RoleName>{roleName}));
313 resolveUserRoles(&resultDoc, {roleName});
314 *result = resultDoc.getObject();
315 return Status::OK();
316 }
317 stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
318 return _getRoleDescription_inlock(roleName, showPrivileges, showRestrictions, result);
319 }
320
getRolesDescription(OperationContext * opCtx,const std::vector<RoleName> & roles,PrivilegeFormat showPrivileges,AuthenticationRestrictionsFormat showRestrictions,BSONObj * result)321 Status AuthzManagerExternalStateLocal::getRolesDescription(
322 OperationContext* opCtx,
323 const std::vector<RoleName>& roles,
324 PrivilegeFormat showPrivileges,
325 AuthenticationRestrictionsFormat showRestrictions,
326 BSONObj* result) {
327 if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
328 mutablebson::Document resultDoc;
329 mutablebson::Element rolesElement = resultDoc.makeElementArray("roles");
330 fassert(40274, resultDoc.root().pushBack(rolesElement));
331 addRoleNameObjectsToArrayElement(rolesElement, makeRoleNameIteratorForContainer(roles));
332 resolveUserRoles(&resultDoc, roles);
333 *result = resultDoc.getObject();
334 return Status::OK();
335 }
336
337 stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
338 BSONArrayBuilder resultBuilder;
339 for (const RoleName& role : roles) {
340 BSONObj roleDoc;
341 Status status =
342 _getRoleDescription_inlock(role, showPrivileges, showRestrictions, &roleDoc);
343 if (!status.isOK()) {
344 if (status.code() == ErrorCodes::RoleNotFound) {
345 continue;
346 }
347 return status;
348 }
349 resultBuilder << roleDoc;
350 }
351 *result = resultBuilder.arr();
352 return Status::OK();
353 }
354
_getRoleDescription_inlock(const RoleName & roleName,PrivilegeFormat showPrivileges,AuthenticationRestrictionsFormat showRestrictions,BSONObj * result)355 Status AuthzManagerExternalStateLocal::_getRoleDescription_inlock(
356 const RoleName& roleName,
357 PrivilegeFormat showPrivileges,
358 AuthenticationRestrictionsFormat showRestrictions,
359 BSONObj* result) {
360 if (!_roleGraph.roleExists(roleName))
361 return Status(ErrorCodes::RoleNotFound, "No role named " + roleName.toString());
362
363 mutablebson::Document resultDoc;
364 fassert(17162,
365 resultDoc.root().appendString(AuthorizationManager::ROLE_NAME_FIELD_NAME,
366 roleName.getRole()));
367 fassert(
368 17163,
369 resultDoc.root().appendString(AuthorizationManager::ROLE_DB_FIELD_NAME, roleName.getDB()));
370 fassert(17267, resultDoc.root().appendBool("isBuiltin", _roleGraph.isBuiltinRole(roleName)));
371
372 auto warningsElement = resultDoc.makeElementArray("warnings");
373
374 auto rolesElement = resultDoc.makeElementArray("roles");
375 fassert(17164, resultDoc.root().pushBack(rolesElement));
376 addRoleNameObjectsToArrayElement(rolesElement, _roleGraph.getDirectSubordinates(roleName));
377
378 auto inheritedRolesElement = resultDoc.makeElementArray("inheritedRoles");
379 fassert(17165, resultDoc.root().pushBack(inheritedRolesElement));
380
381 auto privilegesElement = resultDoc.makeElementArray("privileges");
382 if (showPrivileges == PrivilegeFormat::kShowSeparate) {
383 fassert(17166, resultDoc.root().pushBack(privilegesElement));
384 }
385
386 if (showRestrictions == AuthenticationRestrictionsFormat::kShow) {
387 auto authenticationRestrictionsElement =
388 resultDoc.makeElementArray("authenticationRestrictions");
389 fassert(40559, resultDoc.root().pushBack(authenticationRestrictionsElement));
390
391 const auto& restrictions = _roleGraph.getDirectAuthenticationRestrictions(roleName);
392 if (restrictions.get()) {
393 fassert(40561,
394 authenticationRestrictionsElement.appendArray("", restrictions->toBSON()));
395 }
396 }
397
398 if (_roleGraphState == roleGraphStateConsistent) {
399 addRoleNameObjectsToArrayElement(inheritedRolesElement,
400 _roleGraph.getIndirectSubordinates(roleName));
401
402 if (showPrivileges == PrivilegeFormat::kShowSeparate) {
403 auto inheritedPrivilegesElement = resultDoc.makeElementArray("inheritedPrivileges");
404 addPrivilegeObjectsOrWarningsToArrayElement(
405 privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
406
407 addPrivilegeObjectsOrWarningsToArrayElement(
408 inheritedPrivilegesElement, warningsElement, _roleGraph.getAllPrivileges(roleName));
409
410 fassert(17323, resultDoc.root().pushBack(inheritedPrivilegesElement));
411 }
412
413 if (showRestrictions == AuthenticationRestrictionsFormat::kShow) {
414 auto inheritedAuthenticationRestrictionsElement =
415 resultDoc.makeElementArray("inheritedAuthenticationRestrictions");
416 fassert(40563, resultDoc.root().pushBack(inheritedAuthenticationRestrictionsElement));
417
418 for (const auto& restrictions : _roleGraph.getAllAuthenticationRestrictions(roleName)) {
419 fassert(40562,
420 inheritedAuthenticationRestrictionsElement.appendArray(
421 "", restrictions->toBSON()));
422 }
423 }
424 } else if (showPrivileges == PrivilegeFormat::kShowSeparate) {
425 addPrivilegeObjectsOrWarningsToArrayElement(
426 privilegesElement, warningsElement, _roleGraph.getDirectPrivileges(roleName));
427 fassert(40557,
428 warningsElement.appendString("",
429 "Role graph state inconsistent; only direct "
430 "privileges and restrictions available."));
431 }
432
433 if (warningsElement.hasChildren()) {
434 fassert(17167, resultDoc.root().pushBack(warningsElement));
435 }
436 *result = resultDoc.getObject();
437 return Status::OK();
438 }
439
getRoleDescriptionsForDB(OperationContext * opCtx,const std::string & dbname,PrivilegeFormat showPrivileges,AuthenticationRestrictionsFormat showRestrictions,bool showBuiltinRoles,vector<BSONObj> * result)440 Status AuthzManagerExternalStateLocal::getRoleDescriptionsForDB(
441 OperationContext* opCtx,
442 const std::string& dbname,
443 PrivilegeFormat showPrivileges,
444 AuthenticationRestrictionsFormat showRestrictions,
445 bool showBuiltinRoles,
446 vector<BSONObj>* result) {
447 if (showPrivileges == PrivilegeFormat::kShowAsUserFragment) {
448 return Status(ErrorCodes::IllegalOperation,
449 "Cannot get user fragment for all roles in a database");
450 }
451
452 stdx::lock_guard<stdx::mutex> lk(_roleGraphMutex);
453 for (RoleNameIterator it = _roleGraph.getRolesForDatabase(dbname); it.more(); it.next()) {
454 if (!showBuiltinRoles && _roleGraph.isBuiltinRole(it.get())) {
455 continue;
456 }
457 BSONObj roleDoc;
458 Status status =
459 _getRoleDescription_inlock(it.get(), showPrivileges, showRestrictions, &roleDoc);
460 if (!status.isOK()) {
461 return status;
462 }
463 result->push_back(roleDoc);
464 }
465 return Status::OK();
466 }
467
468 namespace {
469
470 /**
471 * Adds the role described in "doc" to "roleGraph". If the role cannot be added, due to
472 * some error in "doc", logs a warning.
473 */
addRoleFromDocumentOrWarn(RoleGraph * roleGraph,const BSONObj & doc)474 void addRoleFromDocumentOrWarn(RoleGraph* roleGraph, const BSONObj& doc) {
475 Status status = roleGraph->addRoleFromDocument(doc);
476 if (!status.isOK()) {
477 warning() << "Skipping invalid admin.system.roles document while calculating privileges"
478 " for user-defined roles: "
479 << redact(status) << "; document " << redact(doc);
480 }
481 }
482
483
484 } // namespace
485
_initializeRoleGraph(OperationContext * opCtx)486 Status AuthzManagerExternalStateLocal::_initializeRoleGraph(OperationContext* opCtx) {
487 stdx::lock_guard<stdx::mutex> lkInitialzeRoleGraph(_roleGraphMutex);
488
489 _roleGraphState = roleGraphStateInitial;
490 _roleGraph = RoleGraph();
491
492 RoleGraph newRoleGraph;
493 Status status =
494 query(opCtx,
495 AuthorizationManager::rolesCollectionNamespace,
496 BSONObj(),
497 BSONObj(),
498 stdx::bind(addRoleFromDocumentOrWarn, &newRoleGraph, stdx::placeholders::_1));
499 if (!status.isOK())
500 return status;
501
502 status = newRoleGraph.recomputePrivilegeData();
503
504 RoleGraphState newState;
505 if (status == ErrorCodes::GraphContainsCycle) {
506 error() << "Inconsistent role graph during authorization manager initialization. Only "
507 "direct privileges available. "
508 << redact(status);
509 newState = roleGraphStateHasCycle;
510 status = Status::OK();
511 } else if (status.isOK()) {
512 newState = roleGraphStateConsistent;
513 } else {
514 newState = roleGraphStateInitial;
515 }
516
517 if (status.isOK()) {
518 _roleGraph = std::move(newRoleGraph);
519 _roleGraphState = std::move(newState);
520 }
521 return status;
522 }
523
524 class AuthzManagerExternalStateLocal::AuthzManagerLogOpHandler : public RecoveryUnit::Change {
525 public:
526 // None of the parameters below (except opCtx and externalState) need to live longer than the
527 // instantiations of this class
AuthzManagerLogOpHandler(OperationContext * opCtx,AuthzManagerExternalStateLocal * externalState,const char * op,const NamespaceString & nss,const BSONObj & o,const BSONObj * o2)528 AuthzManagerLogOpHandler(OperationContext* opCtx,
529 AuthzManagerExternalStateLocal* externalState,
530 const char* op,
531 const NamespaceString& nss,
532 const BSONObj& o,
533 const BSONObj* o2)
534 : _opCtx(opCtx),
535 _externalState(externalState),
536 _op(op),
537 _nss(nss),
538 _o(o.getOwned()),
539
540 _isO2Set(o2 ? true : false),
541 _o2(_isO2Set ? o2->getOwned() : BSONObj()) {}
542
commit()543 virtual void commit() {
544 stdx::lock_guard<stdx::mutex> lk(_externalState->_roleGraphMutex);
545 Status status = _externalState->_roleGraph.handleLogOp(
546 _opCtx, _op.c_str(), _nss, _o, _isO2Set ? &_o2 : NULL);
547
548 if (status == ErrorCodes::OplogOperationUnsupported) {
549 _externalState->_roleGraph = RoleGraph();
550 _externalState->_roleGraphState = _externalState->roleGraphStateInitial;
551 BSONObjBuilder oplogEntryBuilder;
552 oplogEntryBuilder << "op" << _op << "ns" << _nss.ns() << "o" << _o;
553 if (_isO2Set)
554 oplogEntryBuilder << "o2" << _o2;
555 error() << "Unsupported modification to roles collection in oplog; "
556 "restart this process to reenable user-defined roles; "
557 << redact(status) << "; Oplog entry: " << redact(oplogEntryBuilder.done());
558 } else if (!status.isOK()) {
559 warning() << "Skipping bad update to roles collection in oplog. " << redact(status)
560 << " Oplog entry: " << redact(_op);
561 }
562 status = _externalState->_roleGraph.recomputePrivilegeData();
563 if (status == ErrorCodes::GraphContainsCycle) {
564 _externalState->_roleGraphState = _externalState->roleGraphStateHasCycle;
565 error() << "Inconsistent role graph during authorization manager initialization. "
566 "Only direct privileges available. "
567 << redact(status) << " after applying oplog entry " << redact(_op);
568 } else {
569 fassert(17183, status);
570 _externalState->_roleGraphState = _externalState->roleGraphStateConsistent;
571 }
572 }
573
rollback()574 virtual void rollback() {}
575
576 private:
577 OperationContext* _opCtx;
578 AuthzManagerExternalStateLocal* _externalState;
579 const std::string _op;
580 const NamespaceString _nss;
581 const BSONObj _o;
582
583 const bool _isO2Set;
584 const BSONObj _o2;
585 };
586
logOp(OperationContext * opCtx,const char * op,const NamespaceString & nss,const BSONObj & o,const BSONObj * o2)587 void AuthzManagerExternalStateLocal::logOp(OperationContext* opCtx,
588 const char* op,
589 const NamespaceString& nss,
590 const BSONObj& o,
591 const BSONObj* o2) {
592 if (nss == AuthorizationManager::rolesCollectionNamespace ||
593 nss == AuthorizationManager::adminCommandNamespace) {
594 opCtx->recoveryUnit()->registerChange(
595 new AuthzManagerLogOpHandler(opCtx, this, op, nss, o, o2));
596 }
597 }
598
599 } // namespace mongo
600