/*
 * 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.
 */

/***************************************************************************
 * Copyright (C) 2017-2026 ZmartZone Holding BV
 * All rights reserved.
 *
 * DISCLAIMER OF WARRANTIES:
 *
 * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
 * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
 * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
 * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
 * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
 * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
 * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
 */

#ifndef _MOD_AUTH_OPENIDC_CFG_CFG_H_
#define _MOD_AUTH_OPENIDC_CFG_CFG_H_

#include "const.h" // for the PACKAGE_* defines
#include "http.h"
#include <http_config.h>
#include <http_core.h>
#include <httpd.h>

#include "cache/cache.h"

/* names of the OIDC* configuration directives */
#include "cfg/directives.h"

#define OIDC_CONFIG_POS_INT_UNSET -1
/* -1 might be used for unlimited timeout */
#define OIDC_CONFIG_POS_TIMEOUT_UNSET (apr_interval_time_t) - 2

typedef enum {
	OIDC_STATE_INPUT_HEADERS_NONE = 0,
	OIDC_STATE_INPUT_HEADERS_USER_AGENT = 1,
	OIDC_STATE_INPUT_HEADERS_X_FORWARDED_FOR = 2
} oidc_state_input_hdrs_t;

typedef enum {
	OIDC_HDR_NONE = 0,
	OIDC_HDR_X_FORWARDED_HOST = 1,
	OIDC_HDR_X_FORWARDED_PORT = 2,
	OIDC_HDR_X_FORWARDED_PROTO = 4,
	OIDC_HDR_FORWARDED = 8
} oidc_hdr_x_forwarded_t;

typedef enum {
	OIDC_TRACE_PARENT_OFF = 0,
	OIDC_TRACE_PARENT_PROPAGATE = 1,
	OIDC_TRACE_PARENT_GENERATE = 2
} oidc_trace_parent_t;

typedef enum {
	OIDC_SAMESITE_COOKIE_DISABLED = 0,
	OIDC_SAMESITE_COOKIE_NONE = 1,
	OIDC_SAMESITE_COOKIE_LAX = 2,
	OIDC_SAMESITE_COOKIE_STRICT = 3
} oidc_samesite_cookie_t;

#define OIDC_ERROR_ENVVAR "OIDC_ERROR"
#define OIDC_ERROR_DESC_ENVVAR "OIDC_ERROR_DESC"

#define OIDC_HOOK_INFO_TIMESTAMP "iat"
#define OIDC_HOOK_INFO_ACCES_TOKEN "access_token"
#define OIDC_HOOK_INFO_ACCES_TOKEN_TYPE "access_token_type"
#define OIDC_HOOK_INFO_ACCES_TOKEN_EXP "access_token_expires"
#define OIDC_HOOK_INFO_ID_TOKEN_HINT "id_token_hint"
#define OIDC_HOOK_INFO_ID_TOKEN "id_token"
#define OIDC_HOOK_INFO_USER_INFO "userinfo"
#define OIDC_HOOK_INFO_SESSION "session"
#define OIDC_HOOK_INFO_SESSION_STATE "state"
#define OIDC_HOOK_INFO_SESSION_UUID "uuid"
#define OIDC_HOOK_INFO_SESSION_EXP "exp"
#define OIDC_HOOK_INFO_SESSION_TIMEOUT "timeout"
#define OIDC_HOOK_INFO_SESSION_REMOTE_USER "remote_user"
#define OIDC_HOOK_INFO_REFRESH_TOKEN "refresh_token"

#define OIDC_HTML_ERROR_TEMPLATE_DEPRECATED "deprecated"

typedef struct oidc_apr_expr_t {
	ap_expr_info_t *expr;
	char *str;
} oidc_apr_expr_t;

#define OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN 32

typedef struct oidc_crypto_passphrase_t {
	const char *secret1;
	const char *secret2;
	/* PBKDF2-stretched key material derived from secret{1,2}, computed once at post_config
	 * time (see oidc_cfg_crypto_passphrase_post_config); *_set indicates the corresponding
	 * secret was non-NULL and the derived key is valid to use */
	unsigned char derived_key1[OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN];
	apr_byte_t derived_key1_set;
	unsigned char derived_key2[OIDC_CRYPTO_PASSPHRASE_DERIVED_KEY_LEN];
	apr_byte_t derived_key2_set;
} oidc_crypto_passphrase_t;

typedef struct oidc_remote_user_claim_t {
	const char *claim_name;
	const char *reg_exp;
	const char *replace;
} oidc_remote_user_claim_t;

