|
|
@ -3,6 +3,7 @@ |
|
|
|
require_relative 'parseutils' |
|
|
|
require_relative 'errors' |
|
|
|
require 'date' |
|
|
|
require 'openssl' |
|
|
|
HeaderRegexp = Landline::Util::HeaderRegexp |
|
|
|
ParserCommon = Landline::Util::ParserCommon |
|
|
|
|
|
|
@ -29,15 +30,22 @@ module Landline |
|
|
|
raise Landline::ParsingError, "invalid cookie value: #{value}" |
|
|
|
end |
|
|
|
|
|
|
|
# Make param keys strings |
|
|
|
params.transform_keys!(&:to_s) |
|
|
|
|
|
|
|
# Primary cookie parameters |
|
|
|
@key = key |
|
|
|
@value = value |
|
|
|
setup_params(params) |
|
|
|
|
|
|
|
# Cookie signing parameters |
|
|
|
setup_hmac(params) |
|
|
|
end |
|
|
|
|
|
|
|
# Convert cookie to "Set-Cookie: " string representation. |
|
|
|
# @return [String] |
|
|
|
def to_s |
|
|
|
sign(@hmac, algorithm: @algorithm, sep: @sep) if @hmac |
|
|
|
ParserCommon.make_value( |
|
|
|
"#{key.to_s.strip}=#{value.to_s.strip}", |
|
|
|
{ |
|
|
@ -58,6 +66,26 @@ module Landline |
|
|
|
"#{key.to_s.strip}=#{value.to_s.strip}" |
|
|
|
end |
|
|
|
|
|
|
|
# Sign the cookie value with HMAC |
|
|
|
# @param key [String] HMAC signing key |
|
|
|
# @param algorithm [String] Hash algorithm to use |
|
|
|
# @param sep [String] Hash separator |
|
|
|
def sign(key, algorithm: "sha256", sep: "&") |
|
|
|
@value += sep + ::OpenSSL::HMAC.base64digest(algorithm, key, @value) |
|
|
|
end |
|
|
|
|
|
|
|
# Verify HMAC signature |
|
|
|
# @param key [String] HMAC signing key |
|
|
|
# @param algorithm [String] Hash algorithm |
|
|
|
# @param sep [String] Hash separator |
|
|
|
# @return [Boolean] whether value is signed and valid |
|
|
|
def verify(key, algorithm: "sha256", sep: "&") |
|
|
|
val, sig = @value.match(/\A(.*)#{sep}([A-Za-z0-9+\/=]+)\Z/).to_a[1..] |
|
|
|
return false unless val and sig |
|
|
|
|
|
|
|
sig == ::OpenSSL::HMAC.base64digest(algorithm, key, val) |
|
|
|
end |
|
|
|
|
|
|
|
attr_accessor :key, :value |
|
|
|
attr_reader :domain, :path, :expires, :maxage, :samesite, :secure, :httponly |
|
|
|
|
|
|
@ -66,7 +94,7 @@ module Landline |
|
|
|
# @return [Cookie] |
|
|
|
def self.from_setcookie_string(data) |
|
|
|
kvpair, params = parse_value(data, regexp: HeaderRegexp::COOKIE_PARAM) |
|
|
|
key, value = kvpair.split("=").map(&:strip) |
|
|
|
key, value = kvpair.match(/([^=]+)=?(.*)/).to_a[1..].map(&:strip) |
|
|
|
Cookie.new(key, value, params) |
|
|
|
end |
|
|
|
|
|
|
@ -76,7 +104,8 @@ module Landline |
|
|
|
def self.from_cookie_string(data) |
|
|
|
hash = {} |
|
|
|
data.split(";").map do |cookiestr| |
|
|
|
cookie = Cookie.new(*cookiestr.split("=").map(&:strip)) |
|
|
|
key, value = cookiestr.match(/([^=]+)=?(.*)/).to_a[1..].map(&:strip) |
|
|
|
cookie = Cookie.new(key, value) |
|
|
|
if hash[cookie.key] |
|
|
|
hash[cookie.key].append(cookie) |
|
|
|
else |
|
|
@ -88,6 +117,12 @@ module Landline |
|
|
|
|
|
|
|
private |
|
|
|
|
|
|
|
def setup_hmac(params) |
|
|
|
@hmac = params['hmac'] |
|
|
|
@algorithm = (params['algorithm'] or "sha256") |
|
|
|
@sep = (params['sep'] or "&") |
|
|
|
end |
|
|
|
|
|
|
|
def setup_params(params) |
|
|
|
# Extended cookie params |
|
|
|
params.transform_keys!(&:downcase) |
|
|
|