Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/src/metrics.h
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
20
/***************************************************************************
21
 * Copyright (C) 2017-2026 ZmartZone Holding BV
22
 * All rights reserved.
23
 *
24
 * DISCLAIMER OF WARRANTIES:
25
 *
26
 * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
27
 * ANY WARRANTIES OR REPRESENTATIONS EXPRESS, IMPLIED OR STATUTORY; INCLUDING,
28
 * WITHOUT LIMITATION, WARRANTIES OF QUALITY, PERFORMANCE, NONINFRINGEMENT,
29
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  NOR ARE THERE ANY
30
 * WARRANTIES CREATED BY A COURSE OR DEALING, COURSE OF PERFORMANCE OR TRADE
31
 * USAGE.  FURTHERMORE, THERE ARE NO WARRANTIES THAT THE SOFTWARE WILL MEET
32
 * YOUR NEEDS OR BE FREE FROM ERRORS, OR THAT THE OPERATION OF THE SOFTWARE
33
 * WILL BE UNINTERRUPTED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
34
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF
36
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
37
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
38
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
 *
40
 * @Author: Hans Zandbelt - hans.zandbelt@openidc.com
41
 */
42
43
#ifndef _MOD_AUTH_OPENIDC_METRICS_H_
44
#define _MOD_AUTH_OPENIDC_METRICS_H_
45
46
#include "const.h" // for the PACKAGE_* defines
47
#include "util/request_state.h"
48
#include <apr_hash.h>
49
#include <httpd.h>
50
51
apr_byte_t oidc_metrics_is_valid_classname(apr_pool_t *pool, const char *name, char **valid_names);
52
apr_byte_t oidc_metrics_post_config(apr_pool_t *pool, server_rec *s);
53
apr_status_t oidc_metrics_child_init(apr_pool_t *p, server_rec *s);
54
apr_status_t oidc_metrics_cleanup(server_rec *s);
55
int oidc_metrics_handle_request(request_rec *r);
56
57
// NB: order must match what is defined in metrics.c in array _oidc_metrics_timings_info
58
typedef enum {
59
60
  OM_MOD_AUTH_OPENIDC = 0,
61
62
  OM_AUTHN_REQUEST,
63
  OM_AUTHN_RESPONSE,
64
65
  OM_SESSION_VALID,
66
67
  OM_PROVIDER_METADATA,
68
  OM_PROVIDER_TOKEN,
69
  OM_PROVIDER_REFRESH,
70
  OM_PROVIDER_USERINFO,
71
  OM_PROVIDER_JWKS,
72
  OM_PROVIDER_PAR,
73
74
  OM_CACHE_READ,
75
  OM_CACHE_WRITE,
76
77
} oidc_metrics_timing_type_t;
78
79
typedef struct oidc_metrics_timing_info_t {
80
  char *class_name;
81
  char *metric_name;
82
  char *desc;
83
} oidc_metrics_timing_info_t;
84
85
extern const oidc_metrics_timing_info_t _oidc_metrics_timings_info[];
86
87
void oidc_metrics_timing_add(request_rec *r, oidc_metrics_timing_type_t type, apr_time_t elapsed);
88
89
0
#define OIDC_METRICS_TIMING_VAR apr_time_t _oidc_metrics_tstart = 0;
90
91
#define OIDC_METRICS_TIMING_START(r, cfg)                                                                              \
92
0
  OIDC_METRICS_TIMING_VAR                                                                                        \
93
0
  if (oidc_cfg_metrics_hook_data_get(cfg) != NULL) {                                                             \
94
0
    _oidc_metrics_tstart = apr_time_now();                                                                 \
95
0
  }
96
97
#define OIDC_METRICS_TIMING_ADD(r, cfg, type)                                                                          \
98
0
  if (oidc_cfg_metrics_hook_data_get(cfg) != NULL) {                                                             \
99
0
    if (apr_hash_get(oidc_cfg_metrics_hook_data_get(cfg), _oidc_metrics_timings_info[type].class_name,     \
100
0
         APR_HASH_KEY_STRING) != NULL) {                                                       \
101
0
      oidc_metrics_timing_add(r, type, apr_time_now() - _oidc_metrics_tstart);                       \
102
0
    }                                                                                                      \
103
0
  }
