icfs 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/bin/icfs_demo_fcgi.rb +2 -0
  3. data/{bin/icfs_demo_ssl_gen.rb → devel/demo/ssl_gen.rb} +25 -13
  4. data/devel/demo/ssl_gen.yml +14 -0
  5. data/devel/icfs-wrk/Dockerfile +1 -1
  6. data/devel/run/base.rb +92 -0
  7. data/devel/run/copy-s3.rb +2 -0
  8. data/devel/run/email.rb +36 -0
  9. data/devel/run/email_imap.rb +43 -0
  10. data/devel/run/email_smime.rb +47 -0
  11. data/devel/run/init-icfs.rb +2 -0
  12. data/devel/run/webrick.rb +5 -57
  13. data/lib/icfs/api.rb +101 -90
  14. data/lib/icfs/cache.rb +2 -0
  15. data/lib/icfs/cache_elastic.rb +127 -125
  16. data/lib/icfs/{web/config.rb → config.rb} +3 -3
  17. data/lib/icfs/{web/config_redis.rb → config_redis.rb} +8 -8
  18. data/lib/icfs/{web/config_s3.rb → config_s3.rb} +8 -8
  19. data/lib/icfs/demo/auth.rb +5 -7
  20. data/lib/icfs/demo/static.rb +2 -0
  21. data/lib/icfs/elastic.rb +10 -8
  22. data/lib/icfs/email/basic.rb +242 -0
  23. data/lib/icfs/email/core.rb +293 -0
  24. data/lib/icfs/email/from.rb +52 -0
  25. data/lib/icfs/email/imap.rb +148 -0
  26. data/lib/icfs/email/smime.rb +139 -0
  27. data/lib/icfs/items.rb +5 -3
  28. data/lib/icfs/store.rb +20 -18
  29. data/lib/icfs/store_fs.rb +7 -5
  30. data/lib/icfs/store_s3.rb +4 -2
  31. data/lib/icfs/users.rb +5 -3
  32. data/lib/icfs/users_fs.rb +8 -6
  33. data/lib/icfs/users_redis.rb +12 -10
  34. data/lib/icfs/users_s3.rb +6 -4
  35. data/lib/icfs/utils/backup.rb +30 -29
  36. data/lib/icfs/utils/check.rb +36 -34
  37. data/lib/icfs/validate.rb +24 -15
  38. data/lib/icfs/web/auth_ssl.rb +7 -9
  39. data/lib/icfs/web/client.rb +671 -679
  40. data/lib/icfs.rb +174 -10
  41. metadata +16 -7
  42. data/devel/devel-webrick.yml +0 -49
