diff options
Diffstat (limited to 'server/resty/session/strategies/default.lua')
-rw-r--r-- | server/resty/session/strategies/default.lua | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/server/resty/session/strategies/default.lua b/server/resty/session/strategies/default.lua new file mode 100644 index 0000000..a43ef5a --- /dev/null +++ b/server/resty/session/strategies/default.lua @@ -0,0 +1,232 @@ +local type = type +local concat = table.concat + +local strategy = {} + +function strategy.load(session, cookie, key, keep_lock) + local storage = session.storage + local id = cookie.id + local id_encoded = session.encoder.encode(id) + + local data, err, tag + if storage.open then + data, err = storage:open(id_encoded, keep_lock) + if not data then + return nil, err or "cookie data was not found" + end + + else + data = cookie.data + end + + local expires = cookie.expires + local usebefore = cookie.usebefore + local hash = cookie.hash + + if not key then + key = concat{ id, expires, usebefore } + end + + local hkey = session.hmac(session.secret, key) + + data, err, tag = session.cipher:decrypt(data, hkey, id, session.key, hash) + if not data then + if storage.close then + storage:close(id_encoded) + end + + return nil, err or "unable to decrypt data" + end + + if tag then + if tag ~= hash then + if storage.close then + storage:close(id_encoded) + end + + return nil, "cookie has invalid tag" + end + + else + local input = concat{ key, data, session.key } + if session.hmac(hkey, input) ~= hash then + if storage.close then + storage:close(id_encoded) + end + + return nil, "cookie has invalid signature" + end + end + + data, err = session.compressor:decompress(data) + if not data then + if storage.close then + storage:close(id_encoded) + end + + return nil, err or "unable to decompress data" + end + + data, err = session.serializer.deserialize(data) + if not data then + if storage.close then + storage:close(id_encoded) + end + + return nil, err or "unable to deserialize data" + end + + session.id = id + session.expires = expires + session.usebefore = usebefore + session.data = type(data) == "table" and data or {} + session.present = true + + return true +end + +function strategy.open(session, cookie, keep_lock) + return strategy.load(session, cookie, nil, keep_lock) +end + +function strategy.start(session) + local storage = session.storage + if not storage.start then + return true + end + + local id_encoded = session.encoder.encode(session.id) + + local ok, err = storage:start(id_encoded) + if not ok then + return nil, err or "unable to start session" + end + + return true +end + +function strategy.modify(session, action, close, key) + local id = session.id + local id_encoded = session.encoder.encode(id) + local storage = session.storage + local expires = session.expires + local usebefore = session.usebefore + local ttl = expires - session.now + + if ttl <= 0 then + if storage.close then + storage:close(id_encoded) + end + + return nil, "session is already expired" + end + + if not key then + key = concat{ id, expires, usebefore } + end + + local data, err = session.serializer.serialize(session.data) + if not data then + if close and storage.close then + storage:close(id_encoded) + end + + return nil, err or "unable to serialize data" + end + + data, err = session.compressor:compress(data) + if not data then + if close and storage.close then + storage:close(id_encoded) + end + + return nil, err or "unable to compress data" + end + + local hkey = session.hmac(session.secret, key) + + local encrypted_data, tag + encrypted_data, err, tag = session.cipher:encrypt(data, hkey, id, session.key) + if not encrypted_data then + if close and storage.close then + storage:close(id_encoded) + end + + return nil, err + end + + local hash + if tag then + hash = tag + else + -- it would be better to calculate signature from encrypted_data, + -- but this is kept for backward compatibility + hash = session.hmac(hkey, concat{ key, data, session.key }) + end + + if action == "save" and storage.save then + local ok + ok, err = storage:save(id_encoded, ttl, encrypted_data, close) + if not ok then + return nil, err + end + elseif close and storage.close then + local ok + ok, err = storage:close(id_encoded) + if not ok then + return nil, err + end + end + + if usebefore then + expires = expires .. ":" .. usebefore + end + + hash = session.encoder.encode(hash) + + local cookie + if storage.save then + cookie = concat({ id_encoded, expires, hash }, "|") + else + local encoded_data = session.encoder.encode(encrypted_data) + cookie = concat({ id_encoded, expires, encoded_data, hash }, "|") + end + + return cookie +end + +function strategy.touch(session, close) + return strategy.modify(session, "touch", close) +end + +function strategy.save(session, close) + return strategy.modify(session, "save", close) +end + +function strategy.destroy(session) + local id = session.id + if id then + local storage = session.storage + if storage.destroy then + return storage:destroy(session.encoder.encode(id)) + elseif storage.close then + return storage:close(session.encoder.encode(id)) + end + end + + return true +end + +function strategy.close(session) + local id = session.id + if id then + local storage = session.storage + if storage.close then + return storage:close(session.encoder.encode(id)) + end + end + + return true +end + +return strategy |