store_agent 1.0.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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +5 -0
  6. data/LICENSE +202 -0
  7. data/README.md +433 -0
  8. data/Rakefile +2 -0
  9. data/lib/store_agent.rb +31 -0
  10. data/lib/store_agent/config.rb +152 -0
  11. data/lib/store_agent/data_encoder.rb +32 -0
  12. data/lib/store_agent/data_encoder/gzip_encoder.rb +50 -0
  13. data/lib/store_agent/data_encoder/openssl_aes_256_cbc_encoder.rb +65 -0
  14. data/lib/store_agent/exceptions.rb +87 -0
  15. data/lib/store_agent/node.rb +26 -0
  16. data/lib/store_agent/node/attachment.rb +93 -0
  17. data/lib/store_agent/node/attachment/metadata.rb +120 -0
  18. data/lib/store_agent/node/attachment/permission.rb +121 -0
  19. data/lib/store_agent/node/object.rb +233 -0
  20. data/lib/store_agent/node/object/directory_object.rb +264 -0
  21. data/lib/store_agent/node/object/file_object.rb +197 -0
  22. data/lib/store_agent/node/object/virtual_object.rb +25 -0
  23. data/lib/store_agent/node/prepend_module/locker.rb +125 -0
  24. data/lib/store_agent/node/prepend_module/path_validator.rb +138 -0
  25. data/lib/store_agent/node/prepend_module/permission_checker.rb +96 -0
  26. data/lib/store_agent/user.rb +111 -0
  27. data/lib/store_agent/validator.rb +60 -0
  28. data/lib/store_agent/version.rb +19 -0
  29. data/lib/store_agent/version_manager.rb +101 -0
  30. data/lib/store_agent/version_manager/ruby_git.rb +100 -0
  31. data/lib/store_agent/version_manager/rugged_git.rb +133 -0
  32. data/lib/store_agent/workspace.rb +88 -0
  33. data/spec/spec_helper.rb +47 -0
  34. data/spec/store_agent/data_encoder/encoder_shared_context.rb +74 -0
  35. data/spec/store_agent/data_encoder/gzip_encoder_spec.rb +41 -0
  36. data/spec/store_agent/data_encoder/openssl_aes_256_cbc_encoder_spec.rb +42 -0
  37. data/spec/store_agent/data_encoder_spec.rb +78 -0
  38. data/spec/store_agent/node/directory_object_spec.rb +563 -0
  39. data/spec/store_agent/node/file_object_spec.rb +379 -0
  40. data/spec/store_agent/node/locker_spec.rb +191 -0
  41. data/spec/store_agent/node/metadata_spec.rb +339 -0
  42. data/spec/store_agent/node/object_spec.rb +73 -0
  43. data/spec/store_agent/node/path_validator_spec.rb +121 -0
  44. data/spec/store_agent/node/permission_spec.rb +232 -0
  45. data/spec/store_agent/user_spec.rb +127 -0
  46. data/spec/store_agent/version_manager/git_shared_context.rb +286 -0
  47. data/spec/store_agent/version_manager/ruby_git_spec.rb +32 -0
  48. data/spec/store_agent/version_manager/rugged_git_spec.rb +32 -0
  49. data/spec/store_agent/workspace_spec.rb +107 -0
  50. data/spec/store_agent_spec.rb +53 -0
  51. data/store_agent.gemspec +33 -0
  52. metadata +252 -0
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,31 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "forwardable"
18
+ require "oj"
19
+ require "store_agent/version"
20
+ require "store_agent/config"
21
+ require "store_agent/exceptions"
22
+ require "store_agent/validator"
23
+ require "store_agent/user"
24
+ require "store_agent/workspace"
25
+ require "store_agent/node"
26
+ require "store_agent/version_manager"
27
+ require "store_agent/data_encoder"
28
+
29
+ # https://github.com/realglobe-Inc/store_agent も参照。
30
+ module StoreAgent
31
+ end
@@ -0,0 +1,152 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ module StoreAgent
18
+ module_function
19
+
20
+ # 設定を変更するメソッド
21
+ # StoreAgent.configure do |c|
22
+ # c.storage_root = "/path/to/storage/root"
23
+ # c.version_manager = StoreAgent::VersionManager::RuggedGit
24
+ # end
25
+ # 変更可能な項目は config メソッドを参照
26
+ def configure
27
+ yield config
28
+ end
29
+
30
+ # 設定を参照するメソッド
31
+ # c = StoreAgent.config # => #<StoreAgent::Configuration:...>
32
+ # c.storage_root # => "/path/to/storage/root"
33
+ # c.storage_dirname # => "/storage"
34
+ # 各設定と初期値は以下
35
+ # [storage_root]
36
+ # ライブラリがファイルやメタデータを保存するディレクトリのパス。
37
+ # c.storage_root # => "/tmp/store_agent"
38
+ # [storage_dirname]
39
+ # ワークスペース内で、ファイルの実体を保存するディレクトリ名。
40
+ # c.storage_dirname # => "/storage"
41
+ # [metadata_dirname]
42
+ # ワークスペース内で、ファイルのメタデータを保存するディレクトリ名。
43
+ # c.metadata_dirname # => "/metadata"
44
+ # [permission_dirname]
45
+ # ワークスペース内で、ファイルの権限情報を保存するディレクトリ名。
46
+ # c.permission_dirname # => "/permission"
47
+ # [metadata_extension]
48
+ # メタデータファイルの拡張子。<br>
49
+ # 名前の末尾がこの拡張子と一致する場合、ファイルやディレクトリは作成できない。
50
+ # c.metadata_extension # => ".meta"
51
+ # [permission_extension]
52
+ # 権限情報ファイルの拡張子。<br>
53
+ # 名前の末尾がこの拡張子と一致する場合、ファイルやディレクトリは作成できない。
54
+ # c.permission_extension # => ".perm"
55
+ # [superuser_identifier]
56
+ # スーパーユーザーのユーザーID。
57
+ # c.superuser_identifier # => "root"
58
+ # [guest_identifier]
59
+ # ゲストユーザーのユーザーID。
60
+ # c.guest_identifier # => "nobody"
61
+ # [version_manager]
62
+ # バージョン管理に使用するクラスの名前。
63
+ # c.version_manager # => StoreAgent::VersionManager
64
+ # [storage_data_encoders]
65
+ # ファイルの実体をエンコードするのに使用するインスタンスの配列。<br>
66
+ # 配列が複数要素を含む場合、各インスタンスのencode/decodeメソッドが順に呼ばれる。
67
+ # c.storage_data_encoders # => []
68
+ # [attachment_data_encoders]
69
+ # ファイルのメタデータ/権限情報をエンコードするのに使用するインスタンスの配列。<br>
70
+ # 配列が複数要素を含む場合、各インスタンスのencode/decodeメソッドが順に呼ばれる。
71
+ # c.attachment_data_encoders # => []
72
+ # [reserved_filenames]
73
+ # システムが予約しているファイル名の配列。<br>
74
+ # 名前が含まれている場合、ファイルやディレクトリは作成できない。
75
+ # c.reserved_filenames # => [".", ".."]
76
+ # [lock_timeout]
77
+ # ファイル読み書き時のロックのタイムアウト秒数。
78
+ # c.lock_timeout # => 0.1
79
+ # [default_directory_bytesize_limit]
80
+ # 現在のバージョンでは使用していない。
81
+ # [json_indent_level]
82
+ # メタデータ/権限情報をJSON形式で保存する際、半角スペース何個でインデントするかの指定。
83
+ # c.json_indent_level # => 2
84
+ # [default_owner_permission]
85
+ # ファイルやディレクトリの作成者にデフォルトで付与される権限。<br>
86
+ # 現在のバージョンでは read、write、chown、chmod の4種類が権限として使用できる。
87
+ # c.default_owner_permission
88
+ # # =>
89
+ # # {
90
+ # # "read" => true,
91
+ # # "write" => true,
92
+ # # "execute" => true
93
+ # # }
94
+ # [default_guest_permission]
95
+ # 権限情報が登録されていないユーザーやゲストユーザーに対して付与される権限。
96
+ # c.default_guest_permission # => {}
97
+ def config
98
+ @config ||= StoreAgent::Configuration.new
99
+ end
100
+
101
+ def reserved_filenames # :nodoc:
102
+ config.reserved_filenames | config.version_manager.reserved_filenames
103
+ end
104
+
105
+ # 設定を保持するためのクラス<br>
106
+ # 設定可能な項目は、StoreAgent.config を参照
107
+ class Configuration
108
+ # :enddoc:
109
+ attr_accessor :storage_root
110
+ attr_accessor :storage_dirname
111
+ attr_accessor :metadata_dirname
112
+ attr_accessor :permission_dirname
113
+ attr_accessor :metadata_extension
114
+ attr_accessor :permission_extension
115
+ attr_accessor :superuser_identifier
116
+ attr_accessor :guest_identifier
117
+ attr_accessor :version_manager
118
+ attr_accessor :storage_data_encoders
119
+ attr_accessor :attachment_data_encoders
120
+ attr_accessor :reserved_filenames
121
+ attr_accessor :lock_timeout
122
+ attr_accessor :default_directory_bytesize_limit
123
+ attr_accessor :json_indent_level
124
+ attr_accessor :default_owner_permission
125
+ attr_accessor :default_guest_permission
126
+
127
+ def initialize # :nodoc:
128
+ @storage_root = "/tmp/store_agent"
129
+ @storage_dirname = "/storage"
130
+ @metadata_dirname = "/metadata"
131
+ @permission_dirname = "/permission"
132
+ @metadata_extension = ".meta"
133
+ @permission_extension = ".perm"
134
+ @superuser_identifier = "root"
135
+ @guest_identifier = "nobody"
136
+ @version_manager = StoreAgent::VersionManager
137
+ @storage_data_encoders = []
138
+ @attachment_data_encoders = []
139
+ @reserved_filenames = %w(. ..)
140
+ @lock_timeout = 0.1
141
+ @default_directory_bytesize_limit = 2 ** 30
142
+ @default_owner_permission = {
143
+ "read" => true,
144
+ "write" => true,
145
+ "execute" => true,
146
+ "chown" => true,
147
+ "chmod" => true
148
+ }
149
+ @default_guest_permission = {}
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,32 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "store_agent/data_encoder/gzip_encoder"
18
+ require "store_agent/data_encoder/openssl_aes_256_cbc_encoder"
19
+
20
+ module StoreAgent
21
+ # データを保存する際に使用するエンコーダ<br>
22
+ # このクラス自体は継承して使用するためのインターフェースなので、そのままでは使用できない
23
+ class DataEncoder
24
+ def encode(*, &block)
25
+ yield.force_encoding("UTF-8")
26
+ end
27
+
28
+ def decode(*, &block)
29
+ yield.force_encoding("UTF-8")
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "zlib"
18
+
19
+ module StoreAgent
20
+ class DataEncoder
21
+ # データをgzip圧縮して保存するためのエンコーダ<br>
22
+ # 使用する際には StoreAgent.configure で初期化時に指定する
23
+ # StoreAgent.configure do |c|
24
+ # c.storage_data_encoders = [StoreAgent::DataEncoder::GzipEncoder.new]
25
+ # end
26
+ class GzipEncoder < DataEncoder
27
+ def encode(data, **_)
28
+ super do
29
+ StringIO.open("", "r+") do |sio|
30
+ Zlib::GzipWriter.wrap(sio) do |gz|
31
+ gz.write(data)
32
+ gz.finish
33
+ end
34
+ sio.rewind
35
+ sio.read
36
+ end
37
+ end
38
+ end
39
+
40
+ def decode(encrypted_data, **_)
41
+ super do
42
+ sio = StringIO.new(encrypted_data, "r")
43
+ Zlib::GzipReader.wrap(sio) do |gz|
44
+ gz.read
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,65 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ module StoreAgent
18
+ class DataEncoder
19
+ # データを OpenSSL AES-256-CBC で暗号化して保存するためのエンコーダ
20
+ # StoreAgent.configure do |c|
21
+ # c.storage_data_encoders = [StoreAgent::DataEncoder::OpensslAes256CbcEncoder]
22
+ # end
23
+ # 暗号化にパスワードを使用する場合、環境変数で指定する
24
+ # $ env STORE_AGENT_DATA_ENCODER_PASSWORD=password ruby-command
25
+ # 指定が無い場合には空文字列をパスワードとして使用する
26
+ class OpensslAes256CbcEncoder < DataEncoder
27
+ def initialize # :nodoc:
28
+ @password = ENV["STORE_AGENT_DATA_ENCODER_PASSWORD"] || ""
29
+ @encryptor = OpenSSL::Cipher::AES.new(256, "CBC")
30
+ end
31
+
32
+ def encode(data, password: @password, **_)
33
+ super do
34
+ @encryptor.encrypt
35
+ salt = OpenSSL::Random.random_bytes(8)
36
+ encrypted_data = crypt(data: data, password: password, salt: salt)
37
+ "Salted__#{salt}#{encrypted_data}"
38
+ end
39
+ end
40
+
41
+ def decode(encrypted_data, password: @password, **_)
42
+ super do
43
+ @encryptor.decrypt
44
+ encrypted_data.force_encoding("ASCII-8BIT")
45
+ salt = encrypted_data[8..15]
46
+ data = encrypted_data[16..-1]
47
+ crypt(data: data, password: password, salt: salt)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def crypt(data: "", password: "", salt: "")
54
+ md5_base = "#{password}#{salt}".force_encoding("ASCII-8BIT")
55
+ md5_digest1 = OpenSSL::Digest::MD5.new(md5_base).digest
56
+ md5_digest2 = OpenSSL::Digest::MD5.new("#{md5_digest1}#{md5_base}").digest
57
+ md5_digest3 = OpenSSL::Digest::MD5.new("#{md5_digest2}#{md5_base}").digest
58
+ @encryptor.padding = 1
59
+ @encryptor.key = "#{md5_digest1}#{md5_digest2}"
60
+ @encryptor.iv = md5_digest3
61
+ @encryptor.update(data) + @encryptor.final
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,87 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ module StoreAgent
18
+ # 引数として渡されたパスが不正な場合に発生する例外クラス
19
+ # workspace.directory("invalid/directory/path").create
20
+ # # => 既にディレクトリがある場合、例外が発生する
21
+ # workspace.file("invalid/file/path").delete
22
+ # # => 指定したパスにファイルが存在しない場合、例外が発生する
23
+ class InvalidPathError < StandardError
24
+ end
25
+
26
+ # ファイルのコピー/移動時に、コピー元とコピー先の種類が違う場合に発生する例外クラス
27
+ # workspace.directory("path/to/directory").copy("path/to/file")
28
+ # # => ディレクトリのコピー先にファイルが存在する場合、例外が発生する
29
+ class InvalidNodeTypeError < StandardError
30
+ attr_reader :src_object, :dest_object # :nodoc:
31
+
32
+ def initialize(src_object: nil, dest_object: nil)
33
+ @src_object = src_object
34
+ @dest_object = dest_object
35
+ end
36
+
37
+ def to_s # :nodoc:
38
+ if @src_object && @dest_object
39
+ "invalid node type: '#{@src_object.path}' is #{@src_object.filetype}, '#{@dest_object.path}' is #{@dest_object.filetype}"
40
+ else
41
+ "invalid node type"
42
+ end
43
+ end
44
+ end
45
+
46
+ # 権限が無いユーザーでファイルの読み書きなどを行った場合に発生する例外クラス
47
+ # guest_user.workspace("wc").file("file").create("file body")
48
+ # # => ファイルの書き込み権限が無い場合、例外が発生する
49
+ class PermissionDeniedError < StandardError
50
+ attr_reader :errors, :object, :permission # :nodoc:
51
+
52
+ # 操作対象が1つの場合には object と permission を渡す。<br>
53
+ # ディレクトリの削除時など、操作対象が複数ある場合には errors に例外の配列を渡す。
54
+ def initialize(errors: nil, object: nil, permission: "")
55
+ @errors = errors
56
+ @object = object
57
+ @permission = permission
58
+ end
59
+
60
+ def to_s # :nodoc:
61
+ if @errors
62
+ "permission denied: user=#{@errors.first.object.current_user.identifiers} " +
63
+ @errors.map do |e|
64
+ "workspace=#{e.object.workspace.namespace} permission=#{e.permission} object=#{e.object.path}"
65
+ end.join(", ")
66
+ else
67
+ "permission denied: user=#{object.current_user.identifiers} workspace=#{object.workspace.namespace} permission=#{permission} object=#{object.path}"
68
+ end
69
+ end
70
+ end
71
+
72
+ # バージョン管理時に、不正なリビジョンが指定された場合に発生する例外クラス
73
+ # workspace.file("path/to/file").read(revision: "invalid revision")
74
+ # # => 存在しないリビジョンを指定した場合、例外が発生する
75
+ class InvalidRevisionError < StandardError
76
+ attr_reader :path, :revision # :nodoc:
77
+
78
+ def initialize(path: "", revision: "")
79
+ @path = path
80
+ @revision = revision
81
+ end
82
+
83
+ def to_s # :nodoc:
84
+ "invalid revision: path=#{path} revision=#{revision}"
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,26 @@
1
+ #--
2
+ # Copyright 2015 realglobe, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #++
16
+
17
+ require "store_agent/node/prepend_module/path_validator"
18
+ require "store_agent/node/prepend_module/permission_checker"
19
+ require "store_agent/node/prepend_module/locker"
20
+ require "store_agent/node/object"
21
+ require "store_agent/node/object/virtual_object"
22
+ require "store_agent/node/object/directory_object"
23
+ require "store_agent/node/object/file_object"
24
+ require "store_agent/node/attachment"
25
+ require "store_agent/node/attachment/metadata"
26
+ require "store_agent/node/attachment/permission"