comparison src/http/modules/ngx_http_auth_request_module.c @ 5329:00bdc9f08a16

Auth request module import.
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 21 Aug 2013 19:19:47 +0400
parents
children f01ab2dbcfdc
comparison
equal deleted inserted replaced
5328:17291cb8c76e 5329:00bdc9f08a16
1
2 /*
3 * Copyright (C) Maxim Dounin
4 * Copyright (C) Nginx, Inc.
5 */
6
7
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_http.h>
11
12
13 typedef struct {
14 ngx_str_t uri;
15 ngx_array_t *vars;
16 } ngx_http_auth_request_conf_t;
17
18
19 typedef struct {
20 ngx_uint_t done;
21 ngx_uint_t status;
22 ngx_http_request_t *subrequest;
23 } ngx_http_auth_request_ctx_t;
24
25
26 typedef struct {
27 ngx_int_t index;
28 ngx_http_complex_value_t value;
29 ngx_http_set_variable_pt set_handler;
30 } ngx_http_auth_request_variable_t;
31
32
33 static ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);
34 static ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,
35 void *data, ngx_int_t rc);
36 static ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,
37 ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);
38 static ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,
39 ngx_http_variable_value_t *v, uintptr_t data);
40 static void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);
41 static char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,
42 void *parent, void *child);
43 static ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);
44 static char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,
45 void *conf);
46 static char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,
47 void *conf);
48
49
50 static ngx_command_t ngx_http_auth_request_commands[] = {
51
52 { ngx_string("auth_request"),
53 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
54 ngx_http_auth_request,
55 NGX_HTTP_LOC_CONF_OFFSET,
56 0,
57 NULL },
58
59 { ngx_string("auth_request_set"),
60 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
61 ngx_http_auth_request_set,
62 NGX_HTTP_LOC_CONF_OFFSET,
63 0,
64 NULL },
65
66 ngx_null_command
67 };
68
69
70 static ngx_http_module_t ngx_http_auth_request_module_ctx = {
71 NULL, /* preconfiguration */
72 ngx_http_auth_request_init, /* postconfiguration */
73
74 NULL, /* create main configuration */
75 NULL, /* init main configuration */
76
77 NULL, /* create server configuration */
78 NULL, /* merge server configuration */
79
80 ngx_http_auth_request_create_conf, /* create location configuration */
81 ngx_http_auth_request_merge_conf /* merge location configuration */
82 };
83
84
85 ngx_module_t ngx_http_auth_request_module = {
86 NGX_MODULE_V1,
87 &ngx_http_auth_request_module_ctx, /* module context */
88 ngx_http_auth_request_commands, /* module directives */
89 NGX_HTTP_MODULE, /* module type */
90 NULL, /* init master */
91 NULL, /* init module */
92 NULL, /* init process */
93 NULL, /* init thread */
94 NULL, /* exit thread */
95 NULL, /* exit process */
96 NULL, /* exit master */
97 NGX_MODULE_V1_PADDING
98 };
99
100
101 static ngx_int_t
102 ngx_http_auth_request_handler(ngx_http_request_t *r)
103 {
104 ngx_table_elt_t *h, *ho;
105 ngx_http_request_t *sr;
106 ngx_http_post_subrequest_t *ps;
107 ngx_http_auth_request_ctx_t *ctx;
108 ngx_http_auth_request_conf_t *arcf;
109
110 arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);
111
112 if (arcf->uri.len == 0) {
113 return NGX_DECLINED;
114 }
115
116 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
117 "auth request handler");
118
119 ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);
120
121 if (ctx != NULL) {
122 if (!ctx->done) {
123 return NGX_AGAIN;
124 }
125
126 /*
127 * as soon as we are done - explicitly set variables to make
128 * sure they will be available after internal redirects
129 */
130
131 if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
132 return NGX_ERROR;
133 }
134
135 /* return appropriate status */
136
137 if (ctx->status == NGX_HTTP_FORBIDDEN) {
138 return ctx->status;
139 }
140
141 if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
142 sr = ctx->subrequest;
143
144 h = sr->headers_out.www_authenticate;
145
146 if (!h && sr->upstream) {
147 h = sr->upstream->headers_in.www_authenticate;
148 }
149
150 if (h) {
151 ho = ngx_list_push(&r->headers_out.headers);
152 if (ho == NULL) {
153 return NGX_ERROR;
154 }
155
156 *ho = *h;
157
158 r->headers_out.www_authenticate = ho;
159 }
160
161 return ctx->status;
162 }
163
164 if (ctx->status >= NGX_HTTP_OK
165 && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
166 {
167 return NGX_OK;
168 }
169
170 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
171 "auth request unexpected status: %d", ctx->status);
172
173 return NGX_HTTP_INTERNAL_SERVER_ERROR;
174 }
175
176 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
177 if (ctx == NULL) {
178 return NGX_ERROR;
179 }
180
181 ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
182 if (ps == NULL) {
183 return NGX_ERROR;
184 }
185
186 ps->handler = ngx_http_auth_request_done;
187 ps->data = ctx;
188
189 if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,
190 NGX_HTTP_SUBREQUEST_WAITED)
191 != NGX_OK)
192 {
193 return NGX_ERROR;
194 }
195
196 /*
197 * allocate fake request body to avoid attempts to read it and to make
198 * sure real body file (if already read) won't be closed by upstream
199 */
200
201 sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
202 if (sr->request_body == NULL) {
203 return NGX_ERROR;
204 }
205
206 sr->header_only = 1;
207
208 ctx->subrequest = sr;
209
210 ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);
211
212 return NGX_AGAIN;
213 }
214
215
216 static ngx_int_t
217 ngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)
218 {
219 ngx_http_auth_request_ctx_t *ctx = data;
220
221 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
222 "auth request done s:%d", r->headers_out.status);
223
224 ctx->done = 1;
225 ctx->status = r->headers_out.status;
226
227 return rc;
228 }
229
230
231 static ngx_int_t
232 ngx_http_auth_request_set_variables(ngx_http_request_t *r,
233 ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)
234 {
235 ngx_str_t val;
236 ngx_http_variable_t *v;
237 ngx_http_variable_value_t *vv;
238 ngx_http_auth_request_variable_t *av, *last;
239 ngx_http_core_main_conf_t *cmcf;
240
241 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
242 "auth request set variables");
243
244 if (arcf->vars == NULL) {
245 return NGX_OK;
246 }
247
248 cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
249 v = cmcf->variables.elts;
250
251 av = arcf->vars->elts;
252 last = av + arcf->vars->nelts;
253
254 while (av < last) {
255 /*
256 * explicitly set new value to make sure it will be available after
257 * internal redirects
258 */
259
260 vv = &r->variables[av->index];
261
262 if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)
263 != NGX_OK)
264 {
265 return NGX_ERROR;
266 }
267
268 vv->valid = 1;
269 vv->not_found = 0;
270 vv->data = val.data;
271 vv->len = val.len;
272
273 if (av->set_handler) {
274 /*
275 * set_handler only available in cmcf->variables_keys, so we store
276 * it explicitly
277 */
278
279 av->set_handler(r, vv, v[av->index].data);
280 }
281
282 av++;
283 }
284
285 return NGX_OK;
286 }
287
288
289 static ngx_int_t
290 ngx_http_auth_request_variable(ngx_http_request_t *r,
291 ngx_http_variable_value_t *v, uintptr_t data)
292 {
293 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
294 "auth request variable");
295
296 v->not_found = 1;
297
298 return NGX_OK;
299 }
300
301
302 static void *
303 ngx_http_auth_request_create_conf(ngx_conf_t *cf)
304 {
305 ngx_http_auth_request_conf_t *conf;
306
307 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));
308 if (conf == NULL) {
309 return NULL;
310 }
311
312 /*
313 * set by ngx_pcalloc():
314 *
315 * conf->uri = { 0, NULL };
316 */
317
318 conf->vars = NGX_CONF_UNSET_PTR;
319
320 return conf;
321 }
322
323
324 static char *
325 ngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)
326 {
327 ngx_http_auth_request_conf_t *prev = parent;
328 ngx_http_auth_request_conf_t *conf = child;
329
330 ngx_conf_merge_str_value(conf->uri, prev->uri, "");
331 ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);
332
333 return NGX_CONF_OK;
334 }
335
336
337 static ngx_int_t
338 ngx_http_auth_request_init(ngx_conf_t *cf)
339 {
340 ngx_http_handler_pt *h;
341 ngx_http_core_main_conf_t *cmcf;
342
343 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
344
345 h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
346 if (h == NULL) {
347 return NGX_ERROR;
348 }
349
350 *h = ngx_http_auth_request_handler;
351
352 return NGX_OK;
353 }
354
355
356 static char *
357 ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
358 {
359 ngx_http_auth_request_conf_t *arcf = conf;
360
361 ngx_str_t *value;
362
363 if (arcf->uri.data != NULL) {
364 return "is duplicate";
365 }
366
367 value = cf->args->elts;
368
369 if (ngx_strcmp(value[1].data, "off") == 0) {
370 arcf->uri.len = 0;
371 arcf->uri.data = (u_char *) "";
372
373 return NGX_CONF_OK;
374 }
375
376 arcf->uri = value[1];
377
378 return NGX_CONF_OK;
379 }
380
381
382 static char *
383 ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
384 {
385 ngx_http_auth_request_conf_t *arcf = conf;
386
387 ngx_str_t *value;
388 ngx_http_variable_t *v;
389 ngx_http_auth_request_variable_t *av;
390 ngx_http_compile_complex_value_t ccv;
391
392 value = cf->args->elts;
393
394 if (value[1].data[0] != '$') {
395 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
396 "invalid variable name \"%V\"", &value[1]);
397 return NGX_CONF_ERROR;
398 }
399
400 value[1].len--;
401 value[1].data++;
402
403 if (arcf->vars == NGX_CONF_UNSET_PTR) {
404 arcf->vars = ngx_array_create(cf->pool, 1,
405 sizeof(ngx_http_auth_request_variable_t));
406 if (arcf->vars == NULL) {
407 return NGX_CONF_ERROR;
408 }
409 }
410
411 av = ngx_array_push(arcf->vars);
412 if (av == NULL) {
413 return NGX_CONF_ERROR;
414 }
415
416 v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);
417 if (v == NULL) {
418 return NGX_CONF_ERROR;
419 }
420
421 av->index = ngx_http_get_variable_index(cf, &value[1]);
422 if (av->index == NGX_ERROR) {
423 return NGX_CONF_ERROR;
424 }
425
426 if (v->get_handler == NULL) {
427 v->get_handler = ngx_http_auth_request_variable;
428 v->data = (uintptr_t) av;
429 }
430
431 av->set_handler = v->set_handler;
432
433 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
434
435 ccv.cf = cf;
436 ccv.value = &value[2];
437 ccv.complex_value = &av->value;
438
439 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
440 return NGX_CONF_ERROR;
441 }
442
443 return NGX_CONF_OK;
444 }