@@ -0,0 +1,52 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ # frozen_string_literal: true
13
+
14
+ require_relative 'core'
15
+
16
+ module ICFS
17
+ module Email
18
+
19
+ ##########################################################################
20
+ # Receive email user based on FROM: header
21
+ #
22
+ # @note Only use this in conjunction with some form of email spoofing
23
+ # prevention.
24
+ #
25
+ class From
26
+
27
+
28
+ ###############################################
29
+ # New instance
30
+ #
31
+ # @param map [Object] Maps email address to username
32
+ #
33
+ def initialize(map)
34
+ @map = map
35
+ end # def initialize()
36
+
37
+
38
+ ###############################################
39
+ # Extract the user based on the FROM: email.
40
+ #
41
+ def receive(env)
42
+ email = env[:msg].from.first
43
+ unam = @map[email]
44
+ env[:user] = unam if unam
45
+ return :continue
46
+ end # def receive()
47
+
48
+
49
+ end # class ICFS::Email::From
50
+
51
+ end # module ICFS::Email
52
+ end # module ICFS
@@ -0,0 +1,148 @@
1
+
2
+ #
3
+ # Investigative Case File System
4
+ #
5
+ # Copyright 2019 by Graham A. Field
6
+ #
7
+ # This program is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License version 3.
9
+ #
10
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
11
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
+
13
+ # frozen_string_literal: true
14
+
15
+ require 'net/imap'
16
+ require 'mail'
17
+
18
+ module ICFS
19
+ module Email
20
+
21
+ ##########################################################################
22
+ # Get email using IMAP
23
+ #
24
+ class Imap
25
+
26
+ ###############################################
27
+ # New instance
28
+ #
29
+ # @param core [Email::Core] The core email processor
30
+ # @param log [Logger] The log
31
+ # @param opts [Hash] configuration options
32
+ # @option opts [String] :address The server address
33
+ # @option opts [Integer] :port The server port
34
+ # @option opts [Boolean] :enable_ssl Use SSL or not
35
+ # @option opts [String] :username The login username
36
+ # @option opts [String] :password The login password
37
+ # @option opts [String] :mailbox The mailbox to read
38
+ # @option opts [Array,String] :keys keys to pass to {::Net::IMAP#uid_search}
39
+ # @option opts [Integer] :idle_time Seconds to wait IDLE before polling
40
+ # @option opts [Integer] :reconnect Seconds after which we can reconnect
41
+ #
42
+ def initialize(core, log, opts={})
43
+ @core = core
44
+ @log = log
45
+ @cfg = {
46
+ :port => 993,
47
+ :enable_ssl => true,
48
+ :keys => 'ALL',
49
+ :idle_time => 60*5,
50
+ :reconnect => 60*15,
51
+ }.merge!(opts)
52
+ end # def initialize()
53
+
54
+
55
+ ###############################################
56
+ # Handles reconnects when dropped
57
+ #
58
+ def reconnect()
59
+
60
+ while true
61
+ start = Time.now
62
+ begin
63
+ fetch()
64
+ rescue EOFError
65
+ if( (Time.now - start) > @cfg[:reconnect] )
66
+ @log.info('IMAP: lost connection, reconnecting')
67
+ next
68
+ else
69
+ @log.error('IMAP: disconnected under the reconnect limit')
70
+ break
71
+ end
72
+ end
73
+ end
74
+
75
+ end # def reconnect()
76
+
77
+
78
+ ###############################################
79
+ # Fetch messages
80
+ #
81
+ def fetch()
82
+
83
+ # open & login
84
+ imap = ::Net::IMAP.new(@cfg[:address], @cfg[:port],
85
+ @cfg[:enable_ssl], nil, false)
86
+ imap.login(@cfg[:username], @cfg[:password])
87
+ @log.info('IMAP: open and login')
88
+
89
+ # mailbox
90
+ if @cfg[:mailbox]
91
+ imap.select(::Net::IMAP.encode_utf7(@cfg[:mailbox]))
92
+ @log.info('IMAP: mailbox selected: %s' % @cfg[:mailbox])
93
+ else
94
+ @log.error('IMAP: no mailbox specified')
95
+ mbxs = imap.list('', '*')
96
+ @log.info('IMAP: mailbox list: %s' % mbxs.map{|mb| mb.name }.join(', '))
97
+ return
98
+ end
99
+
100
+ while true
101
+ # fetch messages
102
+ uids = imap.uid_search(@cfg[:keys])
103
+ @log.debug('IMAP: %d messages found' % uids.size)
104
+ uids.each do |uid|
105
+ fd = imap.uid_fetch(uid, ['RFC822'])[0]
106
+ @log.debug('IMAP: message fetched')
107
+ msg = ::Mail.new(fd.attr['RFC822'])
108
+ @core.receive(msg)
109
+ if @cfg[:delete]
110
+ imap.uid_store(uid, "+FLAGS", [:Deleted])
111
+ @log.debug('IMAP: message deleted')
112
+ end
113
+ end
114
+ if @cfg[:delete]
115
+ imap.expunge
116
+ @log.debug('IMAP: expunged')
117
+ end
118
+
119
+ # wait IDLE until new mail
120
+ if @cfg[:idle_time]
121
+ @log.debug('IMAP: starting IDLE')
122
+ imap.idle(@cfg[:idle_time]) do |resp|
123
+ if resp.is_a?(::Net::IMAP::UntaggedResponse) && resp.name == "EXISTS"
124
+ @log.debug('IMAP: new mail, IDLE done')
125
+ imap.idle_done
126
+ end
127
+ end
128
+ @log.debug('IMAP: exited IDLE')
129
+
130
+ # or we just run once
131
+ else
132
+ @log.debug('IMAP: fetch completed')
133
+ break
134
+ end
135
+ end
136
+
137
+ ensure
138
+ if defined?(imap) && imap && !imap.disconnected?
139
+ imap.disconnect
140
+ @log.info('IMAP: disconnected')
141
+ end
142
+ end # def fetch()
143
+
144
+
145
+ end # class ICFS::Email::Imap
146
+
147
+ end # module ICFS::Email
148
+ end # module ICFS
@@ -0,0 +1,139 @@
1
+ #
2
+ # Investigative Case File System
3
+ #
4
+ # Copyright 2019 by Graham A. Field
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License version 3.
8
+ #
9
+ # This program is distributed WITHOUT ANY WARRANTY; without even the
10
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
+
12
+ # frozen_string_literal: true
13
+
14
+ require 'openssl'
15
+
16
+ require_relative 'core'
17
+
18
+ module ICFS
19
+ module Email
20
+
21
+ ##########################################################################
22
+ # Receive S/MIME email
23
+ #
24
+ # This processes the most common S/MIME implementation:
25
+ # * Signed: multipart/signed; protocol="application/pkcs7-signature" with
26
+ # application/pkcs7-signature as the second part
27
+ # * Encrypted: application/pkcs7-mime containing eenvelope data
28
+ # * Both: application/pkcs7-mime containing encrypted data, which contains
29
+ # multipart/signed message
30
+ #
31
+ # In all cases, it will generate an email containing just the content. If
32
+ # the message is signed and verifies, it will try a lookup of the DN to
33
+ # determine the user.
34
+ #
35
+ class Smime
36
+
37
+
38
+ ###############################################
39
+ # New instance
40
+ #
41
+ # @param key [::OpenSSL::PKey] The key for the ICFS gateway
42
+ # @param cert [::OpenSSL::X509::Certificate] the ICFS gateway certificate
43
+ # @param ca [::OpenSSL::X509::Store] Trusted CA certs
44
+ # @param map [Object] Maps DN to user name
45
+ #
46
+ def initialize(key, cert, ca, map)
47
+ @key = key
48
+ @cert = cert
49
+ @ca = ca
50
+ @map = map
51
+ end # end def initialize()
52
+
53
+
54
+ ###############################################
55
+ # Process for S/MIME encryption or signatures
56
+ #
57
+ def receive(env)
58
+
59
+ # start with the main message
60
+ part = env[:msg]
61
+ replace = false
62
+
63
+ # process all PKCS7
64
+ while true
65
+
66
+ case part.header[:content_type].string
67
+
68
+ # encrypted
69
+ when 'application/pkcs7-mime', 'application/x-pkcs7-mime'
70
+
71
+ # decrypt
72
+ enc_raw = part.body.decoded
73
+ enc_p7 = ::OpenSSL::PKCS7.new(enc_raw)
74
+ dec_raw = enc_p7.decrypt(@key, @cert)
75
+
76
+ # new part is whatever we decrypted
77
+ part = ::Mail::Part.new(dec_raw)
78
+ replace = true
79
+
80
+ # signed
81
+ when 'multipart/signed'
82
+
83
+ # check sig
84
+ multi = part.body.parts
85
+ msg = multi[0].raw_source.dup.force_encoding('ASCII-8BIT')
86
+ msg = msg[2..-1] if msg[0,2] == "\r\n" # extra cr/lf
87
+ sig_raw = multi[1].body.decoded
88
+ sig_p7 = ::OpenSSL::PKCS7.new(sig_raw)
89
+ val = sig_p7.verify([], @ca, msg)
90
+ break unless val
91
+
92
+ # get cert that signed first
93
+ si = sig_p7.signers.first
94
+ cert = sig_p7.certificates.select do |c|
95
+ (c.issuer == si.issuer) && (c.serial == si.serial)
96
+ end
97
+ break if cert.empty?
98
+
99
+ # get user
100
+ unam = @map[cert.first.subject.to_s(::OpenSSL::X509::Name::RFC2253)]
101
+ break unless unam
102
+
103
+ # values
104
+ env[:user] = unam
105
+ env[:time] = si.signed_time.to_i
106
+
107
+ # new part is body
108
+ part = multi[0]
109
+ replace = true
110
+
111
+ # not PKCS7
112
+ else
113
+ break
114
+ end
115
+ end
116
+
117
+ return :continue unless replace
118
+
119
+ # create new message
120
+ msg = ::Mail::Message.new
121
+ msg.body = part.body.encoded
122
+ oh = env[:msg].header
123
+ hd = msg.header
124
+ Core::CopyFields.each do |fn|
125
+ fi = oh[fn]
126
+ hd[fn] = fi.value if fi
127
+ end
128
+ part.header.fields.each{|fd| hd[fd.name] = fd.value }
129
+
130
+ # Mail acts wierd about parts unless we run it thru text...
131
+ env[:msg] = ::Mail::Message.new(msg.encoded)
132
+
133
+ return :continue
134
+ end # def receive()
135
+
136
+ end # class ICFS::Email::Smime
137
+
138
+ end # module ICFS::Email
139
+ end # module ICFS
data/lib/icfs/items.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'json'
13
15
 
