commit a3cd342f3e1fffd7b16b83a24e03bb9ed501b319 Author: Théophile Diot Date: Fri Jun 30 15:38:54 2023 -0400 Squashed 'src/deps/src/lua-resty-session/' content from commit 8b5f8752f git-subtree-dir: src/deps/src/lua-resty-session git-subtree-split: 8b5f8752f3046396c414c5b97850e784c07e1641 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]