104
0
#define OIDC_METRICS_REQUEST_STATE_TIMER_KEY "oidc-metrics-request-timer"
105
106
#define OIDC_METRICS_TIMING_REQUEST_START(r, cfg)                                                                      \
107
0
  if (oidc_cfg_metrics_hook_data_get(cfg) != NULL) {                                                             \
108
0
    oidc_request_state_set(r, OIDC_METRICS_REQUEST_STATE_TIMER_KEY,                                        \
109
0
               apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_now()));                     \
110
0
  }
111
112
#define OIDC_METRICS_TIMING_REQUEST_ADD(r, cfg, type)                                                                  \
113
0
  OIDC_METRICS_TIMING_VAR                                                                                        \
114
0
  if (oidc_cfg_metrics_hook_data_get(cfg) != NULL) {                                                             \
115
0
    _oidc_metrics_tstart =                                                                                 \
116
0
        _oidc_str_to_time(oidc_request_state_get(r, OIDC_METRICS_REQUEST_STATE_TIMER_KEY), -1);            \
117
0
    if (_oidc_metrics_tstart > -1) {                                                                       \
118
0
      OIDC_METRICS_TIMING_ADD(r, cfg, type);                                                         \
119
0
    } else {                                                                                               \
120
0
      oidc_warn(r,                                                                                   \
121
0
          "metrics: could not add timing because start timer was not found in request state"); \
122
0
    }                                                                                                      \
123
0
  }
124
125
// NB: order must match what is defined in metrics.c in array _oidc_metrics_counters_info
126
/* Generate both counter enum values and metadata, preserving their index correspondence. */
127
// clang-format off
128
#define OIDC_METRICS_COUNTERS_LIST(X) \
129
  X(OM_AUTHTYPE_MOD_AUTH_OPENIDC,            OM_CLASS_AUTH_TYPE,     "mod_auth_openidc",              "requests handled by mod_auth_openidc") \
130
  X(OM_AUTHTYPE_OPENID_CONNECT,              OM_CLASS_AUTH_TYPE,     "openid-connect",                "requests handled by AuthType openid-connect") \
131
  X(OM_AUTHTYPE_OAUTH20,                     OM_CLASS_AUTH_TYPE,     "oauth20",                       "requests handled by AuthType oauth20") \
132
  X(OM_AUTHTYPE_AUTH_OPENIDC,                OM_CLASS_AUTH_TYPE,     "auth-openidc",                  "requests handled by AuthType auth-openidc") \
133
  X(OM_AUTHTYPE_DECLINED,                    OM_CLASS_AUTH_TYPE,     "declined",                      "requests not handled by mod_auth_openidc") \
134
  X(OM_AUTHN_REQUEST_ERROR_URL,              OM_CLASS_AUTHN,         "request.error.url",             "errors matching the incoming request URL against the configuration") \
135
  X(OM_AUTHN_RESPONSE_ERROR_STATE_MISMATCH,  OM_CLASS_AUTHN,         "response.error.state-mismatch", "state mismatch errors in authentication responses") \
136
  X(OM_AUTHN_RESPONSE_ERROR_STATE_EXPIRED,   OM_CLASS_AUTHN,         "response.error.state-expired",  "state expired errors in authentication responses") \
137
  X(OM_AUTHN_RESPONSE_ERROR_PROVIDER,        OM_CLASS_AUTHN,         "response.error.provider",       "errors returned by the provider in authentication responses") \
138
  X(OM_AUTHN_RESPONSE_ERROR_PROTOCOL,        OM_CLASS_AUTHN,         "response.error.protocol",       "protocol errors handling authentication responses") \
139
  X(OM_AUTHN_RESPONSE_ERROR_REMOTE_USER,     OM_CLASS_AUTHN,         "response.error.remote-user",    "errors identifying the remote user based on provided claims") \
140
  X(OM_AUTHZ_ACTION_AUTH,                    OM_CLASS_AUTHZ,         "action.auth",                   "step-up authentication requests") \
141
  X(OM_AUTHZ_ACTION_401,                     OM_CLASS_AUTHZ,         "action.401",                    "401 authorization errors") \
142
  X(OM_AUTHZ_ACTION_403,                     OM_CLASS_AUTHZ,         "action.403",                    "403 authorization errors") \
143
  X(OM_AUTHZ_ACTION_302,                     OM_CLASS_AUTHZ,         "action.302",                    "302 authorization errors") \
