Skip to content

Commit

Permalink
3.5.0dev: implement OAuth2Require with liboauth2_nginx >= 1.6.3
Browse files Browse the repository at this point in the history
see #7; thanks @smanolache

Signed-off-by: Hans Zandbelt <hans.zandbelt@openidc.com>
  • Loading branch information
zandbelt committed Jun 20, 2024
1 parent f51ec07 commit 3f12c92
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 238 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
/config.sub~
/configure~
/install-sh~
/autom4te.cache/
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
06/20/2024
- implement OAuth2Require with liboauth2_nginx >= 1.6.3
- bump to 3.5.0dev

06/18/2024
- fix variable colision between requests; closes #8; see #7; thanks @smanolache

Expand Down
10 changes: 2 additions & 8 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ AM_LDFLAGS = --coverage

LDADD = $(CODE_COVERAGE_LIBS)

lib_LTLIBRARIES = @PACKAGE_NAME@.la
pkglib_LTLIBRARIES = @PACKAGE_NAME@.la
pkglibdir = $(libdir)/nginx/modules

@PACKAGE_NAME@_la_CFLAGS = @OAUTH2_CFLAGS@ @OAUTH2_NGINX_CFLAGS@ @NGINX_CFLAGS@
@PACKAGE_NAME@_la_LIBADD = @OAUTH2_LIBS@ @OAUTH2_NGINX_LIBS@ @NGINX_LIBS@
Expand All @@ -22,10 +23,3 @@ lib_LTLIBRARIES = @PACKAGE_NAME@.la

clang-format:
clang-format -style=file -i `find . -name *.[ch]`

install:
${INSTALL} -d $(DESTDIR)/usr/local/nginx/modules/
${INSTALL} -p -m 755 .libs/@PACKAGE_NAME@.so $(DESTDIR)/usr/local/nginx/modules/@PACKAGE_NAME@.so

uninstall:
rm -f $(DESTDIR)/usr/local/nginx/modules/@PACKAGE_NAME@.so
6 changes: 3 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
AC_INIT([ngx_oauth2_module],[3.4.0],[hans.zandbelt@openidc.com])
AC_INIT([ngx_oauth2_module],[3.5.0dev],[hans.zandbelt@openidc.com])

AM_INIT_AUTOMAKE([foreign no-define subdir-objects])
AC_CONFIG_MACRO_DIRS([m4])
Expand All @@ -25,11 +25,11 @@ AM_CONDITIONAL(HAVE_NGINX, [test x"$have_nginx" = "xyes"])
AC_SUBST(NGINX_CFLAGS)
AC_SUBST(NGINX_LIBS)

PKG_CHECK_MODULES(OAUTH2, [liboauth2 >= 1.6.2])
PKG_CHECK_MODULES(OAUTH2, [liboauth2 >= 1.6.3])
AC_SUBST(OAUTH2_CFLAGS)
AC_SUBST(OAUTH2_LIBS)

PKG_CHECK_MODULES(OAUTH2_NGINX, [liboauth2_nginx >= 1.6.2])
PKG_CHECK_MODULES(OAUTH2_NGINX, [liboauth2_nginx >= 1.6.3])
AC_SUBST(OAUTH2_NGINX_CFLAGS)
AC_SUBST(OAUTH2_NGINX_LIBS)

Expand Down
252 changes: 25 additions & 227 deletions src/ngx_oauth2_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,11 @@
#include <oauth2/oauth2.h>
#include <oauth2/version.h>

typedef struct ngx_oauth2_claims_hash_s {
ngx_hash_keys_arrays_t keys;
ngx_hash_t h;
} ngx_oauth2_claims_hash_t;

typedef struct ngx_oauth2_cfg_t {
ngx_http_complex_value_t source_token;
oauth2_cfg_token_verify_t *verify;
ngx_conf_t *cf;
ngx_array_t *requirements;
// TODO:
oauth2_log_t *log;
} ngx_oauth2_cfg_t;
Expand All @@ -59,6 +55,7 @@ static void *ngx_oauth2_create_loc_conf(ngx_conf_t *cf)
cfg = ngx_pcalloc(cf->pool, sizeof(ngx_oauth2_cfg_t));
cfg->log = NULL;
cfg->cf = cf;
cfg->requirements = NULL;
cfg->verify = NULL;
cfg->source_token.flushes = NULL;
cfg->source_token.lengths = NULL;
Expand Down Expand Up @@ -88,39 +85,18 @@ static char *ngx_oauth2_merge_loc_conf(ngx_conf_t *cf, void *parent,
ngx_oauth2_cfg_t *cfg = child;

cfg->cf = cf;
if (cfg->requirements == NULL)
/* no requirements were set in the child, copy those of the
* parent; if the child has its own requirements then do not
* override them with the parent's */
cfg->requirements = prev->requirements;
cfg->verify = cfg->verify
? oauth2_cfg_token_verify_clone(NULL, cfg->verify)
: oauth2_cfg_token_verify_clone(NULL, prev->verify);

return NGX_CONF_OK;
}

