module Apache::SecureDownload::Util

Constants

TIMESTAMP_LENGTH
TOKEN_KEY
TOKEN_LENGTH

Public Instance Methods

join(timestamp, token) click to toggle source

Joins timestamp and token parameters into a single value.

# File lib/apache/secure_download/util.rb, line 114
def join(timestamp, token)
  "#{"%0#{TIMESTAMP_LENGTH}x" % timestamp}#{token}"
end
real_path(path) click to toggle source

Returns path with timestamp and token parameter removed.

# File lib/apache/secure_download/util.rb, line 129
def real_path(path)
  clean(path, :path)
end
real_query(query) click to toggle source

Returns query with timestamp and token parameter removed.

# File lib/apache/secure_download/util.rb, line 134
def real_query(query)
  clean(query, :query)
end
secure_url(s, u, e = Time.now + 60) click to toggle source

Creates a valid URL to the secured resource, identified by url. The argument secret is the shared secret string that has been passed to the relevant RubyAccessHandler instance (cf. Apache::SecureDownload.new).

The expiration time may be either given as a Time (or Integer), or as a Hash with the following parameters:

:expires

Same as for the simple expires argument

:offset

The amount of seconds in the future (only if :expires is not given)

:cache

A time window for which identical URLs shall be produced, on average (defaults to :offset, if given)

Examples (s = "secret"):

# Only the path component (and an optional query component) will be taken into account
secure_url(s, "/secure/url")                    #=> "/secure/url?_asd=0047c3f5665671a9b3966e8bbed91fc0bb5594d576c504cdf0"
secure_url(s, "http://example.com/secure/url")  #=> "http://example.com/secure/url?_asd=0047c3f5665671a9b3966e8bbed91fc0bb5594d576c504cdf0"
secure_url(s, "/secure/url?query=value")        #=> "/secure/url?query=value&_asd=0047c3f566b482f943c35f4a1b5da6c646df6a65c0edc364cf"

# Expires in 10 minutes
secure_url(s, "/secure/url", Time.now + 600)  #=> "/secure/url?_asd=0047c3f7827e51f91cf4406f308a8df24f4e2cbf188de3c1bf"
secure_url(s, "/secure/url", :offset => 600)  #=> "/secure/url?_asd=0047c3fa9058eb12f9fc3fcd984fe4e918d3fd0590392c172d"

# Setting an offset will also allow caching; turn it off explicitly
secure_url(s, "/secure/url", :offset => 600, :cache => false)  #=> "/secure/url?_asd=0047c3f7827e51f91cf4406f308a8df24f4e2cbf188de3c1bf"

# Produce identical URLs for a window of 1 minute (on average)
t = Time.now
secure_url(s, "/secure/url", :expires => t,      :cache => 60)  #=> "/secure/url?_asd=0047c3f568ccf279daf1787d34ad063cbf5851ee88aae967fb"
secure_url(s, "/secure/url", :expires => t + 30, :cache => 60)  #=> "/secure/url?_asd=0047c3f568ccf279daf1787d34ad063cbf5851ee88aae967fb"
secure_url(s, "/secure/url", :expires => t + 60, :cache => 60)  #=> "/secure/url?_asd=0047c3f5a4c7dcea5679ad539a7bad1dc4b7f44eb3dd36d6e8"
secure_url(s, "/secure/url", :expires => t + 90, :cache => 60)  #=> "/secure/url?_asd=0047c3f5a4c7dcea5679ad539a7bad1dc4b7f44eb3dd36d6e8"

# Same as before, but use offset
secure_url(s, "/secure/url", :offset => 60) #=> "/secure/url?_asd=0047c3f5a4c7dcea5679ad539a7bad1dc4b7f44eb3dd36d6e8"
# 30 seconds later...
secure_url(s, "/secure/url", :offset => 60) #=> "/secure/url?_asd=0047c3f5a4c7dcea5679ad539a7bad1dc4b7f44eb3dd36d6e8"
# 30 seconds later...
secure_url(s, "/secure/url", :offset => 60) #=> "/secure/url?_asd=0047c3f5e0aa11618f1cc0883a29e9239b777ca53dfc4d9604"
# 30 seconds later...
secure_url(s, "/secure/url", :offset => 60) #=> "/secure/url?_asd=0047c3f5e0aa11618f1cc0883a29e9239b777ca53dfc4d9604"
# File lib/apache/secure_download/util.rb, line 92
def secure_url(s, u, e = Time.now + 60)
  if e.is_a?(Hash)
    e[:offset] ||= 60
    c = e[:cache] || e[:offset]

    t = (e[:expires] || Time.now + e[:offset]).to_i

    unless e[:cache] == false || c.zero?
      # make the URL cacheable for +c+ seconds *on average*
      t = ((t / c.to_f).round + 1) * c.to_i
    end
  else
    t = e.to_i
  end

  r, q = u[0, 1] == '/' ? u.split('?', 2) : URI.split(u).values_at(5, 7)
  r << '?' << q if q

  u.sub(/#|\z/, "#{q ? '&' : '?'}#{TOKEN_KEY}=#{join(t, token(s, r, t))}\\&")
end
split(value) click to toggle source

Splits value into timestamp and token parameters.

# File lib/apache/secure_download/util.rb, line 119
def split(value)
  [value[0, TIMESTAMP_LENGTH].to_i(16), value[TIMESTAMP_LENGTH, TOKEN_LENGTH]]
end
token(secret, path, timestamp) click to toggle source

Computes the token from secret, path, and timestamp.

# File lib/apache/secure_download/util.rb, line 124
def token(secret, path, timestamp)
  Digest::SHA1.hexdigest("#{secret}#{real_path(path)}#{timestamp}")
end

Private Instance Methods

clean(string, type) click to toggle source

Returns string with timestamp and token parameter removed. The type indicates whether it's a path or a query.

# File lib/apache/secure_download/util.rb, line 142
def clean(string, type)
  char = case type
    when :path  then '\?'
    when :query then '\A'
    else raise ArgumentError, "type #{type.inspect} not supported"
  end

  string.sub(/(#{char}|&)_asd=[^&]*(&?)/) { $1 unless $2.empty? }
end