|
| 1 | +--- Signer class for Elasticache tokens for Valkey and Redis OSS access. |
| 2 | + |
| 3 | +-- Elasticache services created will get a `Signer` method to create an instance. |
| 4 | +-- The `Signer` will inherit its configuration from the `AWS` instance. |
| 5 | + |
| 6 | +local httpc = require("resty.luasocket.http") |
| 7 | +local presign_awsv4_request = require("resty.aws.request.signatures.presign") |
| 8 | + |
| 9 | +local ELASTICACHE_IAM_AUTH_EXPIRE_TIME = 15 * 60 |
| 10 | + |
| 11 | +local function getAuthToken(self, opts) --endpoint, region, username, is_serverless) |
| 12 | + opts = setmetatable(opts or {}, { __index = self.config }) -- lookup missing params in inherited config |
| 13 | + |
| 14 | + local region = assert(opts.region, "parameter 'region' not set") |
| 15 | + local hostname = assert(opts.hostname, "parameter 'hostname' not set") |
| 16 | + local port = assert(opts.port, "parameter 'port' not set") |
| 17 | + local username = assert(opts.username, "parameter 'username' not set") |
| 18 | + |
| 19 | + local endpoint = hostname |
| 20 | + if endpoint:sub(1,7) ~= "http://" then |
| 21 | + endpoint = "http://" .. endpoint |
| 22 | + end |
| 23 | + |
| 24 | + local query_args = "Action=connect" |
| 25 | + if opts.is_serverless then |
| 26 | + query_args = query_args .. "&ResourceType=ServerlessCache" |
| 27 | + end |
| 28 | + |
| 29 | + local query_args = query_args .. "&User=" .. username |
| 30 | + |
| 31 | + local canonical_request_url = endpoint .. "/?" .. query_args |
| 32 | + local scheme, host, port, path, query = unpack(httpc:parse_uri(canonical_request_url, false)) |
| 33 | + local req_data = { |
| 34 | + method = "GET", |
| 35 | + scheme = scheme, |
| 36 | + tls = scheme == "https", |
| 37 | + host = host, |
| 38 | + port = port, |
| 39 | + path = path, |
| 40 | + query = query, |
| 41 | + headers = { |
| 42 | + ["Host"] = host, |
| 43 | + }, |
| 44 | + } |
| 45 | + |
| 46 | + local presigned_request, err = presign_awsv4_request(self.config, req_data, opts.signingName, region, ELASTICACHE_IAM_AUTH_EXPIRE_TIME) |
| 47 | + if err then |
| 48 | + return nil, err |
| 49 | + end |
| 50 | + |
| 51 | + return presigned_request.host .. presigned_request.path .. "?" .. presigned_request.query |
| 52 | +end |
| 53 | + |
| 54 | + |
| 55 | +-- signature: intended to be a method on the Elasticache service object, cache_instance == self in that case |
| 56 | +return function(cache_instance, config) |
| 57 | + local token_instance = { |
| 58 | + config = {}, |
| 59 | + getAuthToken = getAuthToken, -- injected method for token generation |
| 60 | + } |
| 61 | + |
| 62 | + -- first copy the inherited config elements NOTE: inherits from AWS, not the cache_instance!!! |
| 63 | + for k,v in pairs(cache_instance.aws.config) do |
| 64 | + token_instance.config[k] = v |
| 65 | + end |
| 66 | + |
| 67 | + -- service specifics |
| 68 | + token_instance.config.signatureVersion = "v4" |
| 69 | + token_instance.config.signingName = "elasticache" |
| 70 | + |
| 71 | + -- then add/overwrite with provided config |
| 72 | + for k,v in pairs(config or {}) do |
| 73 | + token_instance.config[k] = v |
| 74 | + end |
| 75 | + |
| 76 | + return token_instance |
| 77 | +end |
0 commit comments