Class: OpenID::Store::Filesystem

Inherits:
Interface show all
Defined in:
lib/openid/store/filesystem.rb

Constant Summary collapse

@@FILENAME_ALLOWED =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-".split("")

Instance Method Summary collapse

Constructor Details

#initialize(directory) ⇒ Filesystem

Create a Filesystem store instance, putting all data in directory.



17
18
19
20
21
22
23
24
25
# File 'lib/openid/store/filesystem.rb', line 17

def initialize(directory)
  @nonce_dir = File.join(directory, "nonces")
  @association_dir = File.join(directory, "associations")
  @temp_dir = File.join(directory, "temp")

  ensure_dir(@nonce_dir)
  ensure_dir(@association_dir)
  ensure_dir(@temp_dir)
end

Instance Method Details

#_get_association(filename) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/openid/store/filesystem.rb', line 97

def _get_association(filename)
  assoc_file = File.open(filename, "r")
rescue Errno::ENOENT
  nil
else
  begin
    assoc_s = assoc_file.read
  ensure
    assoc_file.close
  end

  begin
    association = Association.deserialize(assoc_s)
  rescue StandardError
    remove_if_present(filename)
    return
  end

  # clean up expired associations
  return association unless association.expires_in == 0

  remove_if_present(filename)
  nil
end

#cleanupObject

Remove expired entries from the database. This is potentially expensive, so only run when it is acceptable to take time.



163
164
165
166
# File 'lib/openid/store/filesystem.rb', line 163

def cleanup
  cleanup_associations
  cleanup_nonces
end

#cleanup_associationsObject



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/openid/store/filesystem.rb', line 168

def cleanup_associations
  association_filenames = Dir[File.join(@association_dir, "*")]
  count = 0
  association_filenames.each do |af|
    f = File.open(af, "r")
  rescue Errno::ENOENT
    next
  else
    begin
      assoc_s = f.read
    ensure
      f.close
    end
    begin
      association = OpenID::Association.deserialize(assoc_s)
    rescue StandardError
      remove_if_present(af)
      next
    else
      if association.expires_in == 0
        remove_if_present(af)
        count += 1
      end
    end
  end
  count
end

#cleanup_noncesObject



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/openid/store/filesystem.rb', line 196

def cleanup_nonces
  nonces = Dir[File.join(@nonce_dir, "*")]
  now = Time.now.to_i

  count = 0
  nonces.each do |filename|
    nonce = filename.split("/")[-1]
    timestamp = nonce.split("-", 2)[0].to_i(16)
    nonce_age = (timestamp - now).abs
    if nonce_age > Nonce.skew
      remove_if_present(filename)
      count += 1
    end
  end
  count
end

#get_association(server_url, handle = nil) ⇒ Object

Retrieve an association



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/openid/store/filesystem.rb', line 77

def get_association(server_url, handle = nil)
  # the filename with empty handle is the prefix for the associations
  # for a given server url
  filename = get_association_filename(server_url, handle)
  return _get_association(filename) if handle

  assoc_filenames = Dir.glob(filename.to_s + "*")

  assocs = assoc_filenames.collect do |f|
    _get_association(f)
  end

  assocs = assocs.find_all { |a| !a.nil? }
  assocs = assocs.sort_by { |a| a.issued }

  return if assocs.empty?

  assocs[-1]
end

#get_association_filename(server_url, handle) ⇒ Object

Create a unique filename for a given server url and handle. The filename that is returned will contain the domain name from the server URL for ease of human inspection of the data dir.

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/openid/store/filesystem.rb', line 30

def get_association_filename(server_url, handle)
  raise ArgumentError, "Bad server URL: #{server_url}" unless server_url.index("://")

  proto, rest = server_url.split("://", 2)
  domain = filename_escape(rest.split("/", 2)[0])
  url_hash = safe64(server_url)
  handle_hash = if handle
    safe64(handle)
  else
    ""
  end
  filename = [proto, domain, url_hash, handle_hash].join("-")
  File.join(@association_dir, filename)
end

#remove_association(server_url, handle) ⇒ Object

Remove an association if it exists, otherwise do nothing.



123
124
125
126
127
128
129
130
# File 'lib/openid/store/filesystem.rb', line 123

def remove_association(server_url, handle)
  assoc = get_association(server_url, handle)

  return false if assoc.nil?

  filename = get_association_filename(server_url, handle)
  remove_if_present(filename)
end

#store_association(server_url, association) ⇒ Object

Store an association in the assoc directory



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/openid/store/filesystem.rb', line 46

def store_association(server_url, association)
  assoc_s = association.serialize
  filename = get_association_filename(server_url, association.handle)
  f, tmp = mktemp

  begin
    begin
      f.write(assoc_s)
      f.fsync
    ensure
      f.close
    end

    begin
      File.rename(tmp, filename)
    rescue Errno::EEXIST
      begin
        File.unlink(filename)
      rescue Errno::ENOENT
        # do nothing
      end

      File.rename(tmp, filename)
    end
  rescue StandardError
    remove_if_present(tmp)
    raise
  end
end

#use_nonce(server_url, timestamp, salt) ⇒ Object

Return whether the nonce is valid



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/openid/store/filesystem.rb', line 133

def use_nonce(server_url, timestamp, salt)
  return false if (timestamp - Time.now.to_i).abs > Nonce.skew

  if server_url and !server_url.empty?
    proto, rest = server_url.split("://", 2)
  else
    proto = ""
    rest = ""
  end
  raise "Bad server URL" unless proto && rest

  domain = filename_escape(rest.split("/", 2)[0])
  url_hash = safe64(server_url)
  salt_hash = safe64(salt)

  nonce_fn = format("%08x-%s-%s-%s-%s", timestamp, proto, domain, url_hash, salt_hash)

  filename = File.join(@nonce_dir, nonce_fn)

  begin
    fd = File.new(filename, File::CREAT | File::EXCL | File::WRONLY, 0o200)
    fd.close
    true
  rescue Errno::EEXIST
    false
  end
end