144
  X(OM_AUTHZ_ERROR_OAUTH20,                  OM_CLASS_AUTHZ,         "error.oauth20",                 "AuthType oauth20 (401) authorization errors") \
145
  X(OM_AUTHZ_MATCH_REQUIRE_CLAIM,            OM_CLASS_REQUIRE_CLAIM, "match",                         "(per-) Require claim authorization matches") \
146
  X(OM_AUTHZ_ERROR_REQUIRE_CLAIM,            OM_CLASS_REQUIRE_CLAIM, "error",                         "(per-) Require claim authorization errors") \
147
  X(OM_CLAIM_ID_TOKEN,                       OM_CLASS_CLAIM,         "id_token",                      "claim values in the ID Token") \
148
  X(OM_CLAIM_USER_INFO,                      OM_CLASS_CLAIM,         "userinfo",                      "claim values returned from the Userinfo Endpoint") \
149
  X(OM_PROVIDER_METADATA_ERROR,              OM_CLASS_PROVIDER,      "metadata.error",                "errors retrieving a provider discovery document") \
150
  X(OM_PROVIDER_TOKEN_ERROR,                 OM_CLASS_PROVIDER,      "token.error",                   "errors making a token request to a provider") \
151
  X(OM_PROVIDER_REFRESH_ERROR,               OM_CLASS_PROVIDER,      "refresh.error",                 "errors refreshing the access token at the token endpoint") \
152
  X(OM_PROVIDER_USERINFO_ERROR,              OM_CLASS_PROVIDER,      "userinfo.error",                "errors calling a provider userinfo endpoint") \
153
  X(OM_PROVIDER_CONNECT_ERROR,               OM_CLASS_PROVIDER,      "http.connect.error",            "(libcurl) provider/network connectivity errors") \
154
  X(OM_PROVIDER_HTTP_RESPONSE_CODE,          OM_CLASS_PROVIDER,      "http.response.code",            "HTTP response code calling a provider endpoint") \
155
  X(OM_PROVIDER_JWKS_ERROR,                  OM_CLASS_PROVIDER,      "jwks.error",                    "errors retrieving a provider JWKs document") \
156
  X(OM_PROVIDER_PAR_ERROR,                   OM_CLASS_PROVIDER,      "par.error",                     "errors making a pushed authorization request to a provider") \
157
  X(OM_PROVIDER_REGISTRATION_ERROR,          OM_CLASS_PROVIDER,      "registration.error",            "errors registering a client dynamically at a provider") \
158
  X(OM_PROVIDER_REVOCATION_ERROR,            OM_CLASS_PROVIDER,      "revocation.error",              "errors revoking a token at a provider revocation endpoint") \
159
  X(OM_PROVIDER_DPOP_RETRY,                  OM_CLASS_PROVIDER,      "dpop.retry",                    "provider calls retried with a fresh DPoP nonce") \
160
  X(OM_SESSION_ERROR_COOKIE_DOMAIN,          OM_CLASS_SESSION,       "error.cookie-domain",           "cookie domain validation errors for existing sessions") \
161
  X(OM_SESSION_ERROR_EXPIRED,                OM_CLASS_SESSION,       "error.expired",                 "sessions that exceeded the maximum duration") \
162
  X(OM_SESSION_ERROR_REFRESH_ACCESS_TOKEN,   OM_CLASS_SESSION,       "error.refresh-access-token",    "errors refreshing the access token before expiry in existing sessions") \
163
  X(OM_SESSION_ERROR_REFRESH_USERINFO,       OM_CLASS_SESSION,       "error.refresh-user-info",       "errors refreshing claims from the userinfo endpoint in existing sessions") \
164
  X(OM_SESSION_ERROR_GENERAL,                OM_CLASS_SESSION,       "error.general",                 "existing sessions that failed validation") \
165
  X(OM_SESSION_FALLBACK_COOKIE,              OM_CLASS_SESSION,       "fallback-cookie",               "sessions stored in a browser cookie after a session cache write failure") \
166
  X(OM_CACHE_ERROR,                          OM_CLASS_CACHE,         "cache.error",                   "cache read/write errors") \
167
  X(OM_CACHE_RETRY,                          OM_CLASS_CACHE,         "cache.retry",                   "cache operations retried after a backend failure") \
168
  X(OM_LOGOUT_BACKCHANNEL,                   OM_CLASS_LOGOUT,        "backchannel",                   "back-channel logout requests processed successfully") \