typedef enum {
	/* pass userinfo as individual claims in headers (default) */
	OIDC_PASS_USERINFO_AS_CLAIMS = 1,
	/* pass userinfo payload as JSON object in header */
	OIDC_PASS_USERINFO_AS_JSON_OBJECT = 2,
	/* pass userinfo as a JWT in header (when returned as a JWT) */
	OIDC_PASS_USERINFO_AS_JWT = 4,
	/* pass as re-signed JWT including id_token claims */
	OIDC_PASS_USERINFO_AS_SIGNED_JWT = 8
} oidc_pass_userinfo_enum_t;

typedef struct oidc_pass_user_info_as_t {
	oidc_pass_userinfo_enum_t type;
	char *name;
} oidc_pass_user_info_as_t;

/* actions to be taken on access token / userinfo refresh error */
typedef enum { OIDC_ON_ERROR_502 = 0, OIDC_ON_ERROR_LOGOUT = 1, OIDC_ON_ERROR_AUTH = 2 } oidc_on_error_action_t;

#define OIDC_CFG_OPTIONS_SIZE(options) ((int)(sizeof(options) / sizeof(oidc_cfg_option_t)))

typedef struct oidc_provider_t oidc_provider_t;
typedef struct oidc_cfg_t oidc_cfg_t;

void oidc_cfg_x_forwarded_headers_check(request_rec *r, oidc_hdr_x_forwarded_t x_forwarded_headers);
const char *oidc_cfg_remote_user_claim_name_get(const oidc_cfg_t *cfg);

oidc_provider_t *oidc_cfg_provider_get(oidc_cfg_t *);
int oidc_cfg_merged_get(const oidc_cfg_t *cfg);
/* whether OIDCRedirectURI was inherited from the base server instead of set on this server itself */
int oidc_cfg_redirect_uri_inherited_get(const oidc_cfg_t *cfg);

void oidc_pre_config_init(void);

void *oidc_cfg_server_create(apr_pool_t *pool, server_rec *s);
void *oidc_cfg_server_merge(apr_pool_t *pool, void *BASE, void *ADD);
oidc_cfg_t *oidc_cfg_request_view(apr_pool_t *pool, const oidc_cfg_t *c);
apr_byte_t oidc_cfg_server_destroy(apr_pool_t *pool, server_rec *s, oidc_cfg_t *cfg);
int oidc_cfg_post_config(apr_pool_t *pool, oidc_cfg_t *cfg, server_rec *s);
void oidc_cfg_child_init(apr_pool_t *pool, const oidc_cfg_t *cfg, server_rec *s);
void oidc_cfg_process_cleanup(oidc_cfg_t *cfg, server_rec *s);
const char *oidc_cfg_string_list_add(apr_pool_t *pool, apr_array_header_t **list, const char *arg);
const char *oidc_cfg_endpoint_auth_set(apr_pool_t *pool, const oidc_cfg_t *cfg, const char *arg, char **auth,
				       char **alg);
void oidc_cfg_crypto_passphrase_secret1_set(oidc_cfg_t *cfg, const char *secret);
apr_byte_t oidc_crypto_passphrase_derive_keys(oidc_crypto_passphrase_t *cp);
apr_byte_t oidc_cfg_crypto_passphrase_derive_keys(oidc_cfg_t *cfg);
apr_byte_t oidc_crypto_passphrase_derive_keys_cached(apr_pool_t *pool, apr_hash_t *kdf_cache,
						     oidc_crypto_passphrase_t *cp);
apr_byte_t oidc_cfg_crypto_passphrase_derive_keys_cached(apr_pool_t *pool, apr_hash_t *kdf_cache, oidc_cfg_t *cfg);

/*
 * Generators for the per-server (oidc_cfg_t) directive accessors.
 *
 * Each call below expands, from the struct member name, to the prototypes of
 * the directive handler and the getter. For member `foo` they declare:
 *
 *   const char *oidc_cmd_foo_set(cmd_parms *, void *, ...);  -- directive handler (cfg/cmds.c)
 *   <type>      oidc_cfg_foo_get(const oidc_cfg_t *);        -- getter (used everywhere)
 *
 * The matching function bodies are generated by the OIDC_CFG_MEMBER_FUNC*
 * macros in cfg/cfg_int.h and live in cfg/cfg.c. Because the names are
 * token-pasted here they cannot be found by grepping for the literal symbol;
 * only an index that preprocesses can resolve them.
 *
 * At most two macro layers: the FUNCS_DECL aggregate expands directly to the
 * single-prototype atoms (FUNC_DECL / FUNC_GET_DECL), which paste the names.
 */

/* const char *oidc_cmd_<member>_set(cmd_parms *, void *, ...) */
#define OIDC_CMD_MEMBER_FUNC_DECL(member, ...) const char *oidc_cmd_##member##_set(cmd_parms *, void *, ##__VA_ARGS__);

/* <type> oidc_cfg_<member>_get(const oidc_cfg_t *) */
#define OIDC_CFG_MEMBER_FUNC_GET_DECL(member, type) type oidc_cfg_##member##_get(const oidc_cfg_t *cfg);

