Mercurial > hg > nginx
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 } |