icfs 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +674 -0
- data/bin/icfs_demo_create.rb +89 -0
- data/bin/icfs_demo_fcgi.rb +51 -0
- data/bin/icfs_demo_ssl_gen.rb +84 -0
- data/bin/icfs_demo_web.rb +50 -0
- data/bin/icfs_dev_todo.rb +20 -0
- data/data/demo_config.yml +94 -0
- data/data/icfs.css +475 -0
- data/data/icfs.js +458 -0
- data/lib/icfs.rb +109 -0
- data/lib/icfs/api.rb +1436 -0
- data/lib/icfs/cache.rb +254 -0
- data/lib/icfs/cache_elastic.rb +1154 -0
- data/lib/icfs/demo/auth.rb +74 -0
- data/lib/icfs/demo/static.rb +59 -0
- data/lib/icfs/demo/timezone.rb +38 -0
- data/lib/icfs/elastic.rb +83 -0
- data/lib/icfs/items.rb +653 -0
- data/lib/icfs/store.rb +278 -0
- data/lib/icfs/store_fs.rb +98 -0
- data/lib/icfs/store_s3.rb +97 -0
- data/lib/icfs/users.rb +80 -0
- data/lib/icfs/users_elastic.rb +166 -0
- data/lib/icfs/users_fs.rb +132 -0
- data/lib/icfs/validate.rb +479 -0
- data/lib/icfs/web/auth_ssl.rb +73 -0
- data/lib/icfs/web/client.rb +4498 -0
- metadata +77 -0
data/lib/icfs/store.rb
ADDED
@@ -0,0 +1,278 @@
|
|
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
|
+
#
|
13
|
+
module ICFS
|
14
|
+
|
15
|
+
##########################################################################
|
16
|
+
# Permanent store for items
|
17
|
+
#
|
18
|
+
# Provides storage for:
|
19
|
+
# * Case
|
20
|
+
# * Log
|
21
|
+
# * Entry
|
22
|
+
# * Attached files
|
23
|
+
# * Action
|
24
|
+
# * Indexes
|
25
|
+
#
|
26
|
+
# @abstract
|
27
|
+
#
|
28
|
+
class Store
|
29
|
+
|
30
|
+
###############################################
|
31
|
+
# Read a case
|
32
|
+
#
|
33
|
+
# @param cid [String] caseid
|
34
|
+
# @param lnum [Integer] Log number
|
35
|
+
# @return [String] The JSON encoded item
|
36
|
+
#
|
37
|
+
def case_read(cid, lnum); _read(_case(cid, lnum)); end
|
38
|
+
|
39
|
+
|
40
|
+
###############################################
|
41
|
+
# Write a case
|
42
|
+
#
|
43
|
+
# @param cid [String] caseid
|
44
|
+
# @param lnum [Integer] Log number
|
45
|
+
# @param item [String] the JSON encoded item
|
46
|
+
#
|
47
|
+
def case_write(cid, lnum, item); _write(_case(cid, lnum), item); end
|
48
|
+
|
49
|
+
|
50
|
+
###############################################
|
51
|
+
# Read a log
|
52
|
+
#
|
53
|
+
# @param cid [String] caseid
|
54
|
+
# @param lnum [Integer] Log number
|
55
|
+
# @return [String] The JSON encoded item
|
56
|
+
#
|
57
|
+
def log_read(cid, lnum); _read(_log(cid, lnum)); end
|
58
|
+
|
59
|
+
|
60
|
+
###############################################
|
61
|
+
# Write a log
|
62
|
+
#
|
63
|
+
# @param cid [String] caseid
|
64
|
+
# @param lnum [Integer] Log number
|
65
|
+
# @param item [String] the JSON encoded item
|
66
|
+
#
|
67
|
+
def log_write(cid, lnum, item); _write(_log(cid, lnum), item); end
|
68
|
+
|
69
|
+
|
70
|
+
###############################################
|
71
|
+
# Read an entry
|
72
|
+
#
|
73
|
+
# @param cid [String] caseid
|
74
|
+
# @param enum [Integer] Entry number
|
75
|
+
# @param lnum [Integer] Log number
|
76
|
+
# @return [String] The JSON encoded item
|
77
|
+
#
|
78
|
+
def entry_read(cid, enum, lnum); _read(_entry(cid, enum, lnum)); end
|
79
|
+
|
80
|
+
|
81
|
+
###############################################
|
82
|
+
# Write an entry
|
83
|
+
#
|
84
|
+
# @param cid [String] caseid
|
85
|
+
# @param enum [Integer] Entry number
|
86
|
+
# @param lnum [Integer] Log number
|
87
|
+
# @param item [String] the JSON encoded item
|
88
|
+
#
|
89
|
+
def entry_write(cid, enum, lnum, item)
|
90
|
+
_write(_entry(cid, enum, lnum), item)
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
###############################################
|
95
|
+
# Read a file
|
96
|
+
#
|
97
|
+
# @param cid [String] caseid
|
98
|
+
# @param enum [Integer] Entry number
|
99
|
+
# @param lnum [Integer] Log number
|
100
|
+
# @param fnum [Integer] File number
|
101
|
+
# @return [File,Tempfile] Read only copy of the file
|
102
|
+
#
|
103
|
+
def file_read(cid, enum, lnum, fnum); raise NotImplementedError; end
|
104
|
+
|
105
|
+
|
106
|
+
###############################################
|
107
|
+
# Write a file
|
108
|
+
#
|
109
|
+
# @param cid [String] caseid
|
110
|
+
# @param enum [Integer] Entry number
|
111
|
+
# @param lnum [Integer] Log number
|
112
|
+
# @param fnum [Integer] File number
|
113
|
+
# @param tmpf [Tempfile] A Tempfile obtained from #tempfile
|
114
|
+
#
|
115
|
+
def file_write(cid, enum, lnum, fnum, tmpf); raise NotImplementedError; end
|
116
|
+
|
117
|
+
|
118
|
+
###############################################
|
119
|
+
# Read an action
|
120
|
+
#
|
121
|
+
# @param cid [String] caseid
|
122
|
+
# @param anum [Integer] Action number
|
123
|
+
# @param lnum [Integer] Log number
|
124
|
+
# @return [String] The JSON encoded item
|
125
|
+
#
|
126
|
+
def action_read(cid, anum, lnum)
|
127
|
+
_read(_action(cid, anum, lnum))
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
###############################################
|
132
|
+
# Write an action
|
133
|
+
#
|
134
|
+
# @param cid [String] caseid
|
135
|
+
# @param anum [Integer] Action number
|
136
|
+
# @param lnum [Integer] Log number
|
137
|
+
# @param item [String] the JSON encoded item
|
138
|
+
#
|
139
|
+
def action_write(cid, anum, lnum, item)
|
140
|
+
_write(_action(cid, anum, lnum), item)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
###############################################
|
145
|
+
# Read an Index
|
146
|
+
#
|
147
|
+
# @param cid [String] caseid
|
148
|
+
# @param xnum [Integer] Index number
|
149
|
+
# @param lnum [Integer] Log number
|
150
|
+
# @return [String] the JSON encoded item
|
151
|
+
#
|
152
|
+
def index_read(cid, xnum, lnum)
|
153
|
+
_read(_index(cid, xnum, lnum))
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
###############################################
|
158
|
+
# Write an Index
|
159
|
+
#
|
160
|
+
# @param cid [String] caseid
|
161
|
+
# @param xnum [Integer] Index number
|
162
|
+
# @param lnum [Integer] Log number
|
163
|
+
# @param item [String] the JSON encoded item
|
164
|
+
#
|
165
|
+
def index_write(cid, xnum, lnum, item)
|
166
|
+
_write(_index(cid, xnum, lnum), item)
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
###############################################
|
171
|
+
# Get a Tempfile to use to write files
|
172
|
+
#
|
173
|
+
# @return [Tempfile] a Tempfile which can be written and passed to
|
174
|
+
# #file_write
|
175
|
+
#
|
176
|
+
def tempfile; raise NotImplementedError; end
|
177
|
+
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
|
182
|
+
###############################################
|
183
|
+
# Read an item
|
184
|
+
#
|
185
|
+
def _read(path); raise NotImplementedError; end
|
186
|
+
|
187
|
+
|
188
|
+
###############################################
|
189
|
+
# Write an item
|
190
|
+
#
|
191
|
+
def _write(path, item); raise NotImplementedError; end
|
192
|
+
|
193
|
+
|
194
|
+
###############################################
|
195
|
+
# Path for case
|
196
|
+
#
|
197
|
+
def _case(cid, lnum)
|
198
|
+
[
|
199
|
+
@base,
|
200
|
+
cid,
|
201
|
+
'c'.freeze,
|
202
|
+
'%d.json'.freeze % lnum
|
203
|
+
].join('/'.freeze)
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
###############################################
|
208
|
+
# Path for log
|
209
|
+
#
|
210
|
+
def _log(cid, lnum)
|
211
|
+
[
|
212
|
+
@base,
|
213
|
+
cid,
|
214
|
+
'l'.freeze,
|
215
|
+
'%d.json'.freeze % lnum
|
216
|
+
].join('/'.freeze)
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
###############################################
|
221
|
+
# Path for entry
|
222
|
+
#
|
223
|
+
def _entry(cid, enum, lnum)
|
224
|
+
[
|
225
|
+
@base,
|
226
|
+
cid,
|
227
|
+
'e'.freeze,
|
228
|
+
enum.to_s,
|
229
|
+
'%d.json'.freeze % lnum
|
230
|
+
].join('/'.freeze)
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
###############################################
|
235
|
+
# Path for file
|
236
|
+
#
|
237
|
+
def _file(cid, enum, lnum, fnum)
|
238
|
+
[
|
239
|
+
@base,
|
240
|
+
cid,
|
241
|
+
'e'.freeze,
|
242
|
+
enum.to_s,
|
243
|
+
'%d-%d.bin'.freeze % [lnum, fnum]
|
244
|
+
].join('/'.freeze)
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
###############################################
|
249
|
+
# Filename for action
|
250
|
+
#
|
251
|
+
def _action(cid, anum, lnum)
|
252
|
+
[
|
253
|
+
@base,
|
254
|
+
cid,
|
255
|
+
'a'.freeze,
|
256
|
+
anum.to_s,
|
257
|
+
lnum.to_s + '.json'.freeze
|
258
|
+
].join('/'.freeze)
|
259
|
+
end
|
260
|
+
|
261
|
+
|
262
|
+
###############################################
|
263
|
+
# Filename for index
|
264
|
+
#
|
265
|
+
def _index(cid, xnum, lnum)
|
266
|
+
[
|
267
|
+
@base,
|
268
|
+
cid,
|
269
|
+
'i'.freeze,
|
270
|
+
xnum.to_s,
|
271
|
+
lnum.to_s + '.json'.freeze
|
272
|
+
].join('/'.freeze)
|
273
|
+
end
|
274
|
+
|
275
|
+
|
276
|
+
end # class ICFS::Store
|
277
|
+
|
278
|
+
end # module ICFS
|
@@ -0,0 +1,98 @@
|
|
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
|
+
require 'fileutils'
|
13
|
+
require 'tempfile'
|
14
|
+
|
15
|
+
module ICFS
|
16
|
+
|
17
|
+
##########################################################################
|
18
|
+
# Permanent store for items using the filesystem
|
19
|
+
#
|
20
|
+
# @deprecated Using a filesystem for a production system is a horrible idea.
|
21
|
+
# This is provided as an example and should be used for development use
|
22
|
+
# only.
|
23
|
+
#
|
24
|
+
class StoreFs < Store
|
25
|
+
|
26
|
+
###############################################
|
27
|
+
# New instance
|
28
|
+
#
|
29
|
+
# @param base [String] the base directory
|
30
|
+
#
|
31
|
+
def initialize(base)
|
32
|
+
@base = base.freeze
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
###############################################
|
37
|
+
# (see Store#file_read)
|
38
|
+
#
|
39
|
+
def file_read(cid, enum, lnum, fnum)
|
40
|
+
File.open(_file(cid, enum, lnum, fnum), 'rb')
|
41
|
+
rescue Errno::ENOENT
|
42
|
+
return nil
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
###############################################
|
47
|
+
# (see Store#file_write)
|
48
|
+
#
|
49
|
+
def file_write(cid, enum, lnum, fnum, tmpf)
|
50
|
+
fn = _file(cid, enum, lnum, fnum)
|
51
|
+
FileUtils.ln(tmpf.path, fn, force: true)
|
52
|
+
tmpf.close!
|
53
|
+
rescue Errno::ENOENT
|
54
|
+
FileUtils.mkdir_p(File.dirname(fn))
|
55
|
+
FileUtils.ln(tmpf.path, fn, force: true)
|
56
|
+
tmpf.close!
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
###############################################
|
61
|
+
# (see Store#tempfile)
|
62
|
+
#
|
63
|
+
def tempfile
|
64
|
+
Tempfile.new('tmp'.freeze, @base, :encoding => 'ascii-8bit'.freeze)
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
|
71
|
+
###############################################
|
72
|
+
# Read an item
|
73
|
+
#
|
74
|
+
def _read(fnam)
|
75
|
+
return File.read(fnam, encoding: 'utf-8'.freeze)
|
76
|
+
rescue Errno::ENOENT
|
77
|
+
return nil
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
###############################################
|
82
|
+
# Write an item
|
83
|
+
#
|
84
|
+
def _write(fnam, item)
|
85
|
+
File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
|
86
|
+
fi.write(item)
|
87
|
+
end
|
88
|
+
rescue Errno::ENOENT
|
89
|
+
FileUtils.mkdir_p(File.dirname(fnam))
|
90
|
+
File.open(fnam, 'w'.freeze, encoding: 'utf-8'.freeze) do |fi|
|
91
|
+
fi.write(item)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end # class StoreFs
|
97
|
+
|
98
|
+
end # module ICFS
|
@@ -0,0 +1,97 @@
|
|
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
|
+
require 'aws-sdk-s3'
|
13
|
+
require 'tempfile'
|
14
|
+
|
15
|
+
module ICFS
|
16
|
+
|
17
|
+
##########################################################################
|
18
|
+
# Permanent store for items using AWS S3
|
19
|
+
#
|
20
|
+
class StoreS3 < Store
|
21
|
+
|
22
|
+
|
23
|
+
###############################################
|
24
|
+
# New store
|
25
|
+
#
|
26
|
+
# @param client [Aws::S3::Client] The configured S3 client
|
27
|
+
# @param bucket [String] The bucket name
|
28
|
+
# @param prefix [String] Prefix to use for object keys
|
29
|
+
#
|
30
|
+
def initialize(client, bucket, prefix=nil)
|
31
|
+
@s3 = client
|
32
|
+
@bck = bucket
|
33
|
+
@base = prefix || ''.freeze
|
34
|
+
end # def initialize()
|
35
|
+
|
36
|
+
|
37
|
+
###############################################
|
38
|
+
# (see Store#file_read)
|
39
|
+
#
|
40
|
+
def file_read(cid, enum, lnum, fnum)
|
41
|
+
tmp = tempfile
|
42
|
+
key = _file(cid, enum, lnum, fnum)
|
43
|
+
@s3.get_object( bucket: @bck, key: key, response_target: tmp)
|
44
|
+
tmp.rewind
|
45
|
+
return tmp
|
46
|
+
rescue Aws::S3::Errors::NoSuchKey
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
###############################################
|
52
|
+
# (see Store#file_write)
|
53
|
+
#
|
54
|
+
def file_write(cid, enum, lnum, fnum, tmpf)
|
55
|
+
key = _file(cid, enum, lnum, fnum)
|
56
|
+
tmpf.rewind
|
57
|
+
@s3.put_object( bucket: @bck, key: key, body: tmpf )
|
58
|
+
|
59
|
+
if tmpf.respond_to?( :close! )
|
60
|
+
tmpf.close!
|
61
|
+
else
|
62
|
+
tmpf.close
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
###############################################
|
68
|
+
# (see Store#tempfile)
|
69
|
+
#
|
70
|
+
def tempfile
|
71
|
+
Tempfile.new('tmp'.freeze, encoding: 'ascii-8bit'.freeze)
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
|
78
|
+
###############################################
|
79
|
+
# (see Store#_read)
|
80
|
+
#
|
81
|
+
def _read(path)
|
82
|
+
@s3.get_object( bucket: @bck, key: path).body.read
|
83
|
+
rescue Aws::S3::Errors::NoSuchKey
|
84
|
+
return nil
|
85
|
+
end # def _read()
|
86
|
+
|
87
|
+
|
88
|
+
###############################################
|
89
|
+
# (see Store#_write)
|
90
|
+
#
|
91
|
+
def _write(path, item)
|
92
|
+
@s3.put_object( bucket: @bck, key: path, body: item )
|
93
|
+
end
|
94
|
+
|
95
|
+
end # class ICFS::Store
|
96
|
+
|
97
|
+
end # module ICFS
|