#define OIDC_CFG_MEMBER_FUNCS_DECL(member, type, ...)                                                                  \
	OIDC_CMD_MEMBER_FUNC_DECL(member, const char *, ##__VA_ARGS__);                                                \
	OIDC_CFG_MEMBER_FUNC_GET_DECL(member, type)

OIDC_CFG_MEMBER_FUNCS_DECL(delete_oldest_state_cookies, int)
OIDC_CFG_MEMBER_FUNCS_DECL(action_on_userinfo_error, oidc_on_error_action_t)
OIDC_CFG_MEMBER_FUNCS_DECL(crypto_passphrase_secret1, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(crypto_passphrase_secret2, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(refresh_mutex, oidc_cache_mutex_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(store_id_token, int)
OIDC_CFG_MEMBER_FUNCS_DECL(post_preserve_template, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(post_restore_template, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(persistent_session_cookie, int)
OIDC_CFG_MEMBER_FUNCS_DECL(public_keys, const apr_array_header_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(private_keys, const apr_array_header_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(redirect_uri, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(default_sso_url, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(default_slo_url, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(cookie_domain, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(cookie_http_only, int)
OIDC_CFG_MEMBER_FUNCS_DECL(claim_delimiter, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(claim_prefix, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(state_timeout, int)
OIDC_CFG_MEMBER_FUNCS_DECL(session_inactivity_timeout, int)
OIDC_CFG_MEMBER_FUNCS_DECL(metadata_dir, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(session_type, int)
OIDC_CFG_MEMBER_FUNCS_DECL(session_cache_fallback_to_cookie, int)
OIDC_CFG_MEMBER_FUNCS_DECL(debug_mask_secrets, int)
OIDC_CFG_MEMBER_FUNCS_DECL(session_cookie_chunk_size, int)
OIDC_CFG_MEMBER_FUNCS_DECL(html_error_template, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(provider_metadata_refresh_interval, int)
OIDC_CFG_MEMBER_FUNCS_DECL(info_hook_data, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(metrics_hook_data, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(metrics_path, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(trace_parent, oidc_trace_parent_t)
OIDC_CFG_MEMBER_FUNCS_DECL(black_listed_claims, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(white_listed_claims, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(state_input_headers, oidc_state_input_hdrs_t)
OIDC_CFG_MEMBER_FUNCS_DECL(redirect_urls_allowed, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(discover_issuers_allowed, apr_hash_t *)
OIDC_CFG_MEMBER_FUNCS_DECL(ca_bundle_path, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(logout_x_frame_options, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(x_forwarded_headers, oidc_hdr_x_forwarded_t)
OIDC_CFG_MEMBER_FUNCS_DECL(dpop_api_enabled, int)

// 2 args
OIDC_CFG_MEMBER_FUNCS_DECL(post_preserve_templates, const char *, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(crypto_passphrase, const oidc_crypto_passphrase_t *, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(max_number_of_state_cookies, int, const char *)

// 3 args
OIDC_CFG_MEMBER_FUNCS_DECL(cookie_same_site_session, oidc_samesite_cookie_t, const char *, const char *)
OIDC_CFG_MEMBER_FUNC_GET_DECL(cookie_same_site_state, oidc_samesite_cookie_t)
OIDC_CFG_MEMBER_FUNC_GET_DECL(cookie_same_site_discovery_csrf, oidc_samesite_cookie_t)
OIDC_CFG_MEMBER_FUNCS_DECL(remote_user_claim, const oidc_remote_user_claim_t *, const char *, const char *)
OIDC_CFG_MEMBER_FUNCS_DECL(outgoing_proxy, const oidc_http_outgoing_proxy_t *, const char *, const char *)
/* NB: the http_timeout getters lazily initialize the timeout struct in-config, so cfg cannot be const here */
const char *oidc_cmd_http_timeout_short_set(cmd_parms *, void *, const char *, const char *, const char *);
oidc_http_timeout_t *oidc_cfg_http_timeout_short_get(oidc_cfg_t *cfg);
const char *oidc_cmd_http_timeout_long_set(cmd_parms *, void *, const char *, const char *, const char *);
oidc_http_timeout_t *oidc_cfg_http_timeout_long_get(oidc_cfg_t *cfg);

// ifdefs
#ifdef USE_LIBJQ
OIDC_CMD_MEMBER_FUNC_DECL(filter_claims_expr, const char *arg)
#endif
OIDC_CFG_MEMBER_FUNC_GET_DECL(filter_claims_expr, oidc_apr_expr_t *)

extern const command_rec oidc_cfg_cmds[];

#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(auth_openidc);
#else
extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
#endif

#endif // _MOD_AUTH_OPENIDC_CFG_CFG_H_
