[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/vtest.json",
    "content": "{\n\t\"problemMatcher\": [\n\t\t{\n\t\t\t\"owner\": \"vtest\",\n\t\t\t\"pattern\": [\n\t\t\t\t{\n\t\t\t\t\t\"regexp\": \"^#(\\\\s+top\\\\s+TEST\\\\s+(.*)\\\\s+FAILED.*)\",\n\t\t\t\t\t\"file\": 2,\n\t\t\t\t\t\"message\": 1\n\t\t\t\t}\n\t\t\t]\n\t\t}\n\t]\n}\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "# The MIT License (MIT)\n#\n# Copyright (c) 2020 Tim Düsterhus\n#\n# Permission is hereby granted, free of charge, to any person obtaining a copy\n# of this software and associated documentation files (the \"Software\"), to deal\n# in the Software without restriction, including without limitation the rights\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n# copies of the Software, and to permit persons to whom the Software is\n# furnished to do so, subject to the following conditions:\n#\n# The above copyright notice and this permission notice shall be included in all\n# copies or substantial portions of the Software.\n#\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n# SOFTWARE.\n#\n# SPDX-License-Identifier: MIT\n\nname: Test\n\non:\n  push:\n  pull_request:\n  schedule:\n  - cron: '0 0 * * 0'\n\njobs:\n  VTest:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        haproxy-versions:\n        - \"1.8\"\n        - \"1.9\"\n        - \"2.0\"\n        - \"2.1\"\n        - \"2.2\"\n        - \"2.3\"\n        - \"2.4\"\n        - \"2.5\"\n        - \"2.6\"\n        - \"2.7\"\n        - \"2.8\"\n        - \"2.9\"\n        - \"3.0\"\n        - \"3.1\"\n        - \"3.2\"\n        - \"3.3\"\n        - \"3.4\"\n      fail-fast: false\n    steps:\n    - name: Install HAProxy ${{ matrix.haproxy-versions }}.\n      uses: timwolla/action-install-haproxy@main\n      id: install-haproxy\n      with:\n        branch: ${{ matrix.haproxy-versions }}\n        install_vtest: yes\n        use_lua: yes\n    - name: Install apt dependencies.\n      run: sudo apt-get install -y lua-json\n    - uses: actions/checkout@v6\n      with:\n        submodules: recursive\n    - name: Install haproxy-lua-http globally.\n      run: |\n        sudo install -m755 -d /usr/local/share/lua/5.3/\n        sudo install -m644 haproxy-lua-http/http.lua /usr/local/share/lua/5.3/haproxy-lua-http.lua\n        sed -i 's/lua-prepend-path/#lua-prepend-path/g' test/*.vtc\n    - name: Install problem matcher for VTest.\n      run: echo \"::add-matcher::.github/vtest.json\"\n    - name: Run tests using HAProxy ${{ steps.install-haproxy.outputs.version }}.\n      run: |\n        set -euo pipefail\n        vtest -Dhaproxy_version=${{ steps.install-haproxy.outputs.version }} -k -t 10 test/*.vtc 2>&1 \\\n          |tee >(\n            awk '\n              $5==\"passed\"{icon=\":heavy_check_mark:\"}\n              $5==\"skipped\"{icon=\":grey_question:\"}\n              $5==\"FAILED\"{icon=\":x:\"}\n              $1 == \"#\" && $3==\"TEST\"{\n                print \"- \" icon \" \" $4\n              }\n              {line=$0}\n              END{print \"# \" $0}\n            ' \\\n            |tac >> $GITHUB_STEP_SUMMARY\n          )\n"
  },
  {
    "path": ".gitignore",
    "content": "debian/debhelper-build-stamp\ndebian/files\ndebian/haproxy-auth-request.debhelper.log\ndebian/haproxy-auth-request.substvars\ndebian/haproxy-auth-request/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"haproxy-lua-http\"]\n\tpath = haproxy-lua-http\n\turl = https://github.com/haproxytech/haproxy-lua-http.git\n\tbranch = master\n"
  },
  {
    "path": ".mailmap",
    "content": "Tim Düsterhus <tim@bastelstu.be> <timwolla@googlemail.com>\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2018 Tim Düsterhus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "Makefile",
    "content": "# SPDX-License-Identifier: MIT\n\ndefault:\n\ninstall: auth-request.lua haproxy-lua-http/http.lua\n\tinstall -d \"$(DESTDIR)/usr/share/haproxy\"\n\tinstall -m644 haproxy-lua-http/http.lua \"$(DESTDIR)/usr/share/haproxy\"\n\tinstall -m644 auth-request.lua \"$(DESTDIR)/usr/share/haproxy\"\n\n.PHONY: install\n"
  },
  {
    "path": "README.md",
    "content": "# auth-request ![Test](https://github.com/TimWolla/haproxy-auth-request/workflows/Test/badge.svg)\n\nauth-request allows you to add access control to your HTTP services based on a\nsubrequest to a configured HAProxy backend. The workings of this Lua script are\nloosely based on the [ngx_http_auth_request_module] module for nginx.\n\n## Requirements\n\n- HAProxy 1.8.4+ (2.2.0+ recommended)\n  - Only the latest version of each HAProxy branch is supported.\n- `USE_LUA=1` must be set at compile time.\n- [haproxy-lua-http] must be available within the Lua path.\n  - A `json` library within the Lua path (dependency of haproxy-lua-http).\n  - With HAProxy 2.1.3+ you can use the [`lua-prepend-path`] configuration\n    option to specify the search path.\n\n## Usage\n\n1. Load this Lua script in the `global` section of your `haproxy.cfg`:\n    ```haproxy\n    global\n        # *snip*\n        lua-prepend-path /usr/share/haproxy/?/http.lua # If haproxy-lua-http is saved as /usr/share/haproxy/haproxy-lua-http/http.lua\n        lua-load /usr/share/haproxy/auth-request.lua\n    ```\n\n2. Define a backend that is used for the subrequests:\n    ```haproxy\n    backend auth_request\n        mode http\n        server auth_request 127.0.0.1:8080 check\n    ```\n\n3. Execute the subrequest in your frontend (as early as possible):\n    ```haproxy\n    frontend http\n        mode http\n        bind :::80 v4v6\n\n        # *snip*\n\n        # auth-request syntax:\n        #                             Backend name     Path to request\n        http-request lua.auth-request auth_request     /is-allowed\n\n        # auth-intercept syntax:                                           (Headers to copy)\n        #                               Backend name  Path         Method  Request  Success  Failure\n        http-request lua.auth-intercept auth_request  /is-allowed  HEAD    *        -        -\n    ```\n\n4. Act on the results:\n    ```haproxy\n    frontend http\n        # *snip*\n\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n    ```\n\n### Parameters\n\nThe scripts receive a list of parameters used to build the authentication\nrequest:\n\n* **Backend name**: is the name of an HAProxy backend. See the\n[Inner Workings](#inner-workings) section.\n* **Path to request**: the request URL sent to the auth-request backend.\n\nThe following parameters are only available in the `auth-intercept` script:\n\n* **Method**: the HTTP method that should be used. Use an asterisk `*` to ask\n`auth-intercept` to copy the same method used by the client. `auth-request`\nuses the `HEAD` method.\n* **Headers to copy on Request**: a comma-separated list of a simplified glob\npattern that should match the HTTP header names to copy from the client to the\nauth-intercept backend. Use a dash `-` to not copy any header.\n* **Headers to copy on Success**: a comma-separated list of a simplified glob\npattern that should match the HTTP header names to copy from the auth-intercept\nbackend to the protected backend server, if the auth-intercept backend respond\nwith 2xx response code and the request succeed. All headers received from the\nauth-intercept will override headers with the same name provided by the client.\nUse `*` to copy all headers, or use a dash `-` to not copy any header. HAProxy\nvariables are always created, see the [Available Variables](#available-variables)\nsection.\n* **Headers to copy on Failure**: a comma-separated list of a simplified glob\npattern that should match the HTTP header names to copy from the auth-intercept\nbackend to the client, if the request failed. `auth-intercept` will use the\nsame HTTP method and body sent by the auth-intercept backend to respond to the\nclient, closing the transaction. The protected backend server will not be used.\nUse `*` to copy all headers. Use a dash `-` to not close the transaction and\nleave to the HAProxy configuration the task to deny the request based on the\n`txn.auth_response_successful` variable. HAProxy variables are always created,\nsee the [Available Variables](#available-variables) section.\n\nSimplified glob pattern: use an asterisk `*` to match any sequence of\ncharacters and `?` to match a single char. `*` will match any header name.\n`x-*` will match all header names started with `x-`. `x-????` will match\n`x-user` but will not match neither `x-token` nor `x-id`.\n\nHAProxy 2.1 or older: the On Failure param (the last one) will close the\ntransaction and respond to the client if the value is not a dash `-`, however\nthis feature is only supported on HAProxy 2.2 or newer. The only supported\noption on 2.1 and older is a dash `-`.\n\n### Available Variables\n\nauth-request uses HAProxy variables to communicate the results back to you. The\n[`var()` sample fetch] can be used to retrieve the variable contents.\n\nThe following list of variables may be set.\n\n<dl>\n<dt><code>txn.auth_response_successful</code></dt>\n<dd>\nSet to <code>true</code> if the subrequest returns an HTTP status code in the\n<code>2xx</code> range. <code>false</code> otherwise.\n</dd>\n\n<dt><code>txn.auth_response_code</code></dt>\n<dd>\nThe HTTP status code of the subrequest. If the subrequest did not return a\nvalid HTTP response the value will be <code>500</code>.\n</dd>\n\n<dt><code>txn.auth_response_location</code></dt>\n<dd>\nThe <code>location</code> response header of the subrequest.\n\nThis variable is only set if the HTTP status code of the subrequest indicates a\nredirect (i.e. <code>301</code>, <code>302</code>, <code>303</code>,\n<code>307</code>, or <code>308</code>).\n</dd>\n\n<dt><code>req.auth_response_header.*</code>\n<dd>\nThese variables store the subrequest’s response headers. The values of\nduplicate response headers will be merged with a comma.\n\nHAProxy variables may only contain alphanumeric characters, the dot\n(<code>.</code>), and an underscore <code>_</code>. Any non-alphanumeric\ncharacters will be replaced with an underscore to be representable. If the\nresponse contains duplicate response headers <em>after</em> normalizing the\nheader name the result for these headers will be undefined.\n\nNormalization examples:\n<dl>\n<dt><code>X-Authenticated-User</code></dt>\n<dd><code>req.auth_response_header.x_authenticated_user</code></dd>\n<dt><code>Success</code></dt>\n<dd><code>req.auth_response_header.success</code></dd>\n</dl>\n\nPlease note: The scope of the response header variables is <code>req</code>\ncompared to <code>txn</code> for the other variables. The contents will no\nlonger be available during response processing to save memory. Copy the values\nof interest into a <code>txn.</code> variable if you need access them during\nresponse processing.\n</dd>\n</dl>\n\n## Inner Workings\n\nThe Lua script will make a HTTP request to the *first* server in the given\nbackend that is either marked as `UP` or that does not have checks enabled.\nThis allows for basic health checking of the auth-request backend. If you need\nmore complex processing of the request forward the auth-request to a separate\nHAProxy *frontend* that performs the required modifications to the request and\nresponse.\n\nThe requested URL is the one given in the second parameter.\n\nAny request headers will be forwarded as-is to the auth-request backend, with\nthe exception of the `content-length` header which will be stripped, because\nthe request body will not be forwarded.\n\n## Known limitations\n\n- The Lua script only supports basic health checking, without redispatching or\n  load balancing of any kind.\n- The backend must not be using TLS.\n\n[ngx_http_auth_request_module]: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html\n[haproxy-lua-http]: https://github.com/haproxytech/haproxy-lua-http\n[`lua-prepend-path`]: http://cbonte.github.io/haproxy-dconv/2.1/configuration.html#lua-prepend-path\n[`var()` sample fetch]: http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7.3.2-var\n"
  },
  {
    "path": "auth-request.lua",
    "content": "-- The MIT License (MIT)\n--\n-- Copyright (c) 2018 Tim Düsterhus\n--\n-- Permission is hereby granted, free of charge, to any person obtaining a copy\n-- of this software and associated documentation files (the \"Software\"), to deal\n-- in the Software without restriction, including without limitation the rights\n-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n-- copies of the Software, and to permit persons to whom the Software is\n-- furnished to do so, subject to the following conditions:\n--\n-- The above copyright notice and this permission notice shall be included in all\n-- copies or substantial portions of the Software.\n--\n-- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n-- SOFTWARE.\n--\n-- SPDX-License-Identifier: MIT\n\nlocal http = require(\"haproxy-lua-http\")\n\ncore.register_action(\"auth-request\", { \"http-req\" }, function(txn, be, path)\n\tauth_request(txn, be, path, \"HEAD\", \".*\", \"-\", \"-\")\nend, 2)\n\ncore.register_action(\"auth-intercept\", { \"http-req\" }, function(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)\n\thdr_req = globToLuaPattern(hdr_req)\n\thdr_succeed = globToLuaPattern(hdr_succeed)\n\thdr_fail = globToLuaPattern(hdr_fail)\n\tauth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)\nend, 6)\n\nfunction globToLuaPattern(glob)\n\tif glob == \"-\" then\n\t\treturn \"-\"\n\tend\n\t-- magic chars: '^', '$', '(', ')', '%', '.', '[', ']', '*', '+', '-', '?'\n\t-- https://www.lua.org/manual/5.4/manual.html#6.4.1\n\t--\n\t-- this chain is:\n\t-- 1. escaping all the magic chars, adding a `%` in front of all of them,\n\t--    except the chars being processed later in the chain;\n\t-- 1.1. all the chars inside the [set] are magic chars and have special\n\t--      meaning inside a set, so we're also escaping all of them to avoid\n\t--      misbehavior;\n\t-- 2. converting \"match all\" `*` and \"match one\" `?` to their Lua pattern\n\t--    counterparts;\n\t-- 3. adding start and finish boundaries outside the whole string and,\n\t--    being a comma-separated list, between every single item as well.\n\treturn \"^\" .. glob:gsub(\"[%^%$%(%)%%%.%[%]%+%-]\", \"%%%1\"):gsub(\"*\", \".*\"):gsub(\"?\", \".\"):gsub(\",\", \"$,^\") .. \"$\"\nend\n\nfunction set_var_pre_2_2(txn, var, value)\n\treturn txn:set_var(var, value)\nend\nfunction set_var_post_2_2(txn, var, value)\n\treturn txn:set_var(var, value, true)\nend\n\nset_var = function(txn, var, value)\n\tlocal success = pcall(set_var_post_2_2, txn, var, value)\n\tif success then\n\t\tset_var = set_var_post_2_2\n\telse\n\t\tset_var = set_var_pre_2_2\n\tend\n\n\treturn set_var(txn, var, value)\nend\n\nfunction sanitize_header_for_variable(header)\n\treturn header:gsub(\"[^a-zA-Z0-9]\", \"_\")\nend\n\n-- header_match checks whether the provided header matches the pattern.\n-- pattern is a comma-separated list of Lua Patterns.\nfunction header_match(header, pattern)\n\tif header == \"content-length\" or header == \"host\" or pattern == \"-\" then\n\t\treturn false\n\tend\n\tfor p in pattern:gmatch(\"[^,]*\") do\n\t\tif header:match(p) then\n\t\t\treturn true\n\t\tend\n\tend\n\treturn false\nend\n\n-- Terminates the transaction and sends the provided response to the client.\n-- hdr_fail filters header names that should be provided using Lua Patterns.\nfunction send_response(txn, response, hdr_fail)\n\tlocal reply = txn:reply()\n\tif response then\n\t\treply:set_status(response.status_code)\n\t\tfor header, value in response:get_headers(false) do\n\t\t\tif header_match(header, hdr_fail) then\n\t\t\t\treply:add_header(header, value)\n\t\t\tend\n\t\tend\n\t\tif response.content then\n\t\t\treply:set_body(response.content)\n\t\tend\n\telse\n\t\treply:set_status(500)\n\tend\n\ttxn:done(reply)\nend\n\n-- auth_request makes the request to the external authentication service\n-- and waits for the response. hdr_* params receive a comma-separated\n-- list of Lua Patterns used to identify the headers that should be\n-- copied between the requests and responses. A dash `-` in these params\n-- mean that the headers shouldn't be copied at all.\n-- Special values and behavior:\n-- * method == \"*\": call the auth service using the same method used by the client.\n-- * hdr_fail == \"-\": make the Lua script to not terminate the request.\nfunction auth_request(txn, be, path, method, hdr_req, hdr_succeed, hdr_fail)\n\tset_var(txn, \"txn.auth_response_successful\", false)\n\n\t-- Check whether the given backend exists.\n\tif core.backends[be] == nil then\n\t\ttxn:Alert(\"Unknown auth-request backend '\" .. be .. \"'\")\n\t\tset_var(txn, \"txn.auth_response_code\", 500)\n\t\treturn\n\tend\n\n\t-- Check whether the given backend has servers that\n\t-- are not `DOWN`.\n\tlocal addr = nil\n\tfor name, server in pairs(core.backends[be].servers) do\n\t\tlocal status = server:get_stats()['status']\n\t\tif status == \"no check\" or status:find(\"UP\") == 1 then\n\t\t\taddr = server:get_addr()\n\t\t\tbreak\n\t\tend\n\tend\n\tif addr == nil then\n\t\ttxn:Warning(\"No servers available for auth-request backend: '\" .. be .. \"'\")\n\t\tset_var(txn, \"txn.auth_response_code\", 500)\n\t\treturn\n\tend\n\n\t-- Transform table of request headers from haproxy's to\n\t-- socket.http's format.\n\tlocal headers = {}\n\tfor header, values in pairs(txn.http:req_get_headers()) do\n\t\tif header_match(header, hdr_req) then\n\t\t\tfor i, v in pairs(values) do\n\t\t\t\tif headers[header] == nil then\n\t\t\t\t\theaders[header] = v\n\t\t\t\telse\n\t\t\t\t\theaders[header] = headers[header] .. \", \" .. v\n\t\t\t\tend\n\t\t\tend\n\t\tend\n\tend\n\n\t-- Make request to backend.\n\tif method == \"*\" then\n\t\tmethod = txn.sf:method()\n\tend\n\tlocal response, err = http.send(method:upper(), {\n\t\turl = \"http://\" .. addr .. path,\n\t\theaders = headers,\n\t})\n\n\t-- `terminate_on_failure == true` means that the Lua script should send the response\n\t-- and terminate the transaction in the case of a failure. This will happen when\n\t-- hdr_fail content isn't a dash `-`.\n\tlocal terminate_on_failure = hdr_fail ~= \"-\"\n\n\t-- Check whether we received a valid HTTP response.\n\tif response == nil then\n\t\ttxn:Warning(\"Failure in auth-request backend '\" .. be .. \"': \" .. err)\n\t\tset_var(txn, \"txn.auth_response_code\", 500)\n\t\tif terminate_on_failure then\n\t\t\tsend_response(txn)\n\t\tend\n\t\treturn\n\tend\n\n\tset_var(txn, \"txn.auth_response_code\", response.status_code)\n\tlocal response_ok = 200 <= response.status_code and response.status_code < 300\n\n\tfor header, value in response:get_headers(true) do\n\t\tset_var(txn, \"req.auth_response_header.\" .. sanitize_header_for_variable(header), value)\n\t\tif response_ok and hdr_succeed ~= \"-\" and header_match(header, hdr_succeed) then\n\t\t\ttxn.http:req_set_header(header, value)\n\t\tend\n\tend\n\n\t-- response_ok means 2xx: allow request.\n\tif response_ok then\n\t\tset_var(txn, \"txn.auth_response_successful\", true)\n\t-- Don't allow codes < 200 or >= 300.\n\t-- Forward the response to the client if required.\n\telseif terminate_on_failure then\n\t\tsend_response(txn, response, hdr_fail)\n\t-- Codes with Location: Passthrough location at redirect.\n\telseif response.status_code == 301 or response.status_code == 302 or response.status_code == 303 or response.status_code == 307 or response.status_code == 308 then\n\t\tset_var(txn, \"txn.auth_response_location\", response:get_header(\"location\", \"last\"))\n\t-- 401 / 403: Do nothing, everything else: log.\n\telseif response.status_code ~= 401 and response.status_code ~= 403 then\n\t\ttxn:Warning(\"Invalid status code in auth-request backend '\" .. be .. \"': \" .. response.status_code)\n\tend\nend\n"
  },
  {
    "path": "debian/changelog",
    "content": "haproxy-auth-request (0) UNRELEASED; urgency=medium\n\n  * Initial Release.\n\n -- Tim Düsterhus <tim@bastelstu.be>  Mon, 08 Jan 2018 22:56:57 +0000\n"
  },
  {
    "path": "debian/compat",
    "content": "9\n"
  },
  {
    "path": "debian/control",
    "content": "Source: haproxy-auth-request\nSection: net\nPriority: optional\nMaintainer: Tim Düsterhus <tim@bastelstu.be>\nBuild-Depends: debhelper (>= 9)\nStandards-Version: 3.9.8\nHomepage: https://github.com/TimWolla/haproxy-auth-request\nVcs-Git: https://github.com/TimWolla/haproxy-auth-request.git\nVcs-Browser: https://github.com/TimWolla/haproxy-auth-request.git\n\nPackage: haproxy-auth-request\nArchitecture: all\nDepends: ${misc:Depends},\n         lua-json\nSuggests: haproxy\nDescription: HTTP access control using subrequests\n auth-request allows you to add access control to your HTTP services based\n on a subrequest to a configured haproxy backend. The workings of this Lua\n script are loosely based on the ngx_http_auth_request_module module for\n nginx.\n"
  },
  {
    "path": "debian/copyright",
    "content": "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: haproxy-auth-request\nSource: <url://example.com>\n\nFiles: *\nCopyright: Tim Düsterhus <tim@bastelstu.be>\nLicense: MIT\n\nFiles: debian/*\nCopyright: 2018 Tim Düsterhus <tim@bastelstu.be>\nLicense: MIT\n\nLicense: MIT\n Permission is hereby granted, free of charge, to any person obtaining a\n copy of this software and associated documentation files (the \"Software\"),\n to deal in the Software without restriction, including without limitation\n the rights to use, copy, modify, merge, publish, distribute, sublicense,\n and/or sell copies of the Software, and to permit persons to whom the\n Software is furnished to do so, subject to the following conditions:\n .\n The above copyright notice and this permission notice shall be included\n in all copies or substantial portions of the Software.\n .\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, \n TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE \n SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n# Please also look if there are files or directories which have a\n# different copyright/license attached and list them here.\n# Please avoid picking licenses with terms that are more restrictive than the\n# packaged work, as it may make Debian's contributions unacceptable upstream.\n"
  },
  {
    "path": "debian/haproxy-auth-request.docs",
    "content": "README.md\n"
  },
  {
    "path": "debian/rules",
    "content": "#!/usr/bin/make -f\n# See debhelper(7) (uncomment to enable)\n# output every command that modifies files on the build system.\n# export DH_VERBOSE = 1\n\n%:\n\tdh $@\n"
  },
  {
    "path": "debian/source/format",
    "content": "3.0 (native)\n"
  },
  {
    "path": "test/allow.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a successful request allows access.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 204\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 200\n} -run\n"
  },
  {
    "path": "test/allow_mt.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a successful request allows access for multi-threaded Lua.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.4\"\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 204\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load-per-thread ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 200\n} -run\n"
  },
  {
    "path": "test/deny.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a failing request denies access.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 403\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 403\n} -run\n"
  },
  {
    "path": "test/deny_close.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a backend close denies access.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 403\n} -run\n"
  },
  {
    "path": "test/deny_garbage.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that a backend sending garbage denies access.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 7 -start\n\nserver s_auth_backend {\n    rxreq\n    send \"foo\"\n    accept\n    rxreq\n    send \"foo\\r\\n\"\n    accept\n    rxreq\n    send \"HTTP/1.0 200 Ok\"\n    accept\n    rxreq\n    send \"HTTP\\r\\n\"\n    accept\n    rxreq\n    send \"HTTP/1\\r\\n\"\n    accept\n    rxreq\n    send \"HTTP/1.0 200 Ok\\r\\n\"\n    accept\n    rxreq\n    send \"HTTP/1.0 XXX Fail\\r\\n\\r\\n\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 403\n} -repeat 7 -run\n"
  },
  {
    "path": "test/dynamic_method.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that auth-request backend receives the client method.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 3 -start\n\nserver s_auth_backend {\n    rxreq\n    expect req.method == \"POST\"\n    txresp\n\n    accept\n    rxreq\n    expect req.method == \"GET\"\n    txresp\n\n    accept\n    rxreq\n    expect req.method == \"HEAD\"\n    txresp\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / * * - -\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq -method \"POST\"\n    rxresp\n    txreq -method \"GET\"\n    rxresp\n    txreq -method \"HEAD\"\n    rxresp\n} -run\n"
  },
  {
    "path": "test/expose_arbitrary_headers.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that arbitrary response headers are exposed.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 4 -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 200 \\\n        -hdr \"content-type: text/plain\" \\\n        -hdr \"x-authenticated-email: guest@example.com\" \\\n        -hdr \"x-authenticated-user: guest\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 200 \\\n        -hdr \"x-authenticated-user: root\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 200 \\\n        -hdr \"content-type: text/plain\" \\\n        -hdr \"x-authenticated-email: root@example.com\" \\\n        -hdr \"x-authenticated-user: root\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 200 \\\n        -hdr \"content-type: text/plain\" \\\n        -hdr \"x-authenticated-email: root@example.com\" \\\n        -hdr \"x-authenticated-email: root@example.net\" \\\n        -hdr \"x-authenticated-user: root\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny unless { var(req.auth_response_header.x_authenticated_user) -m str root }\n\n        http-request set-header x-echo \"%[var(req.auth_response_header.content_type)]|%[var(req.auth_response_header.x_authenticated_user)]|%[var(req.auth_response_header.x_authenticated_email)]\"\n        http-request set-var(txn.echo) req.fhdr(x-echo)\n\n        http-response set-header x-echo %[var(txn.echo)]\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 403\n} -run\n\nclient c2 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 200\n    expect resp.http.x-echo == \"|root|\"\n    txreq\n    rxresp\n    expect resp.status == 200\n    expect resp.http.x-echo == \"text/plain|root|root@example.com\"\n    txreq\n    rxresp\n    expect resp.status == 200\n    expect resp.http.x-echo == \"text/plain|root|root@example.com,root@example.net\"\n} -run\n"
  },
  {
    "path": "test/expose_location.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that location header is exposed for redirects.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 4 -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 301 \\\n        -hdr \"location: https://example.com\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 303 \\\n        -hdr \"location: https://example.com\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 307 \\\n        -hdr \"location: https://example.com\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 200 \\\n        -hdr \"location: https://example.com\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-response set-header x-status %[var(txn.auth_response_code)]\n        http-response set-header x-location %[var(txn.auth_response_location)] if { var(txn.auth_response_location) -m found }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.http.x-status == 301\n    expect resp.http.x-location == \"https://example.com\"\n    txreq\n    rxresp\n    expect resp.http.x-status == 303\n    expect resp.http.x-location == \"https://example.com\"\n    txreq\n    rxresp\n    expect resp.http.x-status == 307\n    expect resp.http.x-location == \"https://example.com\"\n    txreq\n    rxresp\n    expect resp.http.x-status == 200\n    expect resp.http.x-location == \"<undef>\"\n} -run\n"
  },
  {
    "path": "test/expose_location_last.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that last location header is exposed for redirects.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 4 -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 301 \\\n        -hdr \"location: https://example.net\" \\\n        -hdr \"location: https://example.com\"\n\n    accept\n    rxreq\n    txresp \\\n        -status 301 \\\n        -hdr \"location: https://example.com\" \\\n        -hdr \"location: https://example.net\"\n\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-response set-header x-status %[var(txn.auth_response_code)]\n        http-response set-header x-location %[var(txn.auth_response_location)] if { var(txn.auth_response_location) -m found }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.http.x-status == 301\n    expect resp.http.x-location == \"https://example.com\"\n    txreq\n    rxresp\n    expect resp.http.x-status == 301\n    expect resp.http.x-location == \"https://example.net\"\n} -run\n"
  },
  {
    "path": "test/expose_status_code.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that response status is exposed.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 4 -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 200\n\n    accept\n    rxreq\n    txresp \\\n        -status 400\n\n    accept\n    rxreq\n    txresp \\\n        -status 403\n\n    accept\n    rxreq\n    txresp \\\n        -status 200\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-response set-header x-status %[var(txn.auth_response_code)]\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.http.x-status == 200\n    txreq\n    rxresp\n    expect resp.http.x-status == 400\n    txreq\n    rxresp\n    expect resp.http.x-status == 403\n    txreq\n    rxresp\n    expect resp.http.x-status == 200\n} -run\n"
  },
  {
    "path": "test/headers_complete.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered request headers are passed to the auth-intercept backend.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.2\"\n\nserver s1 {\n    rxreq\n    expect req.http.x-user == \"logan\"\n    expect req.http.x-app-data == \"secret\"\n    expect req.http.token == \"bearer value\"\n    txresp \\\n        -status 201 \\\n        -hdr \"x-field: value\"\n} -start\n\nserver s_auth_backend {\n    rxreq\n    expect req.http.x-app-data == \"<undef>\"\n    expect req.http.token == \"bearer value\"\n    txresp \\\n        -status 401 \\\n        -hdr \"x-reason: invalid pwd\"\n\n    accept\n    rxreq\n    expect req.http.x-app-data == \"<undef>\"\n    expect req.http.token == \"bearer value\"\n    txresp \\\n        -status 200 \\\n        -hdr \"x-user: logan\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / HEAD token x-user x-reason\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq \\\n        -hdr \"x-user: try-to-override\" \\\n        -hdr \"x-app-data: secret\" \\\n        -hdr \"token: bearer value\"\n    rxresp\n    expect resp.status == 401\n    expect resp.http.x-reason == \"invalid pwd\"\n    expect resp.http.x-field == \"<undef>\"\n\n    txreq \\\n        -hdr \"x-user: try-to-override\" \\\n        -hdr \"x-app-data: secret\" \\\n        -hdr \"token: bearer value\"\n    rxresp\n    expect resp.status == 201\n    expect resp.http.x-reason == \"<undef>\"\n    expect resp.http.x-field == \"value\"\n} -run\n"
  },
  {
    "path": "test/headers_fail.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered auth backend response headers are passed to the client.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.2\"\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 401 \\\n        -hdr \"x-user: admin\" \\\n        -hdr \"x-passwd: 123\" \\\n        -hdr \"x-reason: invalid pwd\" \\\n        -hdr \"token: asd\" \\\n        -hdr \"other: value\" \\\n        -body \"{\\\"msg\\\":\\\"invalid pwd\\\"}\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / * * - x-user,x-reason\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 401\n    expect resp.http.x-user == \"admin\"\n    expect resp.http.x-passwd == \"<undef>\"\n    expect resp.http.x-reason == \"invalid pwd\"\n    expect resp.http.token == \"<undef>\"\n    expect resp.http.other == \"<undef>\"\n    expect resp.body == \"{\\\"msg\\\":\\\"invalid pwd\\\"}\"\n} -run\n"
  },
  {
    "path": "test/headers_fail_multiple.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multi-valued filtered auth backend response headers are passed to the client.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.2\"\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 401 \\\n        -hdr \"x-user: admin\" \\\n        -hdr \"x-passwd: 123\" \\\n        -hdr \"x-reason: invalid pwd\" \\\n        -hdr \"x-reason: account expired\" \\\n        -hdr \"set-cookie: csrf=1234;\" \\\n        -hdr \"set-cookie: session=;\" \\\n        -body \"{\\\"msg\\\":\\\"invalid pwd\\\"}\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        # VTest only sees the first header with a given name, thus\n        # we split the expected headers into separate headers that\n        # can be checked independently.\n        http-response set-header set-cookie1 %[res.fhdr(set-cookie,1)]\n        http-response set-header set-cookie2 %[res.fhdr(set-cookie,2)]\n        http-response set-header x-reason1 %[res.fhdr(x-reason,1)]\n        http-response set-header x-reason2 %[res.fhdr(x-reason,2)]\n\n        server be ${h1_fe2_addr}:${h1_fe2_port}\n\n    listen fe2\n        mode http\n        bind \"fd@${fe2}\"\n        http-request lua.auth-intercept auth_backend / * * - x-user,x-reason,set-cookie\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 401\n    expect resp.http.x-user == \"admin\"\n    expect resp.http.x-passwd == \"<undef>\"\n\n    expect resp.http.x-reason !~ \",\"\n    expect resp.http.x-reason1 == \"invalid pwd\"\n    expect resp.http.x-reason2 == \"account expired\"\n\n    expect resp.http.set-cookie !~ \",\"\n    expect resp.http.set-cookie1 == \"csrf=1234;\"\n    expect resp.http.set-cookie2 == \"session=;\"\n\n    expect resp.body == \"{\\\"msg\\\":\\\"invalid pwd\\\"}\"\n} -run\n"
  },
  {
    "path": "test/headers_filter.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that headers are correctly filtered using a simplified glob pattern.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp -status 201\n} -start\n\nserver s_auth_backend {\n    rxreq\n    expect req.http.x-user == \"logan\"\n    expect req.http.x-passwd == \"top$secret\"\n    expect req.http.y-token == \"<undef>\"\n    expect req.http.y-uid == \"dead-...-beef\"\n    expect req.http.z-myapp == \"<undef>\"\n    expect req.http.z-app == \"another-data\"\n    txresp\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / * x-*,y-???,*-app - -\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq \\\n        -hdr \"x-user: logan\" \\\n        -hdr \"x-passwd: top$secret\" \\\n        -hdr \"y-token: bearer value\" \\\n        -hdr \"y-uid: dead-...-beef\" \\\n        -hdr \"z-myapp: some-data\" \\\n        -hdr \"z-app: another-data\"\n    rxresp\n    expect resp.status == 201\n} -run\n"
  },
  {
    "path": "test/headers_request.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered request headers are passed to the auth-intercept backend.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp -status 201\n} -start\n\nserver s_auth_backend {\n    rxreq\n    expect req.http.x-user == \"admin\"\n    expect req.http.x-passwd == \"123\"\n    expect req.http.token == \"asd\"\n    expect req.http.other == \"<undef>\"\n    txresp\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / * x-*,token - -\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq \\\n        -hdr \"x-user: admin\" \\\n        -hdr \"x-passwd: 123\" \\\n        -hdr \"token: asd\" \\\n        -hdr \"other: value\"\n    rxresp\n    expect resp.status == 201\n} -run\n"
  },
  {
    "path": "test/headers_succeed.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that filtered auth backend response headers are passed to the protected backend server.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    expect req.http.x-user == \"admin\"\n    expect req.http.x-passwd == \"<undef>\"\n    expect req.http.token == \"asd\"\n    expect req.http.other == \"<undef>\"\n    txresp -status 201\n} -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -hdr \"x-user: admin\" \\\n        -hdr \"x-passwd: 123\" \\\n        -hdr \"token: asd\" \\\n        -hdr \"other: value\"\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        http-request lua.auth-intercept auth_backend / * * x-user,token -\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 201\n} -run\n"
  },
  {
    "path": "test/multiple_requests.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multiple clients work fine.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 150 -start\n\nserver s_auth_backend_allow {\n    rxreq\n    txresp \\\n        -status 204\n} -repeat 100 -start\n\nserver s_auth_backend_deny {\n    rxreq\n    txresp \\\n        -status 403\n} -repeat 50 -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend_allow /allow if { path /allow }\n        http-request lua.auth-request auth_backend_deny  /deny  if { path /deny }\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend_allow\n        mode http\n        server auth_backend_allow ${s_auth_backend_allow_addr}:${s_auth_backend_allow_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend_deny ${s_auth_backend_deny_addr}:${s_auth_backend_deny_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq -url \"/allow\"\n    rxresp\n    expect resp.status == 200\n} -repeat 75 -run\n\nclient c2 -connect ${h1_fe1_sock} {\n    txreq -url \"/deny\"\n    rxresp\n    expect resp.status == 403\n} -repeat 50 -run\n\nclient c3 -connect ${h1_fe1_sock} {\n    txreq -url \"/deny\"\n    rxresp\n    expect resp.status == 403\n} -repeat 50 -run\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq -url \"/allow\"\n    rxresp\n    expect resp.status == 200\n} -repeat 25 -run\n"
  },
  {
    "path": "test/multiple_requests_mt.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that multiple clients work fine for multi-threaded Lua.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.4\"\n\nserver s1 {\n    rxreq\n    txresp\n} -repeat 150 -start\n\nserver s_auth_backend_allow {\n    rxreq\n    txresp \\\n        -status 204\n} -repeat 100 -start\n\nserver s_auth_backend_deny {\n    rxreq\n    txresp \\\n        -status 403\n} -repeat 50 -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load-per-thread ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend_allow /allow if { path /allow }\n        http-request lua.auth-request auth_backend_deny  /deny  if { path /deny }\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend_allow\n        mode http\n        server auth_backend_allow ${s_auth_backend_allow_addr}:${s_auth_backend_allow_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend_deny ${s_auth_backend_deny_addr}:${s_auth_backend_deny_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq -url \"/allow\"\n    rxresp\n    expect resp.status == 200\n} -repeat 75 -run\n\nclient c2 -connect ${h1_fe1_sock} {\n    txreq -url \"/deny\"\n    rxresp\n    expect resp.status == 403\n} -repeat 50 -run\n\nclient c3 -connect ${h1_fe1_sock} {\n    txreq -url \"/deny\"\n    rxresp\n    expect resp.status == 403\n} -repeat 50 -run\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq -url \"/allow\"\n    rxresp\n    expect resp.status == 200\n} -repeat 25 -run\n"
  },
  {
    "path": "test/no_variable_leak.lua",
    "content": "-- SPDX-License-Identifier: MIT\n\ncore.register_fetches(\"leak_check\", function(txn, var)\n\tlocal result = txn:get_var(var)\n\t\n\tif result == nil then\n\t\treturn \"<nil>\"\n\tend\n\t\n\treturn result\nend)\n"
  },
  {
    "path": "test/no_variable_leak.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that we don't leak variables.\"\nfeature ignore_unknown_macro\nfeature cmd \"dpkg --compare-versions ${haproxy_version} ge 2.2\"\nfeature cmd \"dpkg --compare-versions ${haproxy_version} lt 2.5-dev6\"\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n    txresp \\\n        -status 301 \\\n        -hdr \"location: https://example.com\"\n} -repeat 2 -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n        lua-load ${testdir}/no_variable_leak.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-response set-header leak-check %[lua.leak_check(txn.auth_response_location)]\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.http.leak-check == \"<nil>\"\n} -run\n"
  },
  {
    "path": "test/pass_headers_to_backend.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that request headers are passed to the backend.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nserver s_auth_backend {\n    rxreq\n    expect req.http.x-foo == \"bar\"\n    txresp \\\n        -status 200\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n\n        http-request lua.auth-request auth_backend /allow\n        http-request deny if ! { var(txn.auth_response_successful) -m bool }\n\n        server s1 ${s1_addr}:${s1_port}\n\n    backend auth_backend\n        mode http\n        server auth_backend ${s_auth_backend_addr}:${s_auth_backend_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq \\\n        -hdr \"x-foo: bar\"\n    rxresp\n    expect resp.status == 200\n} -run\n"
  },
  {
    "path": "test/starts.vtc",
    "content": "# SPDX-License-Identifier: MIT\n\nvarnishtest \"Verify that HAProxy starts with auth-request.lua loaded.\"\nfeature ignore_unknown_macro\n\nserver s1 {\n    rxreq\n    txresp\n} -start\n\nhaproxy h1 -conf {\n    global\n        lua-prepend-path ${testdir}/../?/http.lua\n        lua-load ${testdir}/../auth-request.lua\n\n    listen fe1\n        mode http\n        bind \"fd@${fe1}\"\n        server s1 ${s1_addr}:${s1_port}\n} -start\n\nclient c1 -connect ${h1_fe1_sock} {\n    txreq\n    rxresp\n    expect resp.status == 200\n} -run\n"
  }
]