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 #pragma once
32 
33 #include <boost/optional.hpp>
34 #include <string>
35 #include <vector>
36 
37 #include "mongo/base/counter.h"
38 #include "mongo/base/status.h"
39 #include "mongo/base/status_with.h"
40 #include "mongo/db/auth/privilege.h"
41 #include "mongo/db/auth/resource_pattern.h"
42 #include "mongo/db/client.h"
43 #include "mongo/db/commands/server_status_metric.h"
44 #include "mongo/db/jsobj.h"
45 #include "mongo/db/query/explain.h"
46 #include "mongo/db/write_concern.h"
47 #include "mongo/rpc/reply_builder_interface.h"
48 #include "mongo/stdx/functional.h"
49 #include "mongo/util/net/op_msg.h"
50 #include "mongo/util/string_map.h"
51 
52 namespace mongo {
53 
54 class OperationContext;
55 class Timer;
56 
57 namespace mutablebson {
58 class Document;
59 }  // namespace mutablebson
60 
61 class CommandInterface {
62 protected:
63     CommandInterface() = default;
64 
65 public:
66     virtual ~CommandInterface() = default;
67 
68     /**
69      * Returns the command's name. This value never changes for the lifetime of the command.
70      */
71     virtual const std::string& getName() const = 0;
72 
73     /**
74      * Return the namespace for the command. If the first field in 'cmdObj' is of type
75      * mongo::String, then that field is interpreted as the collection name, and is
76      * appended to 'dbname' after a '.' character. If the first field is not of type
77      * mongo::String, then 'dbname' is returned unmodified.
78      */
79     virtual std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const = 0;
80 
81     /**
82      * Utility that returns a ResourcePattern for the namespace returned from
83      * parseNs(dbname, cmdObj).  This will be either an exact namespace resource pattern
84      * or a database resource pattern, depending on whether parseNs returns a fully qualifed
85      * collection name or just a database name.
86      */
87     virtual ResourcePattern parseResourcePattern(const std::string& dbname,
88                                                  const BSONObj& cmdObj) const = 0;
89 
90     /**
91      * Used by command implementations to hint to the rpc system how much space they will need in
92      * their replies.
93      */
94     virtual std::size_t reserveBytesForReply() const = 0;
95 
96     /**
97      * supportsWriteConcern returns true if this command should be parsed for a writeConcern
98      * field and wait for that write concern to be satisfied after the command runs.
99      *
100      * @param cmd is a BSONObj representation of the command that is used to determine if the
101      *            the command supports a write concern. Ex. aggregate only supports write concern
102      *            when $out is provided.
103      */
104     virtual bool supportsWriteConcern(const BSONObj& cmd) const = 0;
105 
106     /**
107      * Return true if only the admin ns has privileges to run this command.
108      */
109     virtual bool adminOnly() const = 0;
110 
111     /**
112      * Like adminOnly, but even stricter: we must either be authenticated for admin db,
113      * or, if running without auth, on the local interface.  Used for things which
114      * are so major that remote invocation may not make sense (e.g., shutdownServer).
115      *
116      * When localHostOnlyIfNoAuth() is true, adminOnly() must also be true.
117      */
118     virtual bool localHostOnlyIfNoAuth() = 0;
119 
120     /* Return true if slaves are allowed to execute the command
121     */
122     virtual bool slaveOk() const = 0;
123 
124     /**
125      * Return true if the client force a command to be run on a slave by
126      * turning on the 'slaveOk' option in the command query.
127      */
128     virtual bool slaveOverrideOk() const = 0;
129 
130     /**
131      * Override and return fales if the command opcounters should not be incremented on
132      * behalf of this command.
133      */
134     virtual bool shouldAffectCommandCounter() const = 0;
135 
136     /**
137      * Return true if the command requires auth.
138     */
139     virtual bool requiresAuth() const = 0;
140 
141     /**
142      * Generates help text for this command.
143      */
144     virtual void help(std::stringstream& help) const = 0;
145 
146     /**
147      * Commands which can be explained override this method. Any operation which has a query
148      * part and executes as a tree of execution stages can be explained. A command should
149      * implement explain by:
150      *
151      *   1) Calling its custom parse function in order to parse the command. The output of
152      *   this function should be a CanonicalQuery (representing the query part of the
153      *   operation), and a PlanExecutor which wraps the tree of execution stages.
154      *
155      *   2) Calling Explain::explainStages(...) on the PlanExecutor. This is the function
156      *   which knows how to convert an execution stage tree into explain output.
157      */
158     virtual Status explain(OperationContext* opCtx,
159                            const std::string& dbname,
160                            const BSONObj& cmdObj,
161                            ExplainOptions::Verbosity verbosity,
162                            BSONObjBuilder* out) const = 0;
163 
164     /**
165      * Checks if the client associated with the given OperationContext is authorized to run this
166      * command.
167      */
168     virtual Status checkAuthForRequest(OperationContext* opCtx, const OpMsgRequest& request) = 0;
169 
170     /**
171      * Redacts "cmdObj" in-place to a form suitable for writing to logs.
172      *
173      * The default implementation does nothing.
174      *
175      * This is NOT used to implement user-configurable redaction of PII. Instead, that is
176      * implemented via the set of redact() free functions, which are no-ops when log redaction is
177      * disabled. All PII must pass through one of the redact() overloads before being logged.
178      */
179     virtual void redactForLogging(mutablebson::Document* cmdObj) = 0;
180 
181     /**
182      * Returns a copy of "cmdObj" in a form suitable for writing to logs.
183      * Uses redactForLogging() to transform "cmdObj".
184      */
185     virtual BSONObj getRedactedCopyForLogging(const BSONObj& cmdObj) = 0;
186 
187     /**
188      * Return true if a replica set secondary should go into "recovering"
189      * (unreadable) state while running this command.
190      */
191     virtual bool maintenanceMode() const = 0;
192 
193     /**
194      * Return true if command should be permitted when a replica set secondary is in "recovering"
195      * (unreadable) state.
196      */
197     virtual bool maintenanceOk() const = 0;
198 
199     /**
200      * Returns true if this Command supports the non-local readConcern:level field value. Takes the
201      * command object and the name of the database on which it was invoked as arguments, so that
202      * readConcern can be conditionally rejected based on the command's parameters and/or namespace.
203      *
204      * If the readConcern non-local level argument is sent to a command that returns false the
205      * command processor will reject the command, returning an appropriate error message. For
206      * commands that support the argument, the command processor will instruct the RecoveryUnit to
207      * only return "committed" data, failing if this isn't supported by the storage engine.
208      *
209      * Note that this is never called on mongos. Sharded commands are responsible for forwarding
210      * the option to the shards as needed. We rely on the shards to fail the commands in the
211      * cases where it isn't supported.
212      */
213     virtual bool supportsNonLocalReadConcern(const std::string& dbName,
214                                              const BSONObj& cmdObj) const = 0;
215 
216     /**
217      * Returns true if command allows afterClusterTime in its readConcern. The command may not allow
218      * it if it is specifically intended not to take any LockManager locks. Waiting for
219      * afterClusterTime takes the MODE_IS lock.
220      */
221     virtual bool allowsAfterClusterTime(const BSONObj& cmdObj) const = 0;
222 
223     /**
224      * Returns LogicalOp for this command.
225      */
226     virtual LogicalOp getLogicalOp() const = 0;
227 
228     /**
229      * Returns whether this operation is a read, write, or command.
230      *
231      * Commands which implement database read or write logic should override this to return kRead
232      * or kWrite as appropriate.
233      */
234     enum class ReadWriteType { kCommand, kRead, kWrite };
235     virtual ReadWriteType getReadWriteType() const = 0;
236 
237     /**
238      * Increment counter for how many times this command has executed.
239      */
240     virtual void incrementCommandsExecuted() = 0;
241 
242     /**
243      * Increment counter for how many times this command has failed.
244      */
245     virtual void incrementCommandsFailed() = 0;
246 };
247 
248 /**
249  * Serves as a base for server commands. See the constructor for more details.
250  */
251 class Command : public CommandInterface {
252 public:
253     // The type of the first field in 'cmdObj' must be mongo::String. The first field is
254     // interpreted as a collection name.
255     static std::string parseNsFullyQualified(const std::string& dbname, const BSONObj& cmdObj);
256 
257     // The type of the first field in 'cmdObj' must be mongo::String or Symbol.
258     // The first field is interpreted as a collection name.
259     static NamespaceString parseNsCollectionRequired(const std::string& dbname,
260                                                      const BSONObj& cmdObj);
261     static NamespaceString parseNsOrUUID(OperationContext* opCtx,
262                                          const std::string& dbname,
263                                          const BSONObj& cmdObj);
264 
265     using CommandMap = StringMap<Command*>;
266 
267     /**
268      * Constructs a new command and causes it to be registered with the global commands list. It is
269      * not safe to construct commands other than when the server is starting up.
270      *
271      * @param oldName an old, deprecated name for the command
272      */
Command(StringData name,StringData oldName)273     Command(StringData name, StringData oldName)
274         : Command(name, std::vector<StringData>({oldName})) {}
275 
276     /**
277      * @param aliases the optional list of aliases (e.g., old names) for the command
278      */
279     Command(StringData name, std::vector<StringData> aliases = {});
280 
281     // NOTE: Do not remove this declaration, or relocate it in this class. We
282     // are using this method to control where the vtable is emitted.
283     virtual ~Command();
284 
getName()285     const std::string& getName() const final {
286         return _name;
287     }
288 
289     std::string parseNs(const std::string& dbname, const BSONObj& cmdObj) const override;
290 
291     ResourcePattern parseResourcePattern(const std::string& dbname,
292                                          const BSONObj& cmdObj) const override;
293 
reserveBytesForReply()294     std::size_t reserveBytesForReply() const override {
295         return 0u;
296     }
297 
adminOnly()298     bool adminOnly() const override {
299         return false;
300     }
301 
localHostOnlyIfNoAuth()302     bool localHostOnlyIfNoAuth() override {
303         return false;
304     }
305 
slaveOverrideOk()306     bool slaveOverrideOk() const override {
307         return false;
308     }
309 
shouldAffectCommandCounter()310     bool shouldAffectCommandCounter() const override {
311         return true;
312     }
313 
requiresAuth()314     bool requiresAuth() const override {
315         return true;
316     }
317 
318     void help(std::stringstream& help) const override;
319 
320     Status explain(OperationContext* opCtx,
321                    const std::string& dbname,
322                    const BSONObj& cmdObj,
323                    ExplainOptions::Verbosity verbosity,
324                    BSONObjBuilder* out) const override;
325 
326     void redactForLogging(mutablebson::Document* cmdObj) override;
327 
328     BSONObj getRedactedCopyForLogging(const BSONObj& cmdObj) override;
329 
maintenanceMode()330     bool maintenanceMode() const override {
331         return false;
332     }
333 
maintenanceOk()334     bool maintenanceOk() const override {
335         return true; /* assumed true prior to commit */
336     }
337 
supportsNonLocalReadConcern(const std::string & dbName,const BSONObj & cmdObj)338     bool supportsNonLocalReadConcern(const std::string& dbName,
339                                      const BSONObj& cmdObj) const override {
340         return false;
341     }
342 
allowsAfterClusterTime(const BSONObj & cmdObj)343     bool allowsAfterClusterTime(const BSONObj& cmdObj) const override {
344         return true;
345     }
346 
getLogicalOp()347     LogicalOp getLogicalOp() const override {
348         return LogicalOp::opCommand;
349     }
350 
getReadWriteType()351     ReadWriteType getReadWriteType() const override {
352         return ReadWriteType::kCommand;
353     }
354 
incrementCommandsExecuted()355     void incrementCommandsExecuted() final {
356         _commandsExecuted.increment();
357     }
358 
incrementCommandsFailed()359     void incrementCommandsFailed() final {
360         _commandsFailed.increment();
361     }
362 
363     /**
364      * Runs the command.
365      *
366      * Forwards to enhancedRun, but additionally runs audit checks if run throws unauthorized.
367      */
368     bool publicRun(OperationContext* opCtx, const OpMsgRequest& request, BSONObjBuilder& result);
369 
allCommands()370     static const CommandMap& allCommands() {
371         return *_commands;
372     }
373 
allCommandsByBestName()374     static const CommandMap& allCommandsByBestName() {
375         return *_commandsByBestName;
376     }
377 
378     // Counter for unknown commands
379     static Counter64 unknownCommands;
380 
381     /**
382      * Checks if the command is also known by the provided alias.
383      */
384     bool hasAlias(const StringData& alias) const;
385 
386     /**
387      * Runs a command directly and returns the result. Does not do any other work normally handled
388      * by command dispatch, such as checking auth, dealing with CurOp or waiting for write concern.
389      * It is illegal to call this if the command does not exist.
390      */
391     static BSONObj runCommandDirectly(OperationContext* txn, const OpMsgRequest& request);
392 
393     static Command* findCommand(StringData name);
394 
395     // Helper for setting errmsg and ok field in command result object.
396     static void appendCommandStatus(BSONObjBuilder& result,
397                                     bool ok,
398                                     const std::string& errmsg = {});
399 
400     // @return s.isOK()
401     static bool appendCommandStatus(BSONObjBuilder& result, const Status& status);
402 
403     /**
404      * Helper for setting a writeConcernError field in the command result object if
405      * a writeConcern error occurs.
406      *
407      * @param result is the BSONObjBuilder for the command response. This function creates the
408      *               writeConcernError field for the response.
409      * @param awaitReplicationStatus is the status received from awaitReplication.
410      * @param wcResult is the writeConcernResult object that holds other write concern information.
411      *      This is primarily used for populating errInfo when a timeout occurs, and is populated
412      *      by waitForWriteConcern.
413      */
414     static void appendCommandWCStatus(BSONObjBuilder& result,
415                                       const Status& awaitReplicationStatus,
416                                       const WriteConcernResult& wcResult = WriteConcernResult());
417 
418     /**
419      * If true, then testing commands are available. Defaults to false.
420      *
421      * Testing commands should conditionally register themselves by consulting this flag:
422      *
423      *     MONGO_INITIALIZER(RegisterMyTestCommand)(InitializerContext* context) {
424      *         if (Command::testCommandsEnabled) {
425      *             // Leaked intentionally: a Command registers itself when constructed.
426      *             new MyTestCommand();
427      *         }
428      *         return Status::OK();
429      *     }
430      *
431      * To make testing commands available by default, change the value to true before running any
432      * mongo initializers:
433      *
434      *     int myMain(int argc, char** argv, char** envp) {
435      *         Command::testCommandsEnabled = true;
436      *         ...
437      *         runGlobalInitializersOrDie(argc, argv, envp);
438      *         ...
439      *     }
440      */
441     static bool testCommandsEnabled;
442 
443     /**
444      * Returns true if this a request for the 'help' information associated with the command.
445      */
446     static bool isHelpRequest(const BSONElement& helpElem);
447 
448     static const char kHelpFieldName[];
449 
450     /**
451      * Generates a reply from the 'help' information associated with a command. The state of
452      * the passed ReplyBuilder will be in kOutputDocs after calling this method.
453      */
454     static void generateHelpResponse(OperationContext* opCtx,
455                                      rpc::ReplyBuilderInterface* replyBuilder,
456                                      const Command& command);
457 
458     /**
459      * This function checks if a command is a user management command by name.
460      */
461     static bool isUserManagementCommand(const std::string& name);
462 
463     /**
464      * Checks to see if the client executing "opCtx" is authorized to run the given command with the
465      * given parameters on the given named database.
466      *
467      * Returns Status::OK() if the command is authorized.  Most likely returns
468      * ErrorCodes::Unauthorized otherwise, but any return other than Status::OK implies not
469      * authorized.
470      */
471     static Status checkAuthorization(Command* c,
472                                      OperationContext* opCtx,
473                                      const OpMsgRequest& request);
474 
475     /**
476      * Appends passthrough fields from a cmdObj to a given request.
477      */
478     static BSONObj appendPassthroughFields(const BSONObj& cmdObjWithPassthroughFields,
479                                            const BSONObj& request);
480 
481     /**
482      * Returns a copy of 'cmdObj' with a majority writeConcern appended.
483      */
484     static BSONObj appendMajorityWriteConcern(const BSONObj& cmdObj);
485 
486     /**
487      * Returns true if the provided argument is one that is handled by the command processing layer
488      * and should generally be ignored by individual command implementations. In particular,
489      * commands that fail on unrecognized arguments must not fail for any of these.
490      */
isGenericArgument(StringData arg)491     static bool isGenericArgument(StringData arg) {
492         // Not including "help" since we don't pass help requests through to the command parser.
493         // If that changes, it should be added. When you add to this list, consider whether you
494         // should also change the filterCommandRequestForPassthrough() function.
495         return arg == "$audit" ||                        //
496             arg == "$client" ||                          //
497             arg == "$configServerState" ||               //
498             arg == "$db" ||                              //
499             arg == "allowImplicitCollectionCreation" ||  //
500             arg == "$oplogQueryData" ||                  //
501             arg == "$queryOptions" ||                    //
502             arg == "$readPreference" ||                  //
503             arg == "$replData" ||                        //
504             arg == "$clusterTime" ||                     //
505             arg == "maxTimeMS" ||                        //
506             arg == "readConcern" ||                      //
507             arg == "shardVersion" ||                     //
508             arg == "tracking_info" ||                    //
509             arg == "writeConcern" ||                     //
510             arg == "lsid" ||                             //
511             arg == "txnNumber" ||                        //
512             false;  // These comments tell clang-format to keep this line-oriented.
513     }
514 
515     /**
516      * Rewrites cmdObj into a format safe to blindly forward to shards.
517      *
518      * This performs 2 transformations:
519      * 1) $readPreference fields are moved into a subobject called $queryOptions. This matches the
520      *    "wrapped" format historically used internally by mongos. Moving off of that format will be
521      *    done as SERVER-29091.
522      *
523      * 2) Filter out generic arguments that shouldn't be blindly passed to the shards.  This is
524      *    necessary because many mongos implementations of Command::run() just pass cmdObj through
525      *    directly to the shards. However, some of the generic arguments fields are automatically
526      *    appended in the egress layer. Removing them here ensures that they don't get duplicated.
527      *
528      * Ideally this function can be deleted once mongos run() implementations are more careful about
529      * what they send to the shards.
530      */
531     static BSONObj filterCommandRequestForPassthrough(const BSONObj& cmdObj);
532 
533     /**
534      * Rewrites reply into a format safe to blindly forward from shards to clients.
535      *
536      * Ideally this function can be deleted once mongos run() implementations are more careful about
537      * what they return from the shards.
538      */
539     static void filterCommandReplyForPassthrough(const BSONObj& reply, BSONObjBuilder* output);
540     static BSONObj filterCommandReplyForPassthrough(const BSONObj& reply);
541 
542 private:
543     static CommandMap* _commands;
544     static CommandMap* _commandsByBestName;
545 
546     /**
547      * Runs the command.
548      *
549      * The default implementation verifies that request has no document sections then forwards to
550      * BasicCommand::run().
551      *
552      * For now commands should only implement if they need access to OP_MSG-specific functionality.
553      */
554     virtual bool enhancedRun(OperationContext* opCtx,
555                              const OpMsgRequest& request,
556                              BSONObjBuilder& result) = 0;
557 
558     // Counters for how many times this command has been executed and failed
559     Counter64 _commandsExecuted;
560     Counter64 _commandsFailed;
561 
562     // The full name of the command
563     const std::string _name;
564 
565     // The list of aliases for the command
566     const std::vector<StringData> _aliases;
567 
568     // Pointers to hold the metrics tree references
569     ServerStatusMetricField<Counter64> _commandsExecutedMetric;
570     ServerStatusMetricField<Counter64> _commandsFailedMetric;
571 };
572 
573 /**
574  * A subclass of Command that only cares about the BSONObj body and doesn't need access to document
575  * sequences.
576  */
577 class BasicCommand : public Command {
578 public:
579     using Command::Command;
580 
581     //
582     // Interface for subclasses to implement
583     //
584 
585     /**
586      * run the given command
587      * implement this...
588      *
589      * return value is true if succeeded.  if false, set errmsg text.
590      */
591     virtual bool run(OperationContext* opCtx,
592                      const std::string& db,
593                      const BSONObj& cmdObj,
594                      BSONObjBuilder& result) = 0;
595 
596     /**
597      * Checks if the client associated with the given OperationContext is authorized to run this
598      * command. Default implementation defers to checkAuthForCommand.
599      */
600     virtual Status checkAuthForOperation(OperationContext* opCtx,
601                                          const std::string& dbname,
602                                          const BSONObj& cmdObj);
603 
604 private:
605     //
606     // Deprecated virtual methods.
607     //
608 
609     /**
610      * Checks if the given client is authorized to run this command on database "dbname"
611      * with the invocation described by "cmdObj".
612      *
613      * NOTE: Implement checkAuthForOperation that takes an OperationContext* instead.
614      */
615     virtual Status checkAuthForCommand(Client* client,
616                                        const std::string& dbname,
617                                        const BSONObj& cmdObj);
618 
619     /**
620      * Appends to "*out" the privileges required to run this command on database "dbname" with
621      * the invocation described by "cmdObj".  New commands shouldn't implement this, they should
622      * implement checkAuthForOperation (which takes an OperationContext*), instead.
623      */
addRequiredPrivileges(const std::string & dbname,const BSONObj & cmdObj,std::vector<Privilege> * out)624     virtual void addRequiredPrivileges(const std::string& dbname,
625                                        const BSONObj& cmdObj,
626                                        std::vector<Privilege>* out) {
627         // The default implementation of addRequiredPrivileges should never be hit.
628         fassertFailed(16940);
629     }
630 
631     //
632     // Methods provided for subclasses if they implement above interface.
633     //
634 
635     /**
636      * Calls run().
637      */
638     bool enhancedRun(OperationContext* opCtx,
639                      const OpMsgRequest& request,
640                      BSONObjBuilder& result) final;
641 
642     /**
643      * Calls checkAuthForOperation.
644      */
645     Status checkAuthForRequest(OperationContext* opCtx, const OpMsgRequest& request) final;
646 
647     void uassertNoDocumentSequences(const OpMsgRequest& request);
648 };
649 
650 /**
651  * Deprecated. Do not add new subclasses.
652  */
653 class ErrmsgCommandDeprecated : public BasicCommand {
654     using BasicCommand::BasicCommand;
655     bool run(OperationContext* opCtx,
656              const std::string& db,
657              const BSONObj& cmdObj,
658              BSONObjBuilder& result) final;
659 
660     virtual bool errmsgRun(OperationContext* opCtx,
661                            const std::string& db,
662                            const BSONObj& cmdObj,
663                            std::string& errmsg,
664                            BSONObjBuilder& result) = 0;
665 };
666 
667 }  // namespace mongo
668