169
  X(OM_LOGOUT_BACKCHANNEL_ERROR,             OM_CLASS_LOGOUT,        "backchannel.error",             "back-channel logout requests rejected") \
170
  X(OM_REDIRECT_URI_AUTHN_RESPONSE_REDIRECT, OM_CLASS_REDIRECT_URI,  "authn.response.redirect",       "authentication responses received in a redirect") \
171
  X(OM_REDIRECT_URI_AUTHN_RESPONSE_POST,     OM_CLASS_REDIRECT_URI,  "authn.response.post",           "authentication responses received in a HTTP POST") \
172
  X(OM_REDIRECT_URI_AUTHN_RESPONSE_IMPLICIT, OM_CLASS_REDIRECT_URI,  "authn.response.implicit",       "(presumed) implicit authentication responses to the redirect URI") \
173
  X(OM_REDIRECT_URI_DISCOVERY_RESPONSE,      OM_CLASS_REDIRECT_URI,  "discovery.response",            "discovery responses to the redirect URI") \
174
  X(OM_REDIRECT_URI_REQUEST_LOGOUT,          OM_CLASS_REDIRECT_URI,  "request.logout",                "logout requests to the redirect URI") \
175
  X(OM_REDIRECT_URI_REQUEST_JWKS,            OM_CLASS_REDIRECT_URI,  "request.jwks",                  "JWKs retrieval requests to the redirect URI") \
176
  X(OM_REDIRECT_URI_REQUEST_SESSION,         OM_CLASS_REDIRECT_URI,  "request.session",               "session management requests to the redirect URI") \
177
  X(OM_REDIRECT_URI_REQUEST_REFRESH,         OM_CLASS_REDIRECT_URI,  "request.refresh",               "refresh access token requests to the redirect URI") \
178
  X(OM_REDIRECT_URI_REQUEST_REQUEST_URI,     OM_CLASS_REDIRECT_URI,  "request.request_uri",           "Request URI calls to the redirect URI") \
179
  X(OM_REDIRECT_URI_REQUEST_REMOVE_AT_CACHE, OM_CLASS_REDIRECT_URI,  "request.remove_at_cache",       "access token cache removal requests to the redirect URI") \
180
  X(OM_REDIRECT_URI_REQUEST_REVOKE_SESSION,  OM_CLASS_REDIRECT_URI,  "request.revoke_session",        "revoke session requests to the redirect URI") \
181
  X(OM_REDIRECT_URI_REQUEST_INFO,            OM_CLASS_REDIRECT_URI,  "request.info",                  "info hook requests to the redirect URI") \
182
  X(OM_REDIRECT_URI_REQUEST_DPOP,            OM_CLASS_REDIRECT_URI,  "request.dpop",                  "DPoP requests to the redirect URI") \
183
  X(OM_REDIRECT_URI_ERROR_PROVIDER,          OM_CLASS_REDIRECT_URI,  "error.provider",                "provider authentication response errors received at the redirect URI") \
184
  X(OM_REDIRECT_URI_ERROR_INVALID,           OM_CLASS_REDIRECT_URI,  "error.invalid",                 "invalid requests to the redirect URI") \
185
  X(OM_CONTENT_REQUEST_DECLINED,             OM_CLASS_CONTENT,       "request.declined",              "requests declined by the content handler") \
186
  X(OM_CONTENT_REQUEST_INFO,                 OM_CLASS_CONTENT,       "request.info",                  "info hook requests to the content handler") \
187
  X(OM_CONTENT_REQUEST_DPOP,                 OM_CLASS_CONTENT,       "request.dpop",                  "DPoP requests to the content handler") \
188
  X(OM_CONTENT_REQUEST_JWKS,                 OM_CLASS_CONTENT,       "request.jwks",                  "JWKs requests to the content handler") \
189
  X(OM_CONTENT_REQUEST_DISCOVERY,            OM_CLASS_CONTENT,       "request.discovery",             "discovery requests to the content handler") \
190
  X(OM_CONTENT_REQUEST_POST_PRESERVE,        OM_CLASS_CONTENT,       "request.post-preserve",         "HTTP POST preservation requests to the content handler") \
191
  X(OM_CONTENT_REQUEST_AUTHN_POST,           OM_CLASS_CONTENT,       "request.authn-post",            "HTTP POST authentication requests to the content handler") \
