Skip to content

Commit 2929fdd

Browse files
committed
feat(加密): 添加文档加密功能及用户加密设置
为文档模型添加加密标志验证,为用户模型添加加密相关字段和验证 更新测试夹具以包含加密和非加密用户及文档 添加文档控制器测试用例验证加密功能
1 parent 249335e commit 2929fdd

6 files changed

Lines changed: 170 additions & 22 deletions

File tree

app/models/document.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Document < ActiveRecord::Base
88
has_many :tags, through: :documenttags
99

1010
validates_presence_of :title, :document_date, :user, :document_url
11+
validates :encrypted_flag, inclusion: { in: [true, false] }
1112

1213
def as_json(_options = {})
1314
super include: {

app/models/user.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ class User < ActiveRecord::Base
88
has_many :states
99

1010
validates_presence_of :name, :email, :password_digest
11+
validates :encryption_actived_flag, inclusion: { in: [true, false] }
12+
validates :secret_key, presence: true, if: :encryption_actived_flag?
13+
validates :secret_key, format: { with: /\A[0-9a-f]{64}\z/, message: "must be a 64-character hex string" }, allow_nil: true
1114
end

db/schema.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#
1111
# It's strongly recommended that you check this file into your version control system.
1212

13-
ActiveRecord::Schema.define(version: 2019_05_28_071907) do
13+
ActiveRecord::Schema.define(version: 2024_03_31_170416) do
1414

1515
create_table "documents", force: :cascade do |t|
1616
t.string "title"
@@ -25,6 +25,7 @@
2525
t.integer "person_id"
2626
t.datetime "created_at", null: false
2727
t.datetime "updated_at", null: false
28+
t.boolean "encrypted_flag", default: false
2829
t.index ["folder_id"], name: "index_documents_on_folder_id"
2930
t.index ["person_id"], name: "index_documents_on_person_id"
3031
t.index ["state_id"], name: "index_documents_on_state_id"
@@ -47,7 +48,7 @@
4748
t.datetime "created_at", null: false
4849
t.datetime "updated_at", null: false
4950
t.index ["folder_id"], name: "index_folders_on_folder_id"
50-
t.index ["name"], name: "index_folders_on_name", unique: true
51+
t.index ["name", "user_id", "folder_id"], name: "index_folders_on_name_and_user_id_and_folder_id", unique: true
5152
t.index ["user_id"], name: "index_folders_on_user_id"
5253
end
5354

@@ -63,7 +64,7 @@
6364
t.integer "user_id"
6465
t.datetime "created_at", null: false
6566
t.datetime "updated_at", null: false
66-
t.index ["name"], name: "index_states_on_name", unique: true
67+
t.index ["name", "user_id"], name: "index_states_on_name_and_user_id", unique: true
6768
end
6869

6970
create_table "tags", force: :cascade do |t|
@@ -80,6 +81,8 @@
8081
t.string "email"
8182
t.datetime "created_at", null: false
8283
t.datetime "updated_at", null: false
84+
t.string "secret_key", default: nil
85+
t.boolean "encryption_actived_flag", default: false
8386
t.index ["email"], name: "index_users_on_email", unique: true
8487
end
8588

Lines changed: 138 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,142 @@
11
require 'test_helper'
22

33
class DocumentsControllerTest < ActionController::TestCase
4-
# test "the truth" do
5-
# assert true
6-
# end
4+
setup do
5+
@non_encrypted_user = users(:one)
6+
@encrypted_user = users(:two)
7+
@document = documents(:one)
8+
9+
@test_pdf_content = "%PDF-1.4\n1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>\nendobj\nxref\n0 4\n0000000000 65535 f \n0000000009 00000 n \n0000000058 00000 n \n0000000115 00000 n \ntrailer\n<< /Size 4 /Root 1 0 R >>\nstartxref\n192\n%%EOF"
10+
end
11+
12+
test "should create document without encryption for non-encrypted user" do
13+
token = JsonWebToken.encode(user_id: @non_encrypted_user.id)
14+
@request.headers['Authorization'] = "Bearer #{token}"
15+
16+
temp_file = Tempfile.new(['test', '.pdf'])
17+
temp_file.write(@test_pdf_content)
18+
temp_file.rewind
19+
20+
uploaded_file = Rack::Test::UploadedFile.new(temp_file.path, 'application/pdf')
21+
22+
assert_difference('Document.count') do
23+
post :create, params: {
24+
document: uploaded_file,
25+
title: 'Test Document',
26+
description: 'Test description',
27+
document_date: Date.today.to_s
28+
}
29+
end
30+
31+
assert_response :created
32+
33+
new_document = Document.last
34+
assert_equal false, new_document.encrypted_flag
35+
assert_equal 'Test Document', new_document.title
36+
37+
assert File.exist?(Settings.document_folder + new_document.document_url)
38+
39+
File.delete(Settings.document_folder + new_document.document_url) if File.exist?(Settings.document_folder + new_document.document_url)
40+
temp_file.close
41+
temp_file.unlink
42+
end
43+
44+
test "should create document with encryption for encrypted user" do
45+
token = JsonWebToken.encode(user_id: @encrypted_user.id)
46+
@request.headers['Authorization'] = "Bearer #{token}"
47+
48+
temp_file = Tempfile.new(['test', '.pdf'])
49+
temp_file.write(@test_pdf_content)
50+
temp_file.rewind
51+
52+
uploaded_file = Rack::Test::UploadedFile.new(temp_file.path, 'application/pdf')
53+
54+
assert_difference('Document.count') do
55+
post :create, params: {
56+
document: uploaded_file,
57+
title: 'Encrypted Test Document',
58+
description: 'Encrypted test description',
59+
document_date: Date.today.to_s
60+
}
61+
end
62+
63+
assert_response :created
64+
65+
new_document = Document.last
66+
assert_equal true, new_document.encrypted_flag
67+
assert_equal 'Encrypted Test Document', new_document.title
68+
69+
assert File.exist?(Settings.document_folder + new_document.document_url)
70+
71+
stored_content = File.read(Settings.document_folder + new_document.document_url)
72+
assert_not_equal @test_pdf_content, stored_content
73+
74+
lockbox = Lockbox.new(key: @encrypted_user.secret_key)
75+
decrypted_content = lockbox.decrypt(stored_content)
76+
assert_equal @test_pdf_content, decrypted_content
77+
78+
File.delete(Settings.document_folder + new_document.document_url) if File.exist?(Settings.document_folder + new_document.document_url)
79+
temp_file.close
80+
temp_file.unlink
81+
end
82+
83+
test "should download non-encrypted document" do
84+
token = JsonWebToken.encode(user_id: @non_encrypted_user.id)
85+
@request.headers['Authorization'] = "Bearer #{token}"
86+
87+
document = Document.create!(
88+
title: 'Download Test',
89+
document_date: Date.today,
90+
document_url: 'download_test.pdf',
91+
user: @non_encrypted_user,
92+
encrypted_flag: false
93+
)
94+
95+
File.write(Settings.document_folder + 'download_test.pdf', @test_pdf_content, mode: 'w+b')
96+
97+
get :download, params: { id: document.id }
98+
99+
assert_response :success
100+
assert_equal 'application/pdf', @response.content_type
101+
assert_equal @test_pdf_content, @response.body
102+
103+
File.delete(Settings.document_folder + 'download_test.pdf') if File.exist?(Settings.document_folder + 'download_test.pdf')
104+
document.destroy
105+
end
106+
107+
test "should download and decrypt encrypted document" do
108+
token = JsonWebToken.encode(user_id: @encrypted_user.id)
109+
@request.headers['Authorization'] = "Bearer #{token}"
110+
111+
lockbox = Lockbox.new(key: @encrypted_user.secret_key)
112+
encrypted_content = lockbox.encrypt(@test_pdf_content)
113+
114+
document = Document.create!(
115+
title: 'Encrypted Download Test',
116+
document_date: Date.today,
117+
document_url: 'encrypted_download_test.pdf',
118+
user: @encrypted_user,
119+
encrypted_flag: true
120+
)
121+
122+
File.write(Settings.document_folder + 'encrypted_download_test.pdf', encrypted_content, mode: 'w+b')
123+
124+
get :download, params: { id: document.id }
125+
126+
assert_response :success
127+
assert_equal 'application/pdf', @response.content_type
128+
assert_equal @test_pdf_content, @response.body
129+
130+
File.delete(Settings.document_folder + 'encrypted_download_test.pdf') if File.exist?(Settings.document_folder + 'encrypted_download_test.pdf')
131+
document.destroy
132+
end
133+
134+
test "should return unauthorized without token" do
135+
post :create, params: {
136+
title: 'Test',
137+
document_date: Date.today.to_s
138+
}
139+
140+
assert_response :unauthorized
141+
end
7142
end

test/fixtures/documents.yml

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
11
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
22

33
one:
4-
title: MyString
5-
description: MyText
4+
title: Non Encrypted Document
5+
description: A non-encrypted test document
66
document_date: 2019-05-25
7-
document_url: MyString
8-
version: 9.99
7+
document_url: test1.pdf
8+
version: 1.0
99
folder_id:
10-
user_id:
10+
user_id: one
1111
state_id:
12+
encrypted_flag: false
1213

1314
two:
14-
title: MyString
15-
description: MyText
15+
title: Encrypted Document
16+
description: An encrypted test document
1617
document_date: 2019-05-25
17-
document_url: MyString
18-
version: 9.99
18+
document_url: test2.pdf
19+
version: 1.0
1920
folder_id:
20-
user_id:
21+
user_id: two
2122
state_id:
23+
encrypted_flag: true

test/fixtures/users.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
22

33
one:
4-
name: MyString
5-
password_digest: MyString
6-
email: MyString
4+
name: Non Encrypted User
5+
password_digest: <%= BCrypt::Password.create('password123') %>
6+
email: non_encrypted@example.com
7+
encryption_actived_flag: false
8+
secret_key: null
79

810
two:
9-
name: MyString
10-
password_digest: MyString
11-
email: MyString
11+
name: Encrypted User
12+
password_digest: <%= BCrypt::Password.create('password123') %>
13+
email: encrypted@example.com
14+
encryption_actived_flag: true
15+
secret_key: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef

0 commit comments

Comments
 (0)