static inline ngx_str_t chr_to_ngx_str(ngx_pool_t *p, const char *k)
{
ngx_str_t in = {strlen(k), (u_char *)k};
ngx_str_t out = {in.len, ngx_pstrdup(p, &in)};
return out;
}

static inline char *ngx_str_to_chr(ngx_pool_t *p, const ngx_str_t *str)
{
char *s = ngx_pnalloc(p, str->len + 1);
if (s) {
memcpy(s, str->data, str->len);
s[str->len] = '\0';
}
return s;
}

static inline char *chr_to_chr(ngx_pool_t *p, const char *str)
{
ngx_str_t s = {strlen(str), (u_char *)str};
return ngx_str_to_chr(p, &s);
}

static char *ngx_oauth2_set_claim(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);

// ngx_oauth2_cfg_set_token_verify
OAUTH2_NGINX_CFG_FUNC_START(oauth2, ngx_oauth2_cfg_t, token_verify)
int rc = NGX_OK;
Expand All @@ -140,7 +116,7 @@ if (rc != NGX_OK) {
"expression %.*s",
rc, (int)value[1].len, value[1].data);
ngx_str_t msg = {n, (u_char *)&buf[0]};
char *s = ngx_str_to_chr(cf->pool, &msg);
char *s = oauth2_nginx_str2chr(cf->pool, &msg);
return s ? s : NGX_CONF_ERROR;
}

Expand All @@ -161,17 +137,27 @@ oauth2_mem_free(v2);
oauth2_mem_free(v1);
OAUTH2_NGINX_CFG_FUNC_END(cf, rv)

OAUTH2_NGINX_CFG_FUNC_RET1(oauth2, ngx_oauth2_cfg_t, require,
nginx_oauth2_set_require, requirements)

OAUTH2_NGINX_CFG_FUNC_ARGS1(oauth2, ngx_oauth2_cfg_t, passphrase,
oauth2_crypto_passphrase_set, NULL)
OAUTH2_NGINX_CFG_FUNC_ARGS2(oauth2, ngx_oauth2_cfg_t, cache,
oauth2_cfg_set_cache, NULL)

ngx_module_t ngx_oauth2_module;
OAUTH2_NGINX_CMD_SET_IMPL(oauth2, claim)

static ngx_command_t ngx_oauth2_commands[] = {
OAUTH2_NGINX_CMD(1, oauth2, "OAuth2CryptoPassphrase", passphrase),
OAUTH2_NGINX_CMD(12, oauth2, "OAuth2Cache", cache),
OAUTH2_NGINX_CMD(3 | NGX_CONF_TAKE4, oauth2, "OAuth2TokenVerify",
token_verify),
OAUTH2_NGINX_CMD(2, oauth2, "OAuth2Claim", claim), ngx_null_command};
OAUTH2_NGINX_CMD(123 | NGX_CONF_TAKE4 | NGX_CONF_TAKE5 | NGX_CONF_TAKE6 |
NGX_CONF_TAKE7,
oauth2, "OAuth2Require", require),
OAUTH2_NGINX_CMD(2, oauth2, "OAuth2Claim", claim),
ngx_null_command};

static ngx_int_t ngx_oauth2_post_config(ngx_conf_t *cf);

Expand Down Expand Up @@ -241,199 +227,9 @@ static ngx_int_t ngx_oauth2_post_config(ngx_conf_t *cf)
return rv;
}

static ngx_int_t ngx_oauth2_claim_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v,
uintptr_t data)
{
ngx_oauth2_claims_hash_t *claims = NULL;
const char *value = NULL;

ngx_str_t key = {strlen((const char *)data), (u_char *)data};

claims = (ngx_oauth2_claims_hash_t *)ngx_http_get_module_ctx(
r, ngx_oauth2_module);
if (!claims) {
v->not_found = 1;
return NGX_OK;
}

value = (const char *)ngx_hash_find(
&claims->h, ngx_hash_key(key.data, key.len), key.data, key.len);
if (value) {
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"ngx_oauth2_claim_variable: %V=%s", &key, value);
v->data = (u_char *)value;
v->len = strlen(value);
v->no_cacheable = 1;
v->not_found = 0;
} else {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"ngx_oauth2_claim_variable: %V=(null)", &key);
v->not_found = 1;
}

return NGX_OK;
}

static char *ngx_oauth2_set_claim(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value;
ngx_http_variable_t *v;

value = cf->args->elts;

if (value[2].len <= 1 || value[2].data[0] != '$') {
static const size_t MAX_BUF = 128;
char buf[MAX_BUF];
int n = snprintf(buf, sizeof(buf), "Invalid variable name %.*s",
(int)value[2].len, value[2].data);
ngx_str_t msg = {n, (u_char *)&buf[0]};
char *s = ngx_str_to_chr(cf->pool, &msg);
return s ? s : NGX_CONF_ERROR;
}

value[2].len--;
value[2].data++;

v = ngx_http_add_variable(cf, &value[2], NGX_HTTP_VAR_CHANGEABLE);
if (!v) {
ngx_str_t msg = ngx_string("ngx_http_add_variable failed");
char *rv = ngx_str_to_chr(cf->pool, &msg);
return rv ? rv : NGX_CONF_ERROR;
}

v->get_handler = ngx_oauth2_claim_variable;
char *claim = ngx_str_to_chr(cf->pool, &value[1]);
if (!claim) {
ngx_str_t msg = ngx_string("Out of memory");
char *rv = ngx_str_to_chr(cf->pool, &msg);
return rv ? rv : NGX_CONF_ERROR;
}
v->data = (uintptr_t)claim;

return NGX_CONF_OK;
}