14
16
 
@@ -34,12 +36,12 @@ module Items
34
36
  #
35
37
  def self.parse(json, name, val)
36
38
  if json.nil?
37
- raise(Error::NotFound, '%s not found'.freeze % name)
39
+ raise(Error::NotFound, '%s not found' % name)
38
40
  end
39
41
  begin
40
42
  itm = JSON.parse(json)
41
43
  rescue
42
- raise(Error::Value, 'JSON parsing failed'.freeze)
44
+ raise(Error::Value, 'JSON parsing failed')
43
45
  end
44
46
  Items.validate(itm, name, val)
45
47
  return itm
@@ -74,7 +76,7 @@ module Items
74
76
  def self.validate(obj, name, val)
75
77
  err = Validate.check(obj, val)
76
78
  if err
77
- raise(Error::Value, '%s has bad values: %s'.freeze %
79
+ raise(Error::Value, '%s has bad values: %s' %
78
80
  [name, err.inspect])
79
81
  end
80
82
  end
data/lib/icfs/store.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  #
13
15
  module ICFS
14
16
 
@@ -222,9 +224,9 @@ class Store
222
224
  def _case(cid, lnum)
223
225
  @base + [
224
226
  cid,
225
- 'c'.freeze,
226
- '%d.json'.freeze % lnum
227
- ].join('/'.freeze)
227
+ 'c',
228
+ '%d.json' % lnum
229
+ ].join('/')
228
230
  end