192
  X(OM_CONTENT_REQUEST_UNKNOWN,              OM_CLASS_CONTENT,       "request.unknown",               "unknown requests to the content handler")
193
// clang-format on
194
195
typedef enum {
196
#define OIDC_METRICS_COUNTER_ENUM(id, class, name, desc) id,
197
  OIDC_METRICS_COUNTERS_LIST(OIDC_METRICS_COUNTER_ENUM)
198
#undef OIDC_METRICS_COUNTER_ENUM
199
      OM_NUMBER_OF_COUNTERS
200
} oidc_metrics_counter_type_t;
201
202
typedef struct oidc_metrics_counter_info_t {
203
  char *class_name;
204
  char *metric_name;
205
  char *desc;
206
} oidc_metrics_counter_info_t;
207
208
extern const oidc_metrics_counter_info_t _oidc_metrics_counters_info[];
209
210
void oidc_metrics_counter_inc(request_rec *r, oidc_metrics_counter_type_t type, const char *name, const char *value);
211
212
// NB: "name" is overloaded here: when not NULL it also causes the metric_name to be included
213
0
static inline const char *_oidc_metrics_type_name2s(apr_pool_t *pool, unsigned int type, const char *name) {
214
0
  return apr_psprintf(pool, "%s%s%s%s%s", _oidc_metrics_counters_info[type].class_name, name ? "." : "",
215
0
          name ? _oidc_metrics_counters_info[type].metric_name : "", name ? "." : "",
216
0
          name ? name : "");
217
0
}
Unexecuted instantiation: mod_auth_openidc.c:_oidc_metrics_type_name2s
Unexecuted instantiation: cfg.c:_oidc_metrics_type_name2s
Unexecuted instantiation: common.c:_oidc_metrics_type_name2s
Unexecuted instantiation: authz.c:_oidc_metrics_type_name2s
Unexecuted instantiation: content.c:_oidc_metrics_type_name2s
Unexecuted instantiation: logout.c:_oidc_metrics_type_name2s
Unexecuted instantiation: refresh.c:_oidc_metrics_type_name2s
Unexecuted instantiation: request.c:_oidc_metrics_type_name2s
Unexecuted instantiation: response.c:_oidc_metrics_type_name2s
Unexecuted instantiation: session_management.c:_oidc_metrics_type_name2s
Unexecuted instantiation: dpop.c:_oidc_metrics_type_name2s
Unexecuted instantiation: proto.c:_oidc_metrics_type_name2s
Unexecuted instantiation: token.c:_oidc_metrics_type_name2s
Unexecuted instantiation: userinfo.c:_oidc_metrics_type_name2s
Unexecuted instantiation: metrics.c:_oidc_metrics_type_name2s
Unexecuted instantiation: oauth.c:_oidc_metrics_type_name2s
Unexecuted instantiation: http.c:_oidc_metrics_type_name2s
Unexecuted instantiation: session.c:_oidc_metrics_type_name2s
Unexecuted instantiation: jwks.c:_oidc_metrics_type_name2s
Unexecuted instantiation: provider.c:_oidc_metrics_type_name2s
Unexecuted instantiation: client.c:_oidc_metrics_type_name2s
218
219
#define OIDC_METRICS_COUNTER_INC_NAME_VALUE(r, cfg, type, name, value)                                                 \
220
0
  do {                                                                                                           \
221
0
    if (oidc_cfg_metrics_hook_data_get(cfg) != NULL) {                                                     \
222
0
      if (apr_hash_get(oidc_cfg_metrics_hook_data_get(cfg),                                          \
223
0
           _oidc_metrics_type_name2s(r->pool, type, name),                               \
224
0
           APR_HASH_KEY_STRING) != NULL) {                                               \
225
0
        oidc_metrics_counter_inc(r, type, name, value);                                        \
226
0
      }                                                                                              \
227
0
    }                                                                                                      \
228
0
  } while (0)
229
230
#define OIDC_METRICS_COUNTER_INC_VALUE(r, cfg, type, value)                                                            \
231
0
  OIDC_METRICS_COUNTER_INC_NAME_VALUE(r, cfg, type, NULL, value)
232
233
0
#define OIDC_METRICS_COUNTER_INC(r, cfg, type) OIDC_METRICS_COUNTER_INC_NAME_VALUE(r, cfg, type, NULL, NULL)
234
235
#endif /* _MOD_AUTH_OPENIDC_METRICS_H_ */