#ifndef PROTON_SASL_H
#define PROTON_SASL_H 1

/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */

#include <proton/import_export.h>
#include <proton/type_compat.h>
#include <proton/types.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @file
 *
 * @copybrief sasl
 *
 * @addtogroup sasl
 * @{
 */

/**
 * The SASL layer is responsible for establishing an authenticated
 * and/or encrypted tunnel over which AMQP frames are passed between
 * peers. The peer acting as the SASL Client must provide
 * authentication credentials. The peer acting as the SASL Server must
 * provide authentication against the received credentials.
 */
typedef struct pn_sasl_t pn_sasl_t;

/**
 * The result of the SASL negotiation.
 */
typedef enum {
  PN_SASL_NONE = -1,  /** negotiation not completed */
  PN_SASL_OK = 0,     /** authentication succeeded */
  PN_SASL_AUTH = 1,   /** failed due to bad credentials */
  PN_SASL_SYS = 2,    /** failed due to a system error */
  PN_SASL_PERM = 3,   /** failed due to unrecoverable error */
  PN_SASL_TEMP = 4    /** failed due to transient error */
} pn_sasl_outcome_t;

/**
 * Construct an Authentication and Security Layer object.
 *
 * This will return the SASL layer object for the supplied transport
 * object. If there is currently no SASL layer one will be created.
 *
 * On the client side of an AMQP connection this will have the effect
 * of ensuring that the AMQP SASL layer is used for that connection.
 *
 * @note For incoming connections you must call pn_transport_set_server()
 * before pn_sasl() in order to set up a server SASL configuration.
 *
 * @return an object representing the SASL layer.
 */
PN_EXTERN pn_sasl_t *pn_sasl(pn_transport_t *transport);

/**
 * Do we support extended SASL negotiation
 *
 * Do we support extended SASL negotiation?
 * All implementations of Proton support ANONYMOUS and EXTERNAL on both
 * client and server sides and PLAIN on the client side.
 *
 * Extended SASL implementations use an external library (Cyrus SASL)
 * to support other mechanisms beyond these basic ones.
 *
 * @return true if we support extended SASL negotiation, false if we only support basic negotiation.
 */
PN_EXTERN bool pn_sasl_extended(void);

/**
 * **Deprecated** - Do not use.  There is no correct way to use this
 * API.
 *
 * Set the outcome of SASL negotiation
 *
 * Used by the server to set the result of the negotiation process.
 */
PN_EXTERN void pn_sasl_done(pn_sasl_t *sasl, pn_sasl_outcome_t outcome);

/**
 * Retrieve the outcome of SASL negotiation.
 */
PN_EXTERN pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl);

/**
 * Retrieve the authenticated user
 *
 * This is usually used at the the server end to find the name of the authenticated user.
 * On the client it will merely return whatever user was passed in to the
 * pn_transport_set_user_password() API.
 *
 * If pn_sasl_outcome() returns a value other than PN_SASL_OK, then there will be no user to return.
 * The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
 *
 * @param[in] sasl the sasl layer
 *
 * @return
 * If the SASL layer was not negotiated then 0 is returned
 * If the ANONYMOUS mechanism is used then the user will be "anonymous"
 * Otherwise a string containing the user is returned.
 */
PN_EXTERN const char *pn_sasl_get_user(pn_sasl_t *sasl);

/**
 * Return the selected SASL mechanism
 *
 * The returned value is only reliable after the PN_TRANSPORT_AUTHENTICATED event has been received.
 *
 * @param[in] sasl the SASL layer
 *
 * @return The authentication mechanism selected by the SASL layer
 */
PN_EXTERN const char *pn_sasl_get_mech(pn_sasl_t *sasl);

/**
 * SASL mechanisms that are to be considered for authentication
 *
 * This can be used on either the client or the server to restrict the SASL
 * mechanisms that may be used to the mechanisms on the list.
 *
 * @note By default the GSSAPI and GSS-SPNEGO mechanisms are not enabled for clients. This is because
 * these mechanisms have the problematic behaviour of 'capturing' the client whenever they are installed
 * so that they will be used by the client if offered by the server even if the client can't successfully
 * authenticate this way. This can lead to some very hard to debug failures.
 *
 * @note The GSSAPI or GSS-SPNEGO mechanisms need to be explicitly enabled if they are required (together
 * with any other required mechanisms).
 *
 * @param[in] sasl the SASL layer
 * @param[in] mechs space separated list of mechanisms that are allowed for authentication
 */
PN_EXTERN void pn_sasl_allowed_mechs(pn_sasl_t *sasl, const char *mechs);

/**
 * Boolean to allow use of clear text authentication mechanisms
 *
 * By default the SASL layer is configured not to allow mechanisms that disclose
 * the clear text of the password over an unencrypted AMQP connection. This specifically
 * will disallow the use of the PLAIN mechanism without using SSL encryption.
 *
 * This default is to avoid disclosing password information accidentally over an
 * insecure network.
 *
 * If you actually wish to use a clear text password unencrypted then you can use this
 * API to set allow_insecure_mechs to true.
 *
 * @param[in] sasl the SASL layer
 * @param[in] insecure set this to true to allow unencrypted PLAIN authentication.
 *
 */
PN_EXTERN void pn_sasl_set_allow_insecure_mechs(pn_sasl_t *sasl, bool insecure);

/**
 * Return the current value for allow_insecure_mechs
 *
 * @param[in] sasl the SASL layer
 */
PN_EXTERN bool pn_sasl_get_allow_insecure_mechs(pn_sasl_t *sasl);

/**
 * Set the sasl configuration name
 *
 * This is used to construct the SASL configuration filename. In the current implementation
 * it ".conf" is added to the name and the file is looked for in the configuration directory.
 *
 * If not set it will default to "proton-server" for a sasl server and "proton-client"
 * for a client.
 *
 * @param[in] sasl the SASL layer
 * @param[in] name the configuration name
 */
PN_EXTERN void pn_sasl_config_name(pn_sasl_t *sasl, const char *name);

/**
 * Set the sasl configuration path
 *
 * This is used to tell SASL where to look for the configuration file.
 * In the current implementation it can be a colon separated list of directories.
 *
 * The environment variable PN_SASL_CONFIG_PATH can also be used to set this path,
 * but if both methods are used then this pn_sasl_config_path() will take precedence.
 *
 * If not set the underlying implementation default will be used.
 * for a client.
 *
 * @param[in] sasl the SASL layer
 * @param[in] path the configuration path
 */
PN_EXTERN void pn_sasl_config_path(pn_sasl_t *sasl, const char *path);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* sasl.h */