229
231
 
230
232
 
@@ -234,9 +236,9 @@ class Store
234
236
  def _log(cid, lnum)
235
237
  @base + [
236
238
  cid,
237
- 'l'.freeze,
238
- '%d.json'.freeze % lnum
239
- ].join('/'.freeze)
239
+ 'l',
240
+ '%d.json' % lnum
241
+ ].join('/')
240
242
  end
241
243
 
242
244
 
@@ -246,10 +248,10 @@ class Store
246
248
  def _entry(cid, enum, lnum)
247
249
  @base + [
248
250
  cid,
249
- 'e'.freeze,
251
+ 'e',
250
252
  enum.to_s,
251
- '%d.json'.freeze % lnum
252
- ].join('/'.freeze)
253
+ '%d.json' % lnum
254
+ ].join('/')
253
255
  end
254
256
 
255
257
 
@@ -259,10 +261,10 @@ class Store
259
261
  def _file(cid, enum, lnum, fnum)
260
262
  @base + [
261
263
  cid,
262
- 'e'.freeze,
264
+ 'e',
263
265
  enum.to_s,
264
- '%d-%d.bin'.freeze % [lnum, fnum]
265
- ].join('/'.freeze)
266
+ '%d-%d.bin' % [lnum, fnum]
267
+ ].join('/')
266
268
  end
267
269
 
268
270
 
@@ -272,10 +274,10 @@ class Store
272
274
  def _action(cid, anum, lnum)
273
275
  @base + [
274
276
  cid,
275
- 'a'.freeze,
277
+ 'a',
276
278
  anum.to_s,
277
- lnum.to_s + '.json'.freeze
278
- ].join('/'.freeze)
279
+ lnum.to_s + '.json'
280
+ ].join('/')
279
281
  end
280
282
 
281
283
 
@@ -285,10 +287,10 @@ class Store
285
287
  def _index(cid, xnum, lnum)
286
288
  @base + [
287
289
  cid,
288
- 'i'.freeze,
290
+ 'i',
289
291
  xnum.to_s,
290
- lnum.to_s + '.json'.freeze
291
- ].join('/'.freeze)
292
+ lnum.to_s + '.json'
293
+ ].join('/')
292
294
  end
293
295
 
294
296
 
