aboutsummaryrefslogtreecommitdiffstats
path: root/server/resty/jwt-validators.lua
diff options
context:
space:
mode:
Diffstat (limited to 'server/resty/jwt-validators.lua')
-rw-r--r--server/resty/jwt-validators.lua412
1 files changed, 0 insertions, 412 deletions
diff --git a/server/resty/jwt-validators.lua b/server/resty/jwt-validators.lua
deleted file mode 100644
index df99418..0000000
--- a/server/resty/jwt-validators.lua
+++ /dev/null
@@ -1,412 +0,0 @@
-local _M = { _VERSION = "0.2.3" }
-
---[[
- This file defines "validators" to be used in validating a spec. A "validator" is simply a function with
- a signature that matches:
-
- function(val, claim, jwt_json)
-
- This function returns either true or false. If a validator needs to give more information on why it failed,
- then it can also raise an error (which will be used in the "reason" part of the validated jwt_obj). If a
- validator returns nil, then it is assumed to have passed (same as returning true) and that you just forgot
- to actually return a value.
-
- There is a special claim name of "__jwt" that can be used to validate the entire jwt_obj.
-
- "val" is the value being tested. It may be nil if the claim doesn't exist in the jwt_obj. If the function
- is being called for the "__jwt" claim, then "val" will contain a deep clone of the full jwt object.
-
- "claim" is the claim that is being tested. It is passed in just in case a validator needs to do additional
- checks. It will be the string "__jwt" if the validator is being called for the entire jwt_object.
-
- "jwt_json" is a json-encoded representation of the full object that is being tested. It will never be nil,
- and can always be decoded using cjson.decode(jwt_json).
-]]--
-
-
---[[
- A function which will define a validator. It creates both "opt_" and required (non-"opt_")
- versions. The function that is passed in is the *optional* version.
-]]--
-local function define_validator(name, fx)
- _M["opt_" .. name] = fx
- _M[name] = function(...) return _M.chain(_M.required(), fx(...)) end
-end
-
--- Validation messages
-local messages = {
- nil_validator = "Cannot create validator for nil %s.",
- wrong_type_validator = "Cannot create validator for non-%s %s.",
- empty_table_validator = "Cannot create validator for empty table %s.",
- wrong_table_type_validator = "Cannot create validator for non-%s table %s.",
- required_claim = "'%s' claim is required.",
- wrong_type_claim = "'%s' is malformed. Expected to be a %s.",
- missing_claim = "Missing one of claims - [ %s ]."
-}
-
--- Local function to make sure that a value is non-nil or raises an error
-local function ensure_not_nil(v, e, ...)
- return v ~= nil and v or error(string.format(e, ...), 0)
-end
-
--- Local function to make sure that a value is the given type
-local function ensure_is_type(v, t, e, ...)
- return type(v) == t and v or error(string.format(e, ...), 0)
-end
-
--- Local function to make sure that a value is a (non-empty) table
-local function ensure_is_table(v, e, ...)
- ensure_is_type(v, "table", e, ...)
- return ensure_not_nil(next(v), e, ...)
-end
-
--- Local function to make sure all entries in the table are the given type
-local function ensure_is_table_type(v, t, e, ...)
- if v ~= nil then
- ensure_is_table(v, e, ...)
- for _,val in ipairs(v) do
- ensure_is_type(val, t, e, ...)
- end
- end
- return v
-end
-
--- Local function to ensure that a number is non-negative (positive or 0)
-local function ensure_is_non_negative(v, e, ...)
- if v ~= nil then
- ensure_is_type(v, "number", e, ...)
- if v >= 0 then
- return v
- else
- error(string.format(e, ...), 0)
- end
- end
-end
-
--- A local function which returns simple equality
-local function equality_function(val, check)
- return val == check
-end
-
--- A local function which returns string match
-local function string_match_function(val, pattern)
- return string.match(val, pattern) ~= nil
-end
-
---[[
- A local function which returns truth on existence of check in vals.
- Adopted from auth0/nginx-jwt table_contains by @twistedstream
-]]--
-local function table_contains_function(vals, check)
- for _, val in pairs(vals) do
- if val == check then return true end
- end
- return false
-end
-
-
--- A local function which returns numeric greater than comparison
-local function greater_than_function(val, check)
- return val > check
-end
-
--- A local function which returns numeric greater than or equal comparison
-local function greater_than_or_equal_function(val, check)
- return val >= check
-end
-
--- A local function which returns numeric less than comparison
-local function less_than_function(val, check)
- return val < check
-end
-
--- A local function which returns numeric less than or equal comparison
-local function less_than_or_equal_function(val, check)
- return val <= check
-end
-
-
---[[
- Returns a validator that chains the given functions together, one after
- another - as long as they keep passing their checks.
-]]--
-function _M.chain(...)
- local chain_functions = {...}
- for _, fx in ipairs(chain_functions) do
- ensure_is_type(fx, "function", messages.wrong_type_validator, "function", "chain_function")
- end
-
- return function(val, claim, jwt_json)
- for _, fx in ipairs(chain_functions) do
- if fx(val, claim, jwt_json) == false then
- return false
- end
- end
- return true
- end
-end
-
---[[
- Returns a validator that returns false if a value doesn't exist. If
- the value exists and a chain_function is specified, then the value of
- chain_function(val, claim, jwt_json)
- will be returned, otherwise, true will be returned. This allows for
- specifying that a value is both required *and* it must match some
- additional check. This function will be used in the "required_*" shortcut
- functions for simplification.
-]]--
-function _M.required(chain_function)
- if chain_function ~= nil then
- return _M.chain(_M.required(), chain_function)
- end
-
- return function(val, claim, jwt_json)
- ensure_not_nil(val, messages.required_claim, claim)
- return true
- end
-end
-
---[[
- Returns a validator which errors with a message if *NONE* of the given claim
- keys exist. It is expected that this function is used against a full jwt object.
- The claim_keys must be a non-empty table of strings.
-]]--
-function _M.require_one_of(claim_keys)
- ensure_not_nil(claim_keys, messages.nil_validator, "claim_keys")
- ensure_is_type(claim_keys, "table", messages.wrong_type_validator, "table", "claim_keys")
- ensure_is_table(claim_keys, messages.empty_table_validator, "claim_keys")
- ensure_is_table_type(claim_keys, "string", messages.wrong_table_type_validator, "string", "claim_keys")
-
- return function(val, claim, jwt_json)
- ensure_is_type(val, "table", messages.wrong_type_claim, claim, "table")
- ensure_is_type(val.payload, "table", messages.wrong_type_claim, claim .. ".payload", "table")
-
- for i, v in ipairs(claim_keys) do
- if val.payload[v] ~= nil then return true end
- end
-
- error(string.format(messages.missing_claim, table.concat(claim_keys, ", ")), 0)
- end
-end
-
---[[
- Returns a validator that checks if the result of calling the given function for
- the tested value and the check value returns true. The value of check_val and
- check_function cannot be nil. The optional name is used for error messages and
- defaults to "check_value". The optional check_type is used to make sure that
- the check type matches and defaults to type(check_val). The first parameter
- passed to check_function will *never* be nil (check succeeds if value is nil).
- Use the required version to fail on nil. If the check_function raises an
- error, that will be appended to the error message.
-]]--
-define_validator("check", function(check_val, check_function, name, check_type)
- name = name or "check_val"
- ensure_not_nil(check_val, messages.nil_validator, name)
-
- ensure_not_nil(check_function, messages.nil_validator, "check_function")
- ensure_is_type(check_function, "function", messages.wrong_type_validator, "function", "check_function")
-
- check_type = check_type or type(check_val)
- return function(val, claim, jwt_json)
- if val == nil then return true end
-
- ensure_is_type(val, check_type, messages.wrong_type_claim, claim, check_type)
- return check_function(val, check_val)
- end
-end)
-
-
---[[
- Returns a validator that checks if a value exactly equals the given check_value.
- If the value is nil, then this check succeeds. The value of check_val cannot be
- nil.
-]]--
-define_validator("equals", function(check_val)
- return _M.opt_check(check_val, equality_function, "check_val")
-end)
-
-
---[[
- Returns a validator that checks if a value matches the given pattern. The value
- of pattern must be a string.
-]]--
-define_validator("matches", function (pattern)
- ensure_is_type(pattern, "string", messages.wrong_type_validator, "string", "pattern")
- return _M.opt_check(pattern, string_match_function, "pattern", "string")
-end)
-
-
---[[
- Returns a validator which calls the given function for each of the given values
- and the tested value. If any of these calls return true, then this function
- returns true. The value of check_values must be a non-empty table with all the
- same types, and the value of check_function must not be nil. The optional name
- is used for error messages and defaults to "check_values". The optional
- check_type is used to make sure that the check type matches and defaults to
- type(check_values[1]) - the table type.
-]]--
-define_validator("any_of", function(check_values, check_function, name, check_type, table_type)
- name = name or "check_values"
- ensure_not_nil(check_values, messages.nil_validator, name)
- ensure_is_type(check_values, "table", messages.wrong_type_validator, "table", name)
- ensure_is_table(check_values, messages.empty_table_validator, name)
-
- table_type = table_type or type(check_values[1])
- ensure_is_table_type(check_values, table_type, messages.wrong_table_type_validator, table_type, name)
-
- ensure_not_nil(check_function, messages.nil_validator, "check_function")
- ensure_is_type(check_function, "function", messages.wrong_type_validator, "function", "check_function")
-
- check_type = check_type or table_type
- return _M.opt_check(check_values, function(v1, v2)
- for i, v in ipairs(v2) do
- if check_function(v1, v) then return true end
- end
- return false
- end, name, check_type)
-end)
-
-
---[[
- Returns a validator that checks if a value exactly equals any of the given values.
-]]--
-define_validator("equals_any_of", function(check_values)
- return _M.opt_any_of(check_values, equality_function, "check_values")
-end)
-
-
---[[
- Returns a validator that checks if a value matches any of the given patterns.
-]]--
-define_validator("matches_any_of", function(patterns)
- return _M.opt_any_of(patterns, string_match_function, "patterns", "string", "string")
-end)
-
---[[
- Returns a validator that checks if a value of expected type string exists in any of the given values.
- The value of check_values must be a non-empty table with all the same types.
- The optional name is used for error messages and defaults to "check_values".
-]]--
-define_validator("contains_any_of", function(check_values, name)
- return _M.opt_any_of(check_values, table_contains_function, name, "table", "string")
-end)
-
---[[
- Returns a validator that checks how a value compares (numerically) to a given
- check_value. The value of check_val cannot be nil and must be a number.
-]]--
-define_validator("greater_than", function(check_val)
- ensure_is_type(check_val, "number", messages.wrong_type_validator, "number", "check_val")
- return _M.opt_check(check_val, greater_than_function, "check_val", "number")
-end)
-define_validator("greater_than_or_equal", function(check_val)
- ensure_is_type(check_val, "number", messages.wrong_type_validator, "number", "check_val")
- return _M.opt_check(check_val, greater_than_or_equal_function, "check_val", "number")
-end)
-define_validator("less_than", function(check_val)
- ensure_is_type(check_val, "number", messages.wrong_type_validator, "number", "check_val")
- return _M.opt_check(check_val, less_than_function, "check_val", "number")
-end)
-define_validator("less_than_or_equal", function(check_val)
- ensure_is_type(check_val, "number", messages.wrong_type_validator, "number", "check_val")
- return _M.opt_check(check_val, less_than_or_equal_function, "check_val", "number")
-end)
-
-
---[[
- A function to set the leeway (in seconds) used for is_not_before and is_not_expired. The
- default is to use 0 seconds
-]]--
-local system_leeway = 0
-function _M.set_system_leeway(leeway)
- ensure_is_type(leeway, "number", "leeway must be a non-negative number")
- ensure_is_non_negative(leeway, "leeway must be a non-negative number")
- system_leeway = leeway
-end
-
-
---[[
- A function to set the system clock used for is_not_before and is_not_expired. The
- default is to use ngx.now
-]]--
-local system_clock = ngx.now
-function _M.set_system_clock(clock)
- ensure_is_type(clock, "function", "clock must be a function")
- -- Check that clock returns the correct value
- local t = clock()
- ensure_is_type(t, "number", "clock function must return a non-negative number")
- ensure_is_non_negative(t, "clock function must return a non-negative number")
- system_clock = clock
-end
-
--- Local helper function for date validation
-local function validate_is_date(val, claim, jwt_json)
- ensure_is_non_negative(val, messages.wrong_type_claim, claim, "positive numeric value")
- return true
-end
-
--- Local helper for date formatting
-local function format_date_on_error(date_check_function, error_msg)
- ensure_is_type(date_check_function, "function", messages.wrong_type_validator, "function", "date_check_function")
- ensure_is_type(error_msg, "string", messages.wrong_type_validator, "string", error_msg)
- return function(val, claim, jwt_json)
- local ret = date_check_function(val, claim, jwt_json)
- if ret == false then
- error(string.format("'%s' claim %s %s", claim, error_msg, ngx.http_time(val)), 0)
- end
- return true
- end
-end
-
---[[
- Returns a validator that checks if the current time is not before the tested value
- within the system's leeway. This means that:
- val <= (system_clock() + system_leeway).
-]]--
-define_validator("is_not_before", function()
- return format_date_on_error(
- _M.chain(validate_is_date,
- function(val)
- return val and less_than_or_equal_function(val, (system_clock() + system_leeway))
- end),
- "not valid until"
- )
-end)
-
-
---[[
- Returns a validator that checks if the current time is not equal to or after the
- tested value within the system's leeway. This means that:
- val > (system_clock() - system_leeway).
-]]--
-define_validator("is_not_expired", function()
- return format_date_on_error(
- _M.chain(validate_is_date,
- function(val)
- return val and greater_than_function(val, (system_clock() - system_leeway))
- end),
- "expired at"
- )
-end)
-
---[[
- Returns a validator that checks if the current time is the same as the tested value
- within the system's leeway. This means that:
- val >= (system_clock() - system_leeway) and val <= (system_clock() + system_leeway).
-]]--
-define_validator("is_at", function()
- local now = system_clock()
- return format_date_on_error(
- _M.chain(validate_is_date,
- function(val)
- local now = system_clock()
- return val and
- greater_than_or_equal_function(val, now - system_leeway) and
- less_than_or_equal_function(val, now + system_leeway)
- end),
- "is only valid at"
- )
-end)
-
-
-return _M