static ngx_int_t ngx_set_target_variable(ngx_oauth2_claims_hash_t *claims,
oauth2_nginx_request_context_t *ctx,
const char *k, const char *v)
{
ngx_str_t key = chr_to_ngx_str(claims->keys.pool, k);
if (!key.data)
return NGX_ERROR;

const char *value = chr_to_chr(claims->keys.pool, v);
if (!value)
return NGX_ERROR;

return ngx_hash_add_key(&claims->keys, &key, (char *)value,
NGX_HASH_READONLY_KEY);
}

static ngx_int_t ngx_oauth2_init_keys(ngx_pool_t *pool,
ngx_oauth2_claims_hash_t *claims)
{
claims->keys.pool = pool;
claims->keys.temp_pool = pool;

return ngx_hash_keys_array_init(&claims->keys, NGX_HASH_SMALL);
}

static ngx_int_t ngx_oauth2_init_hash(ngx_pool_t *pool,
ngx_oauth2_claims_hash_t *claims)
{
ngx_hash_init_t init;

init.hash = &claims->h;
init.key = ngx_hash_key;
init.max_size = 64;
init.bucket_size = ngx_align(64, ngx_cacheline_size);
init.name = "claims";
init.pool = pool;
init.temp_pool = pool;

return ngx_hash_init(&init, claims->keys.keys.elts,
claims->keys.keys.nelts);
}

// TODO: generalize/callback part of this (at least the looping and encoding is
// generic)
static ngx_int_t ngx_set_target_variables(ngx_http_request_t *r,
oauth2_nginx_request_context_t *ctx,
json_t *json_token)
{
void *iter = NULL;
const char *key = NULL;
json_t *value = NULL;
ngx_oauth2_claims_hash_t *claims = NULL;
int rc = NGX_OK;

claims = (ngx_oauth2_claims_hash_t *)ngx_http_get_module_ctx(
r, ngx_oauth2_module);
if (!claims) {
claims = ngx_palloc(r->pool, sizeof(*claims));
if (!claims) {
ngx_log_error(NGX_LOG_ERR, r->connection->log,
NGX_ENOMEM,
"ngx_set_target_variables: "
"error allocating claims hash");
return NGX_ERROR;
}

rc = ngx_oauth2_init_keys(r->pool, claims);
if (NGX_OK != rc) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"ngx_set_target_variables: error %d "
"initializing hash keys",
rc);
return rc;
}

ngx_http_set_ctx(r, claims, ngx_oauth2_module);
}

iter = json_object_iter(json_token);
while (iter) {
key = json_object_iter_key(iter);
value = json_object_iter_value(iter);
if (json_is_string(value)) {
rc = ngx_set_target_variable(claims, ctx, key,
json_string_value(value));
} else {
const char *val = oauth2_json_encode(ctx->log, value,
JSON_ENCODE_ANY);
rc = ngx_set_target_variable(claims, ctx, key, val);
oauth2_mem_free((char *)val);
}

if (NGX_OK != rc) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"ngx_set_target_variables: error %d "
"setting value of key %s in claims hash",
rc, key);
return rc;
}

iter = json_object_iter_next(json_token, iter);
}

rc = ngx_oauth2_init_hash(r->pool, claims);
if (NGX_OK != rc) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"ngx_set_target_variables: error %d initializing "
"claims hash",
rc);
return rc;
}

return NGX_OK;
}

static ngx_int_t ngx_oauth2_handler(ngx_http_request_t *r)
{
ngx_int_t rv = NGX_DECLINED;
// bool rc = false;
oauth2_nginx_request_context_t *ctx = NULL;
ngx_oauth2_cfg_t *cfg = NULL;
ngx_str_t ngx_source_token;
Expand Down Expand Up @@ -466,9 +262,6 @@ static ngx_int_t ngx_oauth2_handler(ngx_http_request_t *r)
return NGX_ERROR;
}

// TODO: if we have a verify post config, call it here
// ...

ngx_str_null(&ngx_source_token);
rv = ngx_http_complex_value(r, &cfg->source_token, &ngx_source_token);
if (rv != NGX_OK) {
Expand Down Expand Up @@ -503,7 +296,12 @@ static ngx_int_t ngx_oauth2_handler(ngx_http_request_t *r)

oauth2_debug(ctx->log, "json_payload=%p", json_payload);

rv = ngx_set_target_variables(r, ctx, json_payload);
rv = oauth2_nginx_set_target_variables(ngx_oauth2_module, ctx,
json_payload);
if (rv != NGX_OK)
goto end;

rv = nginx_oauth2_check_requirements(ctx, cfg->requirements);

end:

Expand Down

0 comments on commit 3f12c92

Please sign in to comment.