data/lib/icfs/store_fs.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'fileutils'
13
15
  require 'tempfile'
14
16
 
@@ -29,7 +31,7 @@ class StoreFs < Store
29
31
  # @param base [String] the base directory
30
32
  #
31
33
  def initialize(base)
32
- if base[-1] == '/'.freeze
34
+ if base[-1] == '/'
33
35
  @base = base.freeze
34
36
  else
35
37
  @base = (base + '/').freeze
@@ -75,7 +77,7 @@ class StoreFs < Store
75
77
  # (see Store#tempfile)
76
78
  #
77
79
  def tempfile
78
- Tempfile.new('tmp'.freeze, @base, :encoding => 'ascii-8bit'.freeze)
80
+ Tempfile.new('tmp', @base, :encoding => 'ascii-8bit')
79
81
  end
80
82
 
81
83
 
@@ -86,7 +88,7 @@ class StoreFs < Store
86
88
  # Read an item
87
89
  #
88
90
  def _read(fnam)
89
- return File.read(fnam, encoding: 'utf-8'.freeze)
91
+ return File.read(fnam, encoding: 'utf-8')
90
92
  rescue Errno::ENOENT
91
93
  return nil
92
94
  end
@@ -96,12 +98,12 @@ class StoreFs < Store
96
98
  # Write an item
97
99
  #
98
100
  def _write(fnam, item)
99
- File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
101
+ File.open(fnam, 'w', encoding: 'utf-8') do |fi|
100
102
  fi.write(item)
101
103
  end
102
104
  rescue Errno::ENOENT
103
105
  FileUtils.mkdir_p(File.dirname(fnam))
104
- File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
106
+ File.open(fnam, 'w', encoding: 'utf-8') do |fi|
105
107
  fi.write(item)
106
108
  end
107
109
  end
data/lib/icfs/store_s3.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'aws-sdk-s3'
13
15
  require 'tempfile'
14
16
 
@@ -30,7 +32,7 @@ class StoreS3 < Store
30
32
  def initialize(client, bucket, prefix=nil)
31
33
  @s3 = client
32
34
  @bck = bucket
33
- @base = prefix || ''.freeze
35
+ @base = prefix || ''
34
36
  end # def initialize()
35
37
 
36
38
 
@@ -79,7 +81,7 @@ class StoreS3 < Store
79
81
  # (see Store#tempfile)
80
82
  #
81
83
  def tempfile
82
- Tempfile.new('tmp'.freeze, encoding: 'ascii-8bit'.freeze)
84
+ Tempfile.new('tmp', encoding: 'ascii-8bit')
83
85
  end
84
86
 
85
87
 
data/lib/icfs/users.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'set'
13
15
 
14
16
  module ICFS
@@ -30,9 +32,9 @@ class Users
30
32
  'type' => {
31
33
  method: :string,
32
34
  allowed: Set[
33
- 'user'.freeze,
34
- 'role'.freeze,
35
- 'group'.freeze,
35
+ 'user',
36
+ 'role',
37
+ 'group',
36
38
  ].freeze
37
39
  }.freeze
38
40
  }.freeze,
data/lib/icfs/users_fs.rb CHANGED
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'tempfile'
13
15
  require 'fileutils'
14
16
  require 'json'
@@ -37,7 +39,7 @@ class UsersFs < Users
37
39
  # Path to store the file
38
40
  #
39
41
  def _path(urg)
40
- File.join(@path, urg + '.json'.freeze)
42
+ File.join(@path, urg + '.json')
41
43
  end
42
44
  private :_path
43
45
 
@@ -52,11 +54,11 @@ class UsersFs < Users
52
54
  # (see Users#read)
53
55
  #
54
56
  def read(urg)
55
- Items.validate(urg, 'User/Role/Group name'.freeze, Items::FieldUsergrp)
57
+ Items.validate(urg, 'User/Role/Group name', Items::FieldUsergrp)
56
58
  json = File.read(_path(urg))
57
- obj = Items.parse(json, 'User/Role/Group'.freeze, Users::ValUser)
59
+ obj = Items.parse(json, 'User/Role/Group', Users::ValUser)
58
60
  if obj['name'] != urg
59
- raise(Error::Values, 'UsersFs user %s name mismatch'.freeze % fn)
61
+ raise(Error::Values, 'UsersFs user %s name mismatch' % fn)
60
62
  end
