From a3cd342f3e1fffd7b16b83a24e03bb9ed501b319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Diot?= Date: Fri, 30 Jun 2023 15:38:54 -0400 Subject: [PATCH] Squashed 'src/deps/src/lua-resty-session/' content from commit 8b5f8752f git-subtree-dir: src/deps/src/lua-resty-session git-subtree-split: 8b5f8752f3046396c414c5b97850e784c07e1641 --- .busted | 8 + .github/workflows/integration_tests.yaml | 19 + .github/workflows/unit_tests.yaml | 42 + .gitignore | 1 + .luacheckrc | 4 + .luacov | 31 + Changes.md | 467 +++ LICENSE | 23 + Makefile | 16 + README.md | 1477 +++++++++ config.ld | 10 + dev/Dockerfile | 20 + dist.ini | 7 + docs/classes/resty.session.html | 906 ++++++ docs/classes/session.html | 545 ++++ docs/index.html | 127 + docs/ldoc.css | 303 ++ docs/modules/resty.session.dshm.html | 397 +++ docs/modules/resty.session.file-thread.html | 195 ++ docs/modules/resty.session.file.html | 367 +++ docs/modules/resty.session.file.thread.html | 331 ++ docs/modules/resty.session.file.utils.html | 350 ++ docs/modules/resty.session.html | 1307 ++++++++ docs/modules/resty.session.memcached.html | 400 +++ docs/modules/resty.session.mysql.html | 480 +++ docs/modules/resty.session.postgres.html | 476 +++ docs/modules/resty.session.redis-cluster.html | 413 +++ .../modules/resty.session.redis-sentinel.html | 410 +++ docs/modules/resty.session.redis.cluster.html | 463 +++ docs/modules/resty.session.redis.common.html | 321 ++ docs/modules/resty.session.redis.html | 409 +++ .../modules/resty.session.redis.sentinel.html | 460 +++ docs/modules/resty.session.shm.html | 364 +++ docs/modules/resty.session.utils.html | 1370 ++++++++ lib/resty/session.lua | 2839 +++++++++++++++++ lib/resty/session/dshm.lua | 348 ++ lib/resty/session/file.lua | 177 + lib/resty/session/file/thread.lua | 209 ++ lib/resty/session/file/utils.lua | 290 ++ lib/resty/session/memcached.lua | 395 +++ lib/resty/session/mysql.lua | 379 +++ lib/resty/session/postgres.lua | 354 ++ lib/resty/session/redis.lua | 279 ++ lib/resty/session/redis/cluster.lua | 271 ++ lib/resty/session/redis/common.lua | 172 + lib/resty/session/redis/sentinel.lua | 260 ++ lib/resty/session/shm.lua | 315 ++ lib/resty/session/utils.lua | 1121 +++++++ lua-resty-session-4.0.4-1.rockspec | 37 + spec/01-utils_spec.lua | 139 + spec/02-file-utils_spec.lua | 108 + spec/03-session_spec.lua | 405 +++ spec/04-storage-1_spec.lua | 223 ++ spec/05-storage-2_spec.lua | 252 ++ t/01-cookies.t | 149 + 55 files changed, 21241 insertions(+) create mode 100644 .busted create mode 100644 .github/workflows/integration_tests.yaml create mode 100644 .github/workflows/unit_tests.yaml create mode 100644 .gitignore create mode 100644 .luacheckrc create mode 100644 .luacov create mode 100644 Changes.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 config.ld create mode 100644 dev/Dockerfile create mode 100644 dist.ini create mode 100644 docs/classes/resty.session.html create mode 100644 docs/classes/session.html create mode 100644 docs/index.html create mode 100644 docs/ldoc.css create mode 100644 docs/modules/resty.session.dshm.html create mode 100644 docs/modules/resty.session.file-thread.html create mode 100644 docs/modules/resty.session.file.html create mode 100644 docs/modules/resty.session.file.thread.html create mode 100644 docs/modules/resty.session.file.utils.html create mode 100644 docs/modules/resty.session.html create mode 100644 docs/modules/resty.session.memcached.html create mode 100644 docs/modules/resty.session.mysql.html create mode 100644 docs/modules/resty.session.postgres.html create mode 100644 docs/modules/resty.session.redis-cluster.html create mode 100644 docs/modules/resty.session.redis-sentinel.html create mode 100644 docs/modules/resty.session.redis.cluster.html create mode 100644 docs/modules/resty.session.redis.common.html create mode 100644 docs/modules/resty.session.redis.html create mode 100644 docs/modules/resty.session.redis.sentinel.html create mode 100644 docs/modules/resty.session.shm.html create mode 100644 docs/modules/resty.session.utils.html create mode 100644 lib/resty/session.lua create mode 100644 lib/resty/session/dshm.lua create mode 100644 lib/resty/session/file.lua create mode 100644 lib/resty/session/file/thread.lua create mode 100644 lib/resty/session/file/utils.lua create mode 100644 lib/resty/session/memcached.lua create mode 100644 lib/resty/session/mysql.lua create mode 100644 lib/resty/session/postgres.lua create mode 100644 lib/resty/session/redis.lua create mode 100644 lib/resty/session/redis/cluster.lua create mode 100644 lib/resty/session/redis/common.lua create mode 100644 lib/resty/session/redis/sentinel.lua create mode 100644 lib/resty/session/shm.lua create mode 100644 lib/resty/session/utils.lua create mode 100644 lua-resty-session-4.0.4-1.rockspec create mode 100644 spec/01-utils_spec.lua create mode 100644 spec/02-file-utils_spec.lua create mode 100644 spec/03-session_spec.lua create mode 100644 spec/04-storage-1_spec.lua create mode 100644 spec/05-storage-2_spec.lua create mode 100644 t/01-cookies.t diff --git a/.busted b/.busted new file mode 100644 index 000000000..f8b320062 --- /dev/null +++ b/.busted @@ -0,0 +1,8 @@ +return { + default = { + lua = "resty --shdict 'sessions 1m' --main-conf 'thread_pool default threads=32 max_queue=65536;'", + verbose = true, + coverage = true, + output = "gtest", + } +} diff --git a/.github/workflows/integration_tests.yaml b/.github/workflows/integration_tests.yaml new file mode 100644 index 000000000..b27f0c74c --- /dev/null +++ b/.github/workflows/integration_tests.yaml @@ -0,0 +1,19 @@ +name: integration_tests +on: [pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + luaVersion: ["luajit-openresty"] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup environment + run: docker build dev/ -t resty-session + + - name: Run tests + run: docker run -v $PWD:/test -w /test resty-session bash -c "luarocks make && make prove" diff --git a/.github/workflows/unit_tests.yaml b/.github/workflows/unit_tests.yaml new file mode 100644 index 000000000..edd6aa9c1 --- /dev/null +++ b/.github/workflows/unit_tests.yaml @@ -0,0 +1,42 @@ +name: unit_tests +on: [pull_request] + +jobs: + test: + strategy: + fail-fast: false + matrix: + luaVersion: ["luajit-openresty"] + + services: + redis: + image: bitnami/redis + env: + REDIS_PASSWORD: password + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + memcached: + image: memcached + ports: + - 11211:11211 + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup environment + run: docker build dev/ -t resty-session + + - name: Run tests + run: docker run --network=host -v $PWD:/test -w /test resty-session bash -c "luarocks make && make unit" + + - name: Generate report + run: docker run --network=host -v $PWD:/test -w /test resty-session bash -c "luarocks make && luacov" + + - name: Print report summary + run: docker run --network=host -v $PWD:/test -w /test resty-session sed -n '/Summary/,$p' luacov.report.out diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..d8fe4fa70 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.project diff --git a/.luacheckrc b/.luacheckrc new file mode 100644 index 000000000..abad11fa5 --- /dev/null +++ b/.luacheckrc @@ -0,0 +1,4 @@ +std = "ngx_lua" +redefined = false +max_line_length = false +files["lib/resty/session/file.lua"] = { ignore = {"143"}} \ No newline at end of file diff --git a/.luacov b/.luacov new file mode 100644 index 000000000..84c7529ed --- /dev/null +++ b/.luacov @@ -0,0 +1,31 @@ +return { + + -- filename to store stats collected + ["statsfile"] = "luacov.stats.out", + + -- filename to store report + ["reportfile"] = "luacov.report.out", + + -- Run reporter on completion? (won't work for ticks) + runreport = true, + + -- Patterns for files to include when reporting + -- all will be included if nothing is listed + -- (exclude overrules include, do not include + -- the .lua extension, path separator is always '/') + ["include"] = { 'resty/session' }, + + -- Patterns for files to exclude when reporting + -- all will be included if nothing is listed + -- (exclude overrules include, do not include + -- the .lua extension, path separator is always '/') + ["exclude"] = { + "luacov$", + "luacov/reporter$", + "luacov/defaults$", + "luacov/runner$", + "luacov/stats$", + "luacov/tick$", + }, + +} \ No newline at end of file diff --git a/Changes.md b/Changes.md new file mode 100644 index 000000000..6b7366d0b --- /dev/null +++ b/Changes.md @@ -0,0 +1,467 @@ +# Changelog + +All notable changes to `lua-resty-session` will be documented in this file. + +## [4.0.4] - 2023-06-05 +### Changed +- chore(utils): remove dependency for lua_pack, fix #158 + + +## [4.0.3] - 2023-02-21 +### Fixed +- fix(*): redis authorization + + +## [4.0.2] - 2023-02-15 +### Fixed +- fix(*): hkdf is not approved by FIPS, use PBKDF2 instead on FIPS-mode + + +## [4.0.1] - 2023-02-05 +### Fixed +- fix(session): clear_request cookie to check remember_meta correctly before using it + +### Added +- feat(opm): add more dependencies in requires +- feat(opm): add right version number requirements +- docs(readme): add remark on dependencies on installation section + + +## [4.0.0] - 2023-02-01 +- Full rewrite of the library, and is not backwards compatible. Refer new + documentation on this new library. + + +## [3.10] - 2022-01-14 +### Fixed +- 3.9 introduced an issue where calling session:regenerate with flush=true, + didn't really flush if the session strategy was `regenerate`. + + +## [3.9] - 2022-01-14 +### Fixed +- Fix #138 issue of chunked cookies are not expired when session shrinks, + thanks @alexdowad. +- Fix #134 where regenerate strategy destroyed previous session when calling + `session:regenerate`, it should just `ttl` the old session. + +### Added +- AES GCM mode support was added to AES cipher. + This is recommended, but for backward compatibility it was not set as default. + It will be changed in 4.0 release. +- Redis ACL authentication is now available. + - Add `session_redis_username` + - Add `session_redis_password` + - Deprecate `session_redis_auth`; use `session_redis_password` + +### Changed +- Optimize Redis and Memcache storage adapters to not connect to database + when not needed. + + +## [3.8] - 2021-01-04 +### Added +- Connection options are now passed to `redis cluster client` as well. + + +## [3.7] - 2020-10-27 +### Fixed +- Fix #107 where `session.start` could release a lock for a short period + +### Added +- Add `keep_lock` argument to `session.open` +- Add pluggable compressors, and implement `none` and `zlib` compressor + + +## [3.6] - 2020-06-24 +### Fixed +- Fix `session:hide()` to only send a single `Cookie` header at most as + reported by @jharriman who also provided a fix with #103. Thank you! + + +## [3.5] - 2020-05-22 +### Fixed +- Fix `session:hide()` to not clear non-session request cookies that it + seemed to do in some cases as reported by @altexy who also provided + initial fix with #100. Thank you! + + +## [3.4] - 2020-05-08 +### Fixed +- Fix session_cookie_maxsize - error attempt to compare string with number, + fixes #98, thank you @vavra5 + +### Changed +- More robust and uniform configuration parsing + + +## [3.3] - 2020-05-06 +### Fixed +- Fix `set_timeouts` is only called if all parameters are available, + should fix #96, thank you @notdodo. +### Added +- Add `$session_memcache_connect_timeout` configuration option +- Add `$session_memcache_read_timeout` configuration option +- Add `$session_memcache_send_timeout` configuration option +- Add `$session_memcache_pool_name` configuration option +- Add `$session_memcache_pool_backlog` configuration option +- Add `$session_dshm_connect_timeout` configuration option +- Add `$session_dshm_read_timeout` configuration option +- Add `$session_dshm_send_timeout` configuration option +- Add `$session_dshm_pool_name` configuration option +- Add `$session_dshm_pool_backlog` configuration option + + +## [3.2] - 2020-04-30 +### Added +- Support for Redis clusters +- Add `$session_redis_connect_timeout` configuration option +- Add `$session_redis_read_timeout` configuration option +- Add `$session_redis_send_timeout` configuration option +- Add `$session_redis_pool_name` configuration option +- Add `$session_redis_pool_backlog` configuration option +- Add `$session_redis_cluster_name` configuration option +- Add `$session_redis_cluster_dict` configuration option +- Add `$session_redis_cluster_maxredirections` configuration option +- Add `$session_redis_cluster_nodes` configuration option + + +## [3.1] - 2020-03-28 +### Added +- A more flexible way to specify custom implementations: + `require "resty.session".new { storage = require "my.storage" }` + + +## [3.0] - 2020-03-27 +### Fixed +- Lock releasing is a lot more robust now + +### Added +- Add idletime setting (thanks @Tieske), see `session.cookie.idletime` +- Add support for Cookie prefixes `__Host-` and `__Secure-` on Cookie + name (see: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-05#section-4.1.3) + +### Changed +- The whole codebase was refactored and simplified, especially implementing + new storage adapters is now a lot easier +- Redis and Memcached `spinlockwait` was changed from microseconds to milliseconds and default + is set to `150` milliseconds, +- Redis and Memcache will only release locks that current session instance holds +- DSHM `session_dshm_store` was renamed to `session_dshm_region` +- BASE64 encoding now strips the padding + + +## [2.26] - 2020-02-11 +### Added +- Add support for `SameSite=None` (#83) (thanks @bodewig) +- Style changes (#77) (thanks @Tieske) + + +## [2.25] - 2019-11-06 +### Added +- Add SSL support for the Redis storage option (#75) (thanks @tieske) +- DSHM storage adapter (a distributed SHM storage based on Hazelcast for Nginx) + (thanks @grrolland) + + +## [2.24] - 2019-07-09 +### Fixed +- Avoid use unix socket and redis password with empty string +- Provide session id when closing, otherwise the lock is not deleted + +### Added +- Added a configuration for session cookie max size (`session.cookie.maxsize`) + + +## [2.23] - 2018-12-12 +### Added +- Added pluggable strategies with `default` and a new `regenerate` strategy +- Added pluggable `hmac`s +- Added `session.close` +- Added `ttl` to `storages` +- Added `session.cookie.discard`, a `ttl` how long to keep old sessions when + renewing (used by `regenerate` strategy + + +## [2.22] - 2018-03-17 +### Fixed +- Only sets self.cookie.secure if not defined. + + +## [2.21] - 2018-03-16 +### Screwed +- Forgot to bump version number. + + +## [2.20] - 2018-03-16 +### Fixed +- Fixes issue where check addr and check scheme could be faked. + See also: https://github.com/bungle/lua-resty-session/issues/47 + Thanks @nielsole + + +## [2.19] - 2017-09-19 +### Fixed +- Fixes small bug where aes could generate invalid salt on invalid input + that further crashes Lua with error: bad argument #2 to 'salt' (number + expected, got no value) + + +## [2.18] - 2017-07-10 +### Fixed +- Automatically creates exactly 64 bits salt as required by the latest + lua-resty-string. + See also: https://github.com/bungle/lua-resty-session/issues/40 + Thanks @peturorri + + +## [2.17] - 2017-06-12 +### Added +- Added session.hide() function to hide session cookies from upstream + on reverse proxy scenarios. + + +## [2.16] - 2017-05-31 +### Changed +- Delays setting the defaults until needed, allowing users to safely + require "resty.session" in different contexts. + + +## [2.15] - 2017-02-13 +## Added +- Added a support for chunked cookies. + See also: https://github.com/bungle/lua-resty-session/issues/35 + Thanks @zandbelt + + +## [2.14] - 2016-12-16 +### Fixed +- Lua code configuration parsing corrections (especially on boolean + options). + +## Added +- Added a more natural way to pass config arguments to storage + adapters and ciphers in Lua code. + See also: https://github.com/bungle/lua-resty-session/issues/34 + Thanks @hanxi + + +## [2.13] - 2016-11-21 +### Changed +- On start we do send cookie now also if the settings have changed + and the cookie expiry time needs to be reduced. + +### Fixed +- Memcache storage adapter had a missing ngx.null. + + +## [2.12] - 2016-11-21 +### Added +- Implemented pluggable session identifier generators. +- Implemented random session idenfier generator. + +### Changed +- Now checks if headers were already sent before trying to set the + cookie headers. +- SSL session identifier is not checked by default anymore. +- Lua session.identifier.length changed to session.random.length. +- Nginx $session_identifier_length changed to $session_random_length. + + +## [2.11] - 2016-09-30 +### Changed +- Just another OPM release to correct the name. + + +## [2.10] - 2016-09-29 +### Added +- Support for the official OpenResty package manager (opm). + +### Changed +- Changed the change log format to keep-a-changelog. + + +## [2.9] - 2016-09-01 +### Fixed +- Bugfix: Weird bug where RAND_bytes was not working on Windows platform. + Code changed to use resty.random. See Also: + https://github.com/bungle/lua-resty-session/issues/31 + Thanks @gtuxyco + + +## [2.8] - 2016-07-05 +### Fixed +- Bugfix: AES Cipher used a wrong table for cipher sizes. + See Also: https://github.com/bungle/lua-resty-session/issues/30 + Thanks @pronan + + +## [2.7] - 2016-05-18 +### Added +- Redis storage adapter now supports Redis authentication. + See Also: https://github.com/bungle/lua-resty-session/pull/28 + Thanks @cheng5533062 + + +## [2.6] - 2016-04-18 +### Changed +- Just cleanups and changed _VERSION to point correct version. + + +## [2.5] - 2016-04-18 +### Fixed +- session.save close argument was not defaulting to true. + + +## [2.4] - 2016-04-17 +### Added +- Cookie will now have SameSite attribute set as "Lax" by default. + You can turn it off or set to "Strict" by configuration. + +### Changed +- Calling save will now also set session.id if the save was called + without calling start first. + See Also: https://github.com/bungle/lua-resty-session/issues/27 + Thanks @hcaihao + + +## [2.3] - 2015-10-16 +### Fixed +- Fixes issue #19 where regenerating session would throw an error + when using cookie storage. + See Also: https://github.com/bungle/lua-resty-session/issues/19 + Thanks @hulu1522 + + +## [2.2] - 2015-09-17 +### Changed +- Removed all session_cipher_* deprecated settings (it was somewhat + broken in 2.1). +- Changed session secret to be by default 32 bytes random data + See Also: https://github.com/bungle/lua-resty-session/issues/18 + Thanks @iain-buclaw-sociomantic + +### Added +- Added documentation about removed features and corrected about + session secret size accordingly. + + +## [2.1] - 2015-09-07 +### Added +- Added architecture for Cipher adapter plugins. + See Also: https://github.com/bungle/lua-resty-session/issues/16 + Thanks @mingfang +- Implemented AES cipher adapter (just like it was before) +- Implemented None cipher adapter (no encryption) +- Added documentation about pluggable ciphers + +### Changed +- Changed JSON serializer to use cjson.safe instead + + +## [2.0] - 2015-08-31 +### Added +- Added architecture for Storage adapter plugins. + See Also: https://github.com/bungle/lua-resty-session/issues/13 +- Implemented Client Side Cookie storage adapter. +- Implemented Memcache storage adapter. + See Also: https://github.com/bungle/lua-resty-session/pull/14 + Thanks @zandbelt +- Implemented Redis storage adapter. +- Implemented Shared Dictionary (shm) storage adapter. +- Added architecture for Encoder and Decoder plugins. +- Implemented Base 64 encoder / decoder. +- Implemented Base 16 (hex) encoder / decoder. +- Added architecture for Serializer plugins +- Implemented JSON serializer. +- Persistent cookies will now also contain Max-Age in addition to Expires. +- Cookie domain attribute is not set anymore if not specified. +- Added notes about using lua-resty-session with Lua code cache turned off. + See also: https://github.com/bungle/lua-resty-session/issues/15 + Thanks @BizShuk + + +## [1.7] - 2015-08-03 +### Added +- Added session.open() function that only opens a session but doesn't send + the cookie (until start is called). + See also: https://github.com/bungle/lua-resty-session/issues/12 + Thanks @junhanamaki + +### Fixed +- Fixed cookie expiration time format on Firefox bug: + https://github.com/bungle/lua-resty-session/pull/10 + Thanks @junhanamaki +- Bugfix: Fixed an issue of overwriting a variable: + https://github.com/bungle/lua-resty-session/pull/11 + Thanks @junhanamaki + + +## [1.6] - 2015-05-05 +### Fixed +- Fixed truncated cookie value bug: + https://github.com/bungle/lua-resty-session/pull/8 + Thanks @kipras + + +## [1.5] - 2014-11-27 +### Fixed +- Cookies are not always "secure": + https://github.com/bungle/lua-resty-session/issues/5 + Thanks @vladimir-smirnov-sociomantic + +### Added +- Added documentation about Nginx SSL/TLS configuration settings related + to session lifetime and ssl session ids. + + +## [1.4] - 2014-11-26 +### Fixed +- Bugfix: Fixed an issue where session configurations did get cached + on a module level. This issue is discussed in pull-request #4: + https://github.com/bungle/lua-resty-session/pull/4 + Thanks @kipras. + +### Added +- Added session.new function. +- Added documentation about Nginx configuration used as defaults (not read + on every request), and documented session.new. + +### Changed +- session.start{ ... } (a call with config parameters) works now as expected. +- session.start now returns additional extra boolean parameter that can be + used to check if the session is s new session (false) or a previously + started one (true). + + +## [1.3] - 2014-11-14 +### Added +- Added support for persistent sessions. See issue #2. +- Added session.check.ssi, session.cookie.persistent and the related Nginx + configuration variables. +- Added Max-Age=0 to expiration code. + + +## [1.2] - 2014-10-12 +### Fixed +- Changed encode and decode functions to operate with correct number of + arguments. See issue #1. + + +## [1.1] - 2014-10-03 +### Security +- There was a bug where additional user agent, scheme, and remote addr + (disabled by default) was not checked. + +### Added +- Added _VERSION field. + +### Changed +- Simplied a code a lot (e.g. internal setcookie and getcookie functions are + now cleaner). Removed a lot of unneccessary lines from session.start by + adding configs directly to session prototype. + + +## [1.0] - 2014-09-24 +### Added +- LuaRocks Support via MoonRocks. diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..d6be106e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2014 – 2023 Aapo Talvensaari, 2022 – 2023 Samuele Illuminati +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..0321e1b02 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +.PHONY: lint test docs + +lint: + @luacheck -q ./lib + +unit: + busted --exclude-tags=noci --coverage + +unit-all: + busted --coverage + +prove: + prove + +docs: + ldoc . diff --git a/README.md b/README.md new file mode 100644 index 000000000..6f6b76d0b --- /dev/null +++ b/README.md @@ -0,0 +1,1477 @@ +# lua-resty-session + +**lua-resty-session** is a secure, and flexible session library for OpenResty. + +## TL;DR; + +- Sessions are immutable (each save generates a new session), and lockless. +- Session data is AES-256-GCM encrypted with a key derived using HKDF-SHA256. +- Session has a fixed size header that is protected with HMAC-SHA256 MAC with + a key derived using HKDF-SHA256. +- Session data can be stored in a stateless cookie or in various backend storages. +- A single session cookie can maintain multiple sessions across different audiences. + +*Note:* Version 4.0.0 was a rewrite of this library with a lot of lessons learned +during the years. If you still use older version, please refer +[old documentation](https://github.com/bungle/lua-resty-session/tree/v3.10). + + +## Status + +This library is considered production ready. + + +## Synopsis + +```nginx +worker_processes 1; + +events { + worker_connections 1024; +} + +http { + init_by_lua_block { + require "resty.session".init({ + remember = true, + audience = "demo", + secret = "RaJKp8UQW1", + storage = "cookie", + }) + } + + server { + listen 8080; + server_name localhost; + default_type text/html; + + location / { + content_by_lua_block { + ngx.say([[ + + + Start the test + + + ]]) + } + } + + location /start { + content_by_lua_block { + local session = require "resty.session".new() + session:set_subject("OpenResty Fan") + session:set("quote", "The quick brown fox jumps over the lazy dog") + local ok, err = session:save() + + ngx.say(string.format([[ + + +

Session started (%s)

+

Check if it really was

+ + + ]], err or "no error")) + } + } + + location /started { + content_by_lua_block { + local session, err = require "resty.session".start() + + ngx.say(string.format([[ + + +

Session was started by %s (%s)

+

%s

+

Modify the session

+ + + ]], + session:get_subject() or "Anonymous", + err or "no error", + session:get("quote") or "no quote" + )) + } + } + + location /modify { + content_by_lua_block { + local session, err = require "resty.session".start() + session:set_subject("Lua Fan") + session:set("quote", "Lorem ipsum dolor sit amet") + local _, err_save = session:save() + + ngx.say(string.format([[ + + +

Session was modified (%s)

+

Check if it is modified

+ + + ]], err or err_save or "no error")) + } + } + + location /modified { + content_by_lua_block { + local session, err = require "resty.session".start() + + ngx.say(string.format([[ + + +

Session was started by %s (%s)

+

%s

+

Destroy the session

+ + + ]], + session:get_subject() or "Anonymous", + err or "no error", + session:get("quote") or "no quote" + )) + } + } + + location /destroy { + content_by_lua_block { + local ok, err = require "resty.session".destroy() + + ngx.say(string.format([[ + + +

Session was destroyed (%s)

+

Check that it really was?

+ + + ]], err or "no error")) + } + } + + location /destroyed { + content_by_lua_block { + local session, err = require "resty.session".open() + + ngx.say(string.format([[ + + +

Session was really destroyed, you are known as %s (%s)

+

Start again

+ + + ]], + session:get_subject() or "Anonymous", + err or "no error" + )) + } + } + } +} +``` + + +# Table of Contents + +* [Installation](#installation) + * [Using OpenResty Package Manager (opm)](#using-openresty-package-manager-opm) + * [Using LuaRocks](#using-luarocks) +* [Configuration](#configuration) + * [Session Configuration](#session-configuration) + * [Cookie Storage Configuration](#cookie-storage-configuration) + * [DSHM Storage Configuration](#dshm-storage-configuration) + * [File Storage Configuration](#file-storage-configuration) + * [Memcached Storage Configuration](#memcached-storage-configuration) + * [MySQL / MariaDB Storage Configuration](#mysql--mariadb-storage-configuration) + * [Postgres Configuration](#postgres-configuration) + * [Redis Configuration](#redis-configuration) + * [Single Redis Configuration](#single-redis-configuration) + * [Redis Sentinels Configuration](#redis-sentinels-configuration) + * [Redis Cluster Configuration](#redis-cluster-configuration) + * [SHM Configuration](#shm-configuration) +* [API](#api) + * [Initialization](#initialization) + * [session.init](#sessioninit) + * [Constructors](#constructors) + * [session.new](#sessionnew) + * [Helpers](#helpers) + * [session.open](#sessionopen) + * [session.start](#sessionstart) + * [session.logout](#sessionlogout) + * [session.destroy](#sessiondestroy) + * [Instance Methods](#instance-methods) + * [session:open](#sessionopen-1) + * [session:save](#sessionsave) + * [session:touch](#sessiontouch) + * [session:refresh](#sessionrefresh) + * [session:logout](#sessionlogout-1) + * [session:destroy](#sessiondestroy-1) + * [session:close](#sessionclose) + * [session:set_data](#sessionset_data) + * [session:get_data](#sessionget_data) + * [session:set](#sessionset) + * [session:get](#sessionget) + * [session:set_audience](#sessionset_audience) + * [session:get_audience](#sessionget_audience) + * [session:set_subject](#sessionset_subject) + * [session:get_subject](#sessionget_subject) + * [session:get_property](#sessionget_property) + * [session:set_remember](#sessionset_remember) + * [session:get_remember](#sessionget_remember) + * [session:clear_request_cookie](#sessionclear_request_cookie) + * [session:set_headers](#sessionset_headers) + * [session:set_request_headers](#sessionset_request_headers) + * [session:set_response_headers](#sessionset_response_headers) + * [session.info:set](#sessioninfoset) + * [session.info:get](#sessioninfoget) + * [session.info:save](#sessioninfosave) +* [Cookie Format](#cookie-format) +* [Data Encryption](#data-encryption) +* [Cookie Header Authentication](#cookie-header-authentication) +* [Custom Storage Interface](#custom-storage-interface) +* [License](#license) + + +# Installation + +## Using OpenResty Package Manager (opm) + +```bash +❯ opm get bungle/lua-resty-session +``` + +OPM repository for `lua-resty-session` is located at https://opm.openresty.org/package/bungle/lua-resty-session/. + +Also check the dependencies for each storage (there may be additional dependencies). + +## Using LuaRocks + +```bash +❯ luarocks install lua-resty-session +``` + +LuaRocks repository for `lua-resty-session` is located at https://luarocks.org/modules/bungle/lua-resty-session. + +Also check the dependencies for each storage (there may be additional dependencies). + + +# Configuration + +The configuration can be divided to generic session configuration and the server +side storage configuration. + +Here is an example: + +```lua +init_by_lua_block { + require "resty.session".init({ + remember = true, + store_metadata = true, + secret = "RaJKp8UQW1", + secret_fallbacks = { + "X88FuG1AkY", + "fxWNymIpbb", + }, + storage = "postgres", + postgres = { + username = "my-service", + password = "kVgIXCE5Hg", + database = "sessions", + }, + }) +} +``` + + +## Session Configuration + +Session configuration can be passed to [initialization](#initialization), [constructor](#constructors), +and [helper](#helpers) functions. + +Here are the possible session configuration options: + +| Option | Default | Description | +|-----------------------------|:------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `secret` | `nil` | Secret used for the key derivation. The secret is hashed with SHA-256 before using it. E.g. `"RaJKp8UQW1"`. | +| `secret_fallbacks` | `nil` | Array of secrets that can be used as alternative secrets (when doing key rotation), E.g. `{ "6RfrAYYzYq", "MkbTkkyF9C" }`. | +| `ikm` | (random) | Initial keying material (or ikm) can be specified directly (without using a secret) with exactly 32 bytes of data. E.g. `"5ixIW4QVMk0dPtoIhn41Eh1I9enP2060"` | +| `ikm_fallbacks` | `nil` | Array of initial keying materials that can be used as alternative keys (when doing key rotation), E.g. `{ "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }`. | +| `cookie_prefix` | `nil` | Cookie prefix, use `nil`, `"__Host-"` or `"__Secure-"`. | +| `cookie_name` | `"session"` | Session cookie name, e.g. `"session"`. | +| `cookie_path` | `"/"` | Cookie path, e.g. `"/"`. | +| `cookie_http_only` | `true` | Mark cookie HTTP only, use `true` or `false`. | +| `cookie_secure` | `nil` | Mark cookie secure, use `nil`, `true` or `false`. | +| `cookie_priority` | `nil` | Cookie priority, use `nil`, `"Low"`, `"Medium"`, or `"High"`. | +| `cookie_same_site` | `"Lax"` | Cookie same-site policy, use `nil`, `"Lax"`, `"Strict"`, `"None"`, or `"Default"` | +| `cookie_same_party` | `nil` | Mark cookie with same party flag, use `nil`, `true`, or `false`. | +| `cookie_partitioned` | `nil` | Mark cookie with partitioned flag, use `nil`, `true`, or `false`. | +| `remember` | `false` | Enable or disable persistent sessions, use `nil`, `true`, or `false`. | +| `remember_safety` | `"Medium"` | Remember cookie key derivation complexity, use `nil`, `"None"` (fast), `"Low"`, `"Medium"`, `"High"` or `"Very High"` (slow). | +| `remember_cookie_name` | `"remember"` | Persistent session cookie name, e.g. `"remember"`. | +| `audience` | `"default"` | Session audience, e.g. `"my-application"`. | +| `subject` | `nil` | Session subject, e.g. `"john.doe@example.com"`. | +| `enforce_same_subject` | `false` | When set to `true`, audiences need to share the same subject. The library removes non-subject matching audience data on save. | +| `stale_ttl` | `10` | When session is saved a new session is created, stale ttl specifies how long the old one can still be used, e.g. `10` (in seconds). | +| `idling_timeout` | `900` | Idling timeout specifies how long the session can be inactive until it is considered invalid, e.g. `900` (15 minutes) (in seconds), `0` disables the checks and touching. | +| `rolling_timeout` | `3600` | Rolling timeout specifies how long the session can be used until it needs to be renewed, e.g. `3600` (an hour) (in seconds), `0` disables the checks and rolling. | +| `absolute_timeout` | `86400` | Absolute timeout limits how long the session can be renewed, until re-authentication is required, e.g. `86400` (a day) (in seconds), `0` disables the checks. | +| `remember_rolling_timeout` | `604800` | Remember timeout specifies how long the persistent session is considered valid, e.g. `604800` (a week) (in seconds), `0` disables the checks and rolling. | +| `remember_absolute_timeout` | `2592000` | Remember absolute timeout limits how long the persistent session can be renewed, until re-authentication is required, e.g. `2592000` (30 days) (in seconds), `0` disables the checks. | +| `hash_storage_key` | `false` | Whether to hash or not the storage key. With storage key hashed it is impossible to decrypt data on server side without having a cookie too, use `nil`, `true` or `false`. | +| `hash_subject` | `false` | Whether to hash or not the subject when `store_metadata` is enabled, e.g. for PII reasons. | +| `store_metadata` | `false` | Whether to also store metadata of sessions, such as collecting data of sessions for a specific audience belonging to a specific subject. | +| `touch_threshold` | `60` | Touch threshold controls how frequently or infrequently the `session:refresh` touches the cookie, e.g. `60` (a minute) (in seconds) | +| `compression_threshold` | `1024` | Compression threshold controls when the data is deflated, e.g. `1024` (a kilobyte) (in bytes), `0` disables compression. | +| `request_headers` | `nil` | Set of headers to send to upstream, use `id`, `audience`, `subject`, `timeout`, `idling-timeout`, `rolling-timeout`, `absolute-timeout`. E.g. `{ "id", "timeout" }` will set `Session-Id` and `Session-Timeout` request headers when `set_headers` is called. | +| `response_headers` | `nil` | Set of headers to send to downstream, use `id`, `audience`, `subject`, `timeout`, `idling-timeout`, `rolling-timeout`, `absolute-timeout`. E.g. `{ "id", "timeout" }` will set `Session-Id` and `Session-Timeout` response headers when `set_headers` is called. | +| `storage` | `nil` | Storage is responsible of storing session data, use `nil` or `"cookie"` (data is stored in cookie), `"dshm"`, `"file"`, `"memcached"`, `"mysql"`, `"postgres"`, `"redis"`, or `"shm"`, or give a name of custom module (`"custom-storage"`), or a `table` that implements session storage interface. | +| `dshm` | `nil` | Configuration for dshm storage, e.g. `{ prefix = "sessions" }` (see below) | +| `file` | `nil` | Configuration for file storage, e.g. `{ path = "/tmp", suffix = "session" }` (see below) | +| `memcached` | `nil` | Configuration for memcached storage, e.g. `{ prefix = "sessions" }` (see below) | +| `mysql` | `nil` | Configuration for MySQL / MariaDB storage, e.g. `{ database = "sessions" }` (see below) | +| `postgres` | `nil` | Configuration for Postgres storage, e.g. `{ database = "sessions" }` (see below) | +| `redis` | `nil` | Configuration for Redis / Redis Sentinel / Redis Cluster storages, e.g. `{ prefix = "sessions" }` (see below) | +| `shm` | `nil` | Configuration for shared memory storage, e.g. `{ zone = "sessions" }` | +| `["custom-storage"]` | `nil` | custom storage (loaded with `require "custom-storage"`) configuration. | + + +## Cookie Storage Configuration + +When storing data to cookie, there is no additional configuration required, +just set the `storage` to `nil` or `"cookie"`. + + +## DSHM Storage Configuration + +With DHSM storage you can use the following settings (set the `storage` to `"dshm"`): + +| Option | Default | Description | +|---------------------|:-------------:|----------------------------------------------------------------------------------------------| +| `prefix` | `nil` | The Prefix for the keys stored in DSHM. | +| `suffix` | `nil` | The suffix for the keys stored in DSHM. | +| `host` | `"127.0.0.1"` | The host to connect. | +| `port` | `4321` | The port to connect. | +| `connect_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. | +| `send_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `send` method. | +| `read_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. | +| `keepalive_timeout` | `nil` | Controls the default maximal idle time of the connections in the connection pool. | +| `pool` | `nil` | A custom name for the connection pool being used. | +| `pool_size` | `nil` | The size of the connection pool. | +| `backlog` | `nil` | A queue size to use when the connection pool is full (configured with pool_size). | +| `ssl` | `nil` | Enable SSL. | +| `ssl_verify` | `nil` | Verify server certificate. | +| `server_name` | `nil` | The server name for the new TLS extension Server Name Indication (SNI). | + +Please refer to [ngx-distributed-shm](https://github.com/grrolland/ngx-distributed-shm) to get necessary +dependencies installed. + + +## File Storage Configuration + +With file storage you can use the following settings (set the `storage` to `"file"`): + +| Option | Default | Description | +|---------------------|:---------------:|-------------------------------------------------------------------------------------| +| `prefix` | `nil` | File prefix for session file. | +| `suffix` | `nil` | File suffix (or extension without `.`) for session file. | +| `pool` | `nil` | Name of the thread pool under which file writing happens (available on Linux only). | +| `path` | (tmp directory) | Path (or directory) under which session files are created. | + + +The implementation requires `LuaFileSystem` which you can install with LuaRocks: +```sh +❯ luarocks install LuaFileSystem +``` + + +## Memcached Storage Configuration + +With file Memcached you can use the following settings (set the `storage` to `"memcached"`): + +| Option | Default | Description | +|---------------------|:-----------:|----------------------------------------------------------------------------------------------| +| `prefix` | `nil` | Prefix for the keys stored in memcached. | +| `suffix` | `nil` | Suffix for the keys stored in memcached. | +| `host` | `127.0.0.1` | The host to connect. | +| `port` | `11211` | The port to connect. | +| `socket` | `nil` | The socket file to connect to. | +| `connect_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. | +| `send_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `send` method. | +| `read_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. | +| `keepalive_timeout` | `nil` | Controls the default maximal idle time of the connections in the connection pool. | +| `pool` | `nil` | A custom name for the connection pool being used. | +| `pool_size` | `nil` | The size of the connection pool. | +| `backlog` | `nil` | A queue size to use when the connection pool is full (configured with pool_size). | +| `ssl` | `false` | Enable SSL | +| `ssl_verify` | `nil` | Verify server certificate | +| `server_name` | `nil` | The server name for the new TLS extension Server Name Indication (SNI). | + + +## MySQL / MariaDB Storage Configuration + +With file MySQL / MariaDB you can use the following settings (set the `storage` to `"mysql"`): + +| Option | Default | Description | +|---------------------|:-----------------:|----------------------------------------------------------------------------------------------| +| `host` | `"127.0.0.1"` | The host to connect. | +| `port` | `3306` | The port to connect. | +| `socket` | `nil` | The socket file to connect to. | +| `username` | `nil` | The database username to authenticate. | +| `password` | `nil` | Password for authentication, may be required depending on server configuration. | +| `charset` | `"ascii"` | The character set used on the MySQL connection. | +| `database` | `nil` | The database name to connect. | +| `table_name` | `"sessions"` | Name of database table to which to store session data. | +| `table_name_meta` | `"sessions_meta"` | Name of database meta data table to which to store session meta data. | +| `max_packet_size` | `1048576` | The upper limit for the reply packets sent from the MySQL server (in bytes). | +| `connect_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. | +| `send_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `send` method. | +| `read_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. | +| `keepalive_timeout` | `nil` | Controls the default maximal idle time of the connections in the connection pool. | +| `pool` | `nil` | A custom name for the connection pool being used. | +| `pool_size` | `nil` | The size of the connection pool. | +| `backlog` | `nil` | A queue size to use when the connection pool is full (configured with pool_size). | +| `ssl` | `false` | Enable SSL. | +| `ssl_verify` | `nil` | Verify server certificate. | + +You also need to create following tables in your database: + +```sql +-- +-- Database table that stores session data. +-- +CREATE TABLE IF NOT EXISTS sessions ( + sid CHAR(43) PRIMARY KEY, + name VARCHAR(255), + data MEDIUMTEXT, + exp DATETIME, + INDEX (exp) +) CHARACTER SET ascii; + +-- +-- Sessions metadata table. +-- +-- This is only needed if you want to store session metadata. +-- +CREATE TABLE IF NOT EXISTS sessions_meta ( + aud VARCHAR(255), + sub VARCHAR(255), + sid CHAR(43), + PRIMARY KEY (aud, sub, sid), + CONSTRAINT FOREIGN KEY (sid) REFERENCES sessions(sid) ON DELETE CASCADE ON UPDATE CASCADE +) CHARACTER SET ascii; +``` + + +## Postgres Configuration + +With file Postgres you can use the following settings (set the `storage` to `"postgres"`): + +| Option | Default | Description | +|---------------------|:-----------------:|-----------------------------------------------------------------------------------------------------------| +| `host` | `"127.0.0.1"` | The host to connect. | +| `port` | `5432` | The port to connect. | +| `application` | `5432` | Set the name of the connection as displayed in pg_stat_activity (defaults to `"pgmoon"`). | +| `username` | `"postgres"` | The database username to authenticate. | +| `password` | `nil` | Password for authentication, may be required depending on server configuration. | +| `database` | `nil` | The database name to connect. | +| `table_name` | `"sessions"` | Name of database table to which to store session data (can be `database schema` prefixed). | +| `table_name_meta` | `"sessions_meta"` | Name of database meta data table to which to store session meta data (can be `database schema` prefixed). | +| `connect_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. | +| `send_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `send` method. | +| `read_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. | +| `keepalive_timeout` | `nil` | Controls the default maximal idle time of the connections in the connection pool. | +| `pool` | `nil` | A custom name for the connection pool being used. | +| `pool_size` | `nil` | The size of the connection pool. | +| `backlog` | `nil` | A queue size to use when the connection pool is full (configured with pool_size). | +| `ssl` | `false` | Enable SSL. | +| `ssl_verify` | `nil` | Verify server certificate. | +| `ssl_required` | `nil` | Abort the connection if the server does not support SSL connections. | + +You also need to create following tables in your database: + +```sql +-- +-- Database table that stores session data. +-- +CREATE TABLE IF NOT EXISTS sessions ( + sid TEXT PRIMARY KEY, + name TEXT, + data TEXT, + exp TIMESTAMP WITH TIME ZONE +); +CREATE INDEX ON sessions (exp); + +-- +-- Sessions metadata table. +-- +-- This is only needed if you want to store session metadata. +-- +CREATE TABLE IF NOT EXISTS sessions_meta ( + aud TEXT, + sub TEXT, + sid TEXT REFERENCES sessions (sid) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY (aud, sub, sid) +); +``` + +The implementation requires `pgmoon` which you can install with LuaRocks: +```sh +❯ luarocks install pgmoon +``` + + +## Redis Configuration + +The session library supports single Redis, Redis Sentinel, and Redis Cluster +connections. Common configuration settings among them all: + +| Option | Default | Description | +|---------------------|:-------:|----------------------------------------------------------------------------------------------| +| `prefix` | `nil` | Prefix for the keys stored in Redis. | +| `suffix` | `nil` | Suffix for the keys stored in Redis. | +| `username` | `nil` | The database username to authenticate. | +| `password` | `nil` | Password for authentication. | +| `connect_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. | +| `send_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `send` method. | +| `read_timeout` | `nil` | Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. | +| `keepalive_timeout` | `nil` | Controls the default maximal idle time of the connections in the connection pool. | +| `pool` | `nil` | A custom name for the connection pool being used. | +| `pool_size` | `nil` | The size of the connection pool. | +| `backlog` | `nil` | A queue size to use when the connection pool is full (configured with pool_size). | +| `ssl` | `false` | Enable SSL | +| `ssl_verify` | `nil` | Verify server certificate | +| `server_name` | `nil` | The server name for the new TLS extension Server Name Indication (SNI). | + +The `single redis` implementation is selected when you don't pass either `sentinels` or `nodes`, +which would lead to selecting `sentinel` or `cluster` implementation. + +### Single Redis Configuration + +Single Redis has following additional configuration options (set the `storage` to `"redis"`): + +| Option | Default | Description | +|-------------|:---------------:|--------------------------------| +| `host` | `"127.0.0.1"` | The host to connect. | +| `port` | `6379` | The port to connect. | +| `socket` | `nil` | The socket file to connect to. | +| `database` | `nil` | The database to connect. | + + +### Redis Sentinels Configuration + +Redis Sentinel has following additional configuration options (set the `storage` to `"redis"` +and configure the `sentinels`): + +| Option | Default | Description | +|---------------------|:--------:|--------------------------------| +| `master` | `nil` | Name of master. | +| `role` | `nil` | `"master"` or `"slave"`. | +| `socket` | `nil` | The socket file to connect to. | +| `sentinels` | `nil` | Redis Sentinels. | +| `sentinel_username` | `nil` | Optional sentinel username. | +| `sentinel_password` | `nil` | Optional sentinel password. | +| `database` | `nil` | The database to connect. | + +The `sentinels` is an array of Sentinel records: + +| Option | Default | Description | +|--------|:-------:|----------------------| +| `host` | `nil` | The host to connect. | +| `port` | `nil` | The port to connect. | + +The `sentinel` implementation is selected when you pass `sentinels` as part of `redis` +configuration (and do not pass `nodes`, which would select `cluster` implementation). + +The implementation requires `lua-resty-redis-connector` which you can install with LuaRocks: +```sh +❯ luarocks install lua-resty-redis-connector +``` + + +### Redis Cluster Configuration + +Redis Cluster has following additional configuration options (set the `storage` to `"redis"` +and configure the `nodes`): + +| Option | Default | Description | +|---------------------------|:-------:|--------------------------------------------------------| +| `name` | `nil` | Redis cluster name. | +| `nodes` | `nil` | Redis cluster nodes. | +| `lock_zone` | `nil` | Shared dictionary name for locks. | +| `lock_prefix` | `nil` | Shared dictionary name prefix for lock. | +| `max_redirections` | `nil` | Maximum retry attempts for redirection. | +| `max_connection_attempts` | `nil` | Maximum retry attempts for connection. | +| `max_connection_timeout` | `nil` | Maximum connection timeout in total among the retries. | + +The `nodes` is an array of Cluster node records: + +| Option | Default | Description | +|--------|:-------------:|----------------------------| +| `ip` | `"127.0.0.1"` | The IP address to connect. | +| `port` | `6379` | The port to connect. | + +The `cluster` implementation is selected when you pass `nodes` as part of `redis` +configuration. + +For `cluster` to work properly, you need to configure `lock_zone`, so also add this +to your Nginx configuration: + +```nginx +lua_shared_dict redis_cluster_locks 100k; +``` + +And set the `lock_zone` to `"redis_cluster_locks"` + +The implementation requires `resty-redis-cluster` or `kong-redis-cluster` which you can install with LuaRocks: +```sh +❯ luarocks install resty-redis-cluster +# or +❯ luarocks install kong-redis-cluster +``` + + +## SHM Configuration + +With SHM storage you can use the following settings (set the `storage` to `"shm"`): + +| Option | Default | Description | +|----------|:------------:|------------------------------------| +| `prefix` | `nil` | Prefix for the keys stored in SHM. | +| `suffix` | `nil` | Suffix for the keys stored in SHM. | +| `zone` | `"sessions"` | A name of shared memory zone. | + +You will also need to create a shared dictionary `zone` in Nginx: + +```nginx +lua_shared_dict sessions 10m; +``` + +*Note:* you may need to adjust the size of shared memory zone according to your needs. + + +# API + +LDoc generated API docs can also be viewed at [bungle.github.io/lua-resty-session](https://bungle.github.io/lua-resty-session/). + + +## Initialization + +### session.init + +**syntax:** *session.init(configuration)* + +Initialize the session library. + +This function can be called on `init` or `init_worker` phases on OpenResty +to set global default configuration to all session instances created by this +library. + +```lua +require "resty.session".init({ + audience = "my-application", + storage = "redis", + redis = { + username = "session", + password = "storage", + }, +}) +``` + +See [configuration](#configuration) for possible configuration settings. + + +## Constructors + +### session.new + +**syntax:** *session = session.new(configuration)* + +Creates a new session instance. + +```lua +local session = require "resty.session".new() +-- OR +local session = require "resty.session".new({ + audience = "my-application", +}) +``` + +See [configuration](#configuration) for possible configuration settings. + + +## Helpers + +### session.open + +**syntax:** *session, err, exists = session.open(configuration)* + +This can be used to open a session, and it will either return an existing +session or a new session. The `exists` (a boolean) return parameters tells whether +it was existing or new session that was returned. The `err` (a string) contains +a message of why opening might have failed (the function will still return +`session` too). + +```lua +local session = require "resty.session".open() +-- OR +local session, err, exists = require "resty.session".open({ + audience = "my-application", +}) +``` + +See [configuration](#configuration) for possible configuration settings. + + +### session.start + +**syntax:** *session, err, exists, refreshed = session.start(configuration)* + +This can be used to start a session, and it will either return an existing +session or a new session. In case there is an existing session, the +session will be refreshed as well (as needed). The `exists` (a boolean) +return parameters tells whether it was existing or new session that was +returned. The `refreshed` (a boolean) tells whether the call to `refresh` +was succesful. The `err` (a string) contains a message of why opening or +refreshing might have failed (the function will still return `session` too). + +```lua +local session = require "resty.session".start() +-- OR +local session, err, exists, refreshed = require "resty.session".start({ + audience = "my-application", +}) +``` + +See [configuration](#configuration) for possible configuration settings. + + +### session.logout + +**syntax:** *ok, err, exists, logged_out = session.logout(configuration)* + +It logouts from a specific audience. + +A single session cookie may be shared between multiple audiences +(or applications), thus there is a need to be able to logout from +just a single audience while keeping the session for the other +audiences. The `exists` (a boolean) return parameters tells whether +session existed. The `logged_out` (a boolean) return parameter signals +if the session existed and was also logged out. The `err` (a string) +contains a reason why session didn't exists or why the logout failed. +The `ok` (truthy) will be `true` when session existed and was +successfully logged out. + +When there is only a single audience, then this can be considered +equal to `session.destroy`. + +When the last audience is logged out, the cookie will be destroyed +as well and invalidated on a client. + +```lua +require "resty.session".logout() +-- OR +local ok, err, exists, logged_out = require "resty.session".logout({ + audience = "my-application", +}) +``` + + +See [configuration](#configuration) for possible configuration settings. + + +### session.destroy + +**syntax:** *ok, err, exists, destroyed = session.destroy(configuration)* + +It destroys the whole session and clears the cookies. + +A single session cookie may be shared between multiple audiences +(or applications), thus there is a need to be able to logout from +just a single audience while keeping the session for the other +audiences. The `exists` (a boolean) return parameters tells whether +session existed. The `destroyed` (a boolean) return parameter signals +if the session existed and was also destroyed out. The `err` (a string) +contains a reason why session didn't exists or why the logout failed. +The `ok` (truthy) will be `true` when session existed and was +successfully logged out. + +```lua +require "resty.session".destroy() +-- OR +local ok, err, exists, destroyed = require "resty.session".destroy({ + cookie_name = "auth", +}) +``` + +See [configuration](#configuration) for possible configuration settings. + + +## Instance Methods + +### session:open + +**syntax:** *ok, err = session:open()* + +This can be used to open a session. It returns `true` when +session was opened and validated. Otherwise, it returns `nil` and +an error message. + +```lua +local session = require "resty.session".new() +local ok, err = session:open() +if ok then + -- session exists + +else + -- session did not exists or was invalid +end +``` + + +### session:save + +**syntax:** *ok, err = session:save()* + +Saves the session data and issues a new session cookie with a new session id. +When `remember` is enabled, it will also issue a new persistent cookie and +possibly save the data in backend store. It returns `true` when session was saved. +Otherwise, it returns `nil` and an error message. + +```lua +local session = require "resty.session".new() +session:set_subject("john") +local ok, err = session:save() +if not ok then + -- error when saving session +end +``` + + +### session:touch + +**syntax:** *ok, err = session:touch()* + +Updates idling offset of the session by sending an updated session cookie. +It only sends the client cookie and never calls any backend session store +APIs. Normally the `session:refresh` is used to call this indirectly. In +error case it returns `nil` and an error message, otherwise `true`. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + ok, err = session:touch() +end +``` + + +### session:refresh + +**syntax:** *ok, err = session:refresh()* + +Either saves the session (creating a new session id) or touches the session +depending on whether the rolling timeout is getting closer, which means +by default when 3/4 of rolling timeout is spent, that is 45 minutes with default +rolling timeout of an hour. The touch has a threshold, by default one minute, +so it may be skipped in some cases (you can call `session:touch()` to force it). +In error case it returns `nil` and an error message, otherwise `true`. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local ok, err = session:refresh() +end +``` + +The above code looks a bit like `session.start()` helper. + + +### session:logout + +**syntax:** *ok, err = session:logout()* + +Logout either destroys the session or just clears the data for the current audience, +and saves it (logging out from the current audience). In error case it returns `nil` +and an error message, otherwise `true`. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local ok, err = session:logout() +end +``` + + +### session:destroy + +**syntax:** *ok, err = session:destroy()* + +Destroy the session and clear the cookies. In error case it returns `nil` +and an error message, otherwise `true`. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local ok, err = session:destroy() +end +``` + + +### session:close + +**syntax:** *session:close()* + +Just closes the session instance so that it cannot be used anymore. + +```lua +local session = require "resty.session".new() +session:set_subject("john") +local ok, err = session:save() +if not ok then + -- error when saving session +end +session:close() +``` + + +### session:set_data + +**syntax:** *session:set_data(data)* + +Set session data. The `data` needs to be a `table`. + +```lua +local session, err, exists = require "resty.session".open() +if not exists then + session:set_data({ + cart = {}, + }) + session:save() +end +``` + + +### session:get_data + +**syntax:** *data = session:get_data()* + +Get session data. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local data = session:get_data() + ngx.req.set_header("Authorization", "Bearer " .. data.access_token) +end +``` + + +### session:set + +**syntax:** *session:set(key, value)* + +Set a value in session. + +```lua +local session, err, exists = require "resty.session".open() +if not exists then + session:set("access-token", "eyJ...") + session:save() +end +``` + + +### session:get + +**syntax:** *value = session:get(key)* + +Get a value from session. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local access_token = session:get("access-token") + ngx.req.set_header("Authorization", "Bearer " .. access_token) +end +``` + +### session:set_audience + +**syntax:** *session:set_audience(audience)* + +Set session audience. + +```lua +local session = require "resty.session".new() +session.set_audience("my-service") +``` + + +### session:get_audience + +**syntax:** *audience = session:get_audience()* + +Set session subject. + + +### session:set_subject + +**syntax:** *session:set_subject(subject)* + +Set session audience. + +```lua +local session = require "resty.session".new() +session.set_subject("john@doe.com") +``` + + +### session:get_subject + +**syntax:** *subject = session:get_subject()* + +Get session subject. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local subject = session.get_subject() +end +``` + + +### session:get_property + +**syntax:** *value = session:get_property(name)* + +Get session property. Possible property names: + +- `"id"`: 43 bytes session id (same as nonce, but base64 url-encoded) +- `"nonce"`: 32 bytes nonce (same as session id but in raw bytes) +- `"audience"`: Current session audience +- `"subject"`: Current session subject +- `"timeout"`: Closest timeout (in seconds) (what's left of it) +- `"idling-timeout`"`: Session idling timeout (in seconds) (what's left of it) +- `"rolling-timeout`"`: Session rolling timeout (in seconds) (what's left of it) +- `"absolute-timeout`"`: Session absolute timeout (in seconds) (what's left of it) + +*Note:* the returned value may be `nil`. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local timeout = session.get_property("timeout") +end +``` + + +### session:set_remember + +**syntax:** *session:set_remember(value)* + +Set persistent sessions on/off. + +In many login forms user is given an option for "remember me". +You can call this function based on what user selected. + +```lua +local session = require "resty.session".new() +if ngx.var.args.remember then + session:set_remember(true) +end +session:set_subject(ngx.var.args.username) +session:save() +``` + + +### session:get_remember + +**syntax:** *remember = session:get_remember()* + +Get state of persistent sessions. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local remember = session.get_remember() +end +``` + + +### session:clear_request_cookie + +**syntax:** *session:clear_request_cookie()* + +Modifies the request headers by removing the session related +cookies. This is useful when you use the session library on +a proxy server and don't want the session cookies to be forwarded +to the upstream service. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + session:clear_request_cookie() +end +``` + + +### session:set_headers + +**syntax:** *session:set_headers(arg1, arg2, ...)* + +Sets request and response headers based on configuration. + +```lua +local session, err, exists = require "resty.session".open({ + request_headers = { "audience", "subject", "id" }, + response_headers = { "timeout", "idling-timeout", "rolling-timeout", "absolute-timeout" }, +}) +if exists then + session:set_headers() +end +``` + +When called without arguments it will set request headers configured with `request_headers` +and response headers configured with `response_headers`. + +See [configuration](#configuration) for possible header names. + + +### session:set_request_headers + +**syntax:** *session:set_request_headers(arg1, arg2, ...)* + +Set request headers. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + session:set_request_headers("audience", "subject", "id") +end +``` + +When called without arguments it will set request headers configured with `request_headers`. + +See [configuration](#configuration) for possible header names. + + +### session:set_response_headers + +**syntax:** *session:set_response_headers(arg1, arg2, ...)* + +Set request headers. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + session:set_response_headers("timeout", "idling-timeout", "rolling-timeout", "absolute-timeout") +end +``` + +When called without arguments it will set request headers configured with `response_headers`. + +See [configuration](#configuration) for possible header names. + + +### session.info:set + +**syntax:** *session.info:set(key, value)* + +Set a value in session information store. Session information store +may be used in scenarios when you want to store data on server side +storage, but do not want to create a new session and send a new +session cookie. The information store data is not considered when +checking authentication tag or message authentication code. Thus if +you want to use this for data that needs to be encrypted, you need +to encrypt value before passing it to thus function. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + session.info:set("last-access", ngx.now()) + session.info:save() +end +``` + +With cookie storage this still works, but it is then almost the same as +`session:set`. + + +### session.info:get + +**syntax:** *value = session.info:get(key)* + +Get a value from session information store. + +```lua +local session, err, exists = require "resty.session".open() +if exists then + local last_access = session.info:get("last-access") +end +``` + + +### session.info:save + +**syntax:** *value = session.info:save()* + +Save information. Only updates backend storage. Does not send a new cookie (except with cookie storage). + +```lua +local session = require "resty.session".new() +session.info:set("last-access", ngx.now()) +local ok, err = session.info:save() +``` + + +# Cookie Format + +``` +[ HEADER -------------------------------------------------------------------------------------] +[ Type || Flags || SID || Created at || Rolling Offset || Size || Tag || Idling Offset || Mac ] +[ 1B || 2B || 32B || 5B || 4B || 3B || 16B || 3B || 16B ] +``` + +and + +``` +[ PAYLOAD --] +[ Data *B ] +``` + +Both the `HEADER` and `PAYLOAD` are base64 url-encoded before putting in a `Set-Cookie` header. +When using a server side storage, the `PAYLOAD` is not put in the cookie. With cookie storage +the base64 url-encoded header is concatenated with base64 url-encoded payload. + +The `HEADER` is fixed size 82 bytes binary or 110 bytes in base64 url-encoded form. + +Header fields explained: + +- Type: number `1` binary packed in a single little endian byte (currently the only supported `type`). +- Flags: binary packed flags (short) in a two byte little endian form. +- SID: `32` bytes of crypto random data (Session ID). +- Created at: binary packed secs from epoch in a little endian form, truncated to 5 bytes. +- Rolling Offset: binary packed secs from creation time in a little endian form (integer). +- Size: binary packed data size (short) in a two byte little endian form. +- Tag: `16` bytes of authentication tag from AES-256-GCM encryption of the data. +- Idling Offset: binary packed secs from creation time + rolling offset in a little endian form, truncated to 3 bytes. +- Mac: `16` bytes message authentication code of the header. + + +# Data Encryption + +1. Initial keying material (IKM): + 1. derive IKM from `secret` by hashing `secret` with SHA-256, or + 2. use 32 byte IKM when passed to library with `ikm` +2. Generate 32 bytes of crypto random session id (`sid`) +3. Derive 32 byte encryption key and 12 byte initialization vector with HKDF using SHA-256 (on FIPS-mode it uses PBKDF2 with SHA-256 instead) + 1. Use HKDF extract to derive a new key from `ikm` to get `key` (this step can be done just once per `ikm`): + - output length: `32` + - digest: `"sha256"` + - key: `` + - mode: `extract only` + - info: `""` + - salt: `""` + 2. Use HKDF expand to derive `44` bytes of `output`: + - output length: `44` + - digest: `"sha256"` + - key: `` + - mode: `expand only` + - info: `"encryption:"` + - salt: `""` + 3. The first 32 bytes of `output` are the encryption key (`aes-key`), and the last 12 bytes are the initialization vector (`iv`) +4. Encrypt `plaintext` (JSON encoded and optionally deflated) using AES-256-GCM to get `ciphertext` and `tag` + 1. cipher: `"aes-256-gcm"` + 2. key: `` + 3. iv: `` + 4. plaintext: `` + 5. aad: use the first 47 bytes of `header` as `aad`, that includes: + 1. Type + 2. Flags + 3. Session ID + 4. Creation Time + 5. Rolling Offset + 6. Data Size + +There is a variation for `remember` cookies on step 3, where we may use `PBKDF2` +instead of `HKDF`, depending on `remember_safety` setting (we also use it in FIPS-mode). +The `PBKDF2` settings: + +- output length: `44` +- digest: `"sha256"` +- password: `<key>` +- salt: `"encryption:<sid>"` +- iterations: `<1000|10000|100000|1000000>` + +Iteration counts are based on `remember_safety` setting (`"Low"`, `"Medium"`, `"High"`, `"Very High"`), +if `remember_safety` is set to `"None"`, we will use the HDKF as above. + + +# Cookie Header Authentication + +1. Derive 32 byte authentication key (`mac_key`) with HKDF using SHA-256 (on FIPS-mode it uses PBKDF2 with SHA-256 instead): + 1. Use HKDF extract to derive a new key from `ikm` to get `key` (this step can be done just once per `ikm` and reused with encryption key generation): + - output length: `32` + - digest: `"sha256"` + - key: `<ikm>` + - mode: `extract only` + - info: `""` + - salt: `""` + 2. Use HKDF expand to derive `32` bytes of `mac-key`: + - output length: `32` + - digest: `"sha256"` + - key: `<key>` + - mode: `expand only` + - info: `"authentication:<sid>"` + - salt: `""` +2. Calculate message authentication code using HMAC-SHA256: + - digest: `"sha256"` + - key: `<mac-key>` + - message: use the first 66 bytes of `header`, that includes: + 1. Type + 2. Flags + 3. Session ID + 4. Creation Time + 5. Rolling Offset + 6. Data Size + 7. Tag + 8. Idling Offset + + +# Custom Storage Interface + +If you want to implement custom storage, you need to implement following interface: + +```lua +--- +-- <custom> backend for session library +-- +-- @module <custom> + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + -- NYI +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(name, key) + -- NYI +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(name, key, current_time, metadata) + -- NYI +end + + +local storage = {} + + +--- +-- Constructors +-- @section constructors + + +--- +-- Configuration +-- @section configuration + + +--- +-- <custom> storage backend configuration +-- @field <field-name> TBD +-- @table configuration + + +--- +-- Create a <custom> storage. +-- +-- This creates a new shared memory storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration <custom> storage @{configuration} +-- @treturn table <custom> storage instance +function storage.new(configuration) + -- NYI + -- return setmetatable({}, metatable) +end + + +return storage +``` + +Please check the existing implementations for the defails. And please +make a pull-request so that we can integrate it directly to library +for other users as well. + + +# License + +`lua-resty-session` uses two clause BSD license. + +``` +Copyright (c) 2014 – 2023 Aapo Talvensaari, 2022 – 2023 Samuele Illuminati +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +``` diff --git a/config.ld b/config.ld new file mode 100644 index 000000000..7082fc286 --- /dev/null +++ b/config.ld @@ -0,0 +1,10 @@ +project = "resty.session" +description = "Session Library for OpenResty" +full_description = "`lua-resty-session` is a secure, and flexible session library for OpenResty" +title = "Session Library for OpenResty Documentation" +dir = "docs" +use_markdown_titles = true +package = "session" +format = "discount" +sort_modules=true +file = "./lib/resty" diff --git a/dev/Dockerfile b/dev/Dockerfile new file mode 100644 index 000000000..a031b3551 --- /dev/null +++ b/dev/Dockerfile @@ -0,0 +1,20 @@ +FROM openresty/openresty:1.21.4.1-focal + +ENV DEBIAN_FRONTEND noninteractive +ENV TEST_NGINX_BINARY openresty + +USER root +RUN apt-get update && apt-get install -y gcc git cpanminus + +RUN git clone https://github.com/Olivine-Labs/busted +RUN cd busted && luarocks make + +RUN luarocks install pgmoon +RUN luarocks install lua-resty-rsa +RUN luarocks install lua-resty-redis-connector +RUN luarocks install lua-resty-redis-cluster +RUN luarocks install inspect +RUN luarocks install lua_pack +RUN luarocks install LuaCov + +RUN cpanm --notest Test::Nginx diff --git a/dist.ini b/dist.ini new file mode 100644 index 000000000..e1cc3f953 --- /dev/null +++ b/dist.ini @@ -0,0 +1,7 @@ +name = lua-resty-session +abstract = Session Library for OpenResty - Flexible and Secure +author = Aapo Talvensaari (@bungle), Samuele Illuminati (@samugi) +is_original = yes +license = 2bsd +repo_link = https://github.com/bungle/lua-resty-session +requires = luajit, nginx, ngx_http_lua, fffonion/lua-resty-openssl >= 0.8.0, hamishforbes/lua-ffi-zlib >= 0.5 diff --git a/docs/classes/resty.session.html b/docs/classes/resty.session.html new file mode 100644 index 000000000..7178c7d21 --- /dev/null +++ b/docs/classes/resty.session.html @@ -0,0 +1,906 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Session">Session </a></li> +<li><a href="#Constructors">Constructors </a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><strong>resty.session</strong></li> +</ul> +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis-cluster.html">resty.session.redis-cluster</a></li> + <li><a href="../modules/resty.session.redis-sentinel.html">resty.session.redis-sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>resty.session</code></h1> +<p>Session library provides HTTP session management capabilities for OpenResty based + applications, libraries and proxies.</p> +<p> +</p> + + +<h2><a href="#Session">Session </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#session.info:set">session.info:set (key, value)</a></td> + <td class="summary">Set a value in session information store.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session.info:get">session.info:get (key)</a></td> + <td class="summary">Get a value from session information store.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session.info:save">session.info:save ()</a></td> + <td class="summary">Save information.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:set">resty.session:set (key, value)</a></td> + <td class="summary">Set a value in session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:get">resty.session:get (key)</a></td> + <td class="summary">Get a value from session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:open">resty.session:open ()</a></td> + <td class="summary">Open a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:save">resty.session:save ()</a></td> + <td class="summary">Save the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:touch">resty.session:touch ()</a></td> + <td class="summary">Touch the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:refresh">resty.session:refresh ()</a></td> + <td class="summary">Refresh the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:logout">resty.session:logout ()</a></td> + <td class="summary">Logout the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:destroy">resty.session:destroy ()</a></td> + <td class="summary">Destroy the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#resty.session:close">resty.session:close ()</a></td> + <td class="summary">Close the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:hide">instance:hide ()</a></td> + <td class="summary">Hide the session.</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#resty.session.configuration">resty.session.configuration</a></td> + <td class="summary">Session configuration.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.init">module.init ([configuration])</a></td> + <td class="summary">Initialize the session library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a new session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.open">module.open ([configuration])</a></td> + <td class="summary">Open a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.start">module.start ([configuration])</a></td> + <td class="summary">Start a session and refresh it as needed.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.logout">module.logout ([configuration])</a></td> + <td class="summary">Logout a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.destroy">module.destroy ([configuration])</a></td> + <td class="summary">Destroy a session.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Session"></a>Session </h2> + + <dl class="function"> + <dt> + <a name = "session.info:set"></a> + <strong>session.info:set (key, value)</strong> + </dt> + <dd> + Set a value in session information store. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + <pre><code>key +</code></pre> + + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "session.info:get"></a> + <strong>session.info:get (key)</strong> + </dt> + <dd> + Get a value from session information store. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + <pre><code>key +</code></pre> + + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + value + </ol> + + + + +</dd> + <dt> + <a name = "session.info:save"></a> + <strong>session.info:save ()</strong> + </dt> + <dd> + Save information. </p> + +<p> Only updates backend storage. Does not send a new cookie. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:set"></a> + <strong>resty.session:set (key, value)</strong> + </dt> + <dd> + Set a value in session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + <pre><code>key +</code></pre> + + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "resty.session:get"></a> + <strong>resty.session:get (key)</strong> + </dt> + <dd> + Get a value from session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + <pre><code>key +</code></pre> + + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + value + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:open"></a> + <strong>resty.session:open ()</strong> + </dt> + <dd> + Open a session. </p> + +<p> This can be used to open a session. It will either return an existing + session or a new session. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:save"></a> + <strong>resty.session:save ()</strong> + </dt> + <dd> + Save the session. </p> + +<p> Saves the session data and issues a new session cookie with a new session id. + When <code>remember</code> is enabled, it will also issue a new persistent cookie and + possibly save the data in backend store. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:touch"></a> + <strong>resty.session:touch ()</strong> + </dt> + <dd> + Touch the session. </p> + +<p> Updates idling offset of the session by sending an updated session cookie. + It only sends the client cookie and never calls any backend session store + APIs. Normally the <a href="../classes/resty.session.html#resty.session:refresh">session:refresh</a> is used to call this indirectly. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:refresh"></a> + <strong>resty.session:refresh ()</strong> + </dt> + <dd> + Refresh the session. </p> + +<p> Either saves the session (creating a new session id) or touches the session + depending on whether the rolling timeout is getting closer. The touch has + a threshold, by default one minute, so it may be skipped in some cases. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:logout"></a> + <strong>resty.session:logout ()</strong> + </dt> + <dd> + Logout the session. </p> + +<p> Logout either destroys the session or just clears the data for the current audience, + and saves it (logging out from the current audience). + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:destroy"></a> + <strong>resty.session:destroy ()</strong> + </dt> + <dd> + Destroy the session. </p> + +<p> Destroy the session and clear the cookies. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "resty.session:close"></a> + <strong>resty.session:close ()</strong> + </dt> + <dd> + Close the session. </p> + +<p> Just closes the session instance so that it cannot be used anymore. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:hide"></a> + <strong>instance:hide ()</strong> + </dt> + <dd> + Hide the session. </p> + +<p> Modifies the request headers by removing the session related + cookies. This is useful when you use the session library on + a proxy server and don&rsquo;t want the session cookies to be forwarded + to the upstream service. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "resty.session.configuration"></a> + <strong>resty.session.configuration</strong> + </dt> + <dd> + Session configuration. + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">secret</span> + Secret used for the key derivation. The secret is hashed with SHA-256 before using it. E.g. <code>&quot;RaJKp8UQW1&quot;</code>. + </li> + <li><span class="parameter">secret_fallbacks</span> + Array of secrets that can be used as alternative secrets (when doing key rotation), E.g. <code>{ &quot;6RfrAYYzYq&quot;, &quot;MkbTkkyF9C&quot; }</code>. + </li> + <li><span class="parameter">ikm</span> + Initial key material (or ikm) can be specified directly (without using a secret) with exactly 32 bytes of data, e.g. <code>&quot;5ixIW4QVMk0dPtoIhn41Eh1I9enP2060&quot;</code> + </li> + <li><span class="parameter">ikm_fallbacks</span> + Array of initial key materials that can be used as alternative keys (when doing key rotation), E.g. <code>{ &quot;QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7&quot; }</code>. + </li> + <li><span class="parameter">cookie_prefix</span> + Cookie prefix, use <code>nil</code>, <code>&quot;__Host-&quot;</code> or <code>&quot;__Secure-&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_name</span> + Session cookie name, e.g. <code>&quot;session&quot;</code> (defaults to <code>&quot;session&quot;</code>) + </li> + <li><span class="parameter">cookie_path</span> + Cookie path, e.g. <code>&quot;/&quot;</code> (defaults to <code>&quot;/&quot;</code>) + </li> + <li><span class="parameter">cookie_domain</span> + Cookie domain, e.g. <code>&quot;example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_http_only</span> + Mark cookie HTTP only, use <code>true</code> or <code>false</code> (defaults to <code>true</code>) + </li> + <li><span class="parameter">cookie_secure</span> + Mark cookie secure, use <code>nil</code>, <code>true</code> or <code>false</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_priority</span> + Cookie priority, use <code>nil</code>, <code>&quot;Low&quot;</code>, <code>&quot;Medium&quot;</code>, or <code>&quot;High&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_same_site</span> + Cookie same-site policy, use <code>nil</code>, <code>&quot;Lax&quot;</code>, <code>&quot;Strict&quot;</code>, or <code>&quot;None&quot;</code> (defaults to <code>&quot;Lax&quot;</code>) + </li> + <li><span class="parameter">cookie_same_party</span> + Mark cookie with same party flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">cookie_partitioned</span> + Mark cookie with partitioned flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">remember</span> + Enable or disable persistent sessions, use <code>nil</code>, <code>true</code>, or <code>false</code> (defaults to <code>false</code>) + </li> + <li><span class="parameter">remember_cookie_name</span> + Persistent session cookie name, e.g. <code>&quot;remember&quot;</code> (defaults to <code>&quot;remember&quot;</code>) + </li> + <li><span class="parameter">audience</span> + Session audience, e.g. <code>&quot;my-application&quot;</code> (defaults to <code>&quot;default&quot;</code>) + </li> + <li><span class="parameter">subject</span> + Session subject, e.g. <code>&quot;john.doe@example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">stale_ttl</span> + When session is saved a new session is created, stale ttl specifies how long the old one can still be used, e.g. <code>10</code> (defaults to <code>10</code>) (in seconds) + </li> + <li><span class="parameter">idling_timeout</span> + Idling timeout specifies how long the session can be inactive until it is considered invalid, e.g. <code>900</code> (defaults to <code>900</code>, or 15 minutes) (in seconds) + </li> + <li><span class="parameter">rolling_timeout</span> + Rolling timeout specifies how long the session can be used until it needs to be renewed, e.g. <code>3600</code> (defaults to <code>3600</code>, or an hour) (in seconds) + </li> + <li><span class="parameter">absolute_timeout</span> + Absolute timeout limits how long the session can be renewed, until re-authentication is required, e.g. <code>86400</code> (defaults to <code>86400</code>, or a day) (in seconds) + </li> + <li><span class="parameter">remember_timeout</span> + Remember timeout specifies how long the persistent session is considered valid, e.g. <code>604800</code> (defaults to <code>604800</code>, or a week) (in seconds) + </li> + <li><span class="parameter">touch_threshold</span> + Touch threshold controls how frequently or infrequently the <a href="../classes/resty.session.html#resty.session:refresh">session:refresh</a> touches the cookie, e.g. <code>60</code> (defaults to <code>60</code>, or a minute) (in seconds) + </li> + <li><span class="parameter">compression_threshold</span> + Compression threshold controls when the data is deflated, e.g. <code>1024</code> (defaults to <code>1024</code>, or a kilobyte) (in bytes) + </li> + <li><span class="parameter">storage</span> + Storage is responsible of storing session data, use <code>nil</code> (data is stored in cookie), <a href="../modules/resty.session.dshm.html">dshm</a>, <a href="../modules/resty.session.file.html">file</a>, <a href="../modules/resty.session.memcached.html">memcached</a>, <a href="../modules/resty.session.mysql.html">mysql</a>, <a href="../modules/resty.session.postgres.html">postgres</a>, <a href="../modules/resty.session.redis.html">redis</a>, <a href="../modules/resty.session.redis-cluster.html">redis-cluster</a>, <a href="../modules/resty.session.redis-sentinel.html">redis-sentinel</a>, or <a href="../modules/resty.session.shm.html">shm</a>, or give a name of custom module (<code>&quot;custom.session.storage&quot;</code>), or a <a href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> that implements session storage interface (defaults to <code>nil</code>) + </li> + <li><span class="parameter">dshm</span> + Configuration for dshm storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">file</span> + Configuration for file storage, e.g. <code>{ path = &quot;/tmp&quot;, suffix = &quot;session&quot; }</code> + </li> + <li><span class="parameter">memcached</span> + Configuration for memcached storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">mysql</span> + Configuration for MySQL / MariaDB storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">postgres</span> + Configuration for Postgres storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">redis</span> + Configuration for Redis / Redis Sentinel / Redis Cluster storages, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">shm</span> + Configuration for shared memory storage, e.g. <code>{ zone = &quot;sessions&quot; }</code> + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "module.init"></a> + <strong>module.init ([configuration])</strong> + </dt> + <dd> + Initialize the session library. </p> + +<p> This function can be called on <a href="../classes/resty.session.html#module.init">init</a> or <code>init_worker</code> phases on OpenResty + to set global default configuration to all session instances created by this + library. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.init({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a new session. </p> + +<p> This creates a new session instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new() +<span class="comment">-- OR +</span><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.open"></a> + <strong>module.open ([configuration])</strong> + </dt> + <dd> + Open a session. </p> + +<p> This can be used to open a session, and it will either return an existing + session or a new session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be opened</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.start"></a> + <strong>module.start ([configuration])</strong> + </dt> + <dd> + Start a session and refresh it as needed. </p> + +<p> This can be used to start a session, and it will either return an existing + session or a new session. In case there is an existing session, the + session will be refreshed as well (as needed). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be logged out</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session was refreshed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.start() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists, refreshed = <span class="global">require</span> <span class="string">"resty.session"</span>.start() + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.logout"></a> + <strong>module.logout ([configuration])</strong> + </dt> + <dd> + Logout a session. </p> + +<p> It logouts from a specific audience.</p> + +<p> A single session cookie may be shared between multiple audiences + (or applications), thus there is a need to be able to logout from + just a single audience while keeping the session for the other + audiences.</p> + +<p> When there is only a single audience, then this can be considered + equal to <a href="../classes/resty.session.html#resty.session:destroy">session.destroy</a>.</p> + +<p> When the last audience is logged out, the cookie will be destroyed + as well and invalidated on a client. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists for an audience and was logged out successfully, otherwise <code>false</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why the session could not be logged out</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was logged out, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.logout() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists, logged_out = <span class="global">require</span> <span class="string">"resty.session"</span>.logout({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.destroy"></a> + <strong>module.destroy ([configuration])</strong> + </dt> + <dd> + Destroy a session. </p> + +<p> It destroys the whole session and clears the cookies. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/resty.session.html#resty.session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists and was destroyed successfully, otherwise <code>nil</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be destroyed</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was destroyed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.destroy() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.destroy({ + cookie_name = <span class="string">"auth"</span>, +})</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2022-12-16 17:07:08 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/classes/session.html b/docs/classes/session.html new file mode 100644 index 000000000..1d66f2a1c --- /dev/null +++ b/docs/classes/session.html @@ -0,0 +1,545 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +<li><a href="#Tables">Tables</a></li> +<li><a href="#Methods">Methods</a></li> +</ul> + + +<h2>Classes</h2> +<ul class="nowrap"> + <li><strong>session</strong></li> +</ul> +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis-cluster.html">resty.session.redis-cluster</a></li> + <li><a href="../modules/resty.session.redis-sentinel.html">resty.session.redis-sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Class <code>session</code></h1> +<p>Session library provides HTTP session management capabilities for OpenResty based + applications, libraries and proxies.</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#session_instance:open">session_instance:open ()</a></td> + <td class="summary">Opens session</p> + +<p> This can be used to open a session.</td> + </tr> +</table> +<h2><a href="#Tables">Tables</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#session.configuration">session.configuration</a></td> + <td class="summary">Session configuration options</td> + </tr> +</table> +<h2><a href="#Methods">Methods</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#session:init">session:init ([configuration])</a></td> + <td class="summary">Initializes session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session:new">session:new ([configuration])</a></td> + <td class="summary">Creates new session</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session:open">session:open ([configuration])</a></td> + <td class="summary">Opens session</p> + +<p> This can be used to open a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session:start">session:start ([configuration])</a></td> + <td class="summary">Starts the session and refreshes it as needed</p> + +<p> This can be used to start a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session:logout">session:logout ([configuration])</a></td> + <td class="summary">Logouts session</p> + +<p> It logouts from a specific audience.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#session:destroy">session:destroy ([configuration])</a></td> + <td class="summary">Destroys session</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + Methods + <dl class="function"> + <dt> + <a name = "session_instance:open"></a> + <strong>session_instance:open ()</strong> + </dt> + <dd> + Opens session</p> + +<p> This can be used to open a session. It will either return an existing + session or a new session. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be opened</li> + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Tables"></a>Tables</h2> + + <dl class="function"> + <dt> + <a name = "session.configuration"></a> + <strong>session.configuration</strong> + </dt> + <dd> + Session configuration options + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">secret</span> + Secret used for the key derivation. The secret is hashed with SHA-256 before using it. E.g. <code>&quot;RaJKp8UQW1&quot;</code>. + </li> + <li><span class="parameter">secret_fallbacks</span> + Array of secrets that can be used as alternative secrets (when doing key rotation), E.g. <code>{ &quot;6RfrAYYzYq&quot;, &quot;MkbTkkyF9C&quot; }</code>. + </li> + <li><span class="parameter">ikm</span> + Initial key material (or ikm) can be specified directly (without using a secret) with exactly 32 bytes of data, e.g. <code>&quot;5ixIW4QVMk0dPtoIhn41Eh1I9enP2060&quot;</code> + </li> + <li><span class="parameter">ikm_fallbacks</span> + Array of initial key materials that can be used as alternative keys (when doing key rotation), E.g. <code>{ &quot;QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7&quot; }</code>. + </li> + <li><span class="parameter">cookie_prefix</span> + Cookie prefix, use <code>nil</code>, <code>&quot;__Host-&quot;</code> or <code>&quot;__Secure-&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_name</span> + Session cookie name, e.g. <code>&quot;session&quot;</code> (defaults to <code>&quot;session&quot;</code>) + </li> + <li><span class="parameter">cookie_path</span> + Cookie path, e.g. <code>&quot;/&quot;</code> (defaults to <code>&quot;/&quot;</code>) + </li> + <li><span class="parameter">cookie_domain</span> + Cookie domain, e.g. <code>&quot;example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_http_only</span> + Mark cookie HTTP only, use <code>true</code> or <code>false</code> (defaults to <code>true</code>) + </li> + <li><span class="parameter">cookie_secure</span> + Mark cookie secure, use <code>nil</code>, <code>true</code> or <code>false</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_priority</span> + Cookie priority, use <code>nil</code>, <code>&quot;Low&quot;</code>, <code>&quot;Medium&quot;</code>, or <code>&quot;High&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_same_site</span> + Cookie same-site policy, use <code>nil</code>, <code>&quot;Lax&quot;</code>, <code>&quot;Strict&quot;</code>, or <code>&quot;None&quot;</code> (defaults to <code>&quot;Lax&quot;</code>) + </li> + <li><span class="parameter">cookie_same_party</span> + Mark cookie with same party flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">cookie_partitioned</span> + Mark cookie with partitioned flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">remember</span> + Enable or disable persistent sessions, use <code>nil</code>, <code>true</code>, or <code>false</code> (defaults to <code>false</code>) + </li> + <li><span class="parameter">remember_cookie_name</span> + Persistent session cookie name, e.g. <code>&quot;remember&quot;</code> (defaults to <code>&quot;remember&quot;</code>) + </li> + <li><span class="parameter">audience</span> + Session audience, e.g. <code>&quot;my-application&quot;</code> (defaults to <code>&quot;default&quot;</code>) + </li> + <li><span class="parameter">subject</span> + Session subject, e.g. <code>&quot;john.doe@example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">stale_ttl</span> + When session is saved a new session is created, stale ttl specifies how long the old one can still be used, e.g. <code>10</code> (defaults to <code>10</code>) (in seconds) + </li> + <li><span class="parameter">idling_timeout</span> + Idling timeout specifies how long the session can be inactive until it is considered invalid, e.g. <code>900</code> (defaults to <code>900</code>, or 15 minutes) (in seconds) + </li> + <li><span class="parameter">rolling_timeout</span> + Rolling timeout specifies how long the session can be used until it needs to be renewed, e.g. <code>3600</code> (defaults to <code>3600</code>, or an hour) (in seconds) + </li> + <li><span class="parameter">absolute_timeout</span> + Absolute timeout limits how long the session can be renewed, until re-authentication is required, e.g. <code>86400</code> (defaults to <code>86400</code>, or a day) (in seconds) + </li> + <li><span class="parameter">remember_timeout</span> + Remember timeout specifies how long the persistent session is considered valid, e.g. <code>604800</code> (defaults to <code>604800</code>, or a week) (in seconds) + </li> + <li><span class="parameter">storage</span> + Storage is responsible of storing session data, use <code>nil</code> (data is stored in cookie), <code>dshm</code>, <code>file</code>, <code>memcached</code>, <code>mysql</code>, <code>postgres</code>, <code>redis</code>, <code>redis-cluster</code>, <code>redis-sentinel</code>, or <code>shm</code>, or give a name of custom module (<code>&quot;custom.session.storage&quot;</code>), or a <a href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> that implements session storage interface (defaults to <code>nil</code>) + </li> + <li><span class="parameter">dshm</span> + Configuration for dshm storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">file</span> + Configuration for file storage, e.g. <code>{ path = &quot;/tmp&quot;, suffix = &quot;session&quot; }</code> + </li> + <li><span class="parameter">memcached</span> + Configuration for memcached storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">mysql</span> + Configuration for MySQL / MariaDB storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">postgres</span> + Configuration for Postgres storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">redis</span> + Configuration for Redis / Redis Sentinel / Redis Cluster storages, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">shm</span> + Configuration for shared memory storage, e.g. <code>{ zone = &quot;sessions&quot; }</code> + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Methods"></a>Methods</h2> + + <dl class="function"> + <dt> + <a name = "session:init"></a> + <strong>session:init ([configuration])</strong> + </dt> + <dd> + Initializes session library This function can be called on <a href="../classes/session.html#session:init">init</a> or <code>init_worker</code> phases on OpenResty + to set global default configuration to all session instances created by this + library. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.init({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "session:new"></a> + <strong>session:new ([configuration])</strong> + </dt> + <dd> + Creates new session This creates a new session instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new() +<span class="comment">-- OR +</span><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "session:open"></a> + <strong>session:open ([configuration])</strong> + </dt> + <dd> + Opens session</p> + +<p> This can be used to open a session. It will either return an existing + session or a new session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be opened</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "session:start"></a> + <strong>session:start ([configuration])</strong> + </dt> + <dd> + Starts the session and refreshes it as needed</p> + +<p> This can be used to start a session. It will either return an existing + session or a new session. In case there is an existing session, the + session will be refreshed as well (as needed). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be logged out</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session was refreshed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.start() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists, refreshed = <span class="global">require</span> <span class="string">"resty.session"</span>.start() + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "session:logout"></a> + <strong>session:logout ([configuration])</strong> + </dt> + <dd> + Logouts session</p> + +<p> It logouts from a specific audience. </p> + +<p> A single session cookie may be shared between multiple audiences + (or applications), thus there is a need to be able to logout from + just a single audience while keeping the session for the other + audiences.</p> + +<p> When there is only a single audience, then this can be considered + equal to <a href="../classes/session.html#session:destroy">session.destroy</a>.</p> + +<p> When the last audience is logged out, the cookie will be destroyed + as well and invalidated on a client. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists for an audience and was logged out successfully, otherwise <code>false</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why the session could not be logged out</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was logged out, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.logout() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists, logged_out = <span class="global">require</span> <span class="string">"resty.session"</span>.logout({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "session:destroy"></a> + <strong>session:destroy ([configuration])</strong> + </dt> + <dd> + Destroys session It destroys the whole session and clears the cookies. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../classes/session.html#session.configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists and was destroyed successfully, otherwise <code>nil</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + information why session could not be destroyed</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was destroyed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.destroy() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.destroy({ + cookie_name = <span class="string">"auth"</span>, +})</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2022-12-16 00:35:34 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 000000000..1d4320c86 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,127 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + + + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="modules/resty.session.html">resty.session</a></li> + <li><a href="modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + + + <h2>Session Library for OpenResty</h2> + <p><code>lua-resty-session</code> is a secure, and flexible session library for OpenResty</p> + +<h2>Modules</h2> +<table class="module_list"> + <tr> + <td class="name" nowrap><a href="modules/resty.session.html">resty.session</a></td> + <td class="summary">Session library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.dshm.html">resty.session.dshm</a></td> + <td class="summary">Distributed Shared Memory (DSHM) backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.file.html">resty.session.file</a></td> + <td class="summary">File storage backend for session library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.file.thread.html">resty.session.file.thread</a></td> + <td class="summary">File storage backend worker thread module</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.file.utils.html">resty.session.file.utils</a></td> + <td class="summary">File storage utilities</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.memcached.html">resty.session.memcached</a></td> + <td class="summary">Memcached backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.mysql.html">resty.session.mysql</a></td> + <td class="summary">MySQL / MariaDB backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.postgres.html">resty.session.postgres</a></td> + <td class="summary">Postgres backend for session library.</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.redis.html">resty.session.redis</a></td> + <td class="summary">Redis backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></td> + <td class="summary">Redis Cluster backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.redis.common.html">resty.session.redis.common</a></td> + <td class="summary">Common Redis functions shared between Redis, + Redis Cluster and Redis Sentinel implementations.</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></td> + <td class="summary">Redis Sentinel backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.shm.html">resty.session.shm</a></td> + <td class="summary">Shared Memory (SHM) backend for session library</td> + </tr> + <tr> + <td class="name" nowrap><a href="modules/resty.session.utils.html">resty.session.utils</a></td> + <td class="summary">Common utilities for session library and storage backends</td> + </tr> +</table> + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/ldoc.css b/docs/ldoc.css new file mode 100644 index 000000000..52c4ad2bd --- /dev/null +++ b/docs/ldoc.css @@ -0,0 +1,303 @@ +/* BEGIN RESET + +Copyright (c) 2010, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.com/yui/license.html +version: 2.8.2r1 +*/ +html { + color: #000; + background: #FFF; +} +body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { + margin: 0; + padding: 0; +} +table { + border-collapse: collapse; + border-spacing: 0; +} +fieldset,img { + border: 0; +} +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} +del,ins { + text-decoration: none; +} +li { + margin-left: 20px; +} +caption,th { + text-align: left; +} +h1,h2,h3,h4,h5,h6 { + font-size: 100%; + font-weight: bold; +} +q:before,q:after { + content: ''; +} +abbr,acronym { + border: 0; + font-variant: normal; +} +sup { + vertical-align: baseline; +} +sub { + vertical-align: baseline; +} +legend { + color: #000; +} +input,button,textarea,select,optgroup,option { + font-family: inherit; + font-size: inherit; + font-style: inherit; + font-weight: inherit; +} +input,button,textarea,select {*font-size:100%; +} +/* END RESET */ + +body { + margin-left: 1em; + margin-right: 1em; + font-family: arial, helvetica, geneva, sans-serif; + background-color: #ffffff; margin: 0px; +} + +code, tt { font-family: monospace; font-size: 1.1em; } +span.parameter { font-family:monospace; } +span.parameter:after { content:":"; } +span.types:before { content:"("; } +span.types:after { content:")"; } +.type { font-weight: bold; font-style:italic } + +body, p, td, th { font-size: .95em; line-height: 1.2em;} + +p, ul { margin: 10px 0 0 0px;} + +strong { font-weight: bold;} + +em { font-style: italic;} + +h1 { + font-size: 1.5em; + margin: 20px 0 20px 0; +} +h2, h3, h4 { margin: 15px 0 10px 0; } +h2 { font-size: 1.25em; } +h3 { font-size: 1.15em; } +h4 { font-size: 1.06em; } + +a:link { font-weight: bold; color: #004080; text-decoration: none; } +a:visited { font-weight: bold; color: #006699; text-decoration: none; } +a:link:hover { text-decoration: underline; } + +hr { + color:#cccccc; + background: #00007f; + height: 1px; +} + +blockquote { margin-left: 3em; } + +ul { list-style-type: disc; } + +p.name { + font-family: "Andale Mono", monospace; + padding-top: 1em; +} + +pre { + background-color: rgb(245, 245, 245); + border: 1px solid #C0C0C0; /* silver */ + padding: 10px; + margin: 10px 0 10px 0; + overflow: auto; + font-family: "Andale Mono", monospace; +} + +pre.example { + font-size: .85em; +} + +table.index { border: 1px #00007f; } +table.index td { text-align: left; vertical-align: top; } + +#container { + margin-left: 1em; + margin-right: 1em; + background-color: #f0f0f0; +} + +#product { + text-align: center; + border-bottom: 1px solid #cccccc; + background-color: #ffffff; +} + +#product big { + font-size: 2em; +} + +#main { + background-color: #f0f0f0; + border-left: 2px solid #cccccc; +} + +#navigation { + float: left; + width: 14em; + vertical-align: top; + background-color: #f0f0f0; + overflow: visible; +} + +#navigation h2 { + background-color:#e7e7e7; + font-size:1.1em; + color:#000000; + text-align: left; + padding:0.2em; + border-top:1px solid #dddddd; + border-bottom:1px solid #dddddd; +} + +#navigation ul +{ + font-size:1em; + list-style-type: none; + margin: 1px 1px 10px 1px; +} + +#navigation li { + text-indent: -1em; + display: block; + margin: 3px 0px 0px 22px; +} + +#navigation li li a { + margin: 0px 3px 0px -1em; +} + +#content { + margin-left: 14em; + padding: 1em; + width: 700px; + border-left: 2px solid #cccccc; + border-right: 2px solid #cccccc; + background-color: #ffffff; +} + +#about { + clear: both; + padding: 5px; + border-top: 2px solid #cccccc; + background-color: #ffffff; +} + +@media print { + body { + font: 12pt "Times New Roman", "TimeNR", Times, serif; + } + a { font-weight: bold; color: #004080; text-decoration: underline; } + + #main { + background-color: #ffffff; + border-left: 0px; + } + + #container { + margin-left: 2%; + margin-right: 2%; + background-color: #ffffff; + } + + #content { + padding: 1em; + background-color: #ffffff; + } + + #navigation { + display: none; + } + pre.example { + font-family: "Andale Mono", monospace; + font-size: 10pt; + page-break-inside: avoid; + } +} + +table.module_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.module_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.module_list td.summary { width: 100%; } + + +table.function_list { + border-width: 1px; + border-style: solid; + border-color: #cccccc; + border-collapse: collapse; +} +table.function_list td { + border-width: 1px; + padding: 3px; + border-style: solid; + border-color: #cccccc; +} +table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } +table.function_list td.summary { width: 100%; } + +ul.nowrap { + overflow:auto; + white-space:nowrap; +} + +dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} +dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} +dl.table h3, dl.function h3 {font-size: .95em;} + +/* stop sublists from having initial vertical space */ +ul ul { margin-top: 0px; } +ol ul { margin-top: 0px; } +ol ol { margin-top: 0px; } +ul ol { margin-top: 0px; } + +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + diff --git a/docs/modules/resty.session.dshm.html b/docs/modules/resty.session.dshm.html new file mode 100644 index 000000000..7ed27d7fe --- /dev/null +++ b/docs/modules/resty.session.dshm.html @@ -0,0 +1,397 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><strong>resty.session.dshm</strong></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.dshm</code></h1> +<p>Distributed Shared Memory (DSHM) backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Distributed shared memory storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a distributed shared memory storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Distributed shared memory storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + The prefix for the keys stored in DSHM. + </li> + <li><span class="parameter">suffix</span> + The suffix for the keys stored in DSHM. + </li> + <li><span class="parameter">host</span> + The host to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>4321</code>). + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">server_name</span> + The server name for the new TLS extension Server Name Indication (SNI). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a distributed shared memory storage. </p> + +<p> This creates a new distributed shared memory storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + DSHM storage <a href="../modules/resty.session.dshm.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + DSHM storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.file-thread.html b/docs/modules/resty.session.file-thread.html new file mode 100644 index 000000000..6e83417e8 --- /dev/null +++ b/docs/modules/resty.session.file-thread.html @@ -0,0 +1,195 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><strong>resty.session.file-thread</strong></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis-cluster.html">resty.session.redis-cluster</a></li> + <li><a href="../modules/resty.session.redis-sentinel.html">resty.session.redis-sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.file-thread</code></h1> +<p>File storage backend worker thread module</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#set">set (path, content)</a></td> + <td class="summary">Store data in file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#get">get (path)</a></td> + <td class="summary">Read data from a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#delete">delete (path)</a></td> + <td class="summary">Delete a file.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "set"></a> + <strong>set (path, content)</strong> + </dt> + <dd> + Store data in file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + <pre><code> file path +</code></pre> + + </li> + <li><span class="parameter">content</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file content + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "get"></a> + <strong>get (path)</strong> + </dt> + <dd> + Read data from a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file to read + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + content</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "delete"></a> + <strong>delete (path)</strong> + </dt> + <dd> + Delete a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file to read + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2022-12-23 14:06:58 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.file.html b/docs/modules/resty.session.file.html new file mode 100644 index 000000000..15c26d0d4 --- /dev/null +++ b/docs/modules/resty.session.file.html @@ -0,0 +1,367 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><strong>resty.session.file</strong></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.file</code></h1> +<p>File storage backend for session library.</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">File storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a file storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + File storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + File prefix for session file. + </li> + <li><span class="parameter">suffix</span> + File suffix (or extension without <code>.</code>) for session file. + </li> + <li><span class="parameter">pool</span> + Name of the thread pool under which file writing happens (available on Linux only). + </li> + <li><span class="parameter">path</span> + Path (or directory) under which session files are created. + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a file storage. </p> + +<p> This creates a new file storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + file storage <a href="../modules/resty.session.file.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + file storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.file.thread.html b/docs/modules/resty.session.file.thread.html new file mode 100644 index 000000000..65585f2ed --- /dev/null +++ b/docs/modules/resty.session.file.thread.html @@ -0,0 +1,331 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><strong>resty.session.file.thread</strong></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.file.thread</code></h1> +<p>File storage backend worker thread module</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.set">module.set (path, prefix, suffix, name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.GET">module.GET (path, prefix, suffix, name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.delete">module.delete (path, prefix, suffix, name, key, current_time)</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.read_metadata">module.read_metadata (path, prefix, suffix, name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "module.set"></a> + <strong>module.set (path, prefix, suffix, name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path where sessions are stored + </li> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.GET"></a> + <strong>module.GET (path, prefix, suffix, name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path where sessions are stored + </li> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.delete"></a> + <strong>module.delete (path, prefix, suffix, name, key, current_time)</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path where sessions are stored + </li> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.read_metadata"></a> + <strong>module.read_metadata (path, prefix, suffix, name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path where sessions are stored + </li> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session audience + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session subject + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.file.utils.html b/docs/modules/resty.session.file.utils.html new file mode 100644 index 000000000..1f87560f3 --- /dev/null +++ b/docs/modules/resty.session.file.utils.html @@ -0,0 +1,350 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><strong>resty.session.file.utils</strong></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.file.utils</code></h1> +<p>File storage utilities</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#file_create">file_create (path, content)</a></td> + <td class="summary">Store data in file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#file_append">file_append (path, data)</a></td> + <td class="summary">Append data in file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#file_read">file_read (path)</a></td> + <td class="summary">Read data from a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#get_modification">get_modification (path)</a></td> + <td class="summary">Get the value modification time of a file.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#meta_get_key">meta_get_key (audience, subject)</a></td> + <td class="summary">Given an audience and a subject, generate a metadata key.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#validate_file_name">validate_file_name (prefix, suffix, name, filename)</a></td> + <td class="summary">Validate a file name.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#cleanup">cleanup (path, prefix, suffix, name, current_time)</a></td> + <td class="summary">Clean up expired session and metadata files.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "file_create"></a> + <strong>file_create (path, content)</strong> + </dt> + <dd> + Store data in file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file path + </li> + <li><span class="parameter">content</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file content + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "file_append"></a> + <strong>file_append (path, data)</strong> + </dt> + <dd> + Append data in file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file path + </li> + <li><span class="parameter">data</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file data + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "file_read"></a> + <strong>file_read (path)</strong> + </dt> + <dd> + Read data from a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + file to read + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + content</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "get_modification"></a> + <strong>get_modification (path)</strong> + </dt> + <dd> + Get the value modification time of a file. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path to the file + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "meta_get_key"></a> + <strong>meta_get_key (audience, subject)</strong> + </dt> + <dd> + Given an audience and a subject, generate a metadata key. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session audience + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session subject + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + metadata key + </ol> + + + + +</dd> + <dt> + <a name = "validate_file_name"></a> + <strong>validate_file_name (prefix, suffix, name, filename)</strong> + </dt> + <dd> + Validate a file name. + Run a few checks to try to determine if the file is managed by this library + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">filename</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the name of the file + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">true</span> or <span class="type">false</span></span> + whether the file is managed by the library or not + </ol> + + + + +</dd> + <dt> + <a name = "cleanup"></a> + <strong>cleanup (path, prefix, suffix, name, current_time)</strong> + </dt> + <dd> + Clean up expired session and metadata files. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">path</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the path where sessions are stored + </li> + <li><span class="parameter">prefix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the prefix for session files + </li> + <li><span class="parameter">suffix</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the suffix for session files + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">true</span> or <span class="type">false</span></span> + whether clean up completed + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.html b/docs/modules/resty.session.html new file mode 100644 index 000000000..62c95db4d --- /dev/null +++ b/docs/modules/resty.session.html @@ -0,0 +1,1307 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Session">Session </a></li> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Initialization">Initialization </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Helpers">Helpers </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><strong>resty.session</strong></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session</code></h1> +<p>Session library.</p> +<p> Session library provides HTTP session management capabilities for OpenResty based + applications, libraries and proxies.</p> + + +<h2><a href="#Session">Session </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance.info:set">instance.info:set (key, value)</a></td> + <td class="summary">Set a value in session information store.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance.info:get">instance.info:get (key)</a></td> + <td class="summary">Get a value from session information store.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance.info:save">instance.info:save ()</a></td> + <td class="summary">Save information.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_data">instance:set_data (data)</a></td> + <td class="summary">Set session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get_data">instance:get_data ()</a></td> + <td class="summary">Get session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (key, value)</a></td> + <td class="summary">Set a value in session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (key)</a></td> + <td class="summary">Get a value from session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_audience">instance:set_audience (audience)</a></td> + <td class="summary">Set session audience.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get_audience">instance:get_audience ()</a></td> + <td class="summary">Get session audience.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_subject">instance:set_subject (subject)</a></td> + <td class="summary">Set session subject.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get_subject">instance:get_subject ()</a></td> + <td class="summary">Get session subject.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get_property">instance:get_property ()</a></td> + <td class="summary">Get session property.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_remember">instance:set_remember (value)</a></td> + <td class="summary">Set persistent sessions on/off.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get_remember">instance:get_remember ()</a></td> + <td class="summary">Get state of persistent sessions.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:open">instance:open ()</a></td> + <td class="summary">Open a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:save">instance:save ()</a></td> + <td class="summary">Save the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:touch">instance:touch ()</a></td> + <td class="summary">Touch the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:refresh">instance:refresh ()</a></td> + <td class="summary">Refresh the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:logout">instance:logout ()</a></td> + <td class="summary">Logout the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:destroy">instance:destroy ()</a></td> + <td class="summary">Destroy the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:close">instance:close ()</a></td> + <td class="summary">Close the session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:clear_request_cookie">instance:clear_request_cookie ()</a></td> + <td class="summary">Clear the request session cookie.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_headers">instance:set_headers ([...])</a></td> + <td class="summary">Sets request and response headers.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_request_headers">instance:set_request_headers ([...])</a></td> + <td class="summary">Set request headers.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:set_response_headers">instance:set_response_headers ([...])</a></td> + <td class="summary">Set response headers.</td> + </tr> +</table> +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Session configuration.</td> + </tr> +</table> +<h2><a href="#Initialization">Initialization </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.init">module.init ([configuration])</a></td> + <td class="summary">Initialize the session library.</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a new session.</td> + </tr> +</table> +<h2><a href="#Helpers">Helpers </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.open">module.open ([configuration])</a></td> + <td class="summary">Open a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.start">module.start ([configuration])</a></td> + <td class="summary">Start a session and refresh it as needed.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.logout">module.logout ([configuration])</a></td> + <td class="summary">Logout a session.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.destroy">module.destroy ([configuration])</a></td> + <td class="summary">Destroy a session.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Session"></a>Session </h2> + + <dl class="function"> + <dt> + <a name = "instance.info:set"></a> + <strong>instance.info:set (key, value)</strong> + </dt> + <dd> + Set a value in session information store. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + <li><span class="parameter">value</span> + value + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instance.info:get"></a> + <strong>instance.info:get (key)</strong> + </dt> + <dd> + Get a value from session information store. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + value + </ol> + + + + +</dd> + <dt> + <a name = "instance.info:save"></a> + <strong>instance.info:save ()</strong> + </dt> + <dd> + Save information. </p> + +<p> Only updates backend storage. Does not send a new cookie. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:set_data"></a> + <strong>instance:set_data (data)</strong> + </dt> + <dd> + Set session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">data</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + data + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="keyword">if</span> <span class="keyword">not</span> exists <span class="keyword">then</span> + session:set_data({ + cart = {}, + }) + session:save() +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:get_data"></a> + <strong>instance:get_data ()</strong> + </dt> + <dd> + Get session data. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="keyword">if</span> exists <span class="keyword">then</span> + <span class="keyword">local</span> data = session:get_data() + ngx.req.set_header(<span class="string">"Authorization"</span>, <span class="string">"Bearer "</span> .. data.access_token) +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (key, value)</strong> + </dt> + <dd> + Set a value in session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + <li><span class="parameter">value</span> + value</p> + +<p> local session, err, exists = require &ldquo;resty.session&rdquo;.open() + if not exists then + session:set(&ldquo;access-token&rdquo;, &ldquo;eyJ&hellip;&rdquo;) + session:save() + end + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (key)</strong> + </dt> + <dd> + Get a value from session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="keyword">if</span> exists <span class="keyword">then</span> + <span class="keyword">local</span> access_token = session:get(<span class="string">"access-token"</span>) + ngx.req.set_header(<span class="string">"Authorization"</span>, <span class="string">"Bearer "</span> .. access_token) +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:set_audience"></a> + <strong>instance:set_audience (audience)</strong> + </dt> + <dd> + Set session audience. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + audience + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new() +session.set_audience(<span class="string">"my-service"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "instance:get_audience"></a> + <strong>instance:get_audience ()</strong> + </dt> + <dd> + Get session audience. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + audience + </ol> + + + + +</dd> + <dt> + <a name = "instance:set_subject"></a> + <strong>instance:set_subject (subject)</strong> + </dt> + <dd> + Set session subject. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + subject + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new() +session.set_subject(<span class="string">"john@doe.com"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "instance:get_subject"></a> + <strong>instance:get_subject ()</strong> + </dt> + <dd> + Get session subject. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + subject + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="keyword">if</span> exists <span class="keyword">then</span> + <span class="keyword">local</span> subject = session.get_subject() +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:get_property"></a> + <strong>instance:get_property ()</strong> + </dt> + <dd> + Get session property. </p> + +<p> Possible property names: + <em> <code>&quot;id&quot;</code>: 43 bytes session id (same as nonce, but base64 url-encoded) + </em> <code>&quot;nonce&quot;</code>: 32 bytes nonce (same as session id but in raw bytes) + <em> <code>&quot;audience&quot;</code>: Current session audience + </em> <code>&quot;subject&quot;</code>: Current session subject + <em> <code>&quot;timeout&quot;</code>: Closest timeout (in seconds) (what&rsquo;s left of it) + </em> <code>&quot;idling-timeout</code>&ldquo;<code>: Session idling timeout (in seconds) (what&apos;s left of it) + <em> </code>"rolling-timeout<code>&quot;</code>: Session rolling timeout (in seconds) (what&rsquo;s left of it) + </em> <code>&quot;absolute-timeout</code>&rdquo;<code>: Session absolute timeout (in seconds) (what's left of it)</code> + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">number</span></span> + metadata + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="keyword">if</span> exists <span class="keyword">then</span> + <span class="keyword">local</span> timeout = session.get_property(<span class="string">"timeout"</span>) +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "instance:set_remember"></a> + <strong>instance:set_remember (value)</strong> + </dt> + <dd> + Set persistent sessions on/off. </p> + +<p> In many login forms user is given an option for &ldquo;remember me&rdquo;. + You can call this function based on what user selected. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> to enable persistent session, <code>false</code> to disable them + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instance:get_remember"></a> + <strong>instance:get_remember ()</strong> + </dt> + <dd> + Get state of persistent sessions. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">boolean</span></span> + <code>true</code> when persistent sessions are enabled, otherwise <code>false</code> + </ol> + + + + +</dd> + <dt> + <a name = "instance:open"></a> + <strong>instance:open ()</strong> + </dt> + <dd> + Open a session. </p> + +<p> This can be used to open a session. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:save"></a> + <strong>instance:save ()</strong> + </dt> + <dd> + Save the session. </p> + +<p> Saves the session data and issues a new session cookie with a new session id. + When <code>remember</code> is enabled, it will also issue a new persistent cookie and + possibly save the data in backend store. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:touch"></a> + <strong>instance:touch ()</strong> + </dt> + <dd> + Touch the session. </p> + +<p> Updates idling offset of the session by sending an updated session cookie. + It only sends the client cookie and never calls any backend session store + APIs. Normally the <a href="../modules/resty.session.html#instance:refresh">session:refresh</a> is used to call this indirectly. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:refresh"></a> + <strong>instance:refresh ()</strong> + </dt> + <dd> + Refresh the session. </p> + +<p> Either saves the session (creating a new session id) or touches the session + depending on whether the rolling timeout is getting closer, which means + by default when &frac34; of rolling timeout is spent &ndash; 45 minutes with default + rolling timeout of an hour. The touch has a threshold, by default one minute, + so it may be skipped in some cases (you can call <code>session:touch()</code> to force it). + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:logout"></a> + <strong>instance:logout ()</strong> + </dt> + <dd> + Logout the session. </p> + +<p> Logout either destroys the session or just clears the data for the current audience, + and saves it (logging out from the current audience). + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:destroy"></a> + <strong>instance:destroy ()</strong> + </dt> + <dd> + Destroy the session. </p> + +<p> Destroy the session and clear the cookies. + + + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:close"></a> + <strong>instance:close ()</strong> + </dt> + <dd> + Close the session. </p> + +<p> Just closes the session instance so that it cannot be used anymore. + + + + + + + +</dd> + <dt> + <a name = "instance:clear_request_cookie"></a> + <strong>instance:clear_request_cookie ()</strong> + </dt> + <dd> + Clear the request session cookie. </p> + +<p> Modifies the request headers by removing the session related + cookies. This is useful when you use the session library on + a proxy server and don&rsquo;t want the session cookies to be forwarded + to the upstream service. + + + + + + + +</dd> + <dt> + <a name = "instance:set_headers"></a> + <strong>instance:set_headers ([...])</strong> + </dt> + <dd> + Sets request and response headers. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instance:set_request_headers"></a> + <strong>instance:set_request_headers ([...])</strong> + </dt> + <dd> + Set request headers. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "instance:set_response_headers"></a> + <strong>instance:set_response_headers ([...])</strong> + </dt> + <dd> + Set response headers. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">...</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + + + (<em>optional</em>) + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Session configuration. + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">secret</span> + Secret used for the key derivation. The secret is hashed with SHA-256 before using it. E.g. <code>&quot;RaJKp8UQW1&quot;</code>. + </li> + <li><span class="parameter">secret_fallbacks</span> + Array of secrets that can be used as alternative secrets (when doing key rotation), E.g. <code>{ &quot;6RfrAYYzYq&quot;, &quot;MkbTkkyF9C&quot; }</code>. + </li> + <li><span class="parameter">ikm</span> + Initial key material (or ikm) can be specified directly (without using a secret) with exactly 32 bytes of data. E.g. <code>&quot;5ixIW4QVMk0dPtoIhn41Eh1I9enP2060&quot;</code> + </li> + <li><span class="parameter">ikm_fallbacks</span> + Array of initial key materials that can be used as alternative keys (when doing key rotation), E.g. <code>{ &quot;QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7&quot; }</code>. + </li> + <li><span class="parameter">cookie_prefix</span> + Cookie prefix, use <code>nil</code>, <code>&quot;__Host-&quot;</code> or <code>&quot;__Secure-&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_name</span> + Session cookie name, e.g. <code>&quot;session&quot;</code> (defaults to <code>&quot;session&quot;</code>) + </li> + <li><span class="parameter">cookie_path</span> + Cookie path, e.g. <code>&quot;/&quot;</code> (defaults to <code>&quot;/&quot;</code>) + </li> + <li><span class="parameter">cookie_domain</span> + Cookie domain, e.g. <code>&quot;example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_http_only</span> + Mark cookie HTTP only, use <code>true</code> or <code>false</code> (defaults to <code>true</code>) + </li> + <li><span class="parameter">cookie_secure</span> + Mark cookie secure, use <code>nil</code>, <code>true</code> or <code>false</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_priority</span> + Cookie priority, use <code>nil</code>, <code>&quot;Low&quot;</code>, <code>&quot;Medium&quot;</code>, or <code>&quot;High&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">cookie_same_site</span> + Cookie same-site policy, use <code>nil</code>, <code>&quot;Lax&quot;</code>, <code>&quot;Strict&quot;</code>, <code>&quot;None&quot;</code>, or <code>&quot;Default&quot;</code> (defaults to <code>&quot;Lax&quot;</code>) + </li> + <li><span class="parameter">cookie_same_party</span> + Mark cookie with same party flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">cookie_partitioned</span> + Mark cookie with partitioned flag, use <code>nil</code>, <code>true</code>, or <code>false</code> (default: <code>nil</code>) + </li> + <li><span class="parameter">remember</span> + Enable or disable persistent sessions, use <code>nil</code>, <code>true</code>, or <code>false</code> (defaults to <code>false</code>) + </li> + <li><span class="parameter">remember_safety</span> + Remember cookie key derivation complexity, use <code>nil</code>, <code>&quot;None&quot;</code> (fast), <code>&quot;Low&quot;</code>, <code>&quot;Medium&quot;</code>, <code>&quot;High&quot;</code> or <code>&quot;Very High&quot;</code> (slow) (defaults to <code>&quot;Medium&quot;</code>) + </li> + <li><span class="parameter">remember_cookie_name</span> + Persistent session cookie name, e.g. <code>&quot;remember&quot;</code> (defaults to <code>&quot;remember&quot;</code>) + </li> + <li><span class="parameter">audience</span> + Session audience, e.g. <code>&quot;my-application&quot;</code> (defaults to <code>&quot;default&quot;</code>) + </li> + <li><span class="parameter">subject</span> + Session subject, e.g. <code>&quot;john.doe@example.com&quot;</code> (defaults to <code>nil</code>) + </li> + <li><span class="parameter">enforce_same_subject</span> + When set to <code>true</code>, audiences need to share the same subject. The library removes non-subject matching audience data on save. + </li> + <li><span class="parameter">stale_ttl</span> + When session is saved a new session is created, stale ttl specifies how long the old one can still be used, e.g. <code>10</code> (defaults to <code>10</code>) (in seconds) + </li> + <li><span class="parameter">idling_timeout</span> + Idling timeout specifies how long the session can be inactive until it is considered invalid, e.g. <code>900</code> (defaults to <code>900</code>, or 15 minutes) (in seconds) + </li> + <li><span class="parameter">rolling_timeout</span> + Rolling timeout specifies how long the session can be used until it needs to be renewed, e.g. <code>3600</code> (defaults to <code>3600</code>, or an hour) (in seconds) + </li> + <li><span class="parameter">absolute_timeout</span> + Absolute timeout limits how long the session can be renewed, until re-authentication is required, e.g. <code>86400</code> (defaults to <code>86400</code>, or a day) (in seconds) + </li> + <li><span class="parameter">remember_rolling_timeout</span> + Remember timeout specifies how long the persistent session is considered valid, e.g. <code>604800</code> (defaults to <code>604800</code>, or a week) (in seconds) + </li> + <li><span class="parameter">remember_absolute_timeout</span> + Remember absolute timeout limits how long the persistent session can be renewed, until re-authentication is required, e.g. <code>2592000</code> (defaults to <code>2592000</code>, or 30 days) (in seconds) + </li> + <li><span class="parameter">hash_storage_key</span> + Whether to hash or not the storage key. With storage key hashed it is impossible to decrypt data on server side without having a cookie too (defaults to <code>false</code>). + </li> + <li><span class="parameter">hash_subject</span> + Whether to hash or not the subject when <code>store_metadata</code> is enabled, e.g. for PII reasons (defaults to <code>false</code>). + </li> + <li><span class="parameter">store_metadata</span> + Whether to also store metadata of sessions, such as collecting data of sessions for a specific audience belonging to a specific subject (defaults to <code>false</code>). + </li> + <li><span class="parameter">touch_threshold</span> + Touch threshold controls how frequently or infrequently the <a href="../modules/resty.session.html#instance:refresh">session:refresh</a> touches the cookie, e.g. <code>60</code> (defaults to <code>60</code>, or a minute) (in seconds) + </li> + <li><span class="parameter">compression_threshold</span> + Compression threshold controls when the data is deflated, e.g. <code>1024</code> (defaults to <code>1024</code>, or a kilobyte) (in bytes) + </li> + <li><span class="parameter">request_headers</span> + Set of headers to send to upstream, use <code>id</code>, <code>audience</code>, <code>subject</code>, <code>timeout</code>, <code>idling-timeout</code>, <code>rolling-timeout</code>, <code>absolute-timeout</code>. E.g. <code>{ &quot;id&quot;, &quot;timeout&quot; }</code> will set <code>Session-Id</code> and <code>Session-Timeout</code> request headers when <a href="../modules/resty.session.html#instance:set_headers">set_headers</a> is called. + </li> + <li><span class="parameter">response_headers</span> + Set of headers to send to downstream, use <code>id</code>, <code>audience</code>, <code>subject</code>, <code>timeout</code>, <code>idling-timeout</code>, <code>rolling-timeout</code>, <code>absolute-timeout</code>. E.g. <code>{ &quot;id&quot;, &quot;timeout&quot; }</code> will set <code>Session-Id</code> and <code>Session-Timeout</code> response headers when <a href="../modules/resty.session.html#instance:set_headers">set_headers</a> is called. + </li> + <li><span class="parameter">storage</span> + Storage is responsible of storing session data, use <code>nil</code> or <code>&quot;cookie&quot;</code> (data is stored in cookie), <code>&quot;dshm&quot;</code>, <code>&quot;file&quot;</code>, <code>&quot;memcached&quot;</code>, <code>&quot;mysql&quot;</code>, <code>&quot;postgres&quot;</code>, <code>&quot;redis&quot;</code>, or <code>&quot;shm&quot;</code>, or give a name of custom module (<code>&quot;custom-storage&quot;</code>), or a <a href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> that implements session storage interface (defaults to <code>nil</code>) + </li> + <li><span class="parameter">dshm</span> + Configuration for dshm storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">file</span> + Configuration for file storage, e.g. <code>{ path = &quot;/tmp&quot;, suffix = &quot;session&quot; }</code> + </li> + <li><span class="parameter">memcached</span> + Configuration for memcached storage, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">mysql</span> + Configuration for MySQL / MariaDB storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">postgres</span> + Configuration for Postgres storage, e.g. <code>{ database = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">redis</span> + Configuration for Redis / Redis Sentinel / Redis Cluster storages, e.g. <code>{ prefix = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">shm</span> + Configuration for shared memory storage, e.g. <code>{ zone = &quot;sessions&quot; }</code> + </li> + <li><span class="parameter">custom</span> + -storage"] Custom storage (loaded with <code>require &quot;custom-storage&quot;</code>) configuration + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Initialization"></a>Initialization </h2> + + <dl class="function"> + <dt> + <a name = "module.init"></a> + <strong>module.init ([configuration])</strong> + </dt> + <dd> + Initialize the session library. </p> + +<p> This function can be called on <a href="../modules/resty.session.html#module.init">init</a> or <code>init_worker</code> phases on OpenResty + to set global default configuration to all session instances created by this + library. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.init({ + audience = <span class="string">"my-application"</span>, + storage = <span class="string">"redis"</span>, + redis = { + username = <span class="string">"session"</span>, + password = <span class="string">"storage"</span>, + }, +})</pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a new session. </p> + +<p> This creates a new session instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new() +<span class="comment">-- OR +</span><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.new({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> +</dl> + <h2 class="section-header "><a name="Helpers"></a>Helpers </h2> + + <dl class="function"> + <dt> + <a name = "module.open"></a> + <strong>module.open ([configuration])</strong> + </dt> + <dd> + Open a session. </p> + +<p> This can be used to open a session, and it will either return an existing + session or a new session. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.open() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists = <span class="global">require</span> <span class="string">"resty.session"</span>.open({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.start"></a> + <strong>module.start ([configuration])</strong> + </dt> + <dd> + Start a session and refresh it as needed. </p> + +<p> This can be used to start a session, and it will either return an existing + session or a new session. In case there is an existing session, the + session will be refreshed as well (as needed). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session instance</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code>, if session was refreshed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> session = <span class="global">require</span> <span class="string">"resty.session"</span>.start() +<span class="comment">-- OR +</span><span class="keyword">local</span> session, err, exists, refreshed = <span class="global">require</span> <span class="string">"resty.session"</span>.start({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.logout"></a> + <strong>module.logout ([configuration])</strong> + </dt> + <dd> + Logout a session. </p> + +<p> It logouts from a specific audience.</p> + +<p> A single session cookie may be shared between multiple audiences + (or applications), thus there is a need to be able to logout from + just a single audience while keeping the session for the other + audiences.</p> + +<p> When there is only a single audience, then this can be considered + equal to <a href="../modules/resty.session.html#module.destroy">session.destroy</a>.</p> + +<p> When the last audience is logged out, the cookie will be destroyed + as well and invalidated on a client. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists for an audience and was logged out successfully, otherwise <code>false</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was logged out, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.logout() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists, logged_out = <span class="global">require</span> <span class="string">"resty.session"</span>.logout({ + audience = <span class="string">"my-application"</span>, +})</pre> + </ul> + +</dd> + <dt> + <a name = "module.destroy"></a> + <strong>module.destroy ([configuration])</strong> + </dt> + <dd> + Destroy a session. </p> + +<p> It destroys the whole session and clears the cookies. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session <a href="../modules/resty.session.html#configuration">configuration</a> overrides + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> session exists and was destroyed successfully, otherwise <code>nil</code></li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session existed, otherwise <code>false</code></li> + <li> + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if session was destroyed, otherwise <code>false</code></li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="global">require</span> <span class="string">"resty.session"</span>.destroy() +<span class="comment">-- OR +</span><span class="keyword">local</span> ok, err, exists, destroyed = <span class="global">require</span> <span class="string">"resty.session"</span>.destroy({ + cookie_name = <span class="string">"auth"</span>, +})</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.memcached.html b/docs/modules/resty.session.memcached.html new file mode 100644 index 000000000..6fd39b8fb --- /dev/null +++ b/docs/modules/resty.session.memcached.html @@ -0,0 +1,400 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><strong>resty.session.memcached</strong></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.memcached</code></h1> +<p>Memcached backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Distributed shared memory storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a memcached storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Distributed shared memory storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + Prefix for the keys stored in memcached. + </li> + <li><span class="parameter">suffix</span> + Suffix for the keys stored in memcached. + </li> + <li><span class="parameter">host</span> + The host to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>11211</code>). + </li> + <li><span class="parameter">socket</span> + The socket file to connect to (defaults to <code>nil</code>). + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">server_name</span> + The server name for the new TLS extension Server Name Indication (SNI). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a memcached storage. </p> + +<p> This creates a new memcached storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + memcached storage <a href="../modules/resty.session.memcached.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + memcached storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.mysql.html b/docs/modules/resty.session.mysql.html new file mode 100644 index 000000000..1c5a88540 --- /dev/null +++ b/docs/modules/resty.session.mysql.html @@ -0,0 +1,480 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +<li><a href="#Database">Database </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><strong>resty.session.mysql</strong></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.mysql</code></h1> +<p>MySQL / MariaDB backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Postgres storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a MySQL / MariaDB storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> +<h2><a href="#Database">Database </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#sessions">sessions</a></td> + <td class="summary">Sessions table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#metadata">metadata</a></td> + <td class="summary">Sessions metadata table.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Postgres storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">host</span> + The host to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>3306</code>). + </li> + <li><span class="parameter">socket</span> + The socket file to connect to (defaults to <code>nil</code>). + </li> + <li><span class="parameter">username</span> + The database username to authenticate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">password</span> + Password for authentication, may be required depending on server configuration. + </li> + <li><span class="parameter">charset</span> + The character set used on the MySQL connection (defaults to <code>&quot;ascii&quot;</code>). + </li> + <li><span class="parameter">database</span> + The database name to connect. + </li> + <li><span class="parameter">table_name</span> + Name of database table to which to store session data (defaults to <code>&quot;sessions&quot;</code>). + </li> + <li><span class="parameter">table_name_meta</span> + Name of database meta data table to which to store session meta data (defaults to <code>&quot;sessions_meta&quot;</code>). + </li> + <li><span class="parameter">max_packet_size</span> + The upper limit for the reply packets sent from the MySQL server (defaults to 1 MB). + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a MySQL / MariaDB storage. </p> + +<p> This creates a new MySQL / MariaDB storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + mysql/mariadb storage <a href="../modules/resty.session.mysql.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + mysql/mariadb storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Database"></a>Database </h2> + + <dl class="function"> + <dt> + <a name = "sessions"></a> + <strong>sessions</strong> + </dt> + <dd> + Sessions table. </p> + +<p> Database table that stores session data. + + + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">CREATE TABLE IF NOT EXISTS sessions ( + sid CHAR(<span class="number">43</span>) PRIMARY KEY, + name VARCHAR(<span class="number">255</span>), + data MEDIUMTEXT, + exp DATETIME, + INDEX (exp) +) CHARACTER SET ascii;</pre> + </ul> + +</dd> + <dt> + <a name = "metadata"></a> + <strong>metadata</strong> + </dt> + <dd> + Sessions metadata table. </p> + +<p> This is only needed if you want to store session metadata. + + + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">CREATE TABLE IF NOT EXISTS sessions_meta ( + aud VARCHAR(<span class="number">255</span>), + sub VARCHAR(<span class="number">255</span>), + sid CHAR(<span class="number">43</span>), + PRIMARY KEY (aud, sub, sid), + CONSTRAINT FOREIGN KEY (sid) REFERENCES sessions(sid) ON DELETE CASCADE ON UPDATE CASCADE +) CHARACTER SET ascii;</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.postgres.html b/docs/modules/resty.session.postgres.html new file mode 100644 index 000000000..89e4935e8 --- /dev/null +++ b/docs/modules/resty.session.postgres.html @@ -0,0 +1,476 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +<li><a href="#Database">Database </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><strong>resty.session.postgres</strong></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.postgres</code></h1> +<p>Postgres backend for session library.</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Postgres storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Postgres storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> +<h2><a href="#Database">Database </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#sessions">sessions</a></td> + <td class="summary">Sessions table.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#metadata">metadata</a></td> + <td class="summary">Sessions metadata table.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Postgres storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">host</span> + The host to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>5432</code>). + </li> + <li><span class="parameter">application</span> + Set the name of the connection as displayed in pg_stat_activity (defaults to <code>&quot;pgmoon&quot;</code>). + </li> + <li><span class="parameter">username</span> + The database username to authenticate (defaults to <code>&quot;postgres&quot;</code>). + </li> + <li><span class="parameter">password</span> + Password for authentication, may be required depending on server configuration. + </li> + <li><span class="parameter">database</span> + The database name to connect. + </li> + <li><span class="parameter">table_name</span> + Name of database table to which to store session data (can be <code>database schema</code> prefixed) (defaults to <code>&quot;sessions&quot;</code>). + </li> + <li><span class="parameter">table_name_meta</span> + Name of database meta data table to which to store session meta data (can be <code>database schema</code> prefixed) (defaults to <code>&quot;sessions_meta&quot;</code>). + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">ssl_required</span> + Abort the connection if the server does not support SSL connections (defaults to <code>nil</code>). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Postgres storage. </p> + +<p> This creates a new Postgres storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + postgres storage <a href="../modules/resty.session.postgres.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + postgres storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Database"></a>Database </h2> + + <dl class="function"> + <dt> + <a name = "sessions"></a> + <strong>sessions</strong> + </dt> + <dd> + Sessions table. </p> + +<p> Database table that stores session data. + + + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">CREATE TABLE IF NOT EXISTS sessions ( + sid TEXT PRIMARY KEY, + name TEXT, + data TEXT, + exp TIMESTAMP WITH TIME ZONE +); +CREATE INDEX ON sessions (exp);</pre> + </ul> + +</dd> + <dt> + <a name = "metadata"></a> + <strong>metadata</strong> + </dt> + <dd> + Sessions metadata table. </p> + +<p> This is only needed if you want to store session metadata. + + + + + + + <h3>Usage:</h3> + <ul> + <pre class="example">CREATE TABLE IF NOT EXISTS sessions_meta ( + aud TEXT, + sub TEXT, + sid TEXT REFERENCES sessions (sid) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY (aud, sub, sid) +);</pre> + </ul> + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis-cluster.html b/docs/modules/resty.session.redis-cluster.html new file mode 100644 index 000000000..04b67b020 --- /dev/null +++ b/docs/modules/resty.session.redis-cluster.html @@ -0,0 +1,413 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file-thread.html">resty.session.file-thread</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><strong>resty.session.redis-cluster</strong></li> + <li><a href="../modules/resty.session.redis-sentinel.html">resty.session.redis-sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis-cluster</code></h1> +<p>Redis Cluster backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Redis Cluster storage backend configuration</td> + </tr> + <tr> + <td class="name" nowrap><a href="#nodes">nodes</a></td> + <td class="summary">Cluster Nodes</td> + </tr> + <tr> + <td class="name" nowrap><a href="#node">node</a></td> + <td class="summary">Cluster Node</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Redis Cluster storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Redis Cluster storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + prefix for the keys stored in redis + </li> + <li><span class="parameter">suffix</span> + suffix for the keys stored in redis + </li> + <li><span class="parameter">name</span> + redis cluster name + </li> + <li><span class="parameter">nodes</span> + redis cluster nodes + </li> + <li><span class="parameter">lock_zone</span> + shared dictionary name for locks + </li> + <li><span class="parameter">lock_prefix</span> + shared dictionary name prefix for lock + </li> + <li><span class="parameter">max_redirections</span> + maximum retry attempts for redirection + </li> + <li><span class="parameter">max_connection_attempts</span> + maximum retry attempts for connection + </li> + <li><span class="parameter">max_connection_timeout</span> + maximum connection timeout in total among the retries + </li> + <li><span class="parameter">username</span> + the database username to authenticate + </li> + <li><span class="parameter">password</span> + password for authentication + </li> + <li><span class="parameter">connect_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method + </li> + <li><span class="parameter">send_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method + </li> + <li><span class="parameter">read_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method + </li> + <li><span class="parameter">keepalive_timeout</span> + controls the default maximal idle time of the connections in the connection pool + </li> + <li><span class="parameter">pool</span> + a custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + the size of the connection pool, + </li> + <li><span class="parameter">backlog</span> + a queue size to use when the connection pool is full (configured with @pool_size) + </li> + <li><span class="parameter">ssl</span> + enable ssl (defaults to <code>false</code>) + </li> + <li><span class="parameter">ssl_verify</span> + verify server certificate (defaults to <code>nil</code>) + </li> + <li><span class="parameter">server_name</span> + the server name for the new TLS extension Server Name Indication (SNI) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "nodes"></a> + <strong>nodes</strong> + </dt> + <dd> + Cluster Nodes An array of cluster nodes. + + + + + + + +</dd> + <dt> + <a name = "node"></a> + <strong>node</strong> + </dt> + <dd> + Cluster Node + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">ip</span> + the ip address to connect (defaults to <code>&quot;127.0.0.1&quot;</code>) + </li> + <li><span class="parameter">port</span> + the port to connect (defaults to <code>6379</code>) + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Redis Cluster storage. </p> + +<p> This creates a new Redis Cluster storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis cluster storage <a href="../modules/resty.session.redis-cluster.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis cluster storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + </li> + <li><span class="parameter">remember</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2022-12-23 14:06:58 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis-sentinel.html b/docs/modules/resty.session.redis-sentinel.html new file mode 100644 index 000000000..6571d7a88 --- /dev/null +++ b/docs/modules/resty.session.redis-sentinel.html @@ -0,0 +1,410 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file-thread.html">resty.session.file-thread</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis-cluster.html">resty.session.redis-cluster</a></li> + <li><strong>resty.session.redis-sentinel</strong></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis-sentinel</code></h1> +<p>Redis Sentinel backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Redis Sentinel storage backend configuration</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sentinels">sentinels</a></td> + <td class="summary">Sentinels</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sentinel">sentinel</a></td> + <td class="summary">Sentinel</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Redis Sentinel storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Redis Sentinel storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + prefix for the keys stored in redis + </li> + <li><span class="parameter">suffix</span> + suffix for the keys stored in redis + </li> + <li><span class="parameter">master</span> + name of master + </li> + <li><span class="parameter">role</span> + <code>&quot;master&quot;</code> or <code>&quot;slave&quot;</code> + </li> + <li><span class="parameter">sentinels</span> + redis sentinels + </li> + <li><span class="parameter">sentinel_username</span> + optional sentinel username + </li> + <li><span class="parameter">sentinel_password</span> + optional sentinel password + </li> + <li><span class="parameter">username</span> + the database username to authenticate + </li> + <li><span class="parameter">password</span> + password for authentication + </li> + <li><span class="parameter">database</span> + the database to connect + </li> + <li><span class="parameter">connect_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method + </li> + <li><span class="parameter">send_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method + </li> + <li><span class="parameter">read_timeout</span> + controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method + </li> + <li><span class="parameter">keepalive_timeout</span> + controls the default maximal idle time of the connections in the connection pool + </li> + <li><span class="parameter">pool</span> + a custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + the size of the connection pool, + </li> + <li><span class="parameter">backlog</span> + a queue size to use when the connection pool is full (configured with @pool_size) + </li> + <li><span class="parameter">ssl</span> + enable ssl (defaults to <code>false</code>) + </li> + <li><span class="parameter">ssl_verify</span> + verify server certificate (defaults to <code>nil</code>) + </li> + <li><span class="parameter">server_name</span> + the server name for the new TLS extension Server Name Indication (SNI) + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "sentinels"></a> + <strong>sentinels</strong> + </dt> + <dd> + Sentinels An array of sentinels. + + + + + + + +</dd> + <dt> + <a name = "sentinel"></a> + <strong>sentinel</strong> + </dt> + <dd> + Sentinel + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">host</span> + the host to connect + </li> + <li><span class="parameter">port</span> + the port to connect + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Redis Sentinel storage. </p> + +<p> This creates a new Redis Sentinel storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis sentinel storage <a href="../modules/resty.session.redis-sentinel.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis sentinel storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + </li> + <li><span class="parameter">remember</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2022-12-23 14:06:58 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis.cluster.html b/docs/modules/resty.session.redis.cluster.html new file mode 100644 index 000000000..9b569b0cc --- /dev/null +++ b/docs/modules/resty.session.redis.cluster.html @@ -0,0 +1,463 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><strong>resty.session.redis.cluster</strong></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis.cluster</code></h1> +<p>Redis Cluster backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Redis Cluster storage backend configuration</td> + </tr> + <tr> + <td class="name" nowrap><a href="#nodes">nodes</a></td> + <td class="summary">Cluster Nodes</td> + </tr> + <tr> + <td class="name" nowrap><a href="#node">node</a></td> + <td class="summary">Cluster Node</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Redis Cluster storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Redis Cluster storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + Prefix for the keys stored in redis. + </li> + <li><span class="parameter">suffix</span> + Suffix for the keys stored in redis. + </li> + <li><span class="parameter">name</span> + Redis cluster name. + </li> + <li><span class="parameter">nodes</span> + Redis cluster nodes. + </li> + <li><span class="parameter">lock_zone</span> + Shared dictionary name for locks. + </li> + <li><span class="parameter">lock_prefix</span> + Shared dictionary name prefix for lock. + </li> + <li><span class="parameter">max_redirections</span> + Maximum retry attempts for redirection. + </li> + <li><span class="parameter">max_connection_attempts</span> + Maximum retry attempts for connection. + </li> + <li><span class="parameter">max_connection_timeout</span> + Maximum connection timeout in total among the retries. + </li> + <li><span class="parameter">username</span> + The database username to authenticate. + </li> + <li><span class="parameter">password</span> + Password for authentication. + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + controls The default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + controls The default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">server_name</span> + The server name for the new TLS extension Server Name Indication (SNI). + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "nodes"></a> + <strong>nodes</strong> + </dt> + <dd> + Cluster Nodes An array of cluster nodes. + + + + + + + +</dd> + <dt> + <a name = "node"></a> + <strong>node</strong> + </dt> + <dd> + Cluster Node + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">ip</span> + The IP address to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>6379</code>). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Redis Cluster storage. </p> + +<p> This creates a new Redis Cluster storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis cluster storage <a href="../modules/resty.session.redis.cluster.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis cluster storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis.common.html b/docs/modules/resty.session.redis.common.html new file mode 100644 index 000000000..c633c0362 --- /dev/null +++ b/docs/modules/resty.session.redis.common.html @@ -0,0 +1,321 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><strong>resty.session.redis.common</strong></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis.common</code></h1> +<p>Common Redis functions shared between Redis, + Redis Cluster and Redis Sentinel implementations.</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.SET">module.SET (storage, red, name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.GET">module.GET (storage, red, name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.UNLINK">module.UNLINK (storage, red, name, key, current_time[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#module.READ_METADATA">module.READ_METADATA (storage, red, name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "module.SET"></a> + <strong>module.SET (storage, red, name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the storage + </li> + <li><span class="parameter">red</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the redis instance + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.GET"></a> + <strong>module.GET (storage, red, name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the storage + </li> + <li><span class="parameter">red</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the redis instance + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.UNLINK"></a> + <strong>module.UNLINK (storage, red, name, key, current_time[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the storage + </li> + <li><span class="parameter">red</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the redis instance + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "module.READ_METADATA"></a> + <strong>module.READ_METADATA (storage, red, name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the storage + </li> + <li><span class="parameter">red</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + the redis instance + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis.html b/docs/modules/resty.session.redis.html new file mode 100644 index 000000000..788f9873c --- /dev/null +++ b/docs/modules/resty.session.redis.html @@ -0,0 +1,409 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><strong>resty.session.redis</strong></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis</code></h1> +<p>Redis backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Redis storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Redis storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Redis storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + Prefix for the keys stored in Redis. + </li> + <li><span class="parameter">suffix</span> + Suffix for the keys stored in Redis. + </li> + <li><span class="parameter">host</span> + The host to connect (defaults to <code>&quot;127.0.0.1&quot;</code>). + </li> + <li><span class="parameter">port</span> + The port to connect (defaults to <code>6379</code>). + </li> + <li><span class="parameter">socket</span> + The socket file to connect to (defaults to <code>nil</code>). + </li> + <li><span class="parameter">username</span> + The database username to authenticate. + </li> + <li><span class="parameter">password</span> + Password for authentication. + </li> + <li><span class="parameter">database</span> + The database to connect. + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSL (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">server_name</span> + The server name for the new TLS extension Server Name Indication (SNI). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Redis storage. </p> + +<p> This creates a new Redis storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis storage <a href="../modules/resty.session.redis.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.redis.sentinel.html b/docs/modules/resty.session.redis.sentinel.html new file mode 100644 index 000000000..04db82164 --- /dev/null +++ b/docs/modules/resty.session.redis.sentinel.html @@ -0,0 +1,460 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><strong>resty.session.redis.sentinel</strong></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.redis.sentinel</code></h1> +<p>Redis Sentinel backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Redis Sentinel storage backend configuration</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sentinels">sentinels</a></td> + <td class="summary">Sentinels</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sentinel">sentinel</a></td> + <td class="summary">Sentinel</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a Redis Sentinel storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Redis Sentinel storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + Prefix for the keys stored in redis. + </li> + <li><span class="parameter">suffix</span> + Suffix for the keys stored in redis. + </li> + <li><span class="parameter">master</span> + Name of master. + </li> + <li><span class="parameter">role</span> + <code>&quot;master&quot;</code> or <code>&quot;slave&quot;</code>. + </li> + <li><span class="parameter">sentinels</span> + Redis Sentinels. + </li> + <li><span class="parameter">sentinel_username</span> + Optional sentinel username. + </li> + <li><span class="parameter">sentinel_password</span> + Optional sentinel password. + </li> + <li><span class="parameter">username</span> + The database username to authenticate. + </li> + <li><span class="parameter">password</span> + Password for authentication. + </li> + <li><span class="parameter">database</span> + The database to connect. + </li> + <li><span class="parameter">connect_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>connect</code> method. + </li> + <li><span class="parameter">send_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>send</code> method. + </li> + <li><span class="parameter">read_timeout</span> + Controls the default timeout value used in TCP/unix-domain socket object&rsquo;s <code>receive</code> method. + </li> + <li><span class="parameter">keepalive_timeout</span> + Controls the default maximal idle time of the connections in the connection pool. + </li> + <li><span class="parameter">pool</span> + A custom name for the connection pool being used. + </li> + <li><span class="parameter">pool_size</span> + The size of the connection pool. + </li> + <li><span class="parameter">backlog</span> + A queue size to use when the connection pool is full (configured with @pool_size). + </li> + <li><span class="parameter">ssl</span> + Enable SSK (defaults to <code>false</code>). + </li> + <li><span class="parameter">ssl_verify</span> + Verify server certificate (defaults to <code>nil</code>). + </li> + <li><span class="parameter">server_name</span> + The server name for the new TLS extension Server Name Indication (SNI). + </li> + </ul> + + + + + +</dd> + <dt> + <a name = "sentinels"></a> + <strong>sentinels</strong> + </dt> + <dd> + Sentinels An array of sentinels. + + + + + + + +</dd> + <dt> + <a name = "sentinel"></a> + <strong>sentinel</strong> + </dt> + <dd> + Sentinel + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">host</span> + The host to connect. + </li> + <li><span class="parameter">port</span> + The port to connect. + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a Redis Sentinel storage. </p> + +<p> This creates a new Redis Sentinel storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis sentinel storage <a href="../modules/resty.session.redis.sentinel.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + redis sentinel storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.shm.html b/docs/modules/resty.session.shm.html new file mode 100644 index 000000000..91acef2db --- /dev/null +++ b/docs/modules/resty.session.shm.html @@ -0,0 +1,364 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Configuration">Configuration </a></li> +<li><a href="#Constructors">Constructors </a></li> +<li><a href="#Storage">Storage </a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><strong>resty.session.shm</strong></li> + <li><a href="../modules/resty.session.utils.html">resty.session.utils</a></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.shm</code></h1> +<p>Shared Memory (SHM) backend for session library</p> +<p> +</p> + + +<h2><a href="#Configuration">Configuration </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#configuration">configuration</a></td> + <td class="summary">Shared memory storage backend configuration</td> + </tr> +</table> +<h2><a href="#Constructors">Constructors </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#module.new">module.new ([configuration])</a></td> + <td class="summary">Create a SHM storage.</td> + </tr> +</table> +<h2><a href="#Storage">Storage </a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#instance:set">instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</a></td> + <td class="summary">Store session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:get">instance:get (name, key)</a></td> + <td class="summary">Retrieve session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:delete">instance:delete (name, key[, metadata])</a></td> + <td class="summary">Delete session data.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#instance:read_metadata">instance:read_metadata (name, audience, subject, current_time)</a></td> + <td class="summary">Read session metadata.</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Configuration"></a>Configuration </h2> + + <dl class="function"> + <dt> + <a name = "configuration"></a> + <strong>configuration</strong> + </dt> + <dd> + Shared memory storage backend configuration + + + <h3>Fields:</h3> + <ul> + <li><span class="parameter">prefix</span> + Prefix for the keys stored in SHM. + </li> + <li><span class="parameter">suffix</span> + Suffix for the keys stored in SHM. + </li> + <li><span class="parameter">zone</span> + A name of shared memory zone (defaults to <code>sessions</code>). + </li> + </ul> + + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Constructors"></a>Constructors </h2> + + <dl class="function"> + <dt> + <a name = "module.new"></a> + <strong>module.new ([configuration])</strong> + </dt> + <dd> + Create a SHM storage. </p> + +<p> This creates a new shared memory storage instance. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + shm storage <a href="../modules/resty.session.shm.html#configuration">configuration</a> + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + shm storage instance + </ol> + + + + +</dd> +</dl> + <h2 class="section-header "><a name="Storage"></a>Storage </h2> + + <dl class="function"> + <dt> + <a name = "instance:set"></a> + <strong>instance:set (name, key, value, ttl, current_time[, old_key], stale_ttl[, metadata], remember)</strong> + </dt> + <dd> + Store session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session value + </li> + <li><span class="parameter">ttl</span> + <span class="types"><span class="type">number</span></span> + session ttl + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + <li><span class="parameter">old_key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + old session id + (<em>optional</em>) + </li> + <li><span class="parameter">stale_ttl</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + stale ttl + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + table of metadata + (<em>optional</em>) + </li> + <li><span class="parameter">remember</span> + <span class="types"><span class="type">boolean</span></span> + whether storing persistent session or not + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">true</span> or <span class="type">nil</span></span> + ok</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:get"></a> + <strong>instance:get (name, key)</strong> + </dt> + <dd> + Retrieve session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:delete"></a> + <strong>instance:delete (name, key[, metadata])</strong> + </dt> + <dd> + Delete session data. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">metadata</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session meta data + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">boolean</span> or <span class="type">nil</span></span> + session data</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> + <dt> + <a name = "instance:read_metadata"></a> + <strong>instance:read_metadata (name, audience, subject, current_time)</strong> + </dt> + <dd> + Read session metadata. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + cookie name + </li> + <li><span class="parameter">audience</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + session key + </li> + <li><span class="parameter">current_time</span> + <span class="types"><span class="type">number</span></span> + current time + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + session metadata</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + error message</li> + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/docs/modules/resty.session.utils.html b/docs/modules/resty.session.utils.html new file mode 100644 index 000000000..4e5befb72 --- /dev/null +++ b/docs/modules/resty.session.utils.html @@ -0,0 +1,1370 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +<head> + <title>Session Library for OpenResty Documentation</title> + <link rel="stylesheet" href="../ldoc.css" type="text/css" /> +</head> +<body> + +<div id="container"> + +<div id="product"> + <div id="product_logo"></div> + <div id="product_name"><big><b></b></big></div> + <div id="product_description"></div> +</div> <!-- id="product" --> + + +<div id="main"> + + +<!-- Menu --> + +<div id="navigation"> +<br/> +<h1>resty.session</h1> + +<ul> + <li><a href="../index.html">Index</a></li> +</ul> + +<h2>Contents</h2> +<ul> +<li><a href="#Functions">Functions</a></li> +</ul> + + +<h2>Modules</h2> +<ul class="nowrap"> + <li><a href="../modules/resty.session.html">resty.session</a></li> + <li><a href="../modules/resty.session.dshm.html">resty.session.dshm</a></li> + <li><a href="../modules/resty.session.file.html">resty.session.file</a></li> + <li><a href="../modules/resty.session.file.thread.html">resty.session.file.thread</a></li> + <li><a href="../modules/resty.session.file.utils.html">resty.session.file.utils</a></li> + <li><a href="../modules/resty.session.memcached.html">resty.session.memcached</a></li> + <li><a href="../modules/resty.session.mysql.html">resty.session.mysql</a></li> + <li><a href="../modules/resty.session.postgres.html">resty.session.postgres</a></li> + <li><a href="../modules/resty.session.redis.html">resty.session.redis</a></li> + <li><a href="../modules/resty.session.redis.cluster.html">resty.session.redis.cluster</a></li> + <li><a href="../modules/resty.session.redis.common.html">resty.session.redis.common</a></li> + <li><a href="../modules/resty.session.redis.sentinel.html">resty.session.redis.sentinel</a></li> + <li><a href="../modules/resty.session.shm.html">resty.session.shm</a></li> + <li><strong>resty.session.utils</strong></li> +</ul> + +</div> + +<div id="content"> + +<h1>Module <code>resty.session.utils</code></h1> +<p>Common utilities for session library and storage backends</p> +<p> +</p> + + +<h2><a href="#Functions">Functions</a></h2> +<table class="function_list"> + <tr> + <td class="name" nowrap><a href="#is_fips_mode">is_fips_mode ()</a></td> + <td class="summary">Returns whether OpenSSL is in FIPS-mode.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bpack">bpack (size, value)</a></td> + <td class="summary">Binary pack unsigned integer.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#bunpack">bunpack (size, value)</a></td> + <td class="summary">Binary unpack unsigned integer.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#trim">trim (value)</a></td> + <td class="summary">Trim whitespace from the start and from the end of string.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#encode_json">encode_json (value)</a></td> + <td class="summary">JSON encode value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#decode_json">decode_json (value)</a></td> + <td class="summary">JSON decode value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#encode_base64url">encode_base64url (value)</a></td> + <td class="summary">Base64 URL encode value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#decode_base64url">decode_base64url (value)</a></td> + <td class="summary">Base64 URL decode value</td> + </tr> + <tr> + <td class="name" nowrap><a href="#base64_size">base64_size (size)</a></td> + <td class="summary">Base64 size from original size (without padding).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#deflate">deflate (data)</a></td> + <td class="summary">Compress the data with deflate algorithm.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#inflate">inflate (data)</a></td> + <td class="summary">Decompress the data compressed with deflate algorithm.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#rand_bytes">rand_bytes (length)</a></td> + <td class="summary">Generate crypto random bytes.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#sha256">sha256 (value)</a></td> + <td class="summary">Calculates SHA-256 hash of the value.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#derive_hkdf_sha256">derive_hkdf_sha256 (ikm, nonce, usage, size)</a></td> + <td class="summary">Derive a new key using HKDF with SHA-256.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#derive_pbkdf2_hmac_sha256">derive_pbkdf2_hmac_sha256 (pass, salt, usage, size, iterations)</a></td> + <td class="summary">Derive a new key using PBKDF2 with SHA-256.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#derive_aes_gcm_256_key_and_iv">derive_aes_gcm_256_key_and_iv (ikm, nonce[, safety])</a></td> + <td class="summary">Derive a new AES-256 GCM-mode key and initialization vector.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#derive_hmac_sha256_key">derive_hmac_sha256_key (ikm, nonce)</a></td> + <td class="summary">Derive HMAC SHA-256 key for message authentication using HDKF with SHA-256, + except on FIPS-mode it uses PBKDF2 with SHA-256 (single iteration).</td> + </tr> + <tr> + <td class="name" nowrap><a href="#encrypt_aes_256_gcm">encrypt_aes_256_gcm (key, iv, plaintext, aad)</a></td> + <td class="summary">Encrypt plain text using AES-256 in GCM-mode.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#decrypt_aes_256_gcm">decrypt_aes_256_gcm (key, iv, plaintext, aad, tag)</a></td> + <td class="summary">Decrypt ciphertext using AES-256 in GCM-mode.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#hmac_sha256">hmac_sha256 (key, value)</a></td> + <td class="summary">Calculate message authentication code with HMAC with SHA-256.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#load_storage">load_storage (storage[, configuration])</a></td> + <td class="summary">Loads session storage and creates a new instance using session configuration.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#errmsg">errmsg ([err], msg, ...)</a></td> + <td class="summary">Helper to format error messages.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#get_name">get_name (storage, name, key, subject)</a></td> + <td class="summary">Helper to create a delimited key.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#set_flag">set_flag (flags, flag)</a></td> + <td class="summary">Helper to turn on a flag.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#unset_flag">unset_flag (flags, flag)</a></td> + <td class="summary">Helper to turn off a flag.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#has_flag">has_flag (flags, flag)</a></td> + <td class="summary">Helper to check if flag is enabled.</td> + </tr> + <tr> + <td class="name" nowrap><a href="#meta_get_value">meta_get_value (key, exp)</a></td> + <td class="summary">Helper to get the value used to store metadata for a certain aud and sub + Empty exp means the session id has been invalidated</td> + </tr> + <tr> + <td class="name" nowrap><a href="#meta_get_next">meta_get_next (val, index)</a></td> + <td class="summary">Function to extract the next key and exp from a serialized + metadata list, starting from index</td> + </tr> + <tr> + <td class="name" nowrap><a href="#meta_get_latest">meta_get_latest (sessions)</a></td> + <td class="summary">Function to filter out the latest valid key:exp from a + serialized list, used to store session metadata</td> + </tr> +</table> + +<br/> +<br/> + + + <h2 class="section-header "><a name="Functions"></a>Functions</h2> + + <dl class="function"> + <dt> + <a name = "is_fips_mode"></a> + <strong>is_fips_mode ()</strong> + </dt> + <dd> + Returns whether OpenSSL is in FIPS-mode. + + + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">boolean</span></span> + <code>true</code> if OpenSSL is in FIPS-mode, otherwise <code>false</code> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> is_fips = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.is_fips_mode()</pre> + </ul> + +</dd> + <dt> + <a name = "bpack"></a> + <strong>bpack (size, value)</strong> + </dt> + <dd> + <p>Binary pack unsigned integer. </p> + +<p> Returns binary packed version of an integer in little endian unsigned format.</p> + +<p> Size can be:</p> + +<ul> +<li><code>1</code>, pack input as a little endian unsigned char (<code>&lt;C</code>)</li> +<li><code>2</code>, pack input as a little endian unsigned short (<code>&lt;S</code>)</li> +<li><code>3</code>, pack input as a little endian unsigned integer (truncated) (<code>&lt;I</code>)</li> +<li><code>4</code>, pack input as a little endian unsigned integer (<code>&lt;I</code>)</li> +<li><code>5</code>, pack input as a little endian unsigned long (truncated) (<code>&lt;L</code>)</li> +<li><code>6</code>, pack input as a little endian unsigned long (truncated) (<code>&lt;L</code>)</li> +<li><code>7</code>, pack input as a little endian unsigned long (truncated) (<code>&lt;L</code>)</li> +<li><code>8</code>, pack input as a little endian unsigned long (<code>&lt;L</code>)</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">size</span> + <span class="types"><span class="type">number</span></span> + size of binary packed output + </li> + <li><span class="parameter">value</span> + <span class="types"><span class="type">number</span></span> + value to binary pack + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + binary packed value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> packed_128 = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.bpack(<span class="number">1</span>, <span class="number">128</span>) +<span class="keyword">local</span> packed_now = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.bpack(<span class="number">8</span>, ngx.time())</pre> + </ul> + +</dd> + <dt> + <a name = "bunpack"></a> + <strong>bunpack (size, value)</strong> + </dt> + <dd> + <p>Binary unpack unsigned integer. </p> + +<p> Returns number from a little endian unsigned binary packed format.</p> + +<p> Size can be:</p> + +<ul> +<li><code>1</code>, unpack input from little endian unsigned char (<code>&lt;C</code>)</li> +<li><code>2</code>, unpack input from little endian unsigned short (<code>&lt;S</code>)</li> +<li><code>3</code>, unpack input from little endian unsigned integer (truncated) (<code>&lt;I</code>)</li> +<li><code>4</code>, unpack input from little endian unsigned integer (<code>&lt;I</code>)</li> +<li><code>5</code>, unpack input from little endian unsigned integer (truncated) (<code>&lt;L</code>)</li> +<li><code>6</code>, unpack input from little endian unsigned integer (truncated) (<code>&lt;L</code>)</li> +<li><code>7</code>, unpack input from little endian unsigned integer (truncated) (<code>&lt;L</code>)</li> +<li><code>8</code>, unpack input from little endian unsigned long (<code>&lt;L</code>)</li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">size</span> + <span class="types"><span class="type">number</span></span> + size of binary packed output + </li> + <li><span class="parameter">value</span> + <span class="types"><span class="type">number</span></span> + value to binary pack + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + binary unpacked value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> value = <span class="number">128</span> +<span class="keyword">local</span> packed_value = utils.bpack(<span class="number">1</span>, value) +<span class="keyword">local</span> unpacked_value = utils.bunpack(<span class="number">1</span>, packed_value) +<span class="global">print</span>(value == unpacked_value) <span class="comment">-- true</span></pre> + </ul> + +</dd> + <dt> + <a name = "trim"></a> + <strong>trim (value)</strong> + </dt> + <dd> + <p>Trim whitespace from the start and from the end of string. </p> + +<p> Characters that are trimmed:</p> + +<ul> +<li>space <code>&quot; &quot;</code></li> +<li>tab <code>&quot;\t&quot;</code></li> +<li>carriage return <code>&quot;\r&quot;</code></li> +<li>line feed <code>&quot;\n&quot;</code></li> +<li>vertical tab <code>&quot;\v&quot;</code></li> +<li>form feed <code>&quot;\f&quot;</code></li> +</ul> + + + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string to trim + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + a whitespace trimmed string + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> trimmed = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.trim(<span class="string">" hello world "</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "encode_json"></a> + <strong>encode_json (value)</strong> + </dt> + <dd> + JSON encode value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><span class="type">any</span></span> + value to json encode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + json encoded value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> json = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.encode_json({ hello = <span class="string">"world"</span> })</pre> + </ul> + +</dd> + <dt> + <a name = "decode_json"></a> + <strong>decode_json (value)</strong> + </dt> + <dd> + JSON decode value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string to json decode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">any</span></span> + json decoded value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> tbl = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.decode_json(<span class="string">'{ "hello": "world" }'</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "encode_base64url"></a> + <strong>encode_base64url (value)</strong> + </dt> + <dd> + Base64 URL encode value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string to base64 url encode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + base64 url encoded value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> encoded = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.encode_base64url(<span class="string">"test"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "decode_base64url"></a> + <strong>decode_base64url (value)</strong> + </dt> + <dd> + Base64 URL decode value + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + string to base64 url decode + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + base64 url decoded value + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> encoded = utils.encode_base64url(<span class="string">"test"</span>) +<span class="keyword">local</span> decoded = utils.decode_base64url(encoded)</pre> + </ul> + +</dd> + <dt> + <a name = "base64_size"></a> + <strong>base64_size (size)</strong> + </dt> + <dd> + Base64 size from original size (without padding). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">size</span> + <span class="types"><span class="type">number</span></span> + original size + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + base64 url encoded size without padding + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> test = <span class="string">"test"</span> +<span class="keyword">local</span> b64len = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.base64_size(#test)</pre> + </ul> + +</dd> + <dt> + <a name = "deflate"></a> + <strong>deflate (data)</strong> + </dt> + <dd> + Compress the data with deflate algorithm. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">data</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + data to deflate + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + deflated data + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> test = <span class="string">"test"</span> +<span class="keyword">local</span> deflated = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.deflate((<span class="string">"a"</span>):rep(<span class="number">100</span>))</pre> + </ul> + +</dd> + <dt> + <a name = "inflate"></a> + <strong>inflate (data)</strong> + </dt> + <dd> + Decompress the data compressed with deflate algorithm. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">data</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + data to inflate + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + inflated data + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> deflated = utils.deflate((<span class="string">"a"</span>):rep(<span class="number">100</span>)) +<span class="keyword">local</span> inflated = utils.inflate(deflated)</pre> + </ul> + +</dd> + <dt> + <a name = "rand_bytes"></a> + <strong>rand_bytes (length)</strong> + </dt> + <dd> + Generate crypto random bytes. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">length</span> + <span class="types"><span class="type">number</span></span> + how many bytes of random data to generate + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + random bytes</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> bytes = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.rand_bytes(<span class="number">32</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "sha256"></a> + <strong>sha256 (value)</strong> + </dt> + <dd> + Calculates SHA-256 hash of the value. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + value from which to calculate hash + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + sha-256 hash (32 bytes)</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> hash, err = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.sha256(<span class="string">"hello world"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "derive_hkdf_sha256"></a> + <strong>derive_hkdf_sha256 (ikm, nonce, usage, size)</strong> + </dt> + <dd> + Derive a new key using HKDF with SHA-256. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ikm</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + initial key material + </li> + <li><span class="parameter">nonce</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + nonce + </li> + <li><span class="parameter">usage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + e.g. <code>&quot;encryption&quot;</code> or <code>&quot;authentication&quot;</code> + </li> + <li><span class="parameter">size</span> + <span class="types"><span class="type">number</span></span> + how many bytes to return + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + key material</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err = utils.derive_hkdf_sha256(ikm, nonce, <span class="string">"encryption"</span>, <span class="number">32</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "derive_pbkdf2_hmac_sha256"></a> + <strong>derive_pbkdf2_hmac_sha256 (pass, salt, usage, size, iterations)</strong> + </dt> + <dd> + Derive a new key using PBKDF2 with SHA-256. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">pass</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + password + </li> + <li><span class="parameter">salt</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + salt + </li> + <li><span class="parameter">usage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + e.g. <code>&quot;encryption&quot;</code> or <code>&quot;authentication&quot;</code> + </li> + <li><span class="parameter">size</span> + <span class="types"><span class="type">number</span></span> + how many bytes to return + </li> + <li><span class="parameter">iterations</span> + <span class="types"><span class="type">number</span></span> + how many iterations to run, e.g. <code>10000</code> + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + key material</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> pass = <span class="string">"my-super-secret-password"</span> +<span class="keyword">local</span> salt = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err = utils.derive_pbkdf2_hmac_sha256(pass, salt, <span class="string">"encryption"</span>, <span class="number">32</span>, <span class="number">10000</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "derive_aes_gcm_256_key_and_iv"></a> + <strong>derive_aes_gcm_256_key_and_iv (ikm, nonce[, safety])</strong> + </dt> + <dd> + Derive a new AES-256 GCM-mode key and initialization vector. </p> + +<p> Safety can be: + <em> <code>nil</code> or <code>&quot;None&quot;</code>: key and iv will be derived using HKDF with SHA-256, except on FIPS-mode uses PBKDF2 with SHA-256 (single iteration) + </em> <code>Low</code>: key and iv will be derived using PBKDF2 with SHA-256 (1.000 iterations) + <em> <code>Medium</code>: key and iv will be derived using PBKDF2 with SHA-256 (10.000 iterations) + </em> <code>High</code>: key and iv will be derived using PBKDF2 with SHA-256 (100.000 iterations) + * <code>Very High</code>: key and iv will be derived using PBKDF2 with SHA-256 (1.000.000 iterations) + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ikm</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + initial key material + </li> + <li><span class="parameter">nonce</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + nonce + </li> + <li><span class="parameter">safety</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + safety of key generation + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + key</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + initialization vector</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce, <span class="string">"Medium"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "derive_hmac_sha256_key"></a> + <strong>derive_hmac_sha256_key (ikm, nonce)</strong> + </dt> + <dd> + Derive HMAC SHA-256 key for message authentication using HDKF with SHA-256, + except on FIPS-mode it uses PBKDF2 with SHA-256 (single iteration). + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">ikm</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + initial key material + </li> + <li><span class="parameter">nonce</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + nonce + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + key</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err = utils.derive_hmac_sha256_key(ikm, nonce)</pre> + </ul> + +</dd> + <dt> + <a name = "encrypt_aes_256_gcm"></a> + <strong>encrypt_aes_256_gcm (key, iv, plaintext, aad)</strong> + </dt> + <dd> + Encrypt plain text using AES-256 in GCM-mode. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + encryption key + </li> + <li><span class="parameter">iv</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + initialization vector + </li> + <li><span class="parameter">plaintext</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + plain text + </li> + <li><span class="parameter">aad</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + additional authenticated data + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + ciphertext</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + authentication tag</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce) +<span class="keyword">local</span> enc, err, tag = utils.encrypt_aes_256_gcm(key, iv, <span class="string">"hello"</span>, <span class="string">"john@doe.com"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "decrypt_aes_256_gcm"></a> + <strong>decrypt_aes_256_gcm (key, iv, plaintext, aad, tag)</strong> + </dt> + <dd> + Decrypt ciphertext using AES-256 in GCM-mode. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + encryption key + </li> + <li><span class="parameter">iv</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + initialization vector + </li> + <li><span class="parameter">plaintext</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + plain text + </li> + <li><span class="parameter">aad</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + additional authenticated data + </li> + <li><span class="parameter">tag</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + authentication tag + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + ciphertext</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce) +<span class="keyword">local</span> enc, err, tag = utils.encrypt_aes_256_gcm(key, iv, <span class="string">"hello"</span>, <span class="string">"john@doe.com"</span>) +<span class="keyword">local</span> out, err = utils.decrypt_aes_256_gcm(key, iv, ciphertext, <span class="string">"john@doe.com"</span>, tag)</pre> + </ul> + +</dd> + <dt> + <a name = "hmac_sha256"></a> + <strong>hmac_sha256 (key, value)</strong> + </dt> + <dd> + Calculate message authentication code with HMAC with SHA-256. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + <li><span class="parameter">value</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + value + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + message authentication code (32 bytes)</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> ikm = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> nonce = utils.rand_bytes(<span class="number">32</span>) +<span class="keyword">local</span> key, err = utils.derive_hmac_sha256_key(ikm, nonce) +<span class="keyword">local</span> mac, err = utils.hmac_sha256(key, <span class="string">"hello"</span>)</pre> + </ul> + +</dd> + <dt> + <a name = "load_storage"></a> + <strong>load_storage (storage[, configuration])</strong> + </dt> + <dd> + Loads session storage and creates a new instance using session configuration. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name of the storage to load + </li> + <li><span class="parameter">configuration</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + session configuration + (<em>optional</em>) + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a> or <span class="type">nil</span></span> + instance of session storage</li> + <li> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message</li> + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> postgres = <span class="global">require</span> <span class="string">"resty.session.utils"</span>.load_storage(<span class="string">"postgres"</span>, { + postgres = { + host = <span class="string">"127.0.0.1"</span>, + } +})</pre> + </ul> + +</dd> + <dt> + <a name = "errmsg"></a> + <strong>errmsg ([err], msg, ...)</strong> + </dt> + <dd> + Helper to format error messages. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">err</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + a possible error coming from underlying library + (<em>optional</em>) + </li> + <li><span class="parameter">msg</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a> or <span class="type">nil</span></span> + error message + </li> + <li><span class="parameter">...</span> + <span class="types"><span class="type">any</span></span> + arguments for formatting the msg + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + formatted error message + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> utils = <span class="global">require</span> <span class="string">"resty.session.utils"</span> +<span class="keyword">local</span> test = <span class="string">"aaaa"</span> +<span class="keyword">local</span> data, err = utils.deflate(test) +<span class="keyword">if</span> <span class="keyword">not</span> data <span class="keyword">then</span> + <span class="global">print</span>(utils.errmsg(err, <span class="string">"unable to deflate data '%s'"</span>, test) +<span class="keyword">end</span></pre> + </ul> + +</dd> + <dt> + <a name = "get_name"></a> + <strong>get_name (storage, name, key, subject)</strong> + </dt> + <dd> + Helper to create a delimited key. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">storage</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + a storage implementation + </li> + <li><span class="parameter">name</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + name + </li> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + key + </li> + <li><span class="parameter">subject</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + subject + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + formatted and delimited name + </ol> + + + + +</dd> + <dt> + <a name = "set_flag"></a> + <strong>set_flag (flags, flag)</strong> + </dt> + <dd> + Helper to turn on a flag. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">flags</span> + <span class="types"><span class="type">number</span></span> + flags on which the flag is applied + </li> + <li><span class="parameter">flag</span> + <span class="types"><span class="type">number</span></span> + flag that is applied + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + flags with the flag applied + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> flags = <span class="number">0x0000</span> +<span class="keyword">local</span> FLAG_DOG = <span class="number">0x001</span> +flags = utils.set_flag(flags, FLAG_DOG)</pre> + </ul> + +</dd> + <dt> + <a name = "unset_flag"></a> + <strong>unset_flag (flags, flag)</strong> + </dt> + <dd> + Helper to turn off a flag. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">flags</span> + <span class="types"><span class="type">number</span></span> + flags on which the flag is removed + </li> + <li><span class="parameter">flag</span> + <span class="types"><span class="type">number</span></span> + flag that is removed + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">number</span></span> + flags with the flag removed + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> options = <span class="number">0x0000</span> +<span class="keyword">local</span> FLAG_DOG = <span class="number">0x001</span> +flags = utils.set_flag(options, FLAG_DOG) +flags = utils.unset_flag(options, FLAG_DOG)</pre> + </ul> + +</dd> + <dt> + <a name = "has_flag"></a> + <strong>has_flag (flags, flag)</strong> + </dt> + <dd> + Helper to check if flag is enabled. + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">flags</span> + <span class="types"><span class="type">number</span></span> + flags on which the flag is checked + </li> + <li><span class="parameter">flag</span> + <span class="types"><span class="type">number</span></span> + flag that is checked + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><span class="type">boolean</span></span> + true if flag has is present, otherwise false + </ol> + + + + <h3>Usage:</h3> + <ul> + <pre class="example"><span class="keyword">local</span> flags = <span class="number">0x0000</span> +<span class="keyword">local</span> FLAG_DOG = <span class="number">0x001</span> +<span class="keyword">local</span> FLAG_BONE = <span class="number">0x010</span> +flags = utils.set_flag(flags, FLAG_DOG) +flags = utils.set_flag(flags, FLAG_BONE) +<span class="global">print</span>(utils.has_flag(flags, FLAG_BONE)</pre> + </ul> + +</dd> + <dt> + <a name = "meta_get_value"></a> + <strong>meta_get_value (key, exp)</strong> + </dt> + <dd> + Helper to get the value used to store metadata for a certain aud and sub + Empty exp means the session id has been invalidated + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">key</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + storage key + </li> + <li><span class="parameter">exp</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + expiration + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + the value to store in the metadata collection + </ol> + + + + +</dd> + <dt> + <a name = "meta_get_next"></a> + <strong>meta_get_next (val, index)</strong> + </dt> + <dd> + Function to extract the next key and exp from a serialized + metadata list, starting from index + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">val</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + list of key:exp; + </li> + <li><span class="parameter">index</span> + <span class="types"><span class="type">number</span></span> + start index + </li> + </ul> + + <h3>Returns:</h3> + <ol> + <li> + <span class="types"><span class="type">key</span></span> + string session id</li> + <li> + <span class="types"><span class="type">err</span></span> + string error</li> + <li> + <span class="types"><span class="type">exp</span></span> + number expiration</li> + <li> + <span class="types"><span class="type">index</span></span> + number|nil index of the cursor</li> + </ol> + + + + +</dd> + <dt> + <a name = "meta_get_latest"></a> + <strong>meta_get_latest (sessions)</strong> + </dt> + <dd> + Function to filter out the latest valid key:exp from a + serialized list, used to store session metadata + + + <h3>Parameters:</h3> + <ul> + <li><span class="parameter">sessions</span> + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.4">string</a></span> + list of key:exp; + </li> + </ul> + + <h3>Returns:</h3> + <ol> + + <span class="types"><a class="type" href="https://www.lua.org/manual/5.1/manual.html#5.5">table</a></span> + valid sessions and their exp + </ol> + + + + +</dd> +</dl> + + +</div> <!-- id="content" --> +</div> <!-- id="main" --> +<div id="about"> +<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i> +<i style="float:right;">Last updated 2023-06-05 17:05:22 </i> +</div> <!-- id="about" --> +</div> <!-- id="container" --> +</body> +</html> diff --git a/lib/resty/session.lua b/lib/resty/session.lua new file mode 100644 index 000000000..c9449e91b --- /dev/null +++ b/lib/resty/session.lua @@ -0,0 +1,2839 @@ +--- +-- Session library. +-- +-- Session library provides HTTP session management capabilities for OpenResty based +-- applications, libraries and proxies. +-- +-- @module resty.session + + +local require = require + + +local table_new = require "table.new" +local isempty = require "table.isempty" +local buffer = require "string.buffer" +local utils = require "resty.session.utils" + + +local clear_request_header = ngx.req.clear_header +local set_request_header = ngx.req.set_header +local setmetatable = setmetatable +local http_time = ngx.http_time +local tonumber = tonumber +local select = select +local assert = assert +local remove = table.remove +local header = ngx.header +local error = error +local floor = math.ceil +local time = ngx.time +local byte = string.byte +local type = type +local sub = string.sub +local fmt = string.format +local var = ngx.var +local log = ngx.log +local max = math.max +local min = math.min + + +local derive_aes_gcm_256_key_and_iv = utils.derive_aes_gcm_256_key_and_iv +local derive_hmac_sha256_key = utils.derive_hmac_sha256_key +local encrypt_aes_256_gcm = utils.encrypt_aes_256_gcm +local decrypt_aes_256_gcm = utils.decrypt_aes_256_gcm +local encode_base64url = utils.encode_base64url +local decode_base64url = utils.decode_base64url +local load_storage = utils.load_storage +local encode_json = utils.encode_json +local decode_json = utils.decode_json +local base64_size = utils.base64_size +local hmac_sha256 = utils.hmac_sha256 +local rand_bytes = utils.rand_bytes +local unset_flag = utils.unset_flag +local set_flag = utils.set_flag +local has_flag = utils.has_flag +local inflate = utils.inflate +local deflate = utils.deflate +local bunpack = utils.bunpack +local errmsg = utils.errmsg +local sha256 = utils.sha256 +local bpack = utils.bpack +local trim = utils.trim + + +local NOTICE = ngx.NOTICE +local WARN = ngx.WARN + + +local KEY_SIZE = 32 + + +--[ HEADER ----------------------------------------------------------------------------------------------------] || [ PAYLOAD --] +--[ Type || Flags || Session ID || Creation Time || Rolling Offset || Data Size || Tag || Idling Offset || Mac ] || [ Data ] +--[ 1B || 2B || 32B || 5B || 4B || 3B || 16B || 3B || 16B ] || [ *B ] + + +local COOKIE_TYPE_SIZE = 1 -- 1 +local FLAGS_SIZE = 2 -- 3 +local SID_SIZE = 32 -- 35 +local CREATION_TIME_SIZE = 5 -- 40 +local ROLLING_OFFSET_SIZE = 4 -- 44 +local DATA_SIZE = 3 -- 47 +local TAG_SIZE = 16 -- 63 +local IDLING_OFFSET_SIZE = 3 -- 66 +local MAC_SIZE = 16 -- 82 + + +local HEADER_TAG_SIZE = COOKIE_TYPE_SIZE + FLAGS_SIZE + SID_SIZE + CREATION_TIME_SIZE + ROLLING_OFFSET_SIZE + DATA_SIZE +local HEADER_TOUCH_SIZE = HEADER_TAG_SIZE + TAG_SIZE +local HEADER_MAC_SIZE = HEADER_TOUCH_SIZE + IDLING_OFFSET_SIZE +local HEADER_SIZE = HEADER_MAC_SIZE + MAC_SIZE +local HEADER_ENCODED_SIZE = base64_size(HEADER_SIZE) + + +local COOKIE_TYPE = bpack(COOKIE_TYPE_SIZE, 1) + + +local MAX_COOKIE_SIZE = 4096 +local MAX_COOKIES = 9 +local MAX_COOKIES_SIZE = MAX_COOKIES * MAX_COOKIE_SIZE -- 36864 bytes +local MAX_CREATION_TIME = 2 ^ (CREATION_TIME_SIZE * 8) - 1 -- ~34789 years +local MAX_ROLLING_OFFSET = 2 ^ (ROLLING_OFFSET_SIZE * 8) - 1 -- ~136 years +local MAX_IDLING_OFFSET = 2 ^ (IDLING_OFFSET_SIZE * 8) - 1 -- ~194 days +local MAX_DATA_SIZE = 2 ^ (DATA_SIZE * 8) - 1 -- 16777215 bytes +local MAX_TTL = 34560000 -- 400 days +-- see: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-11#section-4.1.2.1 + + +local FLAGS_NONE = 0x0000 +local FLAG_STORAGE = 0x0001 +local FLAG_FORGET = 0x0002 +local FLAG_DEFLATE = 0x0010 + + +local DEFAULT_AUDIENCE = "default" +local DEFAULT_SUBJECT +local DEFAULT_ENFORCE_SAME_SUBJECT = false +local DEFAULT_META = {} +local DEFAULT_IKM +local DEFAULT_IKM_FALLBACKS +local DEFAULT_HASH_STORAGE_KEY = false +local DEFAULT_HASH_SUBJECT = false +local DEFAULT_STORE_METADATA = false +local DEFAULT_TOUCH_THRESHOLD = 60 -- 1 minute +local DEFAULT_COMPRESSION_THRESHOLD = 1024 -- 1 kB +local DEFAULT_REQUEST_HEADERS +local DEFAULT_RESPONSE_HEADERS + + +local DEFAULT_COOKIE_NAME = "session" +local DEFAULT_COOKIE_PATH = "/" +local DEFAULT_COOKIE_SAME_SITE = "Lax" +local DEFAULT_COOKIE_SAME_PARTY +local DEFAULT_COOKIE_PRIORITY +local DEFAULT_COOKIE_PARTITIONED +local DEFAULT_COOKIE_HTTP_ONLY = true +local DEFAULT_COOKIE_PREFIX +local DEFAULT_COOKIE_DOMAIN +local DEFAULT_COOKIE_SECURE + + +local DEFAULT_REMEMBER_COOKIE_NAME = "remember" +local DEFAULT_REMEMBER_SAFETY = "Medium" +local DEFAULT_REMEMBER_META = false +local DEFAULT_REMEMBER = false + + +local DEFAULT_STALE_TTL = 10 -- 10 seconds +local DEFAULT_IDLING_TIMEOUT = 900 -- 15 minutes +local DEFAULT_ROLLING_TIMEOUT = 3600 -- 1 hour +local DEFAULT_ABSOLUTE_TIMEOUT = 86400 -- 1 day +local DEFAULT_REMEMBER_ROLLING_TIMEOUT = 604800 -- 1 week +local DEFAULT_REMEMBER_ABSOLUTE_TIMEOUT = 2592000 -- 30 days + + +local DEFAULT_STORAGE + + +local STATE_NEW = "new" +local STATE_OPEN = "open" +local STATE_CLOSED = "closed" + + +local AT_BYTE = byte("@") +local EQUALS_BYTE = byte("=") +local SEMICOLON_BYTE = byte(";") + + +local COOKIE_EXPIRE_FLAGS = "; Expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0" + + +local HEADER_BUFFER = buffer.new(HEADER_SIZE) +local FLAGS_BUFFER = buffer.new(128) +local DATA_BUFFER = buffer.new(MAX_COOKIES_SIZE) +local HIDE_BUFFER = buffer.new(256) + + +local DATA = table_new(2, 0) + + +local HEADERS = { + id = "Session-Id", + audience = "Session-Audience", + subject = "Session-Subject", + timeout = "Session-Timeout", + idling_timeout = "Session-Idling-Timeout", + ["idling-timeout"] = "Session-Idling-Timeout", + rolling_timeout = "Session-Rolling-Timeout", + ["rolling-timeout"] = "Session-Rolling-Timeout", + absolute_timeout = "Session-Absolute-Timeout", + ["absolute-timeout"] = "Session-Absolute-Timeout", +} + + +local function set_response_header(name, value) + header[name] = value +end + + +local function sha256_storage_key(sid) + local key, err = sha256(sid) + if not key then + return nil, errmsg(err, "unable to sha256 hash session id") + end + + if SID_SIZE ~= 32 then + key = sub(key, 1, SID_SIZE) + end + + key, err = encode_base64url(key) + if not key then + return nil, errmsg(err, "unable to base64url encode session id") + end + + return key +end + + +local function sha256_subject(subject) + local hashed_subject, err = sha256(subject) + if not hashed_subject then + return nil, errmsg(err, "unable to sha256 hash subject") + end + + hashed_subject, err = encode_base64url(sub(hashed_subject, 1, 16)) + if not hashed_subject then + return nil, errmsg(err, "unable to base64url encode subject") + end + + return hashed_subject +end + + +local function calculate_mac(ikm, nonce, msg) + local mac_key, err = derive_hmac_sha256_key(ikm, nonce) + if not mac_key then + return nil, errmsg(err, "unable to derive session message authentication key") + end + + local mac, err = hmac_sha256(mac_key, msg) + if not mac then + return nil, errmsg(err, "unable to calculate session message authentication code") + end + + if MAC_SIZE ~= 32 then + return sub(mac, 1, MAC_SIZE) + end + + return mac +end + + +local function calculate_cookie_chunks(cookie_name_size, data_size) + local space_needed = cookie_name_size + 1 + HEADER_ENCODED_SIZE + data_size + if space_needed > MAX_COOKIES_SIZE then + return nil, "cookie size limit exceeded" + end + + if space_needed <= MAX_COOKIE_SIZE then + return 1 + end + + for i = 2, MAX_COOKIES do + space_needed = space_needed + cookie_name_size + 2 + if space_needed > MAX_COOKIES_SIZE then + return nil, "cookie size limit exceeded" + elseif space_needed <= (MAX_COOKIE_SIZE * i) then + return i + end + end + + return nil, "cookie size limit exceeded" +end + + +local function merge_cookies(cookies, cookie_name_size, cookie_name, cookie_data) + if not cookies then + return cookie_data + end + + if type(cookies) == "string" then + if byte(cookies, cookie_name_size + 1) == EQUALS_BYTE and + sub(cookies, 1, cookie_name_size) == cookie_name + then + return cookie_data + end + + return { cookies, cookie_data } + end + + if type(cookies) ~= "table" then + return nil, "unable to merge session cookies with response cookies" + end + + local count = #cookies + for i = 1, count do + if byte(cookies[i], cookie_name_size + 1) == EQUALS_BYTE and + sub(cookies[i], 1, cookie_name_size) == cookie_name + then + cookies[i] = cookie_data + return cookies + end + + if i == count then + cookies[i+1] = cookie_data + return cookies + end + end +end + + +local function get_store_metadata(self) + if not self.store_metadata then + return + end + + local data = self.data + local count = #data + if count == 1 then + local audience = data[1][2] + local subject = data[1][3] + if audience and subject then + audience = encode_base64url(audience) + subject = self.hash_subject(subject) + return { + audiences = { audience }, + subjects = { subject }, + } + end + + return + end + + local audiences + local subjects + local index = 0 + for i = 1, count do + local audience = data[i][2] + local subject = data[i][3] + if audience and subject then + audience = encode_base64url(audience) + self.hash_subject(subject) + if not audiences then + audiences = table_new(count, 0) + subjects = table_new(count, 0) + end + + index = index + 1 + audiences[index] = audience + subjects[index] = subject + end + end + + if not audiences then + return + end + + return { + audiences = audiences, + subjects = subjects, + } +end + + +local function get_property(self, name) + if name == "id" then + local sid = self.meta.sid + if not sid then + return + end + + return encode_base64url(sid) + + elseif name == "nonce" then + return self.meta.sid + + elseif name == "audience" then + return self.data[self.data_index][2] + + elseif name == "subject" then + return self.data[self.data_index][3] + + elseif name == "timeout" then + local timeout + local meta = self.meta + + if self.idling_timeout > 0 then + timeout = self.idling_timeout - (meta.timestamp - meta.creation_time - meta.rolling_offset - meta.idling_offset) + end + + if self.rolling_timeout > 0 then + local t = self.rolling_timeout - (meta.timestamp - meta.creation_time - meta.rolling_offset) + timeout = timeout and min(t, timeout) or t + end + + if self.absolute_timeout > 0 then + local t = self.absolute_timeout - (meta.timestamp - meta.creation_time) + timeout = timeout and min(t, timeout) or t + end + + return timeout + + elseif name == "idling-timeout" or name == "idling_timeout" then + local idling_timeout = self.idling_timeout + if idling_timeout == 0 then + return + end + + local meta = self.meta + return idling_timeout - (meta.timestamp - meta.creation_time - meta.rolling_offset - meta.idling_offset) + + elseif name == "rolling-timeout" or name == "rolling_timeout" then + local rolling_timeout = self.rolling_timeout + if rolling_timeout == 0 then + return + end + + local meta = self.meta + return rolling_timeout - (meta.timestamp - meta.creation_time - meta.rolling_offset) + + elseif name == "absolute-timeout" or name == "absolute_timeout" then + local absolute_timeout = self.absolute_timeout + if absolute_timeout == 0 then + return + end + + local meta = self.meta + return absolute_timeout - (meta.timestamp - meta.creation_time) + + else + return self.meta[name] + end +end + + +local function open(self, remember, meta_only) + local storage = self.storage + local current_time = time() + local cookie_name + if remember then + cookie_name = self.remember_cookie_name + else + cookie_name = self.cookie_name + end + + local cookie = var["cookie_" .. cookie_name] + if not cookie then + return nil, "missing session cookie" + end + + local header_decoded do + header_decoded = sub(cookie, 1, HEADER_ENCODED_SIZE) + if #header_decoded ~= HEADER_ENCODED_SIZE then + return nil, "invalid session header" + end + local err + header_decoded, err = decode_base64url(header_decoded) + if not header_decoded then + return nil, errmsg(err, "unable to base64url decode session header") + end + end + + HEADER_BUFFER:set(header_decoded) + + local cookie_type do + cookie_type = HEADER_BUFFER:get(COOKIE_TYPE_SIZE) + if #cookie_type ~= COOKIE_TYPE_SIZE then + return nil, "invalid session cookie type" + end + if cookie_type ~= COOKIE_TYPE then + return nil, "invalid session cookie type" + end + end + + local flags do + flags = HEADER_BUFFER:get(FLAGS_SIZE) + if #flags ~= FLAGS_SIZE then + return nil, "invalid session flags" + end + + flags = bunpack(FLAGS_SIZE, flags) + + if storage then + if not has_flag(flags, FLAG_STORAGE) then + return nil, "invalid session flags" + end + elseif has_flag(flags, FLAG_STORAGE) then + return nil, "invalid session flags" + end + end + + local sid do + sid = HEADER_BUFFER:get(SID_SIZE) + if #sid ~= SID_SIZE then + return nil, "invalid session id" + end + end + + local creation_time do + creation_time = HEADER_BUFFER:get(CREATION_TIME_SIZE) + if #creation_time ~= CREATION_TIME_SIZE then + return nil, "invalid session creation time" + end + + creation_time = bunpack(CREATION_TIME_SIZE, creation_time) + if not creation_time or creation_time < 0 or creation_time > MAX_CREATION_TIME then + return nil, "invalid session creation time" + end + + local absolute_elapsed = current_time - creation_time + if absolute_elapsed > MAX_ROLLING_OFFSET then + return nil, "session lifetime exceeded" + end + + if remember then + local remember_absolute_timeout = self.remember_absolute_timeout + if remember_absolute_timeout ~= 0 then + if absolute_elapsed > remember_absolute_timeout then + return nil, "session remember absolute timeout exceeded" + end + end + + else + local absolute_timeout = self.absolute_timeout + if absolute_timeout ~= 0 then + if absolute_elapsed > absolute_timeout then + return nil, "session absolute timeout exceeded" + end + end + end + end + + local rolling_offset do + rolling_offset = HEADER_BUFFER:get(ROLLING_OFFSET_SIZE) + if #rolling_offset ~= ROLLING_OFFSET_SIZE then + return nil, "invalid session rolling offset" + end + + rolling_offset = bunpack(ROLLING_OFFSET_SIZE, rolling_offset) + if not rolling_offset or rolling_offset < 0 or rolling_offset > MAX_ROLLING_OFFSET then + return nil, "invalid session rolling offset" + end + + local rolling_elapsed = current_time - creation_time - rolling_offset + + if remember then + local remember_rolling_timeout = self.remember_rolling_timeout + if remember_rolling_timeout ~= 0 then + if rolling_elapsed > remember_rolling_timeout then + return nil, "session remember rolling timeout exceeded" + end + end + + else + local rolling_timeout = self.rolling_timeout + if rolling_timeout ~= 0 then + if rolling_elapsed > rolling_timeout then + return nil, "session rolling timeout exceeded" + end + end + end + end + + local data_size do + data_size = HEADER_BUFFER:get(DATA_SIZE) + if #data_size ~= DATA_SIZE then + return nil, "invalid session data size" + end + + data_size = bunpack(DATA_SIZE, data_size) + if not data_size or data_size < 0 or data_size > MAX_DATA_SIZE then + return nil, "invalid session data size" + end + end + + local tag do + tag = HEADER_BUFFER:get(TAG_SIZE) + if #tag ~= TAG_SIZE then + return nil, "invalid session tag" + end + end + + local idling_offset do + idling_offset = HEADER_BUFFER:get(IDLING_OFFSET_SIZE) + if #idling_offset ~= IDLING_OFFSET_SIZE then + return nil, "invalid session idling offset" + end + + idling_offset = bunpack(IDLING_OFFSET_SIZE, idling_offset) + if not idling_offset or idling_offset < 0 or idling_offset > MAX_IDLING_OFFSET then + return nil, "invalid session idling offset" + end + + if remember then + if idling_offset ~= 0 then + return nil, "invalid session idling offset" + end + + else + local idling_timeout = self.idling_timeout + if idling_timeout ~= 0 then + local idling_elapsed = current_time - creation_time - rolling_offset - idling_offset + if idling_elapsed > idling_timeout then + return nil, "session idling timeout exceeded" + end + end + end + end + + local ikm do + ikm = self.ikm + local mac = HEADER_BUFFER:get(MAC_SIZE) + if #mac ~= MAC_SIZE then + return nil, "invalid session message authentication code" + end + + local msg = sub(header_decoded, 1, HEADER_MAC_SIZE) + local expected_mac, err = calculate_mac(ikm, sid, msg) + if mac ~= expected_mac then + local fallback_keys = self.ikm_fallbacks + if fallback_keys then + local count = #fallback_keys + if count > 0 then + for i = 1, count do + ikm = fallback_keys[i] + expected_mac, err = calculate_mac(ikm, sid, msg) + if mac == expected_mac then + break + end + + if i == count then + return nil, errmsg(err, "invalid session message authentication code") + end + end + + else + return nil, errmsg(err, "invalid session message authentication code") + end + + else + return nil, errmsg(err, "invalid session message authentication code") + end + end + end + + local data_index = self.data_index + local audience = self.data[data_index][2] + local initial_chunk, ciphertext, ciphertext_encoded, info_data do + if storage then + local key, err = self.hash_storage_key(sid) + if not key then + return nil, err + end + + local data, err = storage:get(cookie_name, key, current_time) + if not data then + return nil, errmsg(err, "unable to load session") + end + + data, err = decode_json(data) + if not data then + return nil, errmsg(err, "unable to json decode session") + end + + ciphertext = data[1] + ciphertext_encoded = ciphertext + info_data = data[2] + if info_data then + info_data, err = decode_base64url(info_data) + if not info_data then + return nil, errmsg(err, "unable to base64url decode session info") + end + + info_data, err = decode_json(info_data) + if not info_data then + return nil, errmsg(err, "unable to json decode session info") + end + + if not info_data[audience] then + info_data[audience] = self.info.data and self.info.data[audience] or nil + end + end + + else + local cookie_chunks, err = calculate_cookie_chunks(#cookie_name, data_size) + if not cookie_chunks then + return nil, err + end + + if cookie_chunks == 1 then + initial_chunk = sub(cookie, -data_size) + ciphertext = initial_chunk + + else + initial_chunk = sub(cookie, HEADER_ENCODED_SIZE + 1) + DATA_BUFFER:reset():put(initial_chunk) + for i = 2, cookie_chunks do + local chunk = var["cookie_" .. cookie_name .. i] + if not chunk then + return nil, errmsg(err, "missing session cookie chunk") + end + + DATA_BUFFER:put(chunk) + end + + ciphertext = DATA_BUFFER:get() + end + end + + if #ciphertext ~= data_size then + return nil, "invalid session payload" + end + + local err + ciphertext, err = decode_base64url(ciphertext) + if not ciphertext then + return nil, errmsg(err, "unable to base64url decode session data") + end + end + + if remember then + self.remember_meta = { + timestamp = current_time, + flags = flags, + sid = sid, + creation_time = creation_time, + rolling_offset = rolling_offset, + data_size = data_size, + idling_offset = idling_offset, + ikm = ikm, + header = header_decoded, + initial_chunk = initial_chunk, + ciphertext = ciphertext_encoded, + } + + else + self.meta = { + timestamp = current_time, + flags = flags, + sid = sid, + creation_time = creation_time, + rolling_offset = rolling_offset, + data_size = data_size, + idling_offset = idling_offset, + ikm = ikm, + header = header_decoded, + initial_chunk = initial_chunk, + ciphertext = ciphertext_encoded, + } + end + + if meta_only then + return true + end + + local aes_key, err, iv + if remember then + aes_key, err, iv = derive_aes_gcm_256_key_and_iv(ikm, sid, self.remember_safety) + else + aes_key, err, iv = derive_aes_gcm_256_key_and_iv(ikm, sid) + end + + if not aes_key then + return nil, errmsg(err, "unable to derive session decryption key") + end + + local aad = sub(header_decoded, 1, HEADER_TAG_SIZE) + local plaintext, err = decrypt_aes_256_gcm(aes_key, iv, ciphertext, aad, tag) + if not plaintext then + return nil, errmsg(err, "unable to decrypt session data") + end + + local data do + if has_flag(flags, FLAG_DEFLATE) then + plaintext, err = inflate(plaintext) + if not plaintext then + return nil, errmsg(err, "unable to inflate session data") + end + end + + data, err = decode_json(plaintext) + if not data then + return nil, errmsg(err, "unable to json decode session data") + end + end + + if storage then + self.info.data = info_data + end + + local audience_index + local count = #data + for i = 1, count do + if data[i][2] == audience then + audience_index = i + break + end + end + + if not audience_index then + data[count + 1] = self.data[data_index] + self.state = STATE_NEW + self.data = data + self.data_index = count + 1 + return nil, "missing session audience", true + end + + self.state = STATE_OPEN + self.data = data + self.data_index = audience_index + + return true +end + + +local function save(self, state, remember) + local cookie_name + local meta + if remember then + cookie_name = self.remember_cookie_name + meta = self.remember_meta or {} + else + cookie_name = self.cookie_name + meta = self.meta + end + + local cookie_name_size = #cookie_name + local storage = self.storage + local flags = self.flags + + if storage then + flags = set_flag(flags, FLAG_STORAGE) + else + flags = unset_flag(flags, FLAG_STORAGE) + end + + local sid, err = rand_bytes(SID_SIZE) + if not sid then + return nil, errmsg(err, "unable to generate session id") + end + + local current_time = time() + local rolling_offset + + local creation_time = meta.creation_time + if creation_time then + rolling_offset = current_time - creation_time + if rolling_offset > MAX_ROLLING_OFFSET then + return nil, "session maximum rolling offset exceeded" + end + + else + creation_time = current_time + rolling_offset = 0 + end + + if creation_time > MAX_CREATION_TIME then + -- this should only happen at around year 36759 (most likely a clock problem) + return nil, "session maximum creation time exceeded" + end + + do + local meta_flags = meta.flags + if meta_flags and has_flag(meta_flags, FLAG_FORGET) then + flags = set_flag(flags, FLAG_FORGET) + end + end + + local data, data_size, cookie_chunks do + data = self.data + if self.enforce_same_subject then + local count = #data + if count > 1 then + local subject = data[self.data_index][3] + for i = count, 1, -1 do + if data[i][3] ~= subject then + remove(data, i) + end + end + end + end + + data, err = encode_json(data) + if not data then + return nil, errmsg(err, "unable to json encode session data") + end + + data_size = #data + + local compression_threshold = self.compression_threshold + if compression_threshold ~= 0 and data_size > compression_threshold then + local deflated_data, err = deflate(data) + if not deflated_data then + log(NOTICE, "[session] unable to deflate session data (", err , ")") + + else + if deflated_data then + local deflated_size = #deflated_data + if deflated_size < data_size then + flags = set_flag(flags, FLAG_DEFLATE) + data = deflated_data + data_size = deflated_size + end + end + end + end + + data_size = base64_size(data_size) + + if storage then + if data_size > MAX_DATA_SIZE then + return nil, "session maximum data size exceeded" + end + + cookie_chunks = 1 + else + cookie_chunks, err = calculate_cookie_chunks(cookie_name_size, data_size) + if not cookie_chunks then + return nil, err + end + end + end + + local idling_offset = 0 + + local packed_flags = bpack(FLAGS_SIZE, flags) + local packed_data_size = bpack(DATA_SIZE, data_size) + local packed_creation_time = bpack(CREATION_TIME_SIZE, creation_time) + local packed_rolling_offset = bpack(ROLLING_OFFSET_SIZE, rolling_offset) + local packed_idling_offset = bpack(IDLING_OFFSET_SIZE, idling_offset) + + HEADER_BUFFER:reset() + HEADER_BUFFER:put(COOKIE_TYPE, packed_flags, sid, packed_creation_time, packed_rolling_offset, packed_data_size) + + local ikm = self.ikm + local aes_key, iv + if remember then + aes_key, err, iv = derive_aes_gcm_256_key_and_iv(ikm, sid, self.remember_safety) + else + aes_key, err, iv = derive_aes_gcm_256_key_and_iv(ikm, sid) + end + + if not aes_key then + return nil, errmsg(err, "unable to derive session encryption key") + end + + local ciphertext, err, tag = encrypt_aes_256_gcm(aes_key, iv, data, HEADER_BUFFER:tostring()) + if not ciphertext then + return nil, errmsg(err, "unable to encrypt session data") + end + + HEADER_BUFFER:put(tag, packed_idling_offset) + + local mac, err = calculate_mac(ikm, sid, HEADER_BUFFER:tostring()) + if not mac then + return nil, err + end + + local header_decoded = HEADER_BUFFER:put(mac):get() + local header_encoded, err = encode_base64url(header_decoded) + if not header_encoded then + return nil, errmsg(err, "unable to base64url encode session header") + end + + local payload, err = encode_base64url(ciphertext) + if not payload then + return nil, errmsg(err, "unable to base64url encode session data") + end + + local cookies = header["Set-Cookie"] + local cookie_flags = self.cookie_flags + + local initial_chunk + local ciphertext_encoded + + local remember_flags + if remember then + local max_age = self.remember_rolling_timeout + if max_age == 0 or max_age > MAX_TTL then + max_age = MAX_TTL + end + + local expires = http_time(creation_time + max_age) + remember_flags = fmt("; Expires=%s; Max-Age=%d", expires, max_age) + end + + if cookie_chunks == 1 then + local cookie_data + if storage then + ciphertext_encoded = payload + if remember then + cookie_data = fmt("%s=%s%s%s", cookie_name, header_encoded, cookie_flags, remember_flags) + else + cookie_data = fmt("%s=%s%s", cookie_name, header_encoded, cookie_flags) + end + + else + initial_chunk = payload + if remember then + cookie_data = fmt("%s=%s%s%s%s", cookie_name, header_encoded, payload, cookie_flags, remember_flags) + else + cookie_data = fmt("%s=%s%s%s", cookie_name, header_encoded, payload, cookie_flags) + end + end + + cookies, err = merge_cookies(cookies, cookie_name_size, cookie_name, cookie_data) + if not cookies then + return nil, err + end + + else + DATA_BUFFER:set(payload) + + initial_chunk = DATA_BUFFER:get(MAX_COOKIE_SIZE - HEADER_ENCODED_SIZE - cookie_name_size - 1) + + local cookie_data + if remember then + cookie_data = fmt("%s=%s%s%s%s", cookie_name, header_encoded, initial_chunk, cookie_flags, remember_flags) + else + cookie_data = fmt("%s=%s%s%s", cookie_name, header_encoded, initial_chunk, cookie_flags) + end + + cookies, err = merge_cookies(cookies, cookie_name_size, cookie_name, cookie_data) + if not cookies then + return nil, err + end + + for i = 2, cookie_chunks do + local name = fmt("%s%d", cookie_name, i) + cookie_data = DATA_BUFFER:get(MAX_COOKIE_SIZE - cookie_name_size - 2) + if remember then + cookie_data = fmt("%s=%s%s%s", name, cookie_data, cookie_flags, remember_flags) + else + cookie_data = fmt("%s=%s%s", name, cookie_data, cookie_flags) + end + cookies, err = merge_cookies(cookies, cookie_name_size + 1, name, cookie_data) + if not cookies then + return nil, err + end + end + end + + if storage then + local key, err = self.hash_storage_key(sid) + if not key then + return nil, err + end + + DATA[1] = payload + + local info_data = self.info.data + if info_data then + info_data, err = encode_json(info_data) + if not info_data then + return nil, errmsg(err, "unable to json encode session info") + end + + info_data, err = encode_base64url(info_data) + if not info_data then + return nil, errmsg(err, "unable to base64url encode session info") + end + + DATA[2] = info_data + + else + DATA[2] = nil + end + + data, err = encode_json(DATA) + if not data then + return nil, errmsg(err, "unable to json encode session data") + end + + local old_sid = meta.sid + local old_key + if old_sid then + old_key, err = self.hash_storage_key(old_sid) + if not old_key then + log(WARN, "[session] ", err) + end + end + + local ttl = remember and self.remember_rolling_timeout or self.rolling_timeout + if ttl == 0 or ttl > MAX_TTL then + ttl = MAX_TTL + end + + local store_metadata = get_store_metadata(self) + + local ok, err = storage:set(cookie_name, key, data, ttl, current_time, old_key, self.stale_ttl, store_metadata, remember) + if not ok then + return nil, errmsg(err, "unable to store session data") + end + + else + local old_data_size = meta.data_size + if old_data_size then + local old_cookie_chunks = calculate_cookie_chunks(cookie_name_size, old_data_size) + if old_cookie_chunks and old_cookie_chunks > cookie_chunks then + for i = cookie_chunks + 1, old_cookie_chunks do + local name = fmt("%s%d", cookie_name, i) + local cookie_data = fmt("%s=%s%s", name, cookie_flags, COOKIE_EXPIRE_FLAGS) + cookies, err = merge_cookies(cookies, cookie_name_size + 1, name, cookie_data) + if not cookies then + return nil, err + end + end + end + end + end + + header["Set-Cookie"] = cookies + + if remember then + self.remember_meta = { + timestamp = current_time, + flags = flags, + sid = sid, + creation_time = creation_time, + rolling_offset = rolling_offset, + data_size = data_size, + idling_offset = idling_offset, + ikm = ikm, + header = header_decoded, + initial_chunk = initial_chunk, + ciphertext = ciphertext_encoded, + } + + else + self.state = state or STATE_OPEN + self.meta = { + timestamp = current_time, + flags = flags, + sid = sid, + creation_time = creation_time, + rolling_offset = rolling_offset, + data_size = data_size, + idling_offset = idling_offset, + ikm = ikm, + header = header_decoded, + initial_chunk = initial_chunk, + ciphertext = ciphertext_encoded, + } + end + + return true +end + + +local function save_info(self, data, remember) + local cookie_name + local meta + if remember then + cookie_name = self.remember_cookie_name + meta = self.remember_meta or {} + else + cookie_name = self.cookie_name + meta = self.meta + end + + local key, err = self.hash_storage_key(meta.sid) + if not key then + return nil, err + end + + DATA[1] = meta.ciphertext + DATA[2] = data + + data, err = encode_json(DATA) + if not data then + return nil, errmsg(err, "unable to json encode session data") + end + + local current_time = time() + + local ttl = self.rolling_timeout + if ttl == 0 or ttl > MAX_TTL then + ttl = MAX_TTL + end + ttl = max(ttl - (current_time - meta.creation_time - meta.rolling_offset), 1) + local ok, err = self.storage:set(cookie_name, key, data, ttl, current_time) + if not ok then + return nil, errmsg(err, "unable to store session info") + end +end + + +local function destroy(self, remember) + local cookie_name + local meta + if remember then + cookie_name = self.remember_cookie_name + meta = self.remember_meta or {} + else + cookie_name = self.cookie_name + meta = self.meta + end + + local cookie_name_size = #cookie_name + local storage = self.storage + + local cookie_chunks = 1 + local data_size = meta.data_size + if not storage and data_size then + local err + cookie_chunks, err = calculate_cookie_chunks(cookie_name_size, data_size) + if not cookie_chunks then + return nil, err + end + end + + local cookie_flags = self.cookie_flags + local cookie_data = fmt("%s=%s%s", cookie_name, cookie_flags, COOKIE_EXPIRE_FLAGS) + local cookies, err = merge_cookies(header["Set-Cookie"], cookie_name_size, cookie_name, cookie_data) + if not cookies then + return nil, err + end + + if cookie_chunks > 1 then + for i = 2, cookie_chunks do + local name = fmt("%s%d", cookie_name, i) + cookie_data = fmt("%s=%s%s", name, cookie_flags, COOKIE_EXPIRE_FLAGS) + cookies, err = merge_cookies(cookies, cookie_name_size + 1, name, cookie_data) + if not cookies then + return nil, err + end + end + end + + if storage then + local sid = meta.sid + if sid then + local key, err = self.hash_storage_key(sid) + if not key then + return nil, err + end + + local ok, err = storage:delete(cookie_name, key, meta.timestamp, get_store_metadata(self)) + if not ok then + return nil, errmsg(err, "unable to destroy session") + end + end + end + + header["Set-Cookie"] = cookies + + self.state = STATE_CLOSED + + return true +end + + +local function clear_request_cookie(self, remember) + local cookies = var.http_cookie + if not cookies or cookies == "" then + return + end + + local cookie_name + if remember then + cookie_name = self.remember_cookie_name + else + cookie_name = self.cookie_name + end + + local cookie_name_size = #cookie_name + + local cookie_chunks + if self.storage then + cookie_chunks = 1 + else + local data_size = remember and + self.remember_meta and + self.remember_meta.data_size or + self.meta.data_size + cookie_chunks = calculate_cookie_chunks(cookie_name_size, data_size) or 1 + end + + HIDE_BUFFER:reset() + + local size = #cookies + local name + local skip = false + local start = 1 + for i = 1, size do + local b = byte(cookies, i) + if name then + if b == SEMICOLON_BYTE or i == size then + if not skip then + local value + if b == SEMICOLON_BYTE then + value = trim(sub(cookies, start, i - 1)) + else + value = trim(sub(cookies, start)) + end + + if value ~= "" then + HIDE_BUFFER:put(value) + end + + if i ~= size then + HIDE_BUFFER:put("; ") + end + end + + if i == size then + break + end + + name = nil + start = i + 1 + skip = false + end + + else + if b == EQUALS_BYTE or b == SEMICOLON_BYTE then + name = sub(cookies, start, i - 1) + elseif i == size then + name = sub(cookies, start, i) + end + + if name then + name = trim(name) + if b == SEMICOLON_BYTE or i == size then + if name ~= "" then + HIDE_BUFFER:put(name) + if i ~= size then + HIDE_BUFFER:put(";") + end + + elseif i == size then + break + end + + name = nil + + else + if name == cookie_name then + skip = true + + elseif cookie_chunks > 1 then + local chunk_number = tonumber(sub(name, -1), 10) + if chunk_number and chunk_number > 1 and chunk_number <= cookie_chunks + and sub(name, 1, -2) == cookie_name + then + skip = true + end + end + + if not skip then + if name ~= "" then + HIDE_BUFFER:put(name) + end + + if b == EQUALS_BYTE then + HIDE_BUFFER:put("=") + end + end + end + + start = i + 1 + end + end + end + + if #HIDE_BUFFER == 0 then + clear_request_header("Cookie") + else + set_request_header("Cookie", HIDE_BUFFER:get()) + end + + return true +end + + + +local function get_remember(self) + local flags = self.meta.flags + if flags and has_flag(flags, FLAG_FORGET) then + return false + end + + if has_flag(self.flags, FLAG_FORGET) then + return false + end + + return self.remember +end + + +local function set_property_header(self, property_header, set_header) + local name = HEADERS[property_header] + if not name then + return + end + + local value = get_property(self, property_header) + if not value then + return + end + + set_header(name, value) +end + + +local function set_property_headers(self, headers, set_header) + if not headers then + return + end + + local count = #headers + if count == 0 then + return + end + + for i = 1, count do + set_property_header(self, headers[i], set_header) + end +end + + +local function set_property_headers_vararg(self, set_header, count, ...) + if count == 1 then + local header_or_headers = select(1, ...) + if type(header_or_headers) == "table" then + return set_property_headers(self, header_or_headers, set_header) + end + + return set_property_header(self, header_or_headers, set_header) + end + + for i = 1, count do + local property_header = select(i, ...) + set_property_header(self, property_header, set_header) + end +end + + +--- +-- Session +-- @section instance + + +local fake_info_mt = {} + + +function fake_info_mt:set(key, value) + local session = self.session + assert(session.state ~= STATE_CLOSED, "unable to set session info on closed session") + session.data[session.data_index][1]["@" .. key] = value +end + + +function fake_info_mt:get(key) + local session = self.session + assert(session.state ~= STATE_CLOSED, "unable to get session info on closed session") + return session.data[session.data_index][1]["@" .. key] +end + + +function fake_info_mt:save() + local session = self.session + assert(session.state == STATE_OPEN, "unable to save session info on nonexistent or closed session") + return session:save() +end + + +fake_info_mt.__index = fake_info_mt + + +local fake_info = {} + + +function fake_info.new(session) + return setmetatable({ + session = session, + data = false, + }, fake_info_mt) +end + + +local info_mt = {} + + +info_mt.__index = info_mt + + +--- +-- Set a value in session information store. +-- +-- @function instance.info:set +-- @tparam string key key +-- @param value value +function info_mt:set(key, value) + local session = self.session + assert(session.state ~= STATE_CLOSED, "unable to set session info on closed session") + local audience = session.data[session.data_index][2] + local data = self.data + if data then + if data[audience] then + data[audience][key] = value + + else + data[audience] = { + [key] = value, + } + end + + else + self.data = { + [audience] = { + [key] = value, + }, + } + end +end + + +--- +-- Get a value from session information store. +-- +-- @function instance.info:get +-- @tparam string key key +-- @return value +function info_mt:get(key) + local session = self.session + assert(session.state ~= STATE_CLOSED, "unable to get session info on closed session") + local data = self.data + if not data then + return + end + + local audience = session.data[session.data_index][2] + data = self.data[audience] + if not data then + return + end + + return data[key] +end + + +--- +-- Save information. +-- +-- Only updates backend storage. Does not send a new cookie. +-- +-- @function instance.info:save +-- @treturn true|nil ok +-- @treturn string error message +function info_mt:save() + local session = self.session + assert(session.state == STATE_OPEN, "unable to save session info on nonexistent or closed session") + local data = self.data + if not data then + return true + end + + local err + data, err = encode_json(data) + if not data then + return nil, errmsg(err, "unable to json encode session info") + end + + data, err = encode_base64url(data) + if not data then + return nil, errmsg(err, "unable to base64url encode session info") + end + + local ok, err = save_info(session, data) + if not ok then + return nil, err + end + + if get_remember(session) then + if not session.remember_meta then + local remembered = open(self, true, true) + if not remembered then + return save(session, nil, true) + end + end + + return save_info(session, data, true) + end + + return true +end + + +local info = {} + + +function info.new(session) + return setmetatable({ + session = session, + data = false, + }, info_mt) +end + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Set session data. +-- +-- @function instance:set_data +-- @tparam table data data +-- +-- @usage +-- local session, err, exists = require "resty.session".open() +-- if not exists then +-- session:set_data({ +-- cart = {}, +-- }) +-- session:save() +-- end +function metatable:set_data(data) + assert(self.state ~= STATE_CLOSED, "unable to set session data on closed session") + assert(type(data) == "table", "invalid session data") + self.data[self.data_index][1] = data +end + + +--- +-- Get session data. +-- +-- @function instance:get_data +-- @treturn table value +-- +-- @usage +-- local session, err, exists = require "resty.session".open() +-- if exists then +-- local data = session:get_data() +-- ngx.req.set_header("Authorization", "Bearer " .. data.access_token) +-- end +function metatable:get_data() + assert(self.state ~= STATE_CLOSED, "unable to set session data on closed session") + return self.data[self.data_index][1] +end + + +--- +-- Set a value in session. +-- +-- @function instance:set +-- @tparam string key key +-- @param value value +-- +-- local session, err, exists = require "resty.session".open() +-- if not exists then +-- session:set("access-token", "eyJ...") +-- session:save() +-- end +function metatable:set(key, value) + assert(self.state ~= STATE_CLOSED, "unable to set session data value on closed session") + if self.storage or byte(key, 1) ~= AT_BYTE then + self.data[self.data_index][1][key] = value + else + self.data[self.data_index][1]["$" .. key] = value + end +end + + +--- +-- Get a value from session. +-- +-- @function instance:get +-- @tparam string key key +-- @return value +-- +-- @usage +-- local session, err, exists = require "resty.session".open() +-- if exists then +-- local access_token = session:get("access-token") +-- ngx.req.set_header("Authorization", "Bearer " .. access_token) +-- end +function metatable:get(key) + assert(self.state ~= STATE_CLOSED, "unable to get session data value on closed session") + if self.storage or byte(key, 1) ~= AT_BYTE then + return self.data[self.data_index][1][key] + else + return self.data[self.data_index][1]["$" .. key] + end +end + + +--- +-- Set session audience. +-- +-- @function instance:set_audience +-- @tparam string audience audience +-- +-- @usage +-- local session = require "resty.session".new() +-- session.set_audience("my-service") +function metatable:set_audience(audience) + assert(self.state ~= STATE_CLOSED, "unable to set audience on closed session") + + local data = self.data + local data_index = self.data_index + local current_audience = data[data_index][2] + if audience == current_audience then + return + end + + local info_data = self.info.data + if info_data then + info_data[audience] = info_data[current_audience] + info_data[current_audience] = nil + end + + local count = #data + if count == 1 then + data[1][2] = audience + return + end + + local previous_index + for i = 1, count do + if data[i][2] == audience then + previous_index = i + break + end + end + + data[data_index][2] = audience + + if not previous_index or previous_index == data_index then + return + end + + remove(data, previous_index) + + if previous_index < data_index then + self.data_index = data_index - 1 + end +end + + +--- +-- Get session audience. +-- +-- @function instance:get_audience +-- @treturn string audience +function metatable:get_audience() + assert(self.state ~= STATE_CLOSED, "unable to get audience on closed session") + return self.data[self.data_index][2] +end + + +--- +-- Set session subject. +-- +-- @function instance:set_subject +-- @tparam string subject subject +-- +-- @usage +-- local session = require "resty.session".new() +-- session.set_subject("john@doe.com") +function metatable:set_subject(subject) + assert(self.state ~= STATE_CLOSED, "unable to set subject on closed session") + self.data[self.data_index][3] = subject +end + + +--- +-- Get session subject. +-- +-- @function instance:get_subject +-- @treturn string subject +-- +-- @usage +-- local session, err, exists = require "resty.session".open() +-- if exists then +-- local subject = session.get_subject() +-- end +function metatable:get_subject() + assert(self.state ~= STATE_CLOSED, "unable to get subject on closed session") + return self.data[self.data_index][3] +end + + +--- +-- Get session property. +-- +-- Possible property names: +-- * `"id"`: 43 bytes session id (same as nonce, but base64 url-encoded) +-- * `"nonce"`: 32 bytes nonce (same as session id but in raw bytes) +-- * `"audience"`: Current session audience +-- * `"subject"`: Current session subject +-- * `"timeout"`: Closest timeout (in seconds) (what's left of it) +-- * `"idling-timeout`"`: Session idling timeout (in seconds) (what's left of it) +-- * `"rolling-timeout`"`: Session rolling timeout (in seconds) (what's left of it) +-- * `"absolute-timeout`"`: Session absolute timeout (in seconds) (what's left of it) +-- +-- @function instance:get_property +-- @treturn string|number metadata +-- +-- @usage +-- local session, err, exists = require "resty.session".open() +-- if exists then +-- local timeout = session.get_property("timeout") +-- end +function metatable:get_property(name) + assert(self.state ~= STATE_CLOSED, "unable to get session property on closed session") + return get_property(self, name) +end + + +--- +-- Set persistent sessions on/off. +-- +-- In many login forms user is given an option for "remember me". +-- You can call this function based on what user selected. +-- +-- @function instance:set_remember +-- @tparam boolean value `true` to enable persistent session, `false` to disable them +function metatable:set_remember(value) + assert(self.state ~= STATE_CLOSED, "unable to set remember on closed session") + assert(type(value) == "boolean", "invalid remember value") + if value == false then + set_flag(self.flags, FLAG_FORGET) + else + unset_flag(self.flags, FLAG_FORGET) + end + + self.remember = value +end + + +--- +-- Get state of persistent sessions. +-- +-- @function instance:get_remember +-- @treturn boolean `true` when persistent sessions are enabled, otherwise `false` +function metatable:get_remember() + assert(self.state ~= STATE_CLOSED, "unable to get remember on closed session") + return get_remember(self) +end + + +--- +-- Open a session. +-- +-- This can be used to open a session. +-- +-- @function instance:open +-- @treturn true|nil ok +-- @treturn string error message +function metatable:open() + local exists, err, audience_error = open(self) + if exists then + return true + end + + if audience_error then + return nil, err + end + + if not self.remember then + return nil, err + end + + local remembered = open(self, true) + if not remembered then + return nil, err + end + + local ok, err = save(self, nil, true) + if not ok then + return nil, err + end + + self.state = STATE_NEW + self.meta = DEFAULT_META + + local ok, err = save(self, STATE_OPEN) + if not ok then + return nil, err + end + + return true +end + + +--- +-- Save the session. +-- +-- Saves the session data and issues a new session cookie with a new session id. +-- When `remember` is enabled, it will also issue a new persistent cookie and +-- possibly save the data in backend store. +-- +-- @function instance:save +-- @treturn true|nil ok +-- @treturn string error message +function metatable:save() + assert(self.state ~= STATE_CLOSED, "unable to save closed session") + + local ok, err = save(self) + if not ok then + return nil, err + end + + if get_remember(self) then + if not self.remember_meta then + open(self, true, true) + end + + local ok, err = save(self, nil, true) + if not ok then + log(WARN, "[session] ", err) + end + end + + return true +end + + +--- +-- Touch the session. +-- +-- Updates idling offset of the session by sending an updated session cookie. +-- It only sends the client cookie and never calls any backend session store +-- APIs. Normally the `session:refresh` is used to call this indirectly. +-- +-- @function instance:touch +-- @treturn true|nil ok +-- @treturn string error message +function metatable:touch() + assert(self.state == STATE_OPEN, "unable to touch nonexistent or closed session") + + local meta = self.meta + local idling_offset = min(time() - meta.creation_time - meta.rolling_offset, MAX_IDLING_OFFSET) + + HEADER_BUFFER:reset():put(sub(meta.header, 1, HEADER_TOUCH_SIZE), + bpack(IDLING_OFFSET_SIZE, idling_offset)) + + local mac, err = calculate_mac(meta.ikm, meta.sid, HEADER_BUFFER:tostring()) + if not mac then + return nil, err + end + + local payload_header = HEADER_BUFFER:put(mac):get() + + meta.idling_offset = idling_offset + meta.header = payload_header + + payload_header, err = encode_base64url(payload_header) + if not payload_header then + return nil, errmsg(err, "unable to base64url encode session header") + end + + local cookie_flags = self.cookie_flags + local cookie_name = self.cookie_name + local cookie_data + if self.storage then + cookie_data = fmt("%s=%s%s", cookie_name, payload_header, cookie_flags) + else + cookie_data = fmt("%s=%s%s%s", cookie_name, payload_header, meta.initial_chunk, cookie_flags) + end + + header["Set-Cookie"] = merge_cookies(header["Set-Cookie"], #cookie_name, cookie_name, cookie_data) + + return true +end + + +--- +-- Refresh the session. +-- +-- Either saves the session (creating a new session id) or touches the session +-- depending on whether the rolling timeout is getting closer, which means +-- by default when 3/4 of rolling timeout is spent - 45 minutes with default +-- rolling timeout of an hour. The touch has a threshold, by default one minute, +-- so it may be skipped in some cases (you can call `session:touch()` to force it). +-- +-- @function instance:refresh +-- @treturn true|nil ok +-- @treturn string error message +function metatable:refresh() + assert(self.state == STATE_OPEN, "unable to refresh nonexistent or closed session") + + local rolling_timeout = self.rolling_timeout + local idling_timeout = self.idling_timeout + if rolling_timeout == 0 and idling_timeout == 0 then + return true + end + + local meta = self.meta + local rolling_elapsed = meta.timestamp - meta.creation_time - meta.rolling_offset + + if rolling_timeout > 0 then + if rolling_elapsed > floor(rolling_timeout * 0.75) then + -- TODO: in case session was modified before calling this function, the possible remember me cookie needs to be saved too? + return save(self) + end + end + + if idling_timeout > 0 then + local idling_elapsed = rolling_elapsed - meta.idling_offset + if idling_elapsed > self.touch_threshold then + return self:touch() + end + end + + return true +end + + +--- +-- Logout the session. +-- +-- Logout either destroys the session or just clears the data for the current audience, +-- and saves it (logging out from the current audience). +-- +-- @function instance:logout +-- @treturn true|nil ok +-- @treturn string error message +function metatable:logout() + assert(self.state == STATE_OPEN, "unable to logout nonexistent or closed session") + + local data = self.data + if #data == 1 then + return self:destroy() + end + + local data_index = self.data_index + local info_store = self.info + local info_data = info_store and info_store.data + if info_data then + local audience = data[data_index][2] + info_data[audience] = nil + if isempty(info_data) then + info_store.data = false + end + end + + remove(data, data_index) + + local ok, err = save(self, STATE_CLOSED) + if not ok then + return nil, err + end + + if get_remember(self) then + if not self.remember_meta then + open(self, true, true) + end + local ok, err = save(self, nil, true) + if not ok then + log(WARN, "[session] ", err) + end + end + + return true +end + + +--- +-- Destroy the session. +-- +-- Destroy the session and clear the cookies. +-- +-- @function instance:destroy +-- @treturn true|nil ok +-- @treturn string error message +function metatable:destroy() + assert(self.state == STATE_OPEN, "unable to destroy nonexistent or closed session") + + local ok, err = destroy(self) + if not ok then + return nil, err + end + + if get_remember(self) then + if not self.remember_meta then + local remembered = open(self, true, true) + if not remembered then + return true + end + end + + ok, err = destroy(self, true) + if not ok then + return nil, err + end + end + + return true +end + + +--- +-- Close the session. +-- +-- Just closes the session instance so that it cannot be used anymore. +-- +-- @function instance:close +function metatable:close() + self.state = STATE_CLOSED +end + + +--- +-- Clear the request session cookie. +-- +-- Modifies the request headers by removing the session related +-- cookies. This is useful when you use the session library on +-- a proxy server and don't want the session cookies to be forwarded +-- to the upstream service. +-- +-- @function instance:clear_request_cookie +function metatable:clear_request_cookie() + assert(self.state == STATE_OPEN, "unable to hide nonexistent or closed session") + + local ok = clear_request_cookie(self) + if not ok then + log(NOTICE, "[session] unable to clear session request cookie") + end + + if get_remember(self) then + ok = clear_request_cookie(self, true) + if not ok then + log(NOTICE, "[session] unable to clear persistent session request cookie") + end + end +end + + +--- +-- Sets request and response headers. +-- +-- @function instance:set_headers +-- @tparam[opt] string ... +function metatable:set_headers(...) + assert(self.state == STATE_OPEN, "unable to set request/response headers of nonexistent or closed session") + + local count = select("#", ...) + if count == 0 then + set_property_headers(self, self.request_headers, set_request_header) + set_property_headers(self, self.response_headers, set_response_header) + return + end + + set_property_headers_vararg(self, set_request_header, count, ...) + set_property_headers_vararg(self, set_response_header, count, ...) +end + + +--- +-- Set request headers. +-- +-- @function instance:set_request_headers +-- @tparam[opt] string ... +function metatable:set_request_headers(...) + assert(self.state == STATE_OPEN, "unable to set request headers of nonexistent or closed session") + + local count = select("#", ...) + if count == 0 then + set_property_headers(self, self.request_headers, set_request_header) + return + end + + set_property_headers_vararg(self, set_request_header, count, ...) +end + + +--- +-- Set response headers. +-- +-- @function instance:set_response_headers +-- @tparam[opt] string ... +function metatable:set_response_headers(...) + assert(self.state == STATE_OPEN, "unable to set response headers of nonexistent or closed session") + + local count = select("#", ...) + if count == 0 then + set_property_headers(self, self.response_headers, set_response_header) + return + end + + set_property_headers_vararg(self, set_response_header, count, ...) +end + + + +local session = { + _VERSION = "4.0.4", + metatable = metatable, +} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Session configuration. +-- @field secret Secret used for the key derivation. The secret is hashed with SHA-256 before using it. E.g. `"RaJKp8UQW1"`. +-- @field secret_fallbacks Array of secrets that can be used as alternative secrets (when doing key rotation), E.g. `{ "6RfrAYYzYq", "MkbTkkyF9C" }`. +-- @field ikm Initial key material (or ikm) can be specified directly (without using a secret) with exactly 32 bytes of data. E.g. `"5ixIW4QVMk0dPtoIhn41Eh1I9enP2060"` +-- @field ikm_fallbacks Array of initial key materials that can be used as alternative keys (when doing key rotation), E.g. `{ "QvPtlPKxOKdP5MCu1oI3lOEXIVuDckp7" }`. +-- @field cookie_prefix Cookie prefix, use `nil`, `"__Host-"` or `"__Secure-"` (defaults to `nil`) +-- @field cookie_name Session cookie name, e.g. `"session"` (defaults to `"session"`) +-- @field cookie_path Cookie path, e.g. `"/"` (defaults to `"/"`) +-- @field cookie_domain Cookie domain, e.g. `"example.com"` (defaults to `nil`) +-- @field cookie_http_only Mark cookie HTTP only, use `true` or `false` (defaults to `true`) +-- @field cookie_secure Mark cookie secure, use `nil`, `true` or `false` (defaults to `nil`) +-- @field cookie_priority Cookie priority, use `nil`, `"Low"`, `"Medium"`, or `"High"` (defaults to `nil`) +-- @field cookie_same_site Cookie same-site policy, use `nil`, `"Lax"`, `"Strict"`, `"None"`, or `"Default"` (defaults to `"Lax"`) +-- @field cookie_same_party Mark cookie with same party flag, use `nil`, `true`, or `false` (default: `nil`) +-- @field cookie_partitioned Mark cookie with partitioned flag, use `nil`, `true`, or `false` (default: `nil`) +-- @field remember Enable or disable persistent sessions, use `nil`, `true`, or `false` (defaults to `false`) +-- @field remember_safety Remember cookie key derivation complexity, use `nil`, `"None"` (fast), `"Low"`, `"Medium"`, `"High"` or `"Very High"` (slow) (defaults to `"Medium"`) +-- @field remember_cookie_name Persistent session cookie name, e.g. `"remember"` (defaults to `"remember"`) +-- @field audience Session audience, e.g. `"my-application"` (defaults to `"default"`) +-- @field subject Session subject, e.g. `"john.doe@example.com"` (defaults to `nil`) +-- @field enforce_same_subject When set to `true`, audiences need to share the same subject. The library removes non-subject matching audience data on save. +-- @field stale_ttl When session is saved a new session is created, stale ttl specifies how long the old one can still be used, e.g. `10` (defaults to `10`) (in seconds) +-- @field idling_timeout Idling timeout specifies how long the session can be inactive until it is considered invalid, e.g. `900` (defaults to `900`, or 15 minutes) (in seconds) +-- @field rolling_timeout Rolling timeout specifies how long the session can be used until it needs to be renewed, e.g. `3600` (defaults to `3600`, or an hour) (in seconds) +-- @field absolute_timeout Absolute timeout limits how long the session can be renewed, until re-authentication is required, e.g. `86400` (defaults to `86400`, or a day) (in seconds) +-- @field remember_rolling_timeout Remember timeout specifies how long the persistent session is considered valid, e.g. `604800` (defaults to `604800`, or a week) (in seconds) +-- @field remember_absolute_timeout Remember absolute timeout limits how long the persistent session can be renewed, until re-authentication is required, e.g. `2592000` (defaults to `2592000`, or 30 days) (in seconds) +-- @field hash_storage_key Whether to hash or not the storage key. With storage key hashed it is impossible to decrypt data on server side without having a cookie too (defaults to `false`). +-- @field hash_subject Whether to hash or not the subject when `store_metadata` is enabled, e.g. for PII reasons (defaults to `false`). +-- @field store_metadata Whether to also store metadata of sessions, such as collecting data of sessions for a specific audience belonging to a specific subject (defaults to `false`). +-- @field touch_threshold Touch threshold controls how frequently or infrequently the `session:refresh` touches the cookie, e.g. `60` (defaults to `60`, or a minute) (in seconds) +-- @field compression_threshold Compression threshold controls when the data is deflated, e.g. `1024` (defaults to `1024`, or a kilobyte) (in bytes) +-- @field request_headers Set of headers to send to upstream, use `id`, `audience`, `subject`, `timeout`, `idling-timeout`, `rolling-timeout`, `absolute-timeout`. E.g. `{ "id", "timeout" }` will set `Session-Id` and `Session-Timeout` request headers when `set_headers` is called. +-- @field response_headers Set of headers to send to downstream, use `id`, `audience`, `subject`, `timeout`, `idling-timeout`, `rolling-timeout`, `absolute-timeout`. E.g. `{ "id", "timeout" }` will set `Session-Id` and `Session-Timeout` response headers when `set_headers` is called. +-- @field storage Storage is responsible of storing session data, use `nil` or `"cookie"` (data is stored in cookie), `"dshm"`, `"file"`, `"memcached"`, `"mysql"`, `"postgres"`, `"redis"`, or `"shm"`, or give a name of custom module (`"custom-storage"`), or a `table` that implements session storage interface (defaults to `nil`) +-- @field dshm Configuration for dshm storage, e.g. `{ prefix = "sessions" }` +-- @field file Configuration for file storage, e.g. `{ path = "/tmp", suffix = "session" }` +-- @field memcached Configuration for memcached storage, e.g. `{ prefix = "sessions" }` +-- @field mysql Configuration for MySQL / MariaDB storage, e.g. `{ database = "sessions" }` +-- @field postgres Configuration for Postgres storage, e.g. `{ database = "sessions" }` +-- @field redis Configuration for Redis / Redis Sentinel / Redis Cluster storages, e.g. `{ prefix = "sessions" }` +-- @field shm Configuration for shared memory storage, e.g. `{ zone = "sessions" }` +-- @field ["custom-storage"] Custom storage (loaded with `require "custom-storage"`) configuration +-- @table configuration + + +--- +-- Initialization +-- @section initialization + + +--- +-- Initialize the session library. +-- +-- This function can be called on `init` or `init_worker` phases on OpenResty +-- to set global default configuration to all session instances created by this +-- library. +-- +-- @function module.init +-- @tparam[opt] table configuration session @{configuration} overrides +-- +-- @usage +-- require "resty.session".init({ +-- audience = "my-application", +-- storage = "redis", +-- redis = { +-- username = "session", +-- password = "storage", +-- }, +-- }) +function session.init(configuration) + if configuration then + local ikm = configuration.ikm + if ikm then + assert(#ikm == KEY_SIZE, "invalid ikm size") + DEFAULT_IKM = ikm + + else + local secret = configuration.secret + if secret then + DEFAULT_IKM = assert(sha256(secret)) + end + end + + local ikm_fallbacks = configuration.ikm_fallbacks + if ikm_fallbacks then + local count = #ikm_fallbacks + for i = 1, count do + assert(#ikm_fallbacks[i] == KEY_SIZE, "invalid ikm size in ikm_fallbacks") + end + + DEFAULT_IKM_FALLBACKS = ikm_fallbacks + + else + local secret_fallbacks = configuration.secret_fallbacks + if secret_fallbacks then + local count = #secret_fallbacks + if count > 0 then + DEFAULT_IKM_FALLBACKS = table_new(count, 0) + for i = 1, count do + DEFAULT_IKM_FALLBACKS[i] = assert(sha256(secret_fallbacks[i])) + end + + else + DEFAULT_IKM_FALLBACKS = nil + end + end + end + + local request_headers = configuration.request_headers + if request_headers then + local count = #request_headers + for i = 1, count do + assert(HEADERS[request_headers[i]], "invalid request header") + end + + DEFAULT_REQUEST_HEADERS = request_headers + end + + local response_headers = configuration.response_headers + if response_headers then + local count = #response_headers + for i = 1, count do + assert(HEADERS[response_headers[i]], "invalid response header") + end + + DEFAULT_RESPONSE_HEADERS = response_headers + end + + DEFAULT_COOKIE_NAME = configuration.cookie_name or DEFAULT_COOKIE_NAME + DEFAULT_COOKIE_PATH = configuration.cookie_path or DEFAULT_COOKIE_PATH + DEFAULT_COOKIE_DOMAIN = configuration.cookie_domain or DEFAULT_COOKIE_DOMAIN + DEFAULT_COOKIE_SAME_SITE = configuration.cookie_same_site or DEFAULT_COOKIE_SAME_SITE + DEFAULT_COOKIE_PRIORITY = configuration.cookie_priority or DEFAULT_COOKIE_PRIORITY + DEFAULT_COOKIE_PREFIX = configuration.cookie_prefix or DEFAULT_COOKIE_PREFIX + DEFAULT_REMEMBER_SAFETY = configuration.remember_safety or DEFAULT_REMEMBER_SAFETY + DEFAULT_REMEMBER_COOKIE_NAME = configuration.remember_cookie_name or DEFAULT_REMEMBER_COOKIE_NAME + DEFAULT_AUDIENCE = configuration.audience or DEFAULT_AUDIENCE + DEFAULT_SUBJECT = configuration.subject or DEFAULT_SUBJECT + DEFAULT_STALE_TTL = configuration.stale_ttl or DEFAULT_STALE_TTL + DEFAULT_IDLING_TIMEOUT = configuration.idling_timeout or DEFAULT_IDLING_TIMEOUT + DEFAULT_ROLLING_TIMEOUT = configuration.rolling_timeout or DEFAULT_ROLLING_TIMEOUT + DEFAULT_ABSOLUTE_TIMEOUT = configuration.absolute_timeout or DEFAULT_ABSOLUTE_TIMEOUT + DEFAULT_REMEMBER_ROLLING_TIMEOUT = configuration.remember_rolling_timeout or DEFAULT_REMEMBER_ROLLING_TIMEOUT + DEFAULT_REMEMBER_ABSOLUTE_TIMEOUT = configuration.remember_absolute_timeout or DEFAULT_REMEMBER_ABSOLUTE_TIMEOUT + DEFAULT_TOUCH_THRESHOLD = configuration.touch_threshold or DEFAULT_TOUCH_THRESHOLD + DEFAULT_COMPRESSION_THRESHOLD = configuration.compression_threshold or DEFAULT_COMPRESSION_THRESHOLD + DEFAULT_STORAGE = configuration.storage or DEFAULT_STORAGE + + local cookie_http_only = configuration.cookie_http_only + if cookie_http_only ~= nil then + DEFAULT_COOKIE_HTTP_ONLY = cookie_http_only + end + + local cookie_same_party = configuration.cookie_same_party + if cookie_same_party ~= nil then + DEFAULT_COOKIE_SAME_PARTY = cookie_same_party + end + + local cookie_partitioned = configuration.cookie_partitioned + if cookie_partitioned ~= nil then + DEFAULT_COOKIE_PARTITIONED = cookie_partitioned + end + + local cookie_secure = configuration.cookie_secure + if cookie_secure ~= nil then + DEFAULT_COOKIE_SECURE = cookie_secure + end + + local remember = configuration.remember + if remember ~= nil then + DEFAULT_REMEMBER = remember + end + + local hash_storage_key = configuration.hash_storage_key + if hash_storage_key ~= nil then + DEFAULT_HASH_STORAGE_KEY = hash_storage_key + end + + local hash_subject = configuration.hash_subject + if hash_subject ~= nil then + DEFAULT_HASH_SUBJECT = hash_subject + end + + local store_metadate = configuration.store_metadata + if store_metadate ~= nil then + DEFAULT_STORE_METADATA = store_metadate + end + + local enforce_same_subject = configuration.enforce_same_subject + if enforce_same_subject ~= nil then + DEFAULT_ENFORCE_SAME_SUBJECT = enforce_same_subject + end + end + + if not DEFAULT_IKM then + DEFAULT_IKM = assert(sha256(assert(rand_bytes(KEY_SIZE)))) + end + + if type(DEFAULT_STORAGE) == "string" then + DEFAULT_STORAGE = load_storage(DEFAULT_STORAGE, configuration) + end +end + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a new session. +-- +-- This creates a new session instance. +-- +-- @function module.new +-- @tparam[opt] table configuration session @{configuration} overrides +-- @treturn table session instance +-- +-- @usage +-- local session = require "resty.session".new() +-- -- OR +-- local session = require "resty.session".new({ +-- audience = "my-application", +-- }) +function session.new(configuration) + local cookie_name = configuration and configuration.cookie_name or DEFAULT_COOKIE_NAME + local cookie_path = configuration and configuration.cookie_path or DEFAULT_COOKIE_PATH + local cookie_domain = configuration and configuration.cookie_domain or DEFAULT_COOKIE_DOMAIN + local cookie_same_site = configuration and configuration.cookie_same_site or DEFAULT_COOKIE_SAME_SITE + local cookie_priority = configuration and configuration.cookie_priority or DEFAULT_COOKIE_PRIORITY + local cookie_prefix = configuration and configuration.cookie_prefix or DEFAULT_COOKIE_PREFIX + local remember_safety = configuration and configuration.remember_safety or DEFAULT_REMEMBER_SAFETY + local remember_cookie_name = configuration and configuration.remember_cookie_name or DEFAULT_REMEMBER_COOKIE_NAME + local audience = configuration and configuration.audience or DEFAULT_AUDIENCE + local subject = configuration and configuration.subject or DEFAULT_SUBJECT + local stale_ttl = configuration and configuration.stale_ttl or DEFAULT_STALE_TTL + local idling_timeout = configuration and configuration.idling_timeout or DEFAULT_IDLING_TIMEOUT + local rolling_timeout = configuration and configuration.rolling_timeout or DEFAULT_ROLLING_TIMEOUT + local absolute_timeout = configuration and configuration.absolute_timeout or DEFAULT_ABSOLUTE_TIMEOUT + local remember_rolling_timeout = configuration and configuration.remember_rolling_timeout or DEFAULT_REMEMBER_ROLLING_TIMEOUT + local remember_absolute_timeout = configuration and configuration.remember_absolute_timeout or DEFAULT_REMEMBER_ABSOLUTE_TIMEOUT + local touch_threshold = configuration and configuration.touch_threshold or DEFAULT_TOUCH_THRESHOLD + local compression_threshold = configuration and configuration.compression_threshold or DEFAULT_COMPRESSION_THRESHOLD + local storage = configuration and configuration.storage or DEFAULT_STORAGE + local ikm = configuration and configuration.ikm + local ikm_fallbacks = configuration and configuration.ikm_fallbacks + local request_headers = configuration and configuration.request_headers + local response_headers = configuration and configuration.response_headers + + local cookie_http_only = configuration and configuration.cookie_http_only + if cookie_http_only == nil then + cookie_http_only = DEFAULT_COOKIE_HTTP_ONLY + end + + local cookie_secure = configuration and configuration.cookie_secure + if cookie_secure == nil then + cookie_secure = DEFAULT_COOKIE_SECURE + end + + local cookie_same_party = configuration and configuration.cookie_same_party + if cookie_same_party == nil then + cookie_same_party = DEFAULT_COOKIE_SAME_PARTY + end + + local cookie_partitioned = configuration and configuration.cookie_partitioned + if cookie_partitioned == nil then + cookie_partitioned = DEFAULT_COOKIE_PARTITIONED + end + + local remember = configuration and configuration.remember + if remember == nil then + remember = DEFAULT_REMEMBER + end + + local hash_storage_key = configuration and configuration.hash_storage_key + if hash_storage_key == nil then + hash_storage_key = DEFAULT_HASH_STORAGE_KEY + end + + local hash_subject = configuration and configuration.hash_subject + if hash_subject == nil then + hash_subject = DEFAULT_HASH_SUBJECT + end + + local store_metadata = configuration and configuration.store_metadata + if store_metadata == nil then + store_metadata = DEFAULT_STORE_METADATA + end + + local enforce_same_subject = configuration and configuration.enforce_same_subject + if enforce_same_subject == nil then + enforce_same_subject = DEFAULT_ENFORCE_SAME_SUBJECT + end + + if cookie_prefix == "__Host-" then + cookie_name = cookie_prefix .. cookie_name + remember_cookie_name = cookie_prefix .. remember_cookie_name + cookie_path = DEFAULT_COOKIE_PATH + cookie_domain = nil + cookie_secure = true + + elseif cookie_prefix == "__Secure-" then + cookie_name = cookie_prefix .. cookie_name + remember_cookie_name = cookie_prefix .. remember_cookie_name + cookie_secure = true + + elseif cookie_same_site == "None" then + cookie_secure = true + end + + if cookie_same_party then + assert(cookie_same_site ~= "Strict", "SameParty session cookies cannot use SameSite=Strict") + cookie_secure = true + end + + FLAGS_BUFFER:reset() + + if cookie_domain and cookie_domain ~= "localhost" and cookie_domain ~= "" then + FLAGS_BUFFER:put("; Domain=", cookie_domain) + end + + FLAGS_BUFFER:put("; Path=", cookie_path, "; SameSite=", cookie_same_site) + + if cookie_priority then + FLAGS_BUFFER:put("; Priority=", cookie_priority) + end + + if cookie_same_party then + FLAGS_BUFFER:put("; SameParty") + end + + if cookie_partitioned then + FLAGS_BUFFER:put("; Partitioned") + end + + if cookie_secure then + FLAGS_BUFFER:put("; Secure") + end + + if cookie_http_only then + FLAGS_BUFFER:put("; HttpOnly") + end + + local cookie_flags = FLAGS_BUFFER:get() + + if ikm then + assert(#ikm == KEY_SIZE, "invalid ikm size") + + else + local secret = configuration and configuration.secret + if secret then + ikm = assert(sha256(secret)) + + else + if not DEFAULT_IKM then + DEFAULT_IKM = assert(sha256(assert(rand_bytes(KEY_SIZE)))) + end + + ikm = DEFAULT_IKM + end + end + + if ikm_fallbacks then + local count = #ikm_fallbacks + for i = 1, count do + assert(#ikm_fallbacks[i] == KEY_SIZE, "invalid ikm size in ikm_fallbacks") + end + + else + local secret_fallbacks = configuration and configuration.secret_fallbacks + if secret_fallbacks then + local count = #secret_fallbacks + if count > 0 then + ikm_fallbacks = table_new(count, 0) + for i = 1, count do + ikm_fallbacks[i] = assert(sha256(secret_fallbacks[i])) + end + end + + else + ikm_fallbacks = DEFAULT_IKM_FALLBACKS + end + end + + if request_headers then + local count = #request_headers + for i = 1, count do + assert(HEADERS[request_headers[i]], "invalid request header") + end + + else + request_headers = DEFAULT_REQUEST_HEADERS + end + + if response_headers then + local count = #response_headers + for i = 1, count do + assert(HEADERS[response_headers[i]], "invalid response header") + end + + else + response_headers = DEFAULT_RESPONSE_HEADERS + end + + local t = type(storage) + if t == "string" then + storage = load_storage(storage, configuration) + + elseif t ~= "table" then + assert(storage == nil, "invalid session storage") + end + + local self = setmetatable({ + stale_ttl = stale_ttl, + idling_timeout = idling_timeout, + rolling_timeout = rolling_timeout, + absolute_timeout = absolute_timeout, + remember_rolling_timeout = remember_rolling_timeout, + remember_absolute_timeout = remember_absolute_timeout, + touch_threshold = touch_threshold, + compression_threshold = compression_threshold, + hash_storage_key = hash_storage_key and sha256_storage_key or encode_base64url, + hash_subject = hash_subject and sha256_subject or encode_base64url, + store_metadata = store_metadata, + enforce_same_subject = enforce_same_subject, + cookie_name = cookie_name, + cookie_flags = cookie_flags, + remember_cookie_name = remember_cookie_name, + remember_safety = remember_safety, + remember = remember, + flags = FLAGS_NONE, + storage = storage, + ikm = ikm, + ikm_fallbacks = ikm_fallbacks, + request_headers = request_headers, + response_headers = response_headers, + state = STATE_NEW, + meta = DEFAULT_META, + remember_meta = DEFAULT_REMEMBER_META, + info = info, + data_index = 1, + data = { + { + {}, + audience, + subject, + }, + }, + }, metatable) + + if storage then + self.info = info.new(self) + else + self.info = fake_info.new(self) + end + + return self +end + + +--- +-- Helpers +-- @section helpers + + +--- +-- Open a session. +-- +-- This can be used to open a session, and it will either return an existing +-- session or a new session. +-- +-- @function module.open +-- @tparam[opt] table configuration session @{configuration} overrides +-- @treturn table session instance +-- @treturn string error message +-- @treturn boolean `true`, if session existed, otherwise `false` +-- +-- @usage +-- local session = require "resty.session".open() +-- -- OR +-- local session, err, exists = require "resty.session".open({ +-- audience = "my-application", +-- }) +function session.open(configuration) + local self = session.new(configuration) + local exists, err = self:open() + if not exists then + return self, err, false + end + + return self, err, true +end + + +--- +-- Start a session and refresh it as needed. +-- +-- This can be used to start a session, and it will either return an existing +-- session or a new session. In case there is an existing session, the +-- session will be refreshed as well (as needed). +-- +-- @function module.start +-- @tparam[opt] table configuration session @{configuration} overrides +-- @treturn table session instance +-- @treturn string error message +-- @treturn boolean `true`, if session existed, otherwise `false` +-- @treturn boolean `true`, if session was refreshed, otherwise `false` +-- +-- @usage +-- local session = require "resty.session".start() +-- -- OR +-- local session, err, exists, refreshed = require "resty.session".start({ +-- audience = "my-application", +-- }) +function session.start(configuration) + local self, err, exists = session.open(configuration) + if not exists then + return self, err, false, false + end + + local refreshed, err = self:refresh() + if not refreshed then + return self, err, true, false + end + + return self, nil, true, true +end + + +--- +-- Logout a session. +-- +-- It logouts from a specific audience. +-- +-- A single session cookie may be shared between multiple audiences +-- (or applications), thus there is a need to be able to logout from +-- just a single audience while keeping the session for the other +-- audiences. +-- +-- When there is only a single audience, then this can be considered +-- equal to `session.destroy`. +-- +-- When the last audience is logged out, the cookie will be destroyed +-- as well and invalidated on a client. +-- +-- @function module.logout +-- @tparam[opt] table configuration session @{configuration} overrides +-- @treturn boolean `true` session exists for an audience and was logged out successfully, otherwise `false` +-- @treturn string error message +-- @treturn boolean `true` if session existed, otherwise `false` +-- @treturn boolean `true` if session was logged out, otherwise `false` +-- +-- @usage +-- require "resty.session".logout() +-- -- OR +-- local ok, err, exists, logged_out = require "resty.session".logout({ +-- audience = "my-application", +-- }) +function session.logout(configuration) + local self, err, exists = session.open(configuration) + if not exists then + return nil, err, false, false + end + + local ok, err = self:logout() + if not ok then + return nil, err, true, false + end + + return true, nil, true, true +end + +--- +-- Destroy a session. +-- +-- It destroys the whole session and clears the cookies. +-- +-- @function module.destroy +-- @tparam[opt] table configuration session @{configuration} overrides +-- @treturn boolean `true` session exists and was destroyed successfully, otherwise `nil` +-- @treturn string error message +-- @treturn boolean `true` if session existed, otherwise `false` +-- @treturn boolean `true` if session was destroyed, otherwise `false` +-- +-- @usage +-- require "resty.session".destroy() +-- -- OR +-- local ok, err, exists, destroyed = require "resty.session".destroy({ +-- cookie_name = "auth", +-- }) +function session.destroy(configuration) + local self, err, exists = session.open(configuration) + if not exists then + return nil, err, false, false + end + + local ok, err = self:destroy() + if not ok then + return nil, err, true, false + end + + return true, nil, true, true +end + + +function session.__set_ngx_log(ngx_log) + log = ngx_log +end + + +function session.__set_ngx_var(ngx_var) + var = ngx_var +end + + +function session.__set_ngx_header(ngx_header) + header = ngx_header +end + + +function session.__set_ngx_req_clear_header(clear_header) + clear_request_header = clear_header +end + + +function session.__set_ngx_req_set_header(set_header) + set_request_header = set_header +end + + +return session diff --git a/lib/resty/session/dshm.lua b/lib/resty/session/dshm.lua new file mode 100644 index 000000000..325564d0a --- /dev/null +++ b/lib/resty/session/dshm.lua @@ -0,0 +1,348 @@ +--- +-- Distributed Shared Memory (DSHM) backend for session library +-- +-- @module resty.session.dshm + + +local buffer = require "string.buffer" +local utils = require "resty.session.utils" +local dshm = require "resty.dshm" + + +local meta_get_latest = utils.meta_get_latest +local meta_get_value = utils.meta_get_value +local get_name = utils.get_name + + +local setmetatable = setmetatable +local error = error +local pairs = pairs +local null = ngx.null +local max = math.max + + +local DEFAULT_HOST = "127.0.0.1" +local DEFAULT_PORT = 4321 + + +local SESSIONS_BUFFER = buffer.new(128) + + +-- not safe for concurrent access +local function update_meta(dshmc, meta_key, key, exp, current_time) + local metadata = dshmc:get(meta_key) + local sessions = metadata and meta_get_latest(metadata, current_time) or {} + + SESSIONS_BUFFER:reset() + + sessions[key] = exp > 0 and exp or nil + + local max_expiry = current_time + for s, e in pairs(sessions) do + SESSIONS_BUFFER:put(meta_get_value(s, e)) + max_expiry = max(max_expiry, e) + end + + local ser = SESSIONS_BUFFER:get() + + if #ser > 0 then + return dshmc:set(meta_key, ser, max_expiry - current_time) + end + + return dshmc:delete(meta_key) +end + + +local function READ_METADATA(self, dshmc, name, audience, subject, current_time) + local meta_key = get_name(self, name, audience, subject) + local res = dshmc:get(meta_key) + if not res then + return nil, "not found" + end + + return meta_get_latest(res, current_time) +end + + +local function SET(self, dshmc, name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + local inferred_key = get_name(self, name, key) + + if not metadata and not old_key then + return dshmc:set(inferred_key, value, ttl) + end + + local ok, err = dshmc:set(inferred_key, value, ttl) + if err then + return nil, err + end + + local old_name = old_key and get_name(self, name, old_key) + if old_name then + if remember then + dshmc:delete(old_name) + else + dshmc:touch(old_name, stale_ttl) + end + end + + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + update_meta(dshmc, meta_key, key, current_time + ttl, current_time) + + if old_key then + update_meta(dshmc, meta_key, old_key, 0, current_time) + end + end + end + return ok +end + + +local function GET(self, dshmc, name, key) + local res, err = dshmc:get(get_name(self, name, key)) + if err then + return nil, err + end + return res +end + + +local function DELETE(self, dshmc, name, key, current_time, metadata) + local key_name = get_name(self, name, key) + local ok, err = dshmc:delete(key_name) + + if not metadata then + return ok, err + end + + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + update_meta(dshmc, meta_key, key, 0, current_time) + end + + return ok, err +end + + +local function exec(self, func, ...) + local dshmc = dshm:new() + local connect_timeout = self.connect_timeout + local send_timeout = self.send_timeout + local read_timeout = self.read_timeout + if connect_timeout or send_timeout or read_timeout then + dshmc.sock:settimeouts(connect_timeout, send_timeout, read_timeout) + end + + local ok, err = dshmc:connect(self.host, self.port, self.options) + if not ok then + return nil, err + end + + if self.ssl and dshmc:get_reused_times() == 0 then + ok, err = dshmc.sock:sslhandshake(false, self.server_name, self.ssl_verify) + if not ok then + dshmc:close() + return nil, err + end + end + + ok, err = func(self, dshmc, ...) + if err then + dshmc:close() + return nil, err + end + + if not dshmc:set_keepalive(self.keepalive_timeout) then + dshmc:close() + end + + if ok == null then + ok = nil + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return exec(self, SET, ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return exec(self, GET, ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return exec(self, DELETE, ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return exec(self, READ_METADATA, ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Distributed shared memory storage backend configuration +-- @field prefix The prefix for the keys stored in DSHM. +-- @field suffix The suffix for the keys stored in DSHM. +-- @field host The host to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `4321`). +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field server_name The server name for the new TLS extension Server Name Indication (SNI). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a distributed shared memory storage. +-- +-- This creates a new distributed shared memory storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration DSHM storage @{configuration} +-- @treturn table DSHM storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local host = configuration and configuration.host or DEFAULT_HOST + local port = configuration and configuration.port or DEFAULT_PORT + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local server_name = configuration and configuration.server_name + + if pool or pool_size or backlog then + setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + options = { + pool = pool, + pool_size = pool_size, + backlog = backlog, + } + }, metatable) + end + + return setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/file.lua b/lib/resty/session/file.lua new file mode 100644 index 000000000..414447d41 --- /dev/null +++ b/lib/resty/session/file.lua @@ -0,0 +1,177 @@ +--- +-- File storage backend for session library. +-- +-- @module resty.session.file + + +local file_utils = require "resty.session.file.utils" + + +local run_worker_thread = file_utils.run_worker_thread + + +local setmetatable = setmetatable +local error = error +local byte = string.byte + + +local SLASH_BYTE = byte("/") + + +local THREAD_MODULE = "resty.session.file.thread" + + +local DEFAULT_POOL = "default" +local DEFAULT_PATH do + local path = os.tmpname() + local pos + for i = #path, 1, -1 do + if byte(path, i) == SLASH_BYTE then + pos = i + break + end + end + + DEFAULT_PATH = path:sub(1, pos) +end + + +local function run_thread(self, func, ...) + local ok, res, err = run_worker_thread(self.pool, THREAD_MODULE, func, self.path, self.prefix, self.suffix, ...) + if not ok then + return nil, res + end + + return res, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return run_thread(self, "set", ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return run_thread(self, "get", ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return run_thread(self, "delete", ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return run_thread(self, "read_metadata", ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- File storage backend configuration +-- @field prefix File prefix for session file. +-- @field suffix File suffix (or extension without `.`) for session file. +-- @field pool Name of the thread pool under which file writing happens (available on Linux only). +-- @field path Path (or directory) under which session files are created. +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a file storage. +-- +-- This creates a new file storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration file storage @{configuration} +-- @treturn table file storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local pool = configuration and configuration.pool or DEFAULT_POOL + local path = configuration and configuration.path or DEFAULT_PATH + + if byte(path, -1) ~= SLASH_BYTE then + path = path .. "/" + end + + return setmetatable({ + prefix = prefix ~= "" and prefix or nil, + suffix = suffix ~= "" and suffix or nil, + pool = pool, + path = path, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/file/thread.lua b/lib/resty/session/file/thread.lua new file mode 100644 index 000000000..127a6f999 --- /dev/null +++ b/lib/resty/session/file/thread.lua @@ -0,0 +1,209 @@ +--- +-- File storage backend worker thread module +-- +-- @module resty.session.file.thread + + +local file_utils = require "resty.session.file.utils" +local utils = require "resty.session.utils" + + +local get_modification = file_utils.get_modification +local meta_get_key = file_utils.meta_get_key +local file_create = file_utils.file_create +local file_append = file_utils.file_append +local file_delete = file_utils.file_delete +local file_touch = file_utils.file_touch +local file_read = file_utils.file_read +local get_path = file_utils.get_path +local cleanup = file_utils.cleanup + + +local meta_get_latest = utils.meta_get_latest +local meta_get_value = utils.meta_get_value + + +local max = math.max + + +local function update_meta(path, prefix, suffix, name, meta_key, key, exp) + local meta_value = meta_get_value(key, exp) + if not meta_value then + return + end + + local file_path = get_path(path, prefix, suffix, name, meta_key) + file_append(file_path, meta_value) + local current_expiry = get_modification(file_path) or 0 + + local new_expiry = max(current_expiry, exp) + file_touch(file_path, new_expiry) +end + + +--- +-- Store session data. +-- +-- @function module.set +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name the cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam table remember whether storing persistent session or not +-- @treturn table|nil session metadata +-- @treturn string error message +local function set(path, prefix, suffix, name, key, value, ttl, + current_time, old_key, stale_ttl, metadata, remember) + local file_path = get_path(path, prefix, suffix, name, key) + if not metadata and not old_key then + local ok, err = file_create(file_path, value) + + if ok and current_time and ttl then + file_touch(file_path, current_time + ttl) + end + + cleanup(path, prefix, suffix, name, current_time) + + return ok, err + end + + local old_ttl, old_file_path + if old_key then + old_file_path = get_path(path, prefix, suffix, name, old_key) + if not remember then + local exp = get_modification(old_file_path) + if exp then + old_ttl = exp - current_time + end + end + end + + local ok, err = file_create(file_path, value) + if ok and current_time and ttl then + file_touch(file_path, current_time + ttl) + end + + if old_file_path then + if remember then + file_delete(old_file_path) + + elseif not old_ttl or old_ttl > stale_ttl then + file_touch(old_file_path, current_time + stale_ttl) + end + end + + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = meta_get_key(audiences[i], subjects[i]) + update_meta(path, prefix, suffix, name, meta_key, key, current_time + ttl) + + if old_key then + update_meta(path, prefix, suffix, name, meta_key, old_key, 0) + end + end + end + + cleanup(path, prefix, suffix, name, current_time) + + return ok, err +end + + +--- +-- Retrieve session data. +-- +-- @function module.GET +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +local function get(path, prefix, suffix, name, key, current_time) + local file_path = get_path(path, prefix, suffix, name, key) + + -- TODO: do we want to check expiry here? + -- The cookie header already has the info and has a MAC too. + local exp = get_modification(file_path) + if exp and exp < current_time then + return nil, "expired" + end + + return file_read(file_path) +end + + +--- +-- Delete session data. +-- +-- @function module.delete +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name the cookie name +-- @tparam string key session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +local function delete(path, prefix, suffix, name, key, current_time, metadata) + local file_path = get_path(path, prefix, suffix, name, key) + file_delete(file_path) + + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = meta_get_key(audiences[i], subjects[i]) + update_meta(path, prefix, suffix, name, meta_key, key, 0) + end + end + + cleanup(path, prefix, suffix, name, current_time) + + return true +end + + +--- +-- Read session metadata. +-- +-- @function module.read_metadata +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name the cookie name +-- @tparam string audience session audience +-- @tparam string subject session subject +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +local function read_metadata(path, prefix, suffix, name, audience, subject, current_time) + local meta_key = meta_get_key(audience, subject) + local file_path = get_path(path, prefix, suffix, name, meta_key) + local metadata, err = file_read(file_path) + if not metadata then + return nil, err + end + + return meta_get_latest(metadata, current_time) +end + + +return { + set = set, + get = get, + delete = delete, + read_metadata = read_metadata, +} diff --git a/lib/resty/session/file/utils.lua b/lib/resty/session/file/utils.lua new file mode 100644 index 000000000..af4a12c03 --- /dev/null +++ b/lib/resty/session/file/utils.lua @@ -0,0 +1,290 @@ +--- +-- File storage utilities +-- +-- @module resty.session.file.utils + + +local lfs = require "lfs" + + +local attributes = lfs.attributes +local touch = lfs.touch +local dir = lfs.dir + + + +local file_delete = os.remove +local random = math.random +local pcall = pcall +local open = io.open +local fmt = string.format + + +local CLEANUP_PROBABILITY = 0.0005 -- 1 / 2000 + + +local run_worker_thread do + run_worker_thread = ngx.run_worker_thread -- luacheck: ignore + if not run_worker_thread then + local require = require + run_worker_thread = function(_, module, func, ...) + local m = require(module) + return pcall(m[func], ...) + end + end +end + + +local function file_touch(path, mtime) + return touch(path, nil, mtime) +end + + +--- +-- Store data in file. +-- +-- @function file_create +-- @tparam string path file path +-- @tparam string content file content +-- @treturn true|nil ok +-- @treturn string error message +local function file_create(path, content) + local file, err = open(path, "wb") + if not file then + return nil, err + end + + local ok, err = file:write(content) + + file:close() + + if not ok then + file_delete(path) + return nil, err + end + + return true +end + + +--- +-- Append data in file. +-- +-- @function file_append +-- @tparam string path file path +-- @tparam string data file data +-- @treturn true|nil ok +-- @treturn string error message +local function file_append(path, data) + local file, err = open(path, "a") + if not file then + return nil, err + end + + local ok, err = file:write(data) + + file:close() + + if not ok then + file_delete(path) + return nil, err + end + + return true +end + + +--- +-- Read data from a file. +-- +-- @function file_read +-- @tparam string path file to read +-- @treturn string|nil content +-- @treturn string error message +local function file_read(path) + local file, err = open(path, "rb") + if not file then + return nil, err + end + + local content, err = file:read("*a") + + file:close() + + if not content then + return nil, err + end + + return content +end + + +--- +-- Generate the path for a file to be stored at. +-- +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name the cookie name +-- @tparam string key session key +-- @treturn string path +local function get_path(path, prefix, suffix, name, key) + if prefix and suffix then + return fmt("%s%s_%s_%s.%s", path, prefix, name, key, suffix) + elseif prefix then + return fmt("%s%s_%s_%s", path, prefix, name, key) + elseif suffix then + return fmt("%s%s_%s.%s", path, name, key, suffix) + else + return fmt("%s%s_%s", path, name, key) + end +end + + +--- +-- Get the value modification time of a file. +-- +-- @function utils.get_modification +-- @tparam string path the path to the file +local function get_modification(path) + local attr = attributes(path) + if not attr or attr.mode ~= "file" then + return + end + + return attr.modification or nil +end + + +--- +-- Given an audience and a subject, generate a metadata key. +-- +-- @function utils.meta_get_key +-- @tparam string audience session audience +-- @tparam string subject session subject +-- @treturn string metadata key +local function meta_get_key(audience, subject) + return fmt("%s:%s", audience, subject) +end + + +--- +-- Validate a file name. +-- Run a few checks to try to determine if the file is managed by this library +-- +-- @function utils.validate_file_name +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name cookie name +-- @tparam string filename the name of the file +-- @treturn true|false whether the file is managed by the library or not +local validate_file_name do + local byte = string.byte + local sub = string.sub + local find = string.find + + local UNDERSCORE = byte("_") + local DOT = byte(".") + + validate_file_name = function(prefix, suffix, name, filename) + if filename == "." or filename == ".." then + return false + end + + local plen = 0 + if prefix then + plen = #prefix + if byte(filename, plen + 1) ~= UNDERSCORE or + (plen > 0 and sub(filename, 1, plen) ~= prefix) then + return false + end + end + + local slen = 0 + if suffix then + slen = #suffix + + if byte(filename, -1 - slen) ~= DOT or + (slen > 0 and sub(filename, -slen) ~= suffix) + then + return false + end + end + + local nlen = #name + local name_start = plen == 0 and 1 or plen + 2 + local name_end = name_start + nlen - 1 + + if byte(filename, name_end + 1) ~= UNDERSCORE or + sub(filename, name_start, name_end) ~= name + then + return false + end + + local rest + if slen == 0 then + rest = sub(filename, name_end + 2) + else + rest = sub(filename, name_end + 2, -2 - slen) + end + + local rlen = #rest + if rlen < 3 then + return false + end + + if rlen ~= 43 then + local colon_pos = find(rest, ":", 2, true) + if not colon_pos or colon_pos == 43 then + return false + end + end + + return true + end +end + + +--- +-- Clean up expired session and metadata files. +-- +-- @function utils.cleanup +-- @tparam string path the path where sessions are stored +-- @tparam string prefix the prefix for session files +-- @tparam string suffix the suffix for session files +-- @tparam string name cookie name +-- @tparam number current_time current time +-- @treturn true|false whether clean up completed +local function cleanup(path, prefix, suffix, name, current_time) + if random() > CLEANUP_PROBABILITY then + return false + end + + local deleted = 0 + + for file in dir(path) do + if validate_file_name(prefix, suffix, name, file) then + local exp = get_modification(path .. file) + if exp and exp < current_time then + file_delete(path .. file) + deleted = deleted + 1 + end + end + end + return true +end + + +return { + validate_file_name = validate_file_name, + run_worker_thread = run_worker_thread, + get_modification = get_modification, + meta_get_key = meta_get_key, + file_create = file_create, + file_append = file_append, + file_delete = file_delete, + file_touch = file_touch, + file_read = file_read, + get_path = get_path, + cleanup = cleanup, +} diff --git a/lib/resty/session/memcached.lua b/lib/resty/session/memcached.lua new file mode 100644 index 000000000..ab82a3296 --- /dev/null +++ b/lib/resty/session/memcached.lua @@ -0,0 +1,395 @@ +--- +-- Memcached backend for session library +-- +-- @module resty.session.memcached + + +local memcached = require "resty.memcached" +local buffer = require "string.buffer" +local utils = require "resty.session.utils" + + +local meta_get_latest = utils.meta_get_latest +local meta_get_value = utils.meta_get_value +local get_name = utils.get_name +local errmsg = utils.errmsg + + +local setmetatable = setmetatable +local random = math.random +local error = error +local pairs = pairs +local null = ngx.null +local log = ngx.log +local max = math.max + + +local WARN = ngx.WARN + + +local CLEANUP_PROBABILITY = 0.1 -- 1 / 10 +local SESSIONS_BUFFER = buffer.new(128) + + +local function cleanup(memc, meta_key, current_time) + local res, _, cas_unique, err = memc:gets(meta_key) + if not res then + return nil, err + end + + local sessions = meta_get_latest(res, current_time) + + SESSIONS_BUFFER:reset() + + local max_expiry = current_time + for key, exp in pairs(sessions) do + SESSIONS_BUFFER:put(meta_get_value(key, exp)) + max_expiry = max(max_expiry, exp) + end + + local exp = max_expiry - current_time + if exp > 0 then + return memc:cas(meta_key, SESSIONS_BUFFER:get(), cas_unique, exp) + end + + return memc:delete(meta_key) +end + + +local function read_metadata(self, memc, name, audience, subject, current_time) + local meta_key = get_name(self, name, audience, subject) + local res, _, err = memc:get(meta_key) + if not res then + return nil, err + end + + return meta_get_latest(res, current_time) +end + + +-- TODO possible improvement: when available in the lib, use pipelines +local function SET(self, memc, name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + local inferred_key = get_name(self, name, key) + + if not metadata and not old_key then + return memc:set(inferred_key, value, ttl) + end + + local ok, err = memc:set(inferred_key, value, ttl) + if not ok then + return nil, err + end + + local old_name = old_key and get_name(self, name, old_key) + if old_name then + if remember then + memc:delete(old_name) + else + memc:touch(old_name, stale_ttl) + end + end + + if not metadata then + return true + end + + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + local meta_value = meta_get_value(key, current_time + ttl) + + local added, err = memc:add(meta_key, meta_value) + if not added then + local appended, err2 = memc:append(meta_key, meta_value) + if not appended then + log(WARN, "[session] ", errmsg(err2 or err, "failed to store metadata")) + end + end + + if old_key then + meta_value = meta_get_value(old_key, 0) + local ok, err = memc:append(meta_key, meta_value) + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to update metadata")) + end + end + + -- no need to clean up every time we write + -- it is just beneficial when a key is used a lot + if random() < CLEANUP_PROBABILITY then + cleanup(memc, meta_key, current_time) + end + end + + return true +end + + +local function GET(self, memc, name, key) + local res, _, err = memc:get(get_name(self, name, key)) + if err then + return nil, err + end + return res +end + + +local function DELETE(self, memc, name, key, current_time, metadata) + local key_name = get_name(self, name, key) + local ok, err = memc:delete(key_name) + if not metadata then + return ok, err + end + + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + local meta_value = meta_get_value(key, 0) + local ok, err = memc:append(meta_key, meta_value) + if not ok and err ~= "NOT_STORED" then + log(WARN, "[session] ", errmsg(err, "failed to update metadata")) + end + + cleanup(memc, meta_key, current_time) + end + + return ok, err +end + + +local DEFAULT_HOST = "127.0.0.1" +local DEFAULT_PORT = 11211 + + +local function exec(self, func, ...) + local memc = memcached:new() + + local connect_timeout = self.connect_timeout + local send_timeout = self.send_timeout + local read_timeout = self.read_timeout + if connect_timeout or send_timeout or read_timeout then + memc:set_timeouts(connect_timeout, send_timeout, read_timeout) + end + + local ok, err do + local socket = self.socket + if socket then + ok, err = memc:connect(socket, self.options) + else + ok, err = memc:connect(self.host, self.port, self.options) + end + end + if not ok then + return nil, err + end + + if self.ssl and memc:get_reused_times() == 0 then + ok, err = memc:sslhandshake(false, self.server_name, self.ssl_verify) + if not ok then + memc:close() + return nil, err + end + end + + ok, err = func(self, memc, ...) + + if err then + memc:close() + return nil, err + end + + if not memc:set_keepalive(self.keepalive_timeout) then + memc:close() + end + + if ok == null then + ok = nil + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return exec(self, SET, ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return exec(self, GET, ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return exec(self, DELETE, ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return exec(self, read_metadata, ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Distributed shared memory storage backend configuration +-- @field prefix Prefix for the keys stored in memcached. +-- @field suffix Suffix for the keys stored in memcached. +-- @field host The host to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `11211`). +-- @field socket The socket file to connect to (defaults to `nil`). +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field server_name The server name for the new TLS extension Server Name Indication (SNI). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a memcached storage. +-- +-- This creates a new memcached storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration memcached storage @{configuration} +-- @treturn table memcached storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local host = configuration and configuration.host or DEFAULT_HOST + local port = configuration and configuration.port or DEFAULT_PORT + local socket = configuration and configuration.socket + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local server_name = configuration and configuration.server_name + + if pool or pool_size or backlog then + setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + socket = socket, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + options = { + pool = pool, + pool_size = pool_size, + backlog = backlog, + } + }, metatable) + end + + return setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + socket = socket, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/mysql.lua b/lib/resty/session/mysql.lua new file mode 100644 index 000000000..33a202905 --- /dev/null +++ b/lib/resty/session/mysql.lua @@ -0,0 +1,379 @@ +--- +-- MySQL / MariaDB backend for session library +-- +-- @module resty.session.mysql + + +--- +-- Database +-- @section database + + +--- +-- Sessions table. +-- +-- Database table that stores session data. +-- +-- @usage +-- CREATE TABLE IF NOT EXISTS sessions ( +-- sid CHAR(43) PRIMARY KEY, +-- name VARCHAR(255), +-- data MEDIUMTEXT, +-- exp DATETIME, +-- INDEX (exp) +-- ) CHARACTER SET ascii; +-- @table sessions + + +--- +-- Sessions metadata table. +-- +-- This is only needed if you want to store session metadata. +-- +-- @usage +-- CREATE TABLE IF NOT EXISTS sessions_meta ( +-- aud VARCHAR(255), +-- sub VARCHAR(255), +-- sid CHAR(43), +-- PRIMARY KEY (aud, sub, sid), +-- CONSTRAINT FOREIGN KEY (sid) REFERENCES sessions(sid) ON DELETE CASCADE ON UPDATE CASCADE +-- ) CHARACTER SET ascii; +-- @table metadata + + +local buffer = require "string.buffer" +local mysql = require "resty.mysql" + + +local setmetatable = setmetatable +local random = math.random +local ipairs = ipairs +local error = error +local fmt = string.format + + +local DEFAULT_HOST = "127.0.0.1" +local DEFAULT_PORT = 3306 +local DEFAULT_TABLE = "sessions" +local DEFAULT_CHARSET = "ascii" + + +local SET = "INSERT INTO %s (sid, name, data, exp) VALUES ('%s', '%s', '%s', FROM_UNIXTIME(%d)) AS new ON DUPLICATE KEY UPDATE data = new.data" +local SET_META_PREFIX = "INSERT INTO %s (aud, sub, sid) VALUES " +local SET_META_VALUES = "('%s', '%s', '%s')" +local SET_META_SUFFIX = " ON DUPLICATE KEY UPDATE sid = sid" +local GET_META = "SELECT sid, exp FROM %s JOIN %s USING (sid) WHERE aud = '%s' AND sub = '%s' AND exp >= FROM_UNIXTIME(%d)" +local GET = "SELECT data FROM %s WHERE sid = '%s' AND exp >= FROM_UNIXTIME(%d)" +local EXPIRE = "UPDATE %s SET exp = FROM_UNIXTIME(%d) WHERE sid = '%s' AND exp > FROM_UNIXTIME(%d)" +local DELETE = "DELETE FROM %s WHERE sid = '%s'" +local CLEANUP = "DELETE FROM %s WHERE exp < FROM_UNIXTIME(%d)" + + +local SQL = buffer.new() +local STM_DELIM = ";\n" +local VAL_DELIM = ", " + + +local CLEANUP_PROBABILITY = 0.001 -- 1 / 1000 + + +local function exec(self, query) + local my = mysql:new() + + local connect_timeout = self.connect_timeout + local send_timeout = self.send_timeout + local read_timeout = self.read_timeout + if connect_timeout or send_timeout or read_timeout then + if my.sock and my.sock.settimeouts then + my.sock:settimeouts(connect_timeout, send_timeout, read_timeout) + else + my:set_timeout(connect_timeout) + end + end + + local ok, err = my:connect(self.options) + if not ok then + return nil, err + end + + ok, err = my:query(query) + + if not my:set_keepalive(self.keepalive_timeout) then + my:close() + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + local table = self.table + local exp = ttl + current_time + + if not metadata and not old_key then + return exec(self, fmt(SET, table, key, name, value, exp)) + end + + SQL:reset():putf(SET, table, key, name, value, exp) + + if old_key then + if remember then + SQL:put(STM_DELIM):putf(DELETE, table, old_key) + else + local stale_exp = stale_ttl + current_time + SQL:put(STM_DELIM):putf(EXPIRE, table, stale_exp, old_key, stale_exp) + end + end + + local table_meta = self.table_meta + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + + SQL:put(STM_DELIM):putf(SET_META_PREFIX, table_meta) + + for i = 1, count do + if i > 1 then + SQL:put(VAL_DELIM) + end + SQL:putf(SET_META_VALUES, audiences[i], subjects[i], key) + end + + SQL:putf(SET_META_SUFFIX) + end + + if random() < CLEANUP_PROBABILITY then + SQL:put(STM_DELIM):putf(CLEANUP, self.table, current_time) + end + + return exec(self, SQL:get()) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(name, key, current_time) -- luacheck: ignore + local res, err = exec(self, fmt(GET, self.table, key, current_time)) + if not res then + return nil, err + end + + local row = res[1] + if not row then + return nil, "session not found" + end + + local data = row.data + if not row.data then + return nil, "session not found" + end + + return data +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(name, key, current_time, metadata) -- luacheck: ignore + SQL:reset():putf(DELETE, self.table, key) + + if random() < CLEANUP_PROBABILITY then + SQL:put(STM_DELIM):putf(CLEANUP, self.table, current_time) + end + + return exec(self, SQL:get()) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(name, audience, subject, current_time) -- luacheck: ignore + local res = {} + local t = exec(self, fmt(GET_META, self.table_meta, self.table, audience, subject, current_time)) + if not t then + return nil, "not found" + end + + for _, v in ipairs(t) do + local key = v.sid + if key then + res[key] = v.exp + end + end + + return res +end + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Postgres storage backend configuration +-- @field host The host to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `3306`). +-- @field socket The socket file to connect to (defaults to `nil`). +-- @field username The database username to authenticate (defaults to `nil`). +-- @field password Password for authentication, may be required depending on server configuration. +-- @field charset The character set used on the MySQL connection (defaults to `"ascii"`). +-- @field database The database name to connect. +-- @field table_name Name of database table to which to store session data (defaults to `"sessions"`). +-- @field table_name_meta Name of database meta data table to which to store session meta data (defaults to `"sessions_meta"`). +-- @field max_packet_size The upper limit for the reply packets sent from the MySQL server (defaults to 1 MB). +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a MySQL / MariaDB storage. +-- +-- This creates a new MySQL / MariaDB storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration mysql/mariadb storage @{configuration} +-- @treturn table mysql/mariadb storage instance +function storage.new(configuration) + local host = configuration and configuration.host or DEFAULT_HOST + local port = configuration and configuration.port or DEFAULT_PORT + local socket = configuration and configuration.socket + + local username = configuration and configuration.username + local password = configuration and configuration.password + local charset = configuration and configuration.charset or DEFAULT_CHARSET + local database = configuration and configuration.database + local max_packet_size = configuration and configuration.max_packet_size + + local table_name = configuration and configuration.table or DEFAULT_TABLE + local table_name_meta = configuration and configuration.table_meta + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + + if socket then + return setmetatable({ + table = table_name, + table_meta = table_name_meta or (table_name .. "_meta"), + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + options = { + path = socket, + user = username, + password = password, + charset = charset, + database = database, + max_packet_size = max_packet_size, + pool = pool, + pool_size = pool_size, + backlog = backlog, + ssl = ssl, + ssl_verify = ssl_verify, + } + }, metatable) + end + + return setmetatable({ + table = table_name, + table_meta = table_name_meta or (table_name .. "_meta"), + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + options = { + host = host, + port = port, + user = username, + password = password, + charset = charset, + database = database, + max_packet_size = max_packet_size, + pool = pool, + pool_size = pool_size, + backlog = backlog, + ssl = ssl, + ssl_verify = ssl_verify, + } + }, metatable) +end + + +return storage diff --git a/lib/resty/session/postgres.lua b/lib/resty/session/postgres.lua new file mode 100644 index 000000000..25e603920 --- /dev/null +++ b/lib/resty/session/postgres.lua @@ -0,0 +1,354 @@ +--- +-- Postgres backend for session library. +-- +-- @module resty.session.postgres + + +--- +-- Database +-- @section database + + +--- +-- Sessions table. +-- +-- Database table that stores session data. +-- +-- @usage +-- CREATE TABLE IF NOT EXISTS sessions ( +-- sid TEXT PRIMARY KEY, +-- name TEXT, +-- data TEXT, +-- exp TIMESTAMP WITH TIME ZONE +-- ); +-- CREATE INDEX ON sessions (exp); +-- @table sessions + + +--- +-- Sessions metadata table. +-- +-- This is only needed if you want to store session metadata. +-- +-- @usage +-- CREATE TABLE IF NOT EXISTS sessions_meta ( +-- aud TEXT, +-- sub TEXT, +-- sid TEXT REFERENCES sessions (sid) ON DELETE CASCADE ON UPDATE CASCADE, +-- PRIMARY KEY (aud, sub, sid) +-- ); +-- @table metadata + + +local buffer = require "string.buffer" +local pgmoon = require "pgmoon" + + +local setmetatable = setmetatable +local random = math.random +local ipairs = ipairs +local error = error +local fmt = string.format + + +local DEFAULT_HOST = "127.0.0.1" +local DEFAULT_PORT = 5432 +local DEFAULT_TABLE = "sessions" + + +local SET = "INSERT INTO %s (sid, name, data, exp) VALUES ('%s', '%s', '%s', TO_TIMESTAMP(%d) AT TIME ZONE 'UTC') ON CONFLICT (sid) DO UPDATE SET data = EXCLUDED.data, exp = EXCLUDED.exp" +local SET_META_PREFIX = "INSERT INTO %s (aud, sub, sid) VALUES " +local SET_META_VALUES = "('%s', '%s', '%s')" +local SET_META_SUFFIX = " ON CONFLICT DO NOTHING" +local GET_META = "SELECT sid, exp FROM %s JOIN %s USING (sid) WHERE aud = '%s' AND sub = '%s' AND exp >= TO_TIMESTAMP(%d)" +local GET = "SELECT data FROM %s WHERE sid = '%s' AND exp >= TO_TIMESTAMP(%d) AT TIME ZONE 'UTC'" +local EXPIRE = "UPDATE %s SET exp = TO_TIMESTAMP(%d) AT TIME ZONE 'UTC' WHERE sid = '%s' AND exp > TO_TIMESTAMP(%d) AT TIME ZONE 'UTC'" +local DELETE = "DELETE FROM %s WHERE sid = '%s'" +local CLEANUP = "DELETE FROM %s WHERE exp < TO_TIMESTAMP(%d)" + + +local SQL = buffer.new() +local STM_DELIM = ";\n" +local VAL_DELIM = ", " + + +local CLEANUP_PROBABILITY = 0.001 -- 1 / 1000 + + +local function exec(self, query) + local pg = pgmoon.new(self.options) + + local connect_timeout = self.connect_timeout + local send_timeout = self.send_timeout + local read_timeout = self.read_timeout + if connect_timeout or send_timeout or read_timeout then + if pg.sock and pg.sock.settimeouts then + pg.sock:settimeouts(connect_timeout, send_timeout, read_timeout) + else + pg:settimeout(connect_timeout) + end + end + + local ok, err = pg:connect() + if not ok then + return nil, err + end + + ok, err = pg:query(query) + + if not pg:keepalive(self.keepalive_timeout) then + pg:close() + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + local table = self.table + local exp = ttl + current_time + + if not metadata and not old_key then + return exec(self, fmt(SET, table, key, name, value, exp)) + end + + SQL:reset():putf(SET, table, key, name, value, exp) + + if old_key then + if remember then + SQL:put(STM_DELIM):putf(DELETE, table, old_key) + else + local stale_exp = stale_ttl + current_time + SQL:put(STM_DELIM):putf(EXPIRE, table, stale_exp, old_key, stale_exp) + end + end + + local table_meta = self.table_meta + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + + SQL:put(STM_DELIM):putf(SET_META_PREFIX, table_meta) + + for i = 1, count do + if i > 1 then + SQL:put(VAL_DELIM) + end + SQL:putf(SET_META_VALUES, audiences[i], subjects[i], key) + end + + SQL:putf(SET_META_SUFFIX) + end + + if random() < CLEANUP_PROBABILITY then + SQL:put(STM_DELIM):putf(CLEANUP, self.table, current_time) + end + + return exec(self, SQL:get()) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(name, key, current_time) -- luacheck: ignore + local res, err = exec(self, fmt(GET, self.table, key, current_time)) + if not res then + return nil, err + end + + local row = res[1] + if not row then + return nil + end + + local data = row.data + if not row.data then + return nil + end + + return data +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(name, key, current_time, metadata) -- luacheck: ignore + SQL:reset():putf(DELETE, self.table, key) + + if random() < CLEANUP_PROBABILITY then + SQL:put(STM_DELIM):putf(CLEANUP, self.table, current_time) + end + + return exec(self, SQL:get()) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(name, audience, subject, current_time) -- luacheck: ignore + local res = {} + + local t = exec(self, fmt(GET_META, self.table_meta, self.table, audience, subject, current_time)) + if not t then + return nil, "not found" + end + + for _, v in ipairs(t) do + local key = v.sid + if key then + res[key] = v.exp + end + end + + return res +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Postgres storage backend configuration +-- @field host The host to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `5432`). +-- @field application Set the name of the connection as displayed in pg_stat_activity (defaults to `"pgmoon"`). +-- @field username The database username to authenticate (defaults to `"postgres"`). +-- @field password Password for authentication, may be required depending on server configuration. +-- @field database The database name to connect. +-- @field table_name Name of database table to which to store session data (can be `database schema` prefixed) (defaults to `"sessions"`). +-- @field table_name_meta Name of database meta data table to which to store session meta data (can be `database schema` prefixed) (defaults to `"sessions_meta"`). +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field ssl_required Abort the connection if the server does not support SSL connections (defaults to `nil`). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a Postgres storage. +-- +-- This creates a new Postgres storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration postgres storage @{configuration} +-- @treturn table postgres storage instance +function storage.new(configuration) + local host = configuration and configuration.host or DEFAULT_HOST + local port = configuration and configuration.port or DEFAULT_PORT + + local application = configuration and configuration.application + local username = configuration and configuration.username + local password = configuration and configuration.password + local database = configuration and configuration.database + + local table_name = configuration and configuration.table or DEFAULT_TABLE + local table_name_meta = configuration and configuration.table_meta + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local ssl_required = configuration and configuration.ssl_required + + return setmetatable({ + table = table_name, + table_meta = table_name_meta or (table_name .. "_meta"), + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + options = { + host = host, + port = port, + application_name = application, + user = username, + password = password, + database = database, + socket_type = "nginx", + pool = pool, + pool_size = pool_size, + backlog = backlog, + ssl = ssl, + ssl_verify = ssl_verify, + ssl_required = ssl_required, + } + }, metatable) +end + + +return storage diff --git a/lib/resty/session/redis.lua b/lib/resty/session/redis.lua new file mode 100644 index 000000000..baffcce33 --- /dev/null +++ b/lib/resty/session/redis.lua @@ -0,0 +1,279 @@ +--- +-- Redis backend for session library +-- +-- @module resty.session.redis + + +local common = require "resty.session.redis.common" +local redis = require "resty.redis" + + +local setmetatable = setmetatable +local error = error +local null = ngx.null + + +local DEFAULT_HOST = "127.0.0.1" +local DEFAULT_PORT = 6379 + + +local SET = common.SET +local GET = common.GET +local UNLINK = common.UNLINK +local READ_METADATA = common.READ_METADATA + + +local function exec(self, func, ...) + local red = redis:new() + + local connect_timeout = self.connect_timeout + local send_timeout = self.send_timeout + local read_timeout = self.read_timeout + if connect_timeout or send_timeout or read_timeout then + red:set_timeouts(connect_timeout, send_timeout, read_timeout) + end + + local ok, err do + local socket = self.socket + if socket then + ok, err = red:connect(socket, self.options) + else + ok, err = red:connect(self.host, self.port, self.options) + end + end + if not ok then + return nil, err + end + + if red:get_reused_times() == 0 then + local password = self.password + if password then + local username = self.username + if username then + ok, err = red:auth(username, password) + else + ok, err = red:auth(password) + end + + if not ok then + red:close() + return nil, err + end + end + end + + local database = self.database + if database then + ok, err = red:select(database) + if not ok then + return nil, err + end + end + + ok, err = func(self, red, ...) + if err then + red:close() + return nil, err + end + + if not red:set_keepalive(self.keepalive_timeout) then + red:close() + end + + if ok == null then + ok = nil + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return exec(self, SET, ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return exec(self, GET, ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return exec(self, UNLINK, ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return exec(self, READ_METADATA, ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Redis storage backend configuration +-- @field prefix Prefix for the keys stored in Redis. +-- @field suffix Suffix for the keys stored in Redis. +-- @field host The host to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `6379`). +-- @field socket The socket file to connect to (defaults to `nil`). +-- @field username The database username to authenticate. +-- @field password Password for authentication. +-- @field database The database to connect. +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field server_name The server name for the new TLS extension Server Name Indication (SNI). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a Redis storage. +-- +-- This creates a new Redis storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration redis storage @{configuration} +-- @treturn table redis storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local host = configuration and configuration.host or DEFAULT_HOST + local port = configuration and configuration.port or DEFAULT_PORT + local socket = configuration and configuration.socket + + local username = configuration and configuration.username + local password = configuration and configuration.password + local database = configuration and configuration.database + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local server_name = configuration and configuration.server_name + + if ssl ~= nil or ssl_verify ~= nil or server_name or pool or pool_size or backlog then + return setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + socket = socket, + username = username, + password = password, + database = database, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + options = { + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + pool = pool, + pool_size = pool_size, + backlog = backlog, + } + }, metatable) + end + + return setmetatable({ + prefix = prefix, + suffix = suffix, + host = host, + port = port, + socket = socket, + username = username, + password = password, + database = database, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/redis/cluster.lua b/lib/resty/session/redis/cluster.lua new file mode 100644 index 000000000..adc0daacf --- /dev/null +++ b/lib/resty/session/redis/cluster.lua @@ -0,0 +1,271 @@ +--- +-- Redis Cluster backend for session library +-- +-- @module resty.session.redis.cluster + + +local common = require "resty.session.redis.common" +local redis = require "resty.rediscluster" + + +local setmetatable = setmetatable +local error = error +local null = ngx.null + + +local SET = common.SET +local GET = common.GET +local UNLINK = common.UNLINK +local READ_METADATA = common.READ_METADATA + + +local function exec(self, func, ...) + local red, err = redis:new(self.options) + if err then + return nil, err + end + + local ok, err = func(self, red, ...) + if err then + red:close() + return nil, err + end + + if ok == null then + ok = nil + end + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return exec(self, SET, ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return exec(self, GET, ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return exec(self, UNLINK, ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return exec(self, READ_METADATA, ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Redis Cluster storage backend configuration +-- @field prefix Prefix for the keys stored in redis. +-- @field suffix Suffix for the keys stored in redis. +-- @field name Redis cluster name. +-- @field nodes Redis cluster nodes. +-- @field lock_zone Shared dictionary name for locks. +-- @field lock_prefix Shared dictionary name prefix for lock. +-- @field max_redirections Maximum retry attempts for redirection. +-- @field max_connection_attempts Maximum retry attempts for connection. +-- @field max_connection_timeout Maximum connection timeout in total among the retries. +-- @field username The database username to authenticate. +-- @field password Password for authentication. +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout controls The default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout controls The default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSL (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field server_name The server name for the new TLS extension Server Name Indication (SNI). +-- @table configuration + + +--- +-- Cluster Nodes +-- +-- An array of cluster nodes. +-- +-- @table nodes + + +--- +-- Cluster Node +-- @field ip The IP address to connect (defaults to `"127.0.0.1"`). +-- @field port The port to connect (defaults to `6379`). +-- @table node + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a Redis Cluster storage. +-- +-- This creates a new Redis Cluster storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration redis cluster storage @{configuration} +-- @treturn table redis cluster storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local name = configuration and configuration.name + local nodes = configuration and configuration.nodes + + local lock_zone = configuration and configuration.lock_zone + local lock_prefix = configuration and configuration.lock_prefix + local max_redirections = configuration and configuration.max_redirections + local max_connection_attempts = configuration and configuration.max_connection_attempts + local max_connection_timeout = configuration and configuration.max_connection_timeout + + local username = configuration and configuration.username + local password = configuration and configuration.password + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local server_name = configuration and configuration.server_name + + local auth + if password then + if username then + auth = username .. " " .. password + else + auth = password + end + end + + if ssl ~= nil or ssl_verify ~= nil or server_name or pool or pool_size or backlog then + return setmetatable({ + prefix = prefix, + suffix = suffix, + options = { + name = name, + dict_name = lock_zone, + refresh_lock_key = lock_prefix, + serv_list = nodes, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + keepalive_cons = pool_size, + max_redirection = max_redirections, + max_connection_attempts = max_connection_attempts, + max_connection_timeout = max_connection_timeout, + auth = auth, + connect_opts = { + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + pool = pool, + pool_size = pool_size, + backlog = backlog, + }, + }, + }, metatable) + end + + return setmetatable({ + prefix = prefix, + suffix = suffix, + options = { + name = name, + dict_name = lock_zone, + refresh_lock_key = lock_prefix, + serv_list = nodes, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + keepalive_cons = pool_size, + max_redirection = max_redirections, + max_connection_attempts = max_connection_attempts, + max_connection_timeout = max_connection_timeout, + auth = auth, + }, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/redis/common.lua b/lib/resty/session/redis/common.lua new file mode 100644 index 000000000..993da5624 --- /dev/null +++ b/lib/resty/session/redis/common.lua @@ -0,0 +1,172 @@ +--- +-- Common Redis functions shared between Redis, +-- Redis Cluster and Redis Sentinel implementations. +-- +-- @module resty.session.redis.common + + +local utils = require "resty.session.utils" + + +local get_name = utils.get_name +local ipairs = ipairs + + +--- +-- Store session data. +-- +-- @function module.SET +-- @tparam table storage the storage +-- @tparam table red the redis instance +-- @tparam string name the cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam table remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +local function SET(storage, red, name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + if not metadata and not old_key then + return red:set(get_name(storage, name, key), value, "EX", ttl) + end + + local old_name + local old_ttl + if old_key then + old_name = get_name(storage, name, old_key) + if not remember then + -- redis < 7.0 + old_ttl = red:ttl(old_name) + end + end + + red:init_pipeline() + red:set(get_name(storage, name, key), value, "EX", ttl) + + -- redis < 7.0 + if old_name then + if remember then + red:unlink(old_name) + elseif not old_ttl or old_ttl > stale_ttl then + red:expire(old_name, stale_ttl) + end + end + + -- redis >= 7.0 + --if old_key then + -- if remember then + -- red:unlink(get_name(storage, name, old_key)) + -- else + -- red:expire(get_name(storage, name, old_key), stale_ttl, "LT") + -- end + --end + + if metadata then + local audiences = metadata.audiences + local subjects = metadata.subjects + local score = current_time - 1 + local new_score = current_time + ttl + local count = #audiences + for i = 1, count do + local meta_key = get_name(storage, name, audiences[i], subjects[i]) + red:zremrangebyscore(meta_key, 0, score) + red:zadd(meta_key, new_score, key) + if old_key then + red:zrem(meta_key, old_key) + end + red:expire(meta_key, ttl) + end + end + + return red:commit_pipeline() +end + + +--- +-- Retrieve session data. +-- +-- @function module.GET +-- @tparam table storage the storage +-- @tparam table red the redis instance +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +local function GET(storage, red, name, key) + return red:get(get_name(storage, name, key)) +end + + +--- +-- Delete session data. +-- +-- @function module.UNLINK +-- @tparam table storage the storage +-- @tparam table red the redis instance +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam number current_time current time +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +local function UNLINK(storage, red, name, key, current_time, metadata) + if not metadata then + return red:unlink(get_name(storage, name, key)) + end + + red:init_pipeline() + red:unlink(get_name(storage, name, key)) + local audiences = metadata.audiences + local subjects = metadata.subjects + local score = current_time - 1 + local count = #audiences + for i = 1, count do + local meta_key = get_name(storage, name, audiences[i], subjects[i]) + red:zremrangebyscore(meta_key, 0, score) + red:zrem(meta_key, key) + end + + return red:commit_pipeline() +end + + +--- +-- Read session metadata. +-- +-- @function module.READ_METADATA +-- @tparam table storage the storage +-- @tparam table red the redis instance +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +local function READ_METADATA(storage, red, name, audience, subject, current_time) + local sessions = {} + local meta_key = get_name(storage, name, audience, subject) + local res, err = red:zrange(meta_key, current_time, "+inf", "BYSCORE", "WITHSCORES") + if not res then + return nil, err + end + + for i, v in ipairs(res) do + if i % 2 ~= 0 then + sessions[v] = res[i + 1] + end + end + + return sessions +end + + +return { + SET = SET, + GET = GET, + UNLINK = UNLINK, + READ_METADATA = READ_METADATA, +} diff --git a/lib/resty/session/redis/sentinel.lua b/lib/resty/session/redis/sentinel.lua new file mode 100644 index 000000000..73db463fd --- /dev/null +++ b/lib/resty/session/redis/sentinel.lua @@ -0,0 +1,260 @@ +--- +-- Redis Sentinel backend for session library +-- +-- @module resty.session.redis.sentinel + + +local common = require "resty.session.redis.common" +local redis = require "resty.redis.connector" + + +local setmetatable = setmetatable +local error = error +local null = ngx.null + + +local SET = common.SET +local GET = common.GET +local UNLINK = common.UNLINK +local READ_METADATA = common.READ_METADATA + + +local function exec(self, func, ...) + local red, err = self.connector:connect() + if not red then + return nil, err + end + + local ok, err = func(self, red, ...) + if err then + red:close() + return nil, err + end + + if ok == null then + ok = nil + end + + self.connector:set_keepalive(red) + + return ok, err +end + + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam table remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(...) + return exec(self, SET, ...) +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(...) + return exec(self, GET, ...) +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(...) + return exec(self, UNLINK, ...) +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(...) + return exec(self, READ_METADATA, ...) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Redis Sentinel storage backend configuration +-- @field prefix Prefix for the keys stored in redis. +-- @field suffix Suffix for the keys stored in redis. +-- @field master Name of master. +-- @field role `"master"` or `"slave"`. +-- @field sentinels Redis Sentinels. +-- @field sentinel_username Optional sentinel username. +-- @field sentinel_password Optional sentinel password. +-- @field username The database username to authenticate. +-- @field password Password for authentication. +-- @field database The database to connect. +-- @field connect_timeout Controls the default timeout value used in TCP/unix-domain socket object's `connect` method. +-- @field send_timeout Controls the default timeout value used in TCP/unix-domain socket object's `send` method. +-- @field read_timeout Controls the default timeout value used in TCP/unix-domain socket object's `receive` method. +-- @field keepalive_timeout Controls the default maximal idle time of the connections in the connection pool. +-- @field pool A custom name for the connection pool being used. +-- @field pool_size The size of the connection pool. +-- @field backlog A queue size to use when the connection pool is full (configured with @pool_size). +-- @field ssl Enable SSK (defaults to `false`). +-- @field ssl_verify Verify server certificate (defaults to `nil`). +-- @field server_name The server name for the new TLS extension Server Name Indication (SNI). +-- @table configuration + + +--- +-- Sentinels +-- +-- An array of sentinels. +-- +-- @table sentinels + + +--- +-- Sentinel +-- @field host The host to connect. +-- @field port The port to connect. +-- @table sentinel + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a Redis Sentinel storage. +-- +-- This creates a new Redis Sentinel storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration redis sentinel storage @{configuration} +-- @treturn table redis sentinel storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + + local master = configuration and configuration.master + local role = configuration and configuration.role + local sentinels = configuration and configuration.sentinels + local sentinel_username = configuration and configuration.sentinel_username + local sentinel_password = configuration and configuration.sentinel_password + + local username = configuration and configuration.username + local password = configuration and configuration.password + local database = configuration and configuration.database + + local connect_timeout = configuration and configuration.connect_timeout + local send_timeout = configuration and configuration.send_timeout + local read_timeout = configuration and configuration.read_timeout + local keepalive_timeout = configuration and configuration.keepalive_timeout + + local pool = configuration and configuration.pool + local pool_size = configuration and configuration.pool_size + local backlog = configuration and configuration.backlog + local ssl = configuration and configuration.ssl + local ssl_verify = configuration and configuration.ssl_verify + local server_name = configuration and configuration.server_name + + local connector + if ssl ~= nil or ssl_verify ~= nil or server_name or pool or pool_size or backlog then + connector = redis.new({ + master_name = master, + role = role, + sentinels = sentinels, + sentinel_username = sentinel_username, + sentinel_password = sentinel_password, + username = username, + password = password, + db = database, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + keepalive_poolsize = pool_size, + connection_options = { + ssl = ssl, + ssl_verify = ssl_verify, + server_name = server_name, + pool = pool, + pool_size = pool_size, + backlog = backlog, + } + }) + else + connector = redis.new({ + master_name = master, + role = role, + sentinels = sentinels, + sentinel_username = sentinel_username, + sentinel_password = sentinel_password, + username = username, + password = password, + db = database, + connect_timeout = connect_timeout, + send_timeout = send_timeout, + read_timeout = read_timeout, + keepalive_timeout = keepalive_timeout, + keepalive_poolsize = pool_size, + }) + end + + return setmetatable({ + prefix = prefix, + suffix = suffix, + connector = connector, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/shm.lua b/lib/resty/session/shm.lua new file mode 100644 index 000000000..1f5690932 --- /dev/null +++ b/lib/resty/session/shm.lua @@ -0,0 +1,315 @@ +--- +-- Shared Memory (SHM) backend for session library +-- +-- @module resty.session.shm + + +local table_new = require "table.new" +local utils = require "resty.session.utils" + + +local meta_get_value = utils.meta_get_value +local meta_get_next = utils.meta_get_next +local get_name = utils.get_name +local errmsg = utils.errmsg + + +local setmetatable = setmetatable +local shared = ngx.shared +local random = math.random +local assert = assert +local error = error +local pairs = pairs +local max = math.max +local log = ngx.log + + +local WARN = ngx.WARN + + +local DEFAULT_ZONE = "sessions" +local CLEANUP_PROBABILITY = 0.1 -- 1 / 10 + + +local function get_and_clean_metadata(dict, meta_key, current_time) + local size = dict:llen(meta_key) + if not size or size == 0 then + return + end + + local max_expiry = current_time + local sessions = table_new(0, size) + + for _ = 1, size do + local meta_value, err = dict:lpop(meta_key) + if not meta_value then + log(WARN, "[session] ", errmsg(err, "failed read meta value")) + break + end + + local key, err, exp = meta_get_next(meta_value, 1) + if err then + return nil, err + end + + if exp and exp > current_time then + sessions[key] = exp + max_expiry = max(max_expiry, exp) + + else + sessions[key] = nil + end + end + + for key, exp in pairs(sessions) do + local meta_value = meta_get_value(key, exp) + local ok, err = dict:rpush(meta_key, meta_value) + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to update metadata")) + end + end + + local exp = max_expiry - current_time + if exp > 0 then + local ok, err = dict:expire(meta_key, max_expiry - current_time) + if not ok and err ~= "not found" then + log(WARN, "[session] ", errmsg(err, "failed to touch metadata")) + end + + else + dict:delete(meta_key) + end + + return sessions +end + + +local function cleanup(dict, meta_key, current_time) + get_and_clean_metadata(dict, meta_key, current_time) +end + + +local function read_metadata(self, meta_key, current_time) + return get_and_clean_metadata(self.dict, meta_key, current_time) +end + +--- +-- Storage +-- @section instance + + +local metatable = {} + + +metatable.__index = metatable + + +function metatable.__newindex() + error("attempt to update a read-only table", 2) +end + + +--- +-- Store session data. +-- +-- @function instance:set +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam string value session value +-- @tparam number ttl session ttl +-- @tparam number current_time current time +-- @tparam[opt] string old_key old session id +-- @tparam string stale_ttl stale ttl +-- @tparam[opt] table metadata table of metadata +-- @tparam boolean remember whether storing persistent session or not +-- @treturn true|nil ok +-- @treturn string error message +function metatable:set(name, key, value, ttl, current_time, old_key, stale_ttl, metadata, remember) + local dict = self.dict + if not metadata and not old_key then + local ok, err = dict:set(get_name(self, name, key), value, ttl) + if not ok then + return nil, err + end + + return true + end + + local old_name, old_ttl + if old_key then + old_name = get_name(self, name, old_key) + if not remember then + old_ttl = dict:ttl(old_name) + end + end + + local ok, err = dict:set(get_name(self, name, key), value, ttl) + if not ok then + return nil, err + end + + if old_name then + if remember then + dict:delete(old_name) + + elseif (not old_ttl or old_ttl > stale_ttl) then + local ok, err = dict:expire(old_name, stale_ttl) + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to touch old session")) + end + end + end + + if not metadata then + return true + end + + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + local meta_value = meta_get_value(key, current_time + ttl) + + local ok, err = dict:rpush(meta_key, meta_value) + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to update metadata")) + end + + if old_key then + meta_value = meta_get_value(old_key, 0) + local ok, err = dict:rpush(meta_key, meta_value) + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to update old metadata")) + end + end + + -- no need to clean up every time we write + -- it is just beneficial when a key is used a lot + if random() < CLEANUP_PROBABILITY then + cleanup(dict, meta_key, current_time) + end + end + + return true +end + + +--- +-- Retrieve session data. +-- +-- @function instance:get +-- @tparam string name cookie name +-- @tparam string key session key +-- @treturn string|nil session data +-- @treturn string error message +function metatable:get(name, key) + local value, err = self.dict:get(get_name(self, name, key)) + if not value then + return nil, err + end + + return value +end + + +--- +-- Delete session data. +-- +-- @function instance:delete +-- @tparam string name cookie name +-- @tparam string key session key +-- @tparam[opt] table metadata session meta data +-- @treturn boolean|nil session data +-- @treturn string error message +function metatable:delete(name, key, current_time, metadata) + local dict = self.dict + + dict:delete(get_name(self, name, key)) + + if not metadata then + return true + end + + local audiences = metadata.audiences + local subjects = metadata.subjects + local count = #audiences + for i = 1, count do + local meta_key = get_name(self, name, audiences[i], subjects[i]) + local meta_value = meta_get_value(key, 0) + + local ok, err = dict:rpush(meta_key, meta_value) + if not ok then + if not ok then + log(WARN, "[session] ", errmsg(err, "failed to update metadata")) + end + end + + cleanup(dict, meta_key, current_time) + end + + return true +end + + +--- +-- Read session metadata. +-- +-- @function instance:read_metadata +-- @tparam string name cookie name +-- @tparam string audience session key +-- @tparam string subject session key +-- @tparam number current_time current time +-- @treturn table|nil session metadata +-- @treturn string error message +function metatable:read_metadata(name, audience, subject, current_time) + local meta_key = get_name(self, name, audience, subject) + return read_metadata(self, meta_key, current_time) +end + + +local storage = {} + + +--- +-- Configuration +-- @section configuration + + +--- +-- Shared memory storage backend configuration +-- @field prefix Prefix for the keys stored in SHM. +-- @field suffix Suffix for the keys stored in SHM. +-- @field zone A name of shared memory zone (defaults to `sessions`). +-- @table configuration + + +--- +-- Constructors +-- @section constructors + + +--- +-- Create a SHM storage. +-- +-- This creates a new shared memory storage instance. +-- +-- @function module.new +-- @tparam[opt] table configuration shm storage @{configuration} +-- @treturn table shm storage instance +function storage.new(configuration) + local prefix = configuration and configuration.prefix + local suffix = configuration and configuration.suffix + local zone = configuration and configuration.zone or DEFAULT_ZONE + + local dict = assert(shared[zone], "lua_shared_dict " .. zone .. " is missing") + + return setmetatable({ + prefix = prefix, + suffix = suffix, + dict = dict, + }, metatable) +end + + +return storage diff --git a/lib/resty/session/utils.lua b/lib/resty/session/utils.lua new file mode 100644 index 000000000..662e4967c --- /dev/null +++ b/lib/resty/session/utils.lua @@ -0,0 +1,1121 @@ +--- +-- Common utilities for session library and storage backends +-- +-- @module resty.session.utils + + +local require = require + + +local buffer = require "string.buffer" +local bit = require "bit" + + +local select = select +local floor = math.floor +local ceil = math.ceil +local byte = string.byte +local char = string.char +local band = bit.band +local bnot = bit.bnot +local bor = bit.bor +local fmt = string.format +local sub = string.sub + + +local is_fips_mode do + local IS_FIPS + + local function is_fips_mode_real() + return IS_FIPS == true + end + + --- + -- Returns whether OpenSSL is in FIPS-mode. + -- + -- @function utils.is_fips_mode + -- @treturn boolean `true` if OpenSSL is in FIPS-mode, otherwise `false` + -- + -- @usage + -- local is_fips = require "resty.session.utils".is_fips_mode() + is_fips_mode = function() + IS_FIPS = require("resty.openssl").get_fips_mode() + is_fips_mode = is_fips_mode_real + return is_fips_mode() + end +end + + +local bpack, bunpack do + local buf = buffer.new() + + --- + -- Binary pack unsigned integer. + -- + -- Returns binary packed version of an integer in little endian unsigned format. + -- + -- Size can be: + -- + -- * `1`, pack input as a little endian unsigned char (`<C`) + -- * `2`, pack input as a little endian unsigned short (`<S`) + -- * `3`, pack input as a little endian unsigned integer (truncated) (`<I`) + -- * `4`, pack input as a little endian unsigned integer (`<I`) + -- * `5`, pack input as a little endian unsigned long (truncated) (`<L`) + -- * `6`, pack input as a little endian unsigned long (truncated) (`<L`) + -- * `7`, pack input as a little endian unsigned long (truncated) (`<L`) + -- * `8`, pack input as a little endian unsigned long (`<L`) + -- + -- @function utils.bpack + -- @tparam number size size of binary packed output + -- @tparam number value value to binary pack + -- @treturn string binary packed value + -- + -- @usage + -- local packed_128 = require "resty.session.utils".bpack(1, 128) + -- local packed_now = require "resty.session.utils".bpack(8, ngx.time()) + bpack = function(size, value) + buf:reset() + for i = 1, size do + buf:put(char(value % 256)) + if i == size then + return buf:get() + end + value = floor(value / 256) + end + end + + --- + -- Binary unpack unsigned integer. + -- + -- Returns number from a little endian unsigned binary packed format. + -- + -- Size can be: + -- + -- * `1`, unpack input from little endian unsigned char (`<C`) + -- * `2`, unpack input from little endian unsigned short (`<S`) + -- * `3`, unpack input from little endian unsigned integer (truncated) (`<I`) + -- * `4`, unpack input from little endian unsigned integer (`<I`) + -- * `5`, unpack input from little endian unsigned integer (truncated) (`<L`) + -- * `6`, unpack input from little endian unsigned integer (truncated) (`<L`) + -- * `7`, unpack input from little endian unsigned integer (truncated) (`<L`) + -- * `8`, unpack input from little endian unsigned long (`<L`) + -- + -- @function utils.bunpack + -- @tparam number size size of binary packed output + -- @tparam number value value to binary pack + -- @treturn number binary unpacked value + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local value = 128 + -- local packed_value = utils.bpack(1, value) + -- local unpacked_value = utils.bunpack(1, packed_value) + -- print(value == unpacked_value) -- true + bunpack = function(size, value) + buf:set(value) + value = 0 + for i = 1, size do + value = value + byte(buf:get(1)) * (2 ^ ((i - 1) * 8)) + end + return value + end +end + + +local trim do + local SPACE_BYTE = byte(" ") + local TAB_BYTE = byte("\t") + local CR_BYTE = byte("\r") + local LF_BYTE = byte("\n") + local VTAB_BYTE = byte("\v") + local FF_BYTE = byte("\f") + + --- + -- Trim whitespace from the start and from the end of string. + -- + -- Characters that are trimmed: + -- + -- * space `" "` + -- * tab `"\t"` + -- * carriage return `"\r"` + -- * line feed `"\n"` + -- * vertical tab `"\v"` + -- * form feed `"\f"` + -- + -- @function utils.trim + -- @tparam string value string to trim + -- @treturn string a whitespace trimmed string + -- + -- @usage + -- local trimmed = require "resty.session.utils".trim(" hello world ") + trim = function(value) + if value == nil or value == "" then + return "" + end + + local len = #value + + local s = 1 + for i = 1, len do + local b = byte(value, i) + if b == SPACE_BYTE + or b == TAB_BYTE + or b == CR_BYTE + or b == LF_BYTE + or b == VTAB_BYTE + or b == FF_BYTE + then + s = s + 1 + else + break + end + end + + local e = len + for i = len, 1, -1 do + local b = byte(value, i) + if b == SPACE_BYTE + or b == TAB_BYTE + or b == CR_BYTE + or b == LF_BYTE + or b == VTAB_BYTE + or b == FF_BYTE + then + e = e - 1 + else + break + end + end + + if s ~= 1 or e ~= len then + return sub(value, s, e) + end + + return value + end +end + + +local encode_json, decode_json do + local cjson + + --- + -- JSON encode value. + -- + -- @function utils.encode_json + -- @tparam any value value to json encode + -- @treturn string json encoded value + -- + -- @usage + -- local json = require "resty.session.utils".encode_json({ hello = "world" }) + encode_json = function(value) + if not cjson then + cjson = require "cjson.safe".new() + end + encode_json = cjson.encode + return encode_json(value) + end + + --- + -- JSON decode value. + -- + -- @function utils.decode_json + -- @tparam string value string to json decode + -- @treturn any json decoded value + -- + -- @usage + -- local tbl = require "resty.session.utils".decode_json('{ "hello": "world" }') + decode_json = function(value) + if not cjson then + cjson = require "cjson.safe".new() + end + decode_json = cjson.decode + return decode_json(value) + end +end + + +local encode_base64url, decode_base64url, base64_size do + local base64 + + --- + -- Base64 URL encode value. + -- + -- @function utils.encode_base64url + -- @tparam string value string to base64 url encode + -- @treturn string base64 url encoded value + -- + -- @usage + -- local encoded = require "resty.session.utils".encode_base64url("test") + encode_base64url = function(value) + if not base64 then + base64 = require "ngx.base64" + end + encode_base64url = base64.encode_base64url + return encode_base64url(value) + end + + --- + -- Base64 URL decode value + -- + -- @function utils.decode_base64url + -- @tparam string value string to base64 url decode + -- @treturn string base64 url decoded value + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local encoded = utils.encode_base64url("test") + -- local decoded = utils.decode_base64url(encoded) + decode_base64url = function(value) + if not base64 then + base64 = require "ngx.base64" + end + decode_base64url = base64.decode_base64url + return decode_base64url(value) + end + + --- + -- Base64 size from original size (without padding). + -- + -- @function utils.base64_size + -- @tparam number size original size + -- @treturn number base64 url encoded size without padding + -- + -- @usage + -- local test = "test" + -- local b64len = require "resty.session.utils".base64_size(#test) + base64_size = function(size) + return ceil(4 * size / 3) + end +end + + +local deflate, inflate do + local DEFLATE_WINDOW_BITS = -15 + local DEFLATE_CHUNK_SIZE = 8192 + local DEFLATE_OPTIONS = { + windowBits = DEFLATE_WINDOW_BITS, + } + + local zlib + local input_buffer = buffer.new() + local output_buffer = buffer.new() + + local function prepare_buffers(input) + input_buffer:set(input) + output_buffer:reset() + end + + local function read_input_buffer(size) + local data = input_buffer:get(size) + return data ~= "" and data or nil + end + + local function write_output_buffer(data) + return output_buffer:put(data) + end + + local function gzip(inflate_or_deflate, input, chunk_size, window_bits_or_options) + prepare_buffers(input) + local ok, err = inflate_or_deflate(read_input_buffer, write_output_buffer, + chunk_size, window_bits_or_options) + if not ok then + return nil, err + end + + return output_buffer:get() + end + + local function deflate_real(data) + return gzip(zlib.deflateGzip, data, DEFLATE_CHUNK_SIZE, DEFLATE_OPTIONS) + end + + local function inflate_real(data) + return gzip(zlib.inflateGzip, data, DEFLATE_CHUNK_SIZE, DEFLATE_WINDOW_BITS) + end + + + --- + -- Compress the data with deflate algorithm. + -- + -- @function utils.deflate + -- @tparam string data data to deflate + -- @treturn string deflated data + -- + -- @usage + -- local test = "test" + -- local deflated = require "resty.session.utils".deflate(("a"):rep(100)) + deflate = function(data) + if not zlib then + zlib = require "ffi-zlib" + end + deflate = deflate_real + return deflate(data) + end + + --- + -- Decompress the data compressed with deflate algorithm. + -- + -- @function utils.inflate + -- @tparam string data data to inflate + -- @treturn string inflated data + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local deflated = utils.deflate(("a"):rep(100)) + -- local inflated = utils.inflate(deflated) + inflate = function(data) + if not zlib then + zlib = require "ffi-zlib" + end + inflate = inflate_real + return inflate(data) + end +end + + +local rand_bytes do + local rand + + --- + -- Generate crypto random bytes. + -- + -- @function utils.rand_bytes + -- @tparam number length how many bytes of random data to generate + -- @treturn string|nil random bytes + -- @treturn string|nil error message + -- + -- @usage + -- local bytes = require "resty.session.utils".rand_bytes(32) + rand_bytes = function(length) + if not rand then + rand = require "resty.openssl.rand" + end + rand_bytes = rand.bytes + return rand_bytes(length) + end +end + + +local sha256 do + local digest + local sha256_digest + + local function sha256_real(value) + local _, err, output + if not sha256_digest then + sha256_digest, err = digest.new("sha256") + if err then + return nil, err + end + end + + output, err = sha256_digest:final(value) + if err then + sha256_digest = nil + return nil, err + end + + _, err = sha256_digest:reset() + if err then + sha256_digest = nil + end + + return output + end + + --- + -- Calculates SHA-256 hash of the value. + -- + -- @function utils.sha256 + -- @tparam string value value from which to calculate hash + -- @treturn string|nil sha-256 hash (32 bytes) + -- @treturn string|nil error message + -- + -- @usage + -- local hash, err = require "resty.session.utils".sha256("hello world") + sha256 = function(value) + if not digest then + digest = require "resty.openssl.digest" + end + sha256 = sha256_real + return sha256(value) + end +end + + +local derive_hkdf_sha256 do + local kdf_derive + + local EXTRACTED_KEYS = {} + + local HKDF_SHA256_EXTRACT_OPTS + local HKDF_SHA256_EXPAND_OPTS + + local function derive_hkdf_sha256_real(ikm, nonce, usage, size) + local err + local key = EXTRACTED_KEYS[ikm] + if not key then + HKDF_SHA256_EXTRACT_OPTS.hkdf_key = ikm + key, err = kdf_derive(HKDF_SHA256_EXTRACT_OPTS) + HKDF_SHA256_EXTRACT_OPTS.hkdf_key = "" + if not key then + return nil, err + end + EXTRACTED_KEYS[ikm] = key + end + + HKDF_SHA256_EXPAND_OPTS.hkdf_key = key + HKDF_SHA256_EXPAND_OPTS.hkdf_info = usage .. ":" .. nonce + HKDF_SHA256_EXPAND_OPTS.outlen = size + key, err = kdf_derive(HKDF_SHA256_EXPAND_OPTS) + if not key then + return nil, err + end + + return key + end + + --- + -- Derive a new key using HKDF with SHA-256. + -- + -- @function utils.derive_hkdf_sha256 + -- @tparam string ikm initial key material + -- @tparam string nonce nonce + -- @tparam string usage e.g. `"encryption"` or `"authentication"` + -- @tparam number size how many bytes to return + -- @treturn string|nil key material + -- @treturn string|nil error message + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local ikm = utils.rand_bytes(32) + -- local nonce = utils.rand_bytes(32) + -- local key, err = utils.derive_hkdf_sha256(ikm, nonce, "encryption", 32) + derive_hkdf_sha256 = function(ikm, nonce, usage, size) + if not kdf_derive then + local kdf = require "resty.openssl.kdf" + HKDF_SHA256_EXTRACT_OPTS = { + type = kdf.HKDF, + outlen = 32, + md = "sha256", + salt = "", + hkdf_key = "", + hkdf_mode = kdf.HKDEF_MODE_EXTRACT_ONLY, + hkdf_info = "", + } + HKDF_SHA256_EXPAND_OPTS = { + type = kdf.HKDF, + outlen = 44, + md = "sha256", + salt = "", + hkdf_key = "", + hkdf_mode = kdf.HKDEF_MODE_EXPAND_ONLY, + hkdf_info = "", + } + kdf_derive = kdf.derive + end + derive_hkdf_sha256 = derive_hkdf_sha256_real + return derive_hkdf_sha256(ikm, nonce, usage, size) + end +end + + +local derive_pbkdf2_hmac_sha256 do + local kdf_derive + + local PBKDF2_SHA256_OPTS + + local function derive_pbkdf2_hmac_sha256_real(pass, salt, usage, size, iterations) + PBKDF2_SHA256_OPTS.pass = pass + PBKDF2_SHA256_OPTS.salt = usage .. ":" .. salt + PBKDF2_SHA256_OPTS.outlen = size + PBKDF2_SHA256_OPTS.pbkdf2_iter = iterations + local key, err = kdf_derive(PBKDF2_SHA256_OPTS) + if not key then + return nil, err + end + + return key + end + + --- + -- Derive a new key using PBKDF2 with SHA-256. + -- + -- @function utils.derive_pbkdf2_hmac_sha256 + -- @tparam string pass password + -- @tparam string salt salt + -- @tparam string usage e.g. `"encryption"` or `"authentication"` + -- @tparam number size how many bytes to return + -- @tparam number iterations how many iterations to run, e.g. `10000` + -- @treturn string|nil key material + -- @treturn string|nil error message + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local pass = "my-super-secret-password" + -- local salt = utils.rand_bytes(32) + -- local key, err = utils.derive_pbkdf2_hmac_sha256(pass, salt, "encryption", 32, 10000) + derive_pbkdf2_hmac_sha256 = function(pass, salt, usage, size, iterations) + if not kdf_derive then + local kdf = require "resty.openssl.kdf" + PBKDF2_SHA256_OPTS = { + type = kdf.PBKDF2, + outlen = 44, + md = "sha256", + pass = "", + salt = "", + pbkdf2_iter = 10000, + } + kdf_derive = kdf.derive + end + derive_pbkdf2_hmac_sha256 = derive_pbkdf2_hmac_sha256_real + return derive_pbkdf2_hmac_sha256(pass, salt, usage, size, iterations) + end +end + + +--- +-- Derive a new AES-256 GCM-mode key and initialization vector. +-- +-- Safety can be: +-- * `nil` or `"None"`: key and iv will be derived using HKDF with SHA-256, except on FIPS-mode uses PBKDF2 with SHA-256 (single iteration) +-- * `Low`: key and iv will be derived using PBKDF2 with SHA-256 (1.000 iterations) +-- * `Medium`: key and iv will be derived using PBKDF2 with SHA-256 (10.000 iterations) +-- * `High`: key and iv will be derived using PBKDF2 with SHA-256 (100.000 iterations) +-- * `Very High`: key and iv will be derived using PBKDF2 with SHA-256 (1.000.000 iterations) +-- +-- @function utils.derive_aes_gcm_256_key_and_iv +-- @tparam string ikm initial key material +-- @tparam string nonce nonce +-- @tparam[opt] string safety safety of key generation +-- @treturn string|nil key +-- @treturn string|nil error message +-- @treturn string|nil initialization vector +-- +-- @usage +-- local utils = require "resty.session.utils" +-- local ikm = utils.rand_bytes(32) +-- local nonce = utils.rand_bytes(32) +-- local key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce, "Medium") +local function derive_aes_gcm_256_key_and_iv(ikm, nonce, safety) + local bytes, err + if safety and safety ~= "None" then + if safety == "Very High" then + bytes, err = derive_pbkdf2_hmac_sha256(ikm, nonce, "encryption", 44, 1000000) + elseif safety == "High" then + bytes, err = derive_pbkdf2_hmac_sha256(ikm, nonce, "encryption", 44, 100000) + elseif safety == "Low" then + bytes, err = derive_pbkdf2_hmac_sha256(ikm, nonce, "encryption", 44, 1000) + else + bytes, err = derive_pbkdf2_hmac_sha256(ikm, nonce, "encryption", 44, 10000) + end + + else + if is_fips_mode() then + bytes, err = derive_pbkdf2_hmac_sha256(ikm, nonce, "encryption", 44, 1) + else + bytes, err = derive_hkdf_sha256(ikm, nonce, "encryption", 44) + end + end + + if not bytes then + return nil, err + end + + local key = sub(bytes, 1, 32) -- 32 bytes + local iv = sub(bytes, 33, 44) -- 12 bytes + + return key, nil, iv +end + + +--- +-- Derive HMAC SHA-256 key for message authentication using HDKF with SHA-256, +-- except on FIPS-mode it uses PBKDF2 with SHA-256 (single iteration). +-- +-- @function utils.derive_hmac_sha256_key +-- @tparam string ikm initial key material +-- @tparam string nonce nonce +-- @treturn string|nil key +-- @treturn string|nil error message +-- +-- @usage +-- local utils = require "resty.session.utils" +-- local ikm = utils.rand_bytes(32) +-- local nonce = utils.rand_bytes(32) +-- local key, err = utils.derive_hmac_sha256_key(ikm, nonce) +local function derive_hmac_sha256_key(ikm, nonce) + if is_fips_mode() then + return derive_pbkdf2_hmac_sha256(ikm, nonce, "authentication", 32, 1) + end + + return derive_hkdf_sha256(ikm, nonce, "authentication", 32) +end + + +local encrypt_aes_256_gcm, decrypt_aes_256_gcm do + local AES_256_GCP_CIPHER = "aes-256-gcm" + local AES_256_GCM_TAG_SIZE = 16 + + local cipher_aes_256_gcm + local function encrypt_aes_256_gcm_real(key, iv, plaintext, aad) + local ciphertext, err = cipher_aes_256_gcm:encrypt(key, iv, plaintext, false, aad) + if not ciphertext then + return nil, err + end + + local tag, err = cipher_aes_256_gcm:get_aead_tag(AES_256_GCM_TAG_SIZE) + if not tag then + return nil, err + end + + return ciphertext, nil, tag + end + + local function decrypt_aes_256_gcm_real(key, iv, ciphertext, aad, tag) + return cipher_aes_256_gcm:decrypt(key, iv, ciphertext, false, aad, tag) + end + + --- + -- Encrypt plain text using AES-256 in GCM-mode. + -- + -- @function utils.encrypt_aes_256_gcm + -- @tparam string key encryption key + -- @tparam string iv initialization vector + -- @tparam string plaintext plain text + -- @tparam string aad additional authenticated data + -- @treturn string|nil ciphertext + -- @treturn string|nil error message + -- @treturn string|nil authentication tag + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local ikm = utils.rand_bytes(32) + -- local nonce = utils.rand_bytes(32) + -- local key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce) + -- local enc, err, tag = utils.encrypt_aes_256_gcm(key, iv, "hello", "john@doe.com") + encrypt_aes_256_gcm = function(key, iv, plaintext, aad) + if not cipher_aes_256_gcm then + cipher_aes_256_gcm = require("resty.openssl.cipher").new(AES_256_GCP_CIPHER) + end + encrypt_aes_256_gcm = encrypt_aes_256_gcm_real + return encrypt_aes_256_gcm(key, iv, plaintext, aad) + end + + --- + -- Decrypt ciphertext using AES-256 in GCM-mode. + -- + -- @function utils.decrypt_aes_256_gcm + -- @tparam string key encryption key + -- @tparam string iv initialization vector + -- @tparam string plaintext plain text + -- @tparam string aad additional authenticated data + -- @tparam string tag authentication tag + -- @treturn string|nil ciphertext + -- @treturn string|nil error message + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local ikm = utils.rand_bytes(32) + -- local nonce = utils.rand_bytes(32) + -- local key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce) + -- local enc, err, tag = utils.encrypt_aes_256_gcm(key, iv, "hello", "john@doe.com") + -- local out, err = utils.decrypt_aes_256_gcm(key, iv, ciphertext, "john@doe.com", tag) + decrypt_aes_256_gcm = function(key, iv, ciphertext, aad, tag) + if not cipher_aes_256_gcm then + cipher_aes_256_gcm = require("resty.openssl.cipher").new(AES_256_GCP_CIPHER) + end + decrypt_aes_256_gcm = decrypt_aes_256_gcm_real + return decrypt_aes_256_gcm(key, iv, ciphertext, aad, tag) + end +end + + +local hmac_sha256 do + local hmac + local HMAC_SHA256_DIGEST = "sha256" + + local function hmac_sha256_real(key, value) + local mac, err = hmac.new(key, HMAC_SHA256_DIGEST) + if not mac then + return nil, err + end + + local digest, err = mac:final(value) + if not digest then + return nil, err + end + + return digest + end + + --- + -- Calculate message authentication code with HMAC with SHA-256. + -- + -- @function utils.hmac_sha256 + -- @tparam string key key + -- @tparam string value value + -- @treturn string|nil message authentication code (32 bytes) + -- @treturn string|nil error message + -- + -- @usage + -- local utils = require "resty.session.utils" + -- local ikm = utils.rand_bytes(32) + -- local nonce = utils.rand_bytes(32) + -- local key, err = utils.derive_hmac_sha256_key(ikm, nonce) + -- local mac, err = utils.hmac_sha256(key, "hello") + hmac_sha256 = function(key, value) + if not hmac then + hmac = require "resty.openssl.hmac" + end + hmac_sha256 = hmac_sha256_real + return hmac_sha256(key, value) + end +end + + + +local load_storage do + local DSHM + local FILE + local MEMCACHED + local MYSQL + local POSTGRES + local REDIS + local REDIS_SENTINEL + local REDIS_CLUSTER + local SHM + local CUSTOM = {} + + --- + -- Loads session storage and creates a new instance using session configuration. + -- + -- @function utils.load_storage + -- @tparam string storage name of the storage to load + -- @tparam[opt] table configuration session configuration + -- @treturn table|nil instance of session storage + -- @treturn string|nil error message + -- + -- @usage + -- local postgres = require "resty.session.utils".load_storage("postgres", { + -- postgres = { + -- host = "127.0.0.1", + -- } + -- }) + load_storage = function(storage, configuration) + if storage == "cookie" then + return nil + + elseif storage == "dshm" then + if not DSHM then + DSHM = require("resty.session.dshm") + end + return DSHM.new(configuration and configuration.dshm) + + elseif storage == "file" then + if not FILE then + FILE = require("resty.session.file") + end + return FILE.new(configuration and configuration.file) + + elseif storage == "memcached" or storage == "memcache" then + if not MEMCACHED then + MEMCACHED = require("resty.session.memcached") + end + return MEMCACHED.new(configuration and configuration.memcached or configuration.memcache) + + elseif storage == "mysql" or storage == "mariadb" then + if not MYSQL then + MYSQL = require("resty.session.mysql") + end + return MYSQL.new(configuration and configuration.mysql or configuration.mariadb) + + elseif storage == "postgres" or storage == "postgresql" then + if not POSTGRES then + POSTGRES = require("resty.session.postgres") + end + return POSTGRES.new(configuration and configuration.postgres or configuration.postgresql) + + elseif storage == "redis" then + local cfg = configuration and configuration.redis + if cfg then + if cfg.nodes then + if not REDIS_CLUSTER then + REDIS_CLUSTER = require("resty.session.redis.cluster") + end + return REDIS_CLUSTER.new(cfg) + + elseif cfg.sentinels then + if not REDIS_SENTINEL then + REDIS_SENTINEL = require("resty.session.redis.sentinel") + end + return REDIS_SENTINEL.new(cfg) + end + end + + if not REDIS then + REDIS = require("resty.session.redis") + end + return REDIS.new(cfg) + + elseif storage == "shm" then + if not SHM then + SHM = require("resty.session.shm") + end + + return SHM.new(configuration and configuration.shm) + + else + if not CUSTOM[storage] then + CUSTOM[storage] = require(storage) + end + return CUSTOM[storage].new(configuration and configuration[storage]) + end + end +end + + +--- +-- Helper to format error messages. +-- +-- @function utils.errmsg +-- @tparam[opt] string|nil err a possible error coming from underlying library +-- @tparam string|nil msg error message +-- @tparam any ... arguments for formatting the msg +-- @treturn string formatted error message +-- +-- @usage +-- local utils = require "resty.session.utils" +-- local test = "aaaa" +-- local data, err = utils.deflate(test) +-- if not data then +-- print(utils.errmsg(err, "unable to deflate data '%s'", test) +-- end +local function errmsg(err, msg, ...) + if not msg then + if err then + return err + end + + return "unknown error" + end + + if select("#", ...) > 0 then + msg = fmt(msg, ...) + end + + if err then + return fmt("%s (%s)", msg, err) + end + + return msg +end + + +--- +-- Helper to create a delimited key. +-- +-- @function utils.get_name +-- @tparam table storage a storage implementation +-- @tparam string name name +-- @tparam string key key +-- @tparam string subject subject +-- @treturn string formatted and delimited name +local function get_name(storage, name, key, subject) + local prefix = storage.prefix + local suffix = storage.suffix + if prefix and suffix and subject then + return fmt("%s:%s:%s:%s:%s", prefix, name, key, subject, suffix) + elseif prefix and suffix then + return fmt("%s:%s:%s:%s", prefix, name, key, suffix) + elseif prefix and subject then + return fmt("%s:%s:%s:%s", prefix, name, key, subject) + elseif suffix and subject then + return fmt("%s:%s:%s:%s", name, key, subject, suffix) + elseif prefix then + return fmt("%s:%s:%s", prefix, name, key) + elseif suffix then + return fmt("%s:%s:%s", name, key, suffix) + elseif subject then + return fmt("%s:%s:%s", name, key, subject) + else + return fmt("%s:%s", name, key) + end +end + + +--- +-- Helper to turn on a flag. +-- +-- @function utils.set_flag +-- @tparam number flags flags on which the flag is applied +-- @tparam number flag flag that is applied +-- @treturn number flags with the flag applied +-- +-- @usage +-- local flags = 0x0000 +-- local FLAG_DOG = 0x001 +-- flags = utils.set_flag(flags, FLAG_DOG) +local function set_flag(options, flag) + return bor(options, flag) +end + + +--- +-- Helper to turn off a flag. +-- +-- @function utils.unset_flag +-- @tparam number flags flags on which the flag is removed +-- @tparam number flag flag that is removed +-- @treturn number flags with the flag removed +-- +-- @usage +-- local options = 0x0000 +-- local FLAG_DOG = 0x001 +-- flags = utils.set_flag(options, FLAG_DOG) +-- flags = utils.unset_flag(options, FLAG_DOG) +local function unset_flag(flags, flag) + return band(flags, bnot(flag)) +end + + +--- +-- Helper to check if flag is enabled. +-- +-- @function utils.has_flag +-- @tparam number flags flags on which the flag is checked +-- @tparam number flag flag that is checked +-- @treturn boolean true if flag has is present, otherwise false +-- +-- @usage +-- local flags = 0x0000 +-- local FLAG_DOG = 0x001 +-- local FLAG_BONE = 0x010 +-- flags = utils.set_flag(flags, FLAG_DOG) +-- flags = utils.set_flag(flags, FLAG_BONE) +-- print(utils.has_flag(flags, FLAG_BONE) +local function has_flag(flags, flag) + return band(flags, flag) ~= 0 +end + + +--- +-- Helper to get the value used to store metadata for a certain aud and sub +-- Empty exp means the session id has been invalidated +-- +-- @function utils.meta_get_value +-- @tparam string key storage key +-- @tparam string exp expiration +-- @treturn string the value to store in the metadata collection +local function meta_get_value(key, exp) + if not exp or exp == 0 then + return fmt("%s;", key) + end + return fmt("%s:%s;", key, encode_base64url(bpack(5, exp))) +end + + +local meta_get_next do + local KEY_OFFSET = 43 + local DEL_OFFSET = 1 + local EXP_OFFSET = 7 + + local COLON = byte(":") + + --- + -- Function to extract the next key and exp from a serialized + -- metadata list, starting from index + -- + -- @function utils.meta_get_next + -- @tparam string val list of key:exp; + -- @tparam number index start index + -- @treturn key string session id + -- @treturn err string error + -- @treturn exp number expiration + -- @treturn index number|nil index of the cursor + meta_get_next = function(val, index) + local key = sub(val, index, index + KEY_OFFSET - 1) + index = index + KEY_OFFSET + local del = byte(val, index + DEL_OFFSET - 1) + index = index + DEL_OFFSET + + if del ~= COLON then + return key, nil, nil, index + end + + local exp = sub(val, index, index + EXP_OFFSET - 1) + index = index + EXP_OFFSET + DEL_OFFSET + local exp, err = decode_base64url(exp) + if err then + return nil, err + end + + exp = bunpack(5, exp) + return key, nil, exp, index + end +end + + +--- +-- Function to filter out the latest valid key:exp from a +-- serialized list, used to store session metadata +-- +-- @function utils.meta_get_latest +-- @tparam string sessions list of key:exp; +-- @treturn table valid sessions and their exp +local function meta_get_latest(sessions, current_time) + current_time = current_time + + local latest = {} + local index = 1 + local length = #sessions + while index < length do + local key, err, exp + key, err, exp, index = meta_get_next(sessions, index) + if err then + return nil, err + end + + if exp and exp > current_time then + latest[key] = exp + else + latest[key] = nil + end + end + + return latest +end + + +return { + is_fips_mode = is_fips_mode, + bpack = bpack, + bunpack = bunpack, + trim = trim, + encode_json = encode_json, + decode_json = decode_json, + encode_base64url = encode_base64url, + decode_base64url = decode_base64url, + base64_size = base64_size, + inflate = inflate, + deflate = deflate, + rand_bytes = rand_bytes, + sha256 = sha256, + derive_hkdf_sha256 = derive_hkdf_sha256, + derive_pbkdf2_hmac_sha256 = derive_pbkdf2_hmac_sha256, + derive_aes_gcm_256_key_and_iv = derive_aes_gcm_256_key_and_iv, + derive_hmac_sha256_key = derive_hmac_sha256_key, + encrypt_aes_256_gcm = encrypt_aes_256_gcm, + decrypt_aes_256_gcm = decrypt_aes_256_gcm, + hmac_sha256 = hmac_sha256, + load_storage = load_storage, + errmsg = errmsg, + get_name = get_name, + set_flag = set_flag, + unset_flag = unset_flag, + has_flag = has_flag, + meta_get_value = meta_get_value, + meta_get_next = meta_get_next, + meta_get_latest = meta_get_latest, +} diff --git a/lua-resty-session-4.0.4-1.rockspec b/lua-resty-session-4.0.4-1.rockspec new file mode 100644 index 000000000..d080bbe3b --- /dev/null +++ b/lua-resty-session-4.0.4-1.rockspec @@ -0,0 +1,37 @@ +package = "lua-resty-session" +version = "4.0.4-1" +source = { + url = "git+https://github.com/bungle/lua-resty-session.git", + tag = "v4.0.4", +} +description = { + summary = "Session Library for OpenResty - Flexible and Secure", + detailed = "lua-resty-session is a secure, and flexible session library for OpenResty.", + homepage = "https://github.com/bungle/lua-resty-session", + maintainer = "Aapo Talvensaari <aapo.talvensaari@gmail.com>, Samuele Illuminati <samuele@konghq.com>", + license = "BSD", +} +dependencies = { + "lua >= 5.1", + "lua-ffi-zlib >= 0.5", + "lua-resty-openssl >= 0.8.0", +} +build = { + type = "builtin", + modules = { + ["resty.session"] = "lib/resty/session.lua", + ["resty.session.dshm"] = "lib/resty/session/dshm.lua", + ["resty.session.file"] = "lib/resty/session/file.lua", + ["resty.session.file.thread"] = "lib/resty/session/file/thread.lua", + ["resty.session.file.utils"] = "lib/resty/session/file/utils.lua", + ["resty.session.memcached"] = "lib/resty/session/memcached.lua", + ["resty.session.mysql"] = "lib/resty/session/mysql.lua", + ["resty.session.postgres"] = "lib/resty/session/postgres.lua", + ["resty.session.redis"] = "lib/resty/session/redis.lua", + ["resty.session.redis.cluster"] = "lib/resty/session/redis/cluster.lua", + ["resty.session.redis.sentinel"] = "lib/resty/session/redis/sentinel.lua", + ["resty.session.redis.common"] = "lib/resty/session/redis/common.lua", + ["resty.session.shm"] = "lib/resty/session/shm.lua", + ["resty.session.utils"] = "lib/resty/session/utils.lua", + }, +} diff --git a/spec/01-utils_spec.lua b/spec/01-utils_spec.lua new file mode 100644 index 000000000..bb96cba51 --- /dev/null +++ b/spec/01-utils_spec.lua @@ -0,0 +1,139 @@ +local utils = require "resty.session.utils" + + +local describe = describe +local tostring = tostring +local random = math.random +local assert = assert +local ipairs = ipairs +local match = string.match +local it = it + + +describe("Testing utils", function() + describe("pack/unpack data", function() + local function range_max(bytes) + if bytes == 8 then + bytes = bytes - 1 + end + return 2 ^ (8 * bytes) - 1 + end + + it("bpack and bunpack produce consistent output", function() + for _, i in ipairs{ 1, 2, 4, 8 } do + local input = random(1, range_max(i)) + local packed = utils.bpack(i, input) + local unpacked = utils.bunpack(i, packed) + + assert.equals(i, #packed) + assert.equals(unpacked, input) + end + end) + end) + + describe("trim", function() + it("characters are trimmed as expected", function() + local input = " \t\t\r\n\n\v\f\f\fyay\ntrim!\f\f\v\n\r\t " + local expected_output = "yay\ntrim!" + local output = utils.trim(input) + + assert.equals(output, expected_output) + end) + end) + + describe("encode/decode json", function() + it("produce consistent output", function() + local input = { + foo = "bar", + test = 123 + } + local encoded = utils.encode_json(input) + local decoded = utils.decode_json(encoded) + + assert.same(decoded, input) + end) + end) + + describe("encode/decode base64url", function() + it("produce consistent output", function() + local input = "<<<!?>>>?!" + local encoded = utils.encode_base64url(input) + + assert.is_nil(match(encoded, "[/=+]")) + local decoded = utils.decode_base64url(encoded) + assert.equals(input, decoded) + end) + end) + + describe("deflate/inflate", function() + it("produce consistent output", function() + local input = utils.rand_bytes(1024) + local deflated = utils.deflate(input) + local inflated = utils.inflate(deflated) + + assert.equals(input, inflated) + end) + end) + + describe("Derive keys, encrypt and decrypt", function() + local ikm = "some key material" + local nonce = "0000000000000000" + local usage = "encryption" + local size = 44 + + it("derives key of expected size with derive_hkdf_sha256", function() + local k_bytes, err = utils.derive_hkdf_sha256(ikm, nonce, usage, size) + assert.is_not_nil(k_bytes) + assert.is_nil(err) + assert.equals(size, #tostring(k_bytes)) + end) + + it("derives key of expected size with derive_pbkdf2_hmac_sha256", function() + local k_bytes, err = utils.derive_pbkdf2_hmac_sha256(ikm, nonce, usage, size) + assert.is_not_nil(k_bytes) + assert.is_nil(err) + assert.equals(size, #tostring(k_bytes)) + end) + + it("derives a valid key and calculates mac with it", function() + local key, err, mac + key, err = utils.derive_hmac_sha256_key(ikm, nonce) + assert.is_not_nil(key) + assert.is_nil(err) + assert.equals(32, #key) + + mac, err = utils.hmac_sha256(key, "some message") + assert.is_not_nil(mac) + assert.is_nil(err) + end) + + it("successfully derives key and iv; encryption and decryption succeeds", function() + local data = { + foo = "bar", + tab = { "val" }, + num = 123, + } + local aad = "some_aad" + local input_data = utils.encode_json(data) + + local key, err, iv, ciphertext, tag, plaintext + for _, slow in ipairs({ true, false }) do + key, err, iv = utils.derive_aes_gcm_256_key_and_iv(ikm, nonce, slow) + assert.is_not_nil(key) + assert.is_not_nil(iv) + assert.is_nil(err) + + ciphertext, err, tag = utils.encrypt_aes_256_gcm(key, iv, input_data, aad) + assert.is_not_nil(ciphertext) + assert.is_not_nil(tag) + assert.is_nil(err) + + plaintext, err = utils.decrypt_aes_256_gcm(key, iv, ciphertext, aad, tag) + assert.is_not_nil(plaintext) + assert.is_nil(err) + + assert.equals(plaintext, input_data) + end + end) + end) +end) diff --git a/spec/02-file-utils_spec.lua b/spec/02-file-utils_spec.lua new file mode 100644 index 000000000..cca9b76c0 --- /dev/null +++ b/spec/02-file-utils_spec.lua @@ -0,0 +1,108 @@ +local file_utils = require "resty.session.file.utils" + + +local fmt = string.format +local describe = describe +local assert = assert +local it = it + + +describe("Testing file utils", function() + describe("validate file name", function() + local validate_file_name = file_utils.validate_file_name + local name = "name" + local sid1 = "ABCdef123_-iJqwertYUIOpasdfgHJKLzxcvbnmJuis" + local aud_sub1 = "some-aud:some-sub" + local simple_prefix = "pref" + local special_prefix = "@-_-" + local simple_suffix = "suff" + local special_suffix = "-_-@" + local filename_inv1 = "some.conf" + local filename_inv2 = "abc" + local filename_inv3 = name.."_".. "abc" + + it("validation fails with invalid files with prefix and suffix", function() + assert.is_false(validate_file_name(simple_prefix, simple_suffix, name, filename_inv1)) + assert.is_false(validate_file_name(simple_prefix, simple_suffix, name, filename_inv2)) + assert.is_false(validate_file_name(simple_prefix, simple_suffix, name, filename_inv3)) + end) + + + it("validation fails with invalid files with prefix only", function() + assert.is_false(validate_file_name(simple_prefix, nil, name, filename_inv1)) + assert.is_false(validate_file_name(simple_prefix, nil, name, filename_inv2)) + assert.is_false(validate_file_name(simple_prefix, nil, name, filename_inv3)) + end) + + it("validation fails with invalid files with suffix only", function() + assert.is_false(validate_file_name(nil, simple_suffix, name, filename_inv1)) + assert.is_false(validate_file_name(nil, simple_suffix, name, filename_inv2)) + assert.is_false(validate_file_name(nil, simple_suffix, name, filename_inv3)) + end) + + it("validation fails with invalid files with no prefix or suffix", function() + assert.is_false(validate_file_name(nil, nil, name, filename_inv1)) + assert.is_false(validate_file_name(nil, nil, name, filename_inv2)) + assert.is_false(validate_file_name(nil, nil, name, filename_inv3)) + end) + + it("validation passes with prefix and suffix", function() + local filename_sess = fmt("%s_%s_%s.%s", simple_prefix, name, sid1, simple_suffix) + local filename_meta = fmt("%s_%s_%s.%s", simple_prefix, name, aud_sub1, simple_suffix) + + assert.is_true(validate_file_name(simple_prefix, simple_suffix, name, filename_sess)) + assert.is_true(validate_file_name(simple_prefix, simple_suffix, name, filename_meta)) + assert.is_false(validate_file_name(simple_prefix, simple_suffix, name, filename_inv1)) + assert.is_false(validate_file_name(simple_prefix, simple_suffix, name, filename_inv2)) + end) + + it("validation passes with special prefix and suffix", function() + local sp_filename_sess = fmt("%s_%s_%s.%s", special_prefix, name, sid1, special_suffix) + local sp_filename_meta = fmt("%s_%s_%s.%s", special_prefix, name, aud_sub1, special_suffix) + + assert.is_true(validate_file_name(special_prefix, special_suffix, name, sp_filename_sess)) + assert.is_true(validate_file_name(special_prefix, special_suffix, name, sp_filename_meta)) + end) + + + it("validation passes with prefix", function() + local filename_sess = fmt("%s_%s_%s", simple_prefix, name, sid1) + local filename_meta = fmt("%s_%s_%s", simple_prefix, name, aud_sub1) + + assert.is_true(validate_file_name(simple_prefix, nil, name, filename_sess)) + assert.is_true(validate_file_name(simple_prefix, nil, name, filename_meta)) + end) + + it("validation passes with special prefix", function() + local sp_filename_sess = fmt("%s_%s_%s", special_prefix, name, sid1) + local sp_filename_meta = fmt("%s_%s_%s", special_prefix, name, aud_sub1) + + assert.is_true(validate_file_name(special_prefix, nil, name, sp_filename_sess)) + assert.is_true(validate_file_name(special_prefix, nil, name, sp_filename_meta)) + end) + + it("#only validation passes with suffix", function() + local filename_sess = fmt("%s_%s.%s", name, sid1, simple_suffix) + local filename_meta = fmt("%s_%s.%s", name, aud_sub1, simple_suffix) + + assert.is_true(validate_file_name(nil, simple_suffix, name, filename_sess)) + assert.is_true(validate_file_name(nil, simple_suffix, name, filename_meta)) + end) + + it("validation passes with special suffix", function() + local sp_filename_sess = fmt("%s_%s.%s", name, sid1, special_suffix) + local sp_filename_meta = fmt("%s_%s.%s", name, aud_sub1, special_suffix) + + assert.is_true(validate_file_name(nil, special_suffix, name, sp_filename_sess)) + assert.is_true(validate_file_name(nil, special_suffix, name, sp_filename_meta)) + end) + + it("validation passes with no prefix or suffix", function() + local filename_sess = fmt("%s_%s", name, sid1) + local filename_meta = fmt("%s_%s", name, aud_sub1) + + assert.is_true(validate_file_name(nil, nil, name, filename_sess)) + assert.is_true(validate_file_name(nil, nil, name, filename_meta)) + end) + end) +end) diff --git a/spec/03-session_spec.lua b/spec/03-session_spec.lua new file mode 100644 index 000000000..78e7dbebf --- /dev/null +++ b/spec/03-session_spec.lua @@ -0,0 +1,405 @@ +local session = require "resty.session" + + +local before_each = before_each +local describe = describe +local assert = assert +local pcall = pcall +local it = it + + +local function extract_cookie(cookie_name, cookies) + local session_cookie + if type(cookies) == "table" then + for _, v in ipairs(cookies) do + session_cookie = ngx.re.match(v, cookie_name .. "=([\\w-]+);") + if session_cookie then + return session_cookie[1] + end + end + return "" + end + session_cookie = ngx.re.match(cookies, cookie_name .. "=([\\w-]+);") + return session_cookie and session_cookie[1] or "" +end + + +describe("Session", function() + local configuration = {} + + describe("instance methods behave as expected", function() + local cookie_name = "session_cookie" + local remember_cookie_name = "remember_cookie" + local test_key = "test_key" + local data = "test_data" + local test_subject = "test_subject" + local test_audience = "test_audience" + local lout_subject = "lout_subject" + local lout_audience = "lout_audience" + + local function test_session_set_get(s) + assert.is_nil( + s:get(test_key) or + s:get(test_subject) or + s:get(test_audience) + ) + + s:set(test_key, data) + s:set_subject(test_subject) + s:set_audience(test_audience) + + assert.equals(s:get(test_key), data) + assert.equals(s:get_subject(), test_subject) + assert.equals(s:get_audience(), test_audience) + end + + local function test_session_save(s, cookies) + session.__set_ngx_header(cookies) + + local ok, err = s:save() + + assert.equals(s.state, "open") + assert.is_true(ok) + assert.is_nil(err) + assert.is_not_nil(s.meta) + assert.is_not_nil(s.meta.data_size) + assert(s.meta.data_size > 0) + + local session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + return session_cookie + end + + local function test_session_close_open(s, session_cookie) + s:close() + + assert.equals(s.state, "closed") + + local ok, err = pcall(s.get, s, "anything") + assert.is_false(ok) + assert.matches("unable to get session data", err) + + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie + }) + + ok, err = s:open() + assert.is_true(ok) + assert.is_nil(err) + assert.equals(s.state, "open") + assert.equals(data, s:get(test_key)) + end + + local function test_session_get_property(s) + assert.equals(43, #s:get_property("id")) + assert.equals(32, #s:get_property("nonce")) + assert.equals(test_audience, s:get_property("audience")) + assert.equals(test_subject, s:get_property("subject")) + assert.match.is_number(s:get_property("timeout")) + assert.match.is_number(s:get_property("idling-timeout")) + assert.match.is_number(s:get_property("rolling-timeout")) + assert.match.is_number(s:get_property("absolute-timeout")) + end + + local function test_session_touch(s) + local ok, err = s:touch() + assert.is_true(ok) + assert.is_nil(err) + assert.equals(s.state, "open") + end + + local function test_session_destroy_open(s) + local cookies = {} + + session.__set_ngx_header(cookies) + + local ok, err = s:destroy() + assert.is_true(ok) + assert.is_nil(err) + assert.equals(s.state, "closed") + + ok, err = pcall(s.get, s, "anything") + assert.is_false(ok) + assert.matches("unable to get session data", err) + + local session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) -- empty + + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie + }) + + ok, err = s:open() + assert.is_nil(ok) + assert.equals("invalid session header", err) + assert.equals(s.state, "closed") + + ok, err = pcall(s.get, s, "anything") + assert.is_false(ok) + assert.matches("unable to get session data", err) + end + + local function test_session(s) + local session_cookie + local cookies = {} + + test_session_set_get(s) + session_cookie = test_session_save(s, cookies) + test_session_close_open(s, session_cookie) + test_session_get_property(s) + test_session_touch(s) + test_session_destroy_open(s) + end + + before_each(function() + configuration = { + cookie_name = cookie_name, + remember_cookie_name = remember_cookie_name + } + end) + + it("with default values", function() + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + end) + + it("with custom secret", function() + configuration.secret = "t" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + end) + + it("custom ikm takes precedence on secret", function() + configuration.secret = "t" + configuration.ikm = "00000000000000000000000000000000" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + + assert.equals(configuration.ikm, s.meta.ikm) + end) + + it("logout individual audience and subject", function() + local cookies = {} + session.__set_ngx_header(cookies) + session.init(configuration) + + configuration.audience = test_audience + configuration.subject = test_subject + local s1 = session.new(configuration) + assert.is_not_nil(s1) + test_session_save(s1, cookies) + local session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie + }) + assert.is_not_nil(session_cookie) + assert.is_not_equal("", session_cookie) + assert.match(s1:get_audience(), configuration.audience) + + configuration.audience = lout_audience + configuration.subject = lout_subject + local s2 = session.open(configuration) + assert.is_not_nil(s2) + test_session_save(s2, cookies) + session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie + }) + assert.is_not_nil(session_cookie) + assert.is_not_equal("", session_cookie) + assert.equals(configuration.audience, s2:get_audience()) + + s2:logout() + assert.equals(s1.state, "open") + assert.equals(s2.state, "closed") + session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie + }) + assert.is_not_nil(session_cookie) + assert.is_not_equal("", session_cookie) + + s1:logout() + assert.equals(s1.state, "closed") + session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + assert.is_not_nil(session_cookie) + assert.equals("", session_cookie) + end) + + it("set_remember=true produces remember cookie, get_remember returns expected values", function() + local cookies = {} + session.__set_ngx_header(cookies) + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + assert.is_false(s:get_remember()) + s:save() + assert.equals(s.state, "open") + local session_cookie = extract_cookie(cookie_name, cookies["Set-Cookie"]) + local remember_cookie = extract_cookie(remember_cookie_name, cookies["Set-Cookie"]) + assert.is_not_nil(remember_cookie) + assert.equals("", remember_cookie) + + session.__set_ngx_var({ + ["cookie_" .. cookie_name] = session_cookie, + ["cookie_" .. remember_cookie_name] = remember_cookie, + }) + s:set_remember(true) + assert.is_true(s:get_remember()) + s:save() + assert.equals(s.state, "open") + remember_cookie = extract_cookie(remember_cookie_name, cookies["Set-Cookie"]) + assert.is_not_nil(remember_cookie) + assert.is_not_equal(remember_cookie, "") + end) + + describe("with custom cookie attribute", function() + it("Domain", function() + configuration.cookie_domain = "example.org" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("Domain=example.org", s.cookie_flags) + end) + + it("Path", function() + configuration.cookie_path = "/test" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("Path=/test", s.cookie_flags) + end) + + it("SameSite", function() + configuration.cookie_same_site = "Default" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("SameSite=Default", s.cookie_flags) + end) + + it("HttpOnly", function() + configuration.cookie_http_only = false + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.does_not.match("HttpOnly", s.cookie_flags) + end) + + it("Secure", function() + configuration.cookie_secure = true + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("Secure", s.cookie_flags) + end) + + it("Priority", function() + configuration.cookie_priority = "High" + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("Priority=High", s.cookie_flags) + end) + + it("Partitioned", function() + configuration.cookie_partitioned = true + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("Partitioned", s.cookie_flags) + end) + + it("SameParty", function() + configuration.cookie_same_party = true + session.init(configuration) + + local s = session.new() + assert.is_not_nil(s) + test_session(s) + assert.matches("SameParty", s.cookie_flags) + end) + end) + end) + + describe("Fields validation", function() + describe("init validates fields", function() + before_each(function() + configuration = {} + end) + + it("custom ikm must be 32 bytes", function() + configuration.ikm = "12345" + + local ok, err = pcall(session.init,configuration) + assert.is_false(ok) + assert.matches("invalid ikm size", err) + end) + + it("custom ikm_fallbacks must be 32 bytes", function() + configuration.ikm_fallbacks = { + "00000000000000000000000000000000", + "123456", + } + + local ok, err = pcall(session.init,configuration) + assert.is_false(ok) + assert.matches("invalid ikm size in ikm_fallbacks", err) + end) + end) + + describe("new validates fields", function() + before_each(function() + configuration = {} + end) + + it("custom ikm must be 32 bytes", function() + configuration.ikm = "12345" + local ok, err = pcall(session.new, configuration) + assert.is_false(ok) + assert.matches("invalid ikm size", err) + end) + + it("custom ikm_fallbacks must be 32 bytes", function() + configuration.ikm_fallbacks = { + "00000000000000000000000000000000", + "123456", + } + local ok, err = pcall(session.new, configuration) + assert.is_false(ok) + assert.matches("invalid ikm size", err) + end) + + it("SameParty and SameSite=strict fails to instantiate session", function() + configuration.cookie_same_party = true + configuration.cookie_same_site = "Strict" + + local ok, err = pcall(session.new, configuration) + assert.is_false(ok) + assert.matches("SameParty session cookies cannot use SameSite=Strict", err) + end) + end) + end) +end) diff --git a/spec/04-storage-1_spec.lua b/spec/04-storage-1_spec.lua new file mode 100644 index 000000000..6271622ea --- /dev/null +++ b/spec/04-storage-1_spec.lua @@ -0,0 +1,223 @@ +--- +-- Ensure to keep the tests consistent with those in 05-storage-1_spec.lua + + +local utils = require "resty.session.utils" + + +local before_each = before_each +local after_each = after_each +local lazy_setup = lazy_setup +local describe = describe +local ipairs = ipairs +local assert = assert +local sleep = ngx.sleep +local time = ngx.time +local it = it + + +local storage_configs = { + file = { + suffix = "session", + }, + shm = { + prefix = "sessions", + connect_timeout = 10000, + send_timeout = 10000, + read_timeout = 10000, + }, + redis = { + prefix = "sessions", + password = "password", + }, + memcached = { + prefix = "sessions", + connect_timeout = 10000, + send_timeout = 10000, + read_timeout = 10000, + }, +} + + +for _, st in ipairs({ + "file", + "shm", + "redis", + "memcached", +}) do + describe("Storage tests 1", function() + local current_time + local storage + local long_ttl = 60 + local short_ttl = 2 + local key = "test_key_1iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local key1 = "test_key_2iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local key2 = "test_key_3iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local old_key = "old_key_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local name = "test_name" + local value = "test_value" + + lazy_setup(function() + local conf = { + remember = true, + store_metadata = true, + } + conf[st] = storage_configs[st] + storage = utils.load_storage(st, conf) + assert.is_not_nil(storage) + end) + + before_each(function() + current_time = time() + end) + + describe("[#" .. st .. "] storage: SET + GET", function() + local audiences = { "foo", "bar" } + local subjects = { "john", "jane" } + + local metadata = { + audiences = audiences, + subjects = subjects, + } + + after_each(function() + storage:delete(name, key, current_time, metadata) + storage:delete(name, key1, current_time, metadata) + storage:delete(name, key2, current_time, metadata) + end) + + it("SET: simple set does not return errors, GET fetches value correctly", function() + local ok = storage:set(name, key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + local v, err = storage:get(name, key, current_time) + assert.is_not_nil(v) + assert.is_nil(err) + assert.equals(v, value) + end) + + it("SET: with metadata and remember works correctly", function() + local ok = storage:set(name, key, value, long_ttl, current_time, nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + local v, err = storage:get(name, key, time()) + assert.is_not_nil(v) + assert.is_nil(err) + assert.equals(v, value) + end) + + it("SET: with metadata (long ttl) correctly appends metadata to collection", function() + local ok = storage:set(name, key, value, long_ttl, current_time, nil, nil, metadata, true) + ok = ok and storage:set(name, key1, value, long_ttl, current_time, nil, nil, metadata, true) + ok = ok and storage:set(name, key2, value, long_ttl, current_time, nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.is_not_nil(meta_values) + assert.truthy(meta_values[key ]) + assert.truthy(meta_values[key1]) + assert.truthy(meta_values[key2]) + end + end) + + it("SET: with metadata (short ttl) correctly expires metadata", function() + local ok = storage:set(name, key, value, short_ttl, current_time, nil, nil, metadata, true) + + sleep(short_ttl + 1) + + ok = ok and storage:set(name, key1, value, long_ttl, time(), nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.falsy(meta_values[key]) + assert.truthy(meta_values[key1]) + end + end) + + it("SET: with old_key correctly applies stale ttl on old key", function() + local stale_ttl = 1 + + local ok = storage:set(name, old_key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + ok = storage:set(name, key, value, long_ttl, current_time, old_key, stale_ttl, nil, false) + assert.is_not_nil(ok) + + sleep(3) + + local v = storage:get(name, old_key, time()) + assert.is_nil(v) + end) + + it("SET: remember deletes file in old_key", function() + local stale_ttl = long_ttl + + local ok = storage:set(name, old_key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + ok = storage:set(name, key, value, long_ttl, current_time, old_key, stale_ttl, nil, true) + assert.is_not_nil(ok) + + local v = storage:get(name, old_key, current_time) + assert.is_nil(v) + end) + + it("SET: ttl works as expected", function() + local ok = storage:set(name, key, value, short_ttl, current_time) + assert.is_not_nil(ok) + + sleep(3) + + local v = storage:get(name, key, time()) + assert.is_nil(v) + end) + end) + + describe("[#" .. st .. "] storage: DELETE", function() + local audiences = { "foo" } + local subjects = { "john" } + + local metadata = { + audiences = audiences, + subjects = subjects, + } + + it("deleted file is really deleted", function() + local ok = storage:set(name, key, value, short_ttl, current_time) + assert.is_not_nil(ok) + + storage:delete(name, key, current_time, nil) + + local v = storage:get(name, key, current_time) + assert.is_nil(v) + end) + + it("with metadata correctly deletes metadata collection", function() + local ok = storage:set(name, key1, value, long_ttl, current_time, nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.truthy(meta_values[key1]) + ok = storage:delete(name, key1, time(), metadata) + assert.is_not_nil(ok) + + sleep(2) + + meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) or {} + assert.falsy(meta_values[key1]) + end + end) + end) + end) +end diff --git a/spec/05-storage-2_spec.lua b/spec/05-storage-2_spec.lua new file mode 100644 index 000000000..f6eb4c265 --- /dev/null +++ b/spec/05-storage-2_spec.lua @@ -0,0 +1,252 @@ +--- +-- For now these tests don't run on CI. +-- Ensure to keep the tests consistent with those in 04-storage-1_spec.lua + + +local utils = require "resty.session.utils" + + +local before_each = before_each +local after_each = after_each +local lazy_setup = lazy_setup +local describe = describe +local ipairs = ipairs +local assert = assert +local sleep = ngx.sleep +local time = ngx.time +local it = it + + +local storage_configs = { + mysql = { + username = "root", + password = "password", + database = "test", + }, + postgres = { + username = "postgres", + password = "password", + database = "test", + }, + redis_sentinel = { + prefix = "sessions", + password = "password", + sentinels = { + { host = "127.0.0.1", port = "26379" } + }, + connect_timeout = 10000, + send_timeout = 10000, + read_timeout = 10000, + }, + redis_cluster = { + password = "password", + nodes = { + { ip = "127.0.0.1", port = "6380" } + }, + name = "somecluster", + lock_zone = "sessions", + connect_timeout = 10000, + send_timeout = 10000, + read_timeout = 10000, + }, + dshm = { + prefix = "sessions", + connect_timeout = 10000, + send_timeout = 10000, + read_timeout = 10000, + }, +} + + +local function storage_type(ty) + if ty == "redis_cluster" or ty == "redis_sentinel" then + return "redis" + end + return ty +end + + +for _, st in ipairs({ + "mysql", + "postgres", + "redis_cluster", + "redis_sentinel", + "dshm" +}) do + describe("Storage tests 2 #noci", function() + local current_time + local storage + local long_ttl = 60 + local short_ttl = 2 + local key = "test_key_1iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local key1 = "test_key_2iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local key2 = "test_key_3iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local old_key = "old_key_iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii" + local name = "test_name" + local value = "test_value" + + lazy_setup(function() + local conf = { + remember = true, + store_metadata = true, + } + conf[storage_type(st)] = storage_configs[st] + storage = utils.load_storage(storage_type(st), conf) + assert.is_not_nil(storage) + end) + + before_each(function() + current_time = time() + end) + + describe("[#" .. st .. "] storage: SET + GET", function() + local audiences = { "foo", "bar" } + local subjects = { "john", "jane" } + + local metadata = { + audiences = audiences, + subjects = subjects, + } + + after_each(function() + current_time = time() + storage:delete(name, key, current_time, metadata) + storage:delete(name, key1, current_time, metadata) + storage:delete(name, key2, current_time, metadata) + end) + + it("SET: simple set does not return errors, GET fetches value correctly", function() + local ok = storage:set(name, key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + local v, err = storage:get(name, key, current_time) + assert.is_not_nil(v) + assert.is_nil(err) + assert.equals(v, value) + end) + + it("SET: with metadata and remember works correctly", function() + local ok = storage:set(name, key, value, long_ttl, time(), nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + local v, err = storage:get(name, key, time()) + assert.is_not_nil(v) + assert.is_nil(err) + assert.equals(v, value) + end) + + it("SET: with metadata (long ttl) correctly appends metadata to collection", function() + local ok = storage:set(name, key, value, long_ttl, current_time, nil, nil, metadata, true) + ok = ok and storage:set(name, key1, value, long_ttl, current_time, nil, nil, metadata, true) + ok = ok and storage:set(name, key2, value, long_ttl, current_time, nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.is_not_nil(meta_values) + assert.truthy(meta_values[key ]) + assert.truthy(meta_values[key1]) + assert.truthy(meta_values[key2]) + end + end) + + it("SET: with metadata (short ttl) correctly expires metadata", function() + local ok = storage:set(name, key, value, short_ttl, current_time, nil, nil, metadata, true) + + sleep(short_ttl + 1) + + ok = ok and storage:set(name, key1, value, long_ttl, time(), nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.falsy(meta_values[key]) + assert.truthy(meta_values[key1]) + end + end) + + it("SET: with old_key correctly applies stale ttl on old key", function() + local stale_ttl = 1 + + local ok = storage:set(name, old_key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + ok = storage:set(name, key, value, long_ttl, current_time, old_key, stale_ttl, nil, false) + assert.is_not_nil(ok) + + sleep(3) + + local v = storage:get(name, old_key, time()) + assert.is_nil(v) + end) + + it("SET: remember deletes file in old_key", function() + local stale_ttl = long_ttl + local ok = storage:set(name, old_key, value, long_ttl, current_time) + assert.is_not_nil(ok) + + ok = storage:set(name, key, value, long_ttl, current_time, old_key, stale_ttl, nil, true) + assert.is_not_nil(ok) + + local v = storage:get(name, old_key, current_time) + assert.is_nil(v) + end) + + it("SET: ttl works as expected", function() + local ok = storage:set(name, key, value, short_ttl, current_time) + assert.is_not_nil(ok) + + sleep(3) + + local v = storage:get(name, key, time()) + assert.is_nil(v) + end) + end) + + describe("[#" .. st .. "] storage: DELETE", function() + local audiences = { "foo" } + local subjects = { "john" } + + local metadata = { + audiences = audiences, + subjects = subjects, + } + + it("deleted file is really deleted", function() + local ok = storage:set(name, key, value, short_ttl, current_time) + assert.is_not_nil(ok) + + storage:delete(name, key, current_time) + + local v = storage:get(name, key, current_time) + assert.is_nil(v) + end) + + it("with metadata correctly deletes metadata collection", function() + local ok = storage:set(name, key1, value, long_ttl, current_time, nil, nil, metadata, true) + assert.is_not_nil(ok) + + sleep(1) + + for i = 1, #audiences do + local meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) + assert.truthy(meta_values[key1]) + + ok = storage:delete(name, key1, time(), metadata) + assert.is_not_nil(ok) + + sleep(2) + + meta_values = storage:read_metadata(name, audiences[i], subjects[i], time()) or {} + assert.falsy(meta_values[key1]) + end + end) + end) + end) +end diff --git a/t/01-cookies.t b/t/01-cookies.t new file mode 100644 index 000000000..df7fc1308 --- /dev/null +++ b/t/01-cookies.t @@ -0,0 +1,149 @@ +use Test::Nginx::Socket; +repeat_each(2); + +$ENV{TEST_NGINX_NXSOCK} ||= html_dir(); +plan tests => repeat_each() * blocks() * 3 + 4; + +run_tests(); + +__DATA__ + +=== TEST 1: session cookie is returned +--- http_config + init_by_lua_block { + require("resty.session").init({ + storage = "cookie", + }) + } +--- config +location = /test { + content_by_lua_block { + local session = require "resty.session".new() + local ok = session:save() + if ok then + ngx.say("yay") + end + } +} + +--- request +GET /test +--- response_body +yay +--- response_headers_like +Set-Cookie: .*session=.+;.* +--- error_code: 200 +--- no_error_log +[error] + + +=== TEST 2: remember cookie is returned when remember=true +--- http_config + init_by_lua_block { + require("resty.session").init({ + remember = true, + storage = "cookie", + }) + } +--- config +location = /test { + content_by_lua_block { + local session = require "resty.session".new() + local ok = session:save() + + if ok then + ngx.say("yay") + end + } +} + +--- request +GET /test +--- response_body +yay +--- response_headers_like +Set-Cookie: .*remember=.+;.* +--- error_code: 200 +--- no_error_log +[error] + + +=== TEST 3: session.open() opens a session from a valid cookie and data is +extracted correctly +--- http_config + init_by_lua_block { + require("resty.session").init({ + secret = "RaJKp8UQW1", + storage = "cookie", + audience = "my_application", + idling_timeout = 0, + rolling_timeout = 0, + absolute_timeout = 0, + }) + } +--- config +location = /test { + content_by_lua_block { + local session = require "resty.session".open() + local sub = session:get_subject() + local aud = session:get_audience() + local quote = session:get("quote") + + ngx.say(sub .. "|" .. aud .. "|" .. quote) + } +} + +--- request +GET /test +--- more_headers +Cookie: session=AQAAS3ZGU0k8tUKsWSci9Fb6PM5xbm469FlR5g_B5HWZ6KYGSOZjAAAAAABcAABTCuHjqpE7B6Ux7m4GCylZAAAAzcWnTvzG51whooR_4QQwDgGdMOOa5W7tG4JWiDFU3zuYLFzakWEi-y-ogrwTpnt24zQXP_uJK7r5lMPNzRSMJM9H1a_MIegzEMm-QSgVRaoZVJq3Oo; Path=/; SameSite=Lax; HttpOnly +--- response_body +Lua Fan|my_application|Lorem ipsum dolor sit amet +--- error_code: 200 +--- no_error_log +[error] + + +=== TEST 4: clear_request_cookie() clears session Cookie from request to +upstream +--- http_config + init_by_lua_block { + require("resty.session").init({ + secret = "RaJKp8UQW1", + storage = "cookie", + audience = "my_application", + idling_timeout = 0, + rolling_timeout = 0, + absolute_timeout = 0, + }) + } + + server { + listen unix:/$TEST_NGINX_NXSOCK/nginx.sock; + + location /t { + content_by_lua_block { + local headers = ngx.req.get_headers() + ngx.say("session_cookie: [", tostring(headers["Cookie"]), "]") + } + } + } + +--- config +location = /test { + access_by_lua_block { + local session = require "resty.session".open() + session:clear_request_cookie() + } + proxy_pass http://unix:/$TEST_NGINX_NXSOCK/nginx.sock; +} + +--- request +GET /test +--- more_headers +Cookie: session=AQAAS3ZGU0k8tUKsWSci9Fb6PM5xbm469FlR5g_B5HWZ6KYGSOZjAAAAAABcAABTCuHjqpE7B6Ux7m4GCylZAAAAzcWnTvzG51whooR_4QQwDgGdMOOa5W7tG4JWiDFU3zuYLFzakWEi-y-ogrwTpnt24zQXP_uJK7r5lMPNzRSMJM9H1a_MIegzEMm-QSgVRaoZVJq3Oo; Path=/; SameSite=Lax; HttpOnly +--- response_body +session_cookie: [Path=/; SameSite=Lax; HttpOnly] +--- error_code: 200 +--- no_error_log +[error]