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.



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

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

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

Instance Method Details

#_get_association(filename) ⇒ Object



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

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

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

    # clean up expired associations
    if association.expires_in == 0
      self.remove_if_present(filename)
      return nil
    else
      return association
    end
  end
end

#cleanupObject

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



170
171
172
173
# File 'lib/openid/store/filesystem.rb', line 170

def cleanup
  cleanup_associations
  cleanup_nonces
end

#cleanup_associationsObject



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/openid/store/filesystem.rb', line 175

def cleanup_associations
  association_filenames = Dir[File.join(@association_dir, "*")]
  count = 0
  association_filenames.each do |af|
    begin
      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
        self.remove_if_present(af)
        next
      else
        if association.expires_in == 0
          self.remove_if_present(af)
          count += 1
        end
      end
    end
  end
  return count
end

#cleanup_noncesObject



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/openid/store/filesystem.rb', line 205

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
      self.remove_if_present(filename)
      count += 1
    end
  end
  return count
end

#get_association(server_url, handle = nil) ⇒ Object

Retrieve an association



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

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)
  if handle
    return _get_association(filename)
  end
  assoc_filenames = Dir.glob(filename.to_s + '*')

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

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

  return nil if assocs.empty?
  return 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.



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

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

  proto, rest = server_url.split('://', 2)
  domain = filename_escape(rest.split('/',2)[0])
  url_hash = safe64(server_url)
  if handle
    handle_hash = safe64(handle)
  else
    handle_hash = ''
  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.



129
130
131
132
133
134
135
136
137
138
# File 'lib/openid/store/filesystem.rb', line 129

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

  if assoc.nil?
    return false
  else
    filename = get_association_filename(server_url, handle)
    return self.remove_if_present(filename)
  end
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
75
76
# 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
    self.remove_if_present(tmp)
    raise
  end
end

#use_nonce(server_url, timestamp, salt) ⇒ Object

Return whether the nonce is valid



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/openid/store/filesystem.rb', line 141

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 = '%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, 0200)
    fd.close
    return true
  rescue Errno::EEXIST
    return false
  end
end