61
63
  return obj
62
64
  rescue Errno::ENOENT
@@ -68,11 +70,11 @@ class UsersFs < Users
68
70
  # (see Users#write)
69
71
  #
70
72
  def write(obj)
71
- Items.validate(obj, 'User/Role/Group'.freeze, Users::ValUser)
73
+ Items.validate(obj, 'User/Role/Group', Users::ValUser)
72
74
  json = JSON.pretty_generate(obj)
73
75
 
74
76
  # write to temp file
75
- tmp = Tempfile.new('_tmp'.freeze, @path, :encoding => 'ascii-8bit'.freeze)
77
+ tmp = Tempfile.new('_tmp', @path, :encoding => 'ascii-8bit')
76
78
  tmp.write(json)
77
79
  tmp.close
78
80
 
@@ -9,6 +9,8 @@
9
9
  # This program is distributed WITHOUT ANY WARRANTY; without even the
10
10
  # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
11
 
12
+ # frozen_string_literal: true
13
+
12
14
  require 'redis'
13
15
 
14
16
  module ICFS
@@ -31,7 +33,7 @@ class UsersRedis < Users
31
33
  def initialize(redis, base, opts={})
32
34
  @redis = redis
33
35
  @base = base
34
- @pre = opts[:prefix] || ''.freeze
36
+ @pre = opts[:prefix] || ''
35
37
  @exp = opts[:expires] || 1*60*60 # 1 hour default
36
38
  @log = opts[:log]
37
39
  end
@@ -50,8 +52,8 @@ class UsersRedis < Users
50
52
  # (see Users#flush)
51
53
  #
52
54
  def flush(urg)
53
- Items.validate(urg, 'User/role/group name'.freeze, Items::FieldUsergrp)
54
- @log.info('User/role/group cache flush: %s'.freeze % urg) if @log
55
+ Items.validate(urg, 'User/role/group name', Items::FieldUsergrp)
56
+ @log.info('User/role/group cache flush: %s' % urg) if @log
55
57
  @redis.del(_key(urg))
56
58
  return true
57
59
  end # def flush()
@@ -61,21 +63,21 @@ class UsersRedis < Users
61
63
  # (see Users#read)
62
64
  #
63
65
  def read(urg)
64
- Items.validate(urg, 'User/role/group name'.freeze, Items::FieldUsergrp)
66
+ Items.validate(urg, 'User/role/group name', Items::FieldUsergrp)
65
67
  key = _key(urg)
66
- @log.debug('User/role/group read: %s'.freeze % urg) if @log
68
+ @log.debug('User/role/group read: %s' % urg) if @log
67
69
 
68
70
  # try cache
69
71
  json = @redis.get(key)
70
72
  if json
71
- @log.debug('User/role/group cache hit: %s'.freeze % urg) if @log
73
+ @log.debug('User/role/group cache hit: %s' % urg) if @log
72
74
  return JSON.parse(json)
73
75
  end
74
76
 
75
77
  # get base object from base store
76
78
  bse = @base.read(urg)
77
79
  if !bse
78
- @log.warn('User/role/group not found: %s'.freeze % urg) if @log
80
+ @log.warn('User/role/group not found: %s' % urg) if @log
79
81
  return nil
80
82
  end
81
83
 
@@ -128,7 +130,7 @@ class UsersRedis < Users
128
130
  # save to cache
129
131
  @redis.set(key, json)
130
132
  @redis.expire(key, @exp)
131
- @log.info('User/role/group cached: %s %s'.freeze % [urg, json]) if @log
133
+ @log.info('User/role/group cached: %s %s' % [urg, json]) if @log
132
134
  return bse
133
135
  end # def read()
134
136
 
@@ -137,9 +139,9 @@ class UsersRedis < Users
137
139
  # (see Users#write)
138
140
  #
139
141
  def write(obj)
140
- json = Items.generate(obj, 'User/Role/Group'.freeze, Users::ValUser)
142
+ json = Items.generate(obj, 'User/Role/Group', Users::ValUser)
141
143
  key = _key(obj['name'])
142
- @log.info('User/role/group write: %s'.freeze % urg) if @log
144
+ @log.info('User/role/group write: %s' % urg) if @log
143
145
  @redis.del(key)
144
146
  @base.write(obj)
145
147
  end # def write()