/**
 *    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 "mongo/base/status.h"
#include "mongo/bson/bsontypes.h"
#include "mongo/client/authenticate.h"
#include "mongo/executor/remote_command_request.h"
#include "mongo/executor/remote_command_response.h"
#include "mongo/util/future.h"

namespace mongo {
class BSONObj;
class SaslClientSession;

/**
 * Attempts to authenticate "client" using the SASL protocol.
 *
 * Do not use directly in client code.  Use the auth::authenticateClient() method, instead.
 *
 * Test against NULL for availability.  Client driver must be compiled with SASL support _and_
 * client application must have successfully executed mongo::runGlobalInitializersOrDie() or its
 * ilk to make this functionality available.
 *
 * The "saslParameters" BSONObj should be initialized with zero or more of the
 * fields below.  Which fields are required depends on the mechanism.  Consult the
 * relevant IETF standards.
 *
 *     "mechanism": The std::string name of the sasl mechanism to use.  Mandatory.
 *     "autoAuthorize": Truthy values tell the server to automatically acquire privileges on
 *         all resources after successful authentication, which is the default.  Falsey values
 *         instruct the server to await separate privilege-acquisition commands.
 *     "user": The std::string name of the user to authenticate.
 *     "db": The database target of the auth command, which identifies the location
 *         of the credential information for the user.  May be "$external" if credential
 *         information is stored outside of the mongo cluster.
 *     "pwd": The password.
 *     "serviceName": The GSSAPI service name to use.  Defaults to "mongodb".
 *     "serviceHostname": The GSSAPI hostname to use.  Defaults to the name of the remote host.
 *
 * Other fields in saslParameters are silently ignored.
 *
 * Returns an OK status on success, and ErrorCodes::AuthenticationFailed if authentication is
 * rejected.  Other failures, all of which are tantamount to authentication failure, may also be
 * returned.
 */
extern Future<void> (*saslClientAuthenticate)(auth::RunCommandHook runCommand,
                                              const HostAndPort& hostname,
                                              const BSONObj& saslParameters);

/**
 * Extracts the payload field from "cmdObj", and store it into "*payload".
 *
 * Sets "*type" to the BSONType of the payload field in cmdObj.
 *
 * If the type of the payload field is String, the contents base64 decodes and
 * stores into "*payload".  If the type is BinData, the contents are stored directly
 * into "*payload".  In all other cases, returns
 */
Status saslExtractPayload(const BSONObj& cmdObj, std::string* payload, BSONType* type);

// Default log level on the client for SASL log messages.
constexpr int kSaslClientLogLevelDefault = 4;

/**
 * Configures and initializes "session" to perform the client side of a
 * SASL conversation over connection "client".
 *
 * "saslParameters" is a BSON document providing the necessary configuration information.
 *
 * Returns Status::OK() on success.
 */
Status saslConfigureSession(SaslClientSession* session,
                            const HostAndPort& hostname,
                            StringData targetDatabase,
                            const BSONObj& saslParameters);

/**
 * Continue a previously started sasl session and proceed until completion.
 */
Future<void> asyncSaslConversation(auth::RunCommandHook runCommand,
                                   const std::shared_ptr<SaslClientSession>& session,
                                   const BSONObj& saslCommandPrefix,
                                   const BSONObj& inputObj,
                                   std::string targetDatabase,
                                   int saslLogLevel);
}  // namespace mongo
