318 |
319 | This program is free software: you can redistribute it and/or modify
320 | it under the terms of the GNU General Public License as published by
321 | the Free Software Foundation, either version 3 of the License, or
322 | (at your option) any later version.
323 |
324 | This program is distributed in the hope that it will be useful,
325 | but WITHOUT ANY WARRANTY; without even the implied warranty of
326 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
327 | GNU General Public License for more details.
328 |
329 | You should have received a copy of the GNU General Public License
330 | along with this program. If not, see .
331 |
--------------------------------------------------------------------------------
/app/controllers/pgpkeys_controller.rb:
--------------------------------------------------------------------------------
1 | class PgpkeysController < ApplicationController
2 |
3 | def index
4 | @server_pgpkey = Pgpkey.find_by user_id: 0
5 | @user_pgpkey = Pgpkey.find_by user_id: User.current.id
6 | end
7 |
8 | def create
9 | redirect_to :action => 'index'
10 |
11 | # params conversions
12 | @key = params['key']
13 | @user_id = params['user_id'].to_i
14 | @secret = params['secret']
15 |
16 | # sanity checks FIXME move these to some kind of form object
17 | flash[:error] = l(:flash_key_exists) and return if key_exists?
18 | flash[:error] = (@user_id == 0 ? l(:flash_private_key_not_valid) : l(:flash_public_key_not_valid)) and
19 | return if not key_valid?
20 | flash[:error] = l(:flash_update_not_allowed) and return if not update_allowed?
21 | flash[:warning] = l(:flash_no_secret) if @user_id == 0 and @secret == ''
22 |
23 | key = nil
24 | begin
25 | unless key = Pgpkey.import(user_id: @user_id, key: @key, secret: @secret)
26 |
27 | flash[:error] = l(:flash_import_error)
28 | return
29 | end
30 | rescue GPGME::Error::BadPassphrase
31 | flash.delete(:warning)
32 | flash[:error] = l(:flash_bad_passphrase)
33 | return
34 | end
35 |
36 | if key.persisted?
37 | @fpr = key.fpr
38 | flash[:notice] = l(:flash_create_successful)
39 | else
40 | flash[:error] = l(:flash_unknown_error)
41 | end
42 | end
43 |
44 | def delete
45 | redirect_to :action => 'index'
46 |
47 | # params conversions
48 | @user_id = params['user_id'].to_i
49 |
50 | # sanity checks
51 | flash[:error] = l(:flash_key_not_exists) and return if not key_exists?
52 | flash[:error] = l(:flash_update_not_allowed) and return if not update_allowed?
53 |
54 | # remove key from db
55 | key = Pgpkey.find_by user_id: @user_id
56 | fpr = key.fpr
57 | if key.delete
58 | flash[:notice] = l(:flash_delete_successful)
59 | else
60 | flash[:error] = l(:flash_unknown_error)
61 | end
62 |
63 | # remove key from gpg key ring, if no other reference exists
64 | if not Pgpkey.find_by fpr: fpr
65 | gpgme_key = GPGME::Key.get(fpr)
66 | gpgme_key.delete!(true)
67 | end
68 | end
69 |
70 | def generate
71 | redirect_to :action => 'index'
72 | @user_id = 0
73 |
74 | # sanity checks
75 | flash[:error] = l(:flash_key_exists) and return if key_exists?
76 | flash[:error] = l(:flash_update_not_allowed) and return if not update_allowed?
77 | flash[:warning] = l(:flash_no_secret) if @user_id == 0 and @secret == ''
78 |
79 | # prepare gpg parameter
80 | data = "\n"
81 | data += "Key-Type: " +params['key_type'] +"\n"
82 | data += "Key-Length: " +params['key_length'] +"\n"
83 | data += "Subkey-Type: " +params['subkey_type'] +"\n"
84 | data += "Subkey-Length: " +params['subkey_length'] +"\n"
85 | data += "Name-Real: " +params['name_real'] +"\n"
86 | data += "Name-Comment: " +params['name_comment'] +"\n" unless params['name_comment'].blank?
87 | data += "Name-Email: " +params['name_email'] +"\n"
88 | data += "Expire-Date: " +params['expire_date'] +"\n"
89 | data += "Passphrase: " +params['passphrase'] +"\n" unless params['passphrase'].blank?
90 | data += ""
91 |
92 | # create key and save into gpg key ring
93 | GPGME::Ctx.new.genkey(data)
94 |
95 | # save generated key into db
96 | key = GPGME::Key.find(nil, params['name_email']).first
97 | if Pgpkey.create(:user_id => 0, :fpr => key.fingerprint, :secret => params['passphrase'])
98 | flash[:notice] = l(:flash_generate_successful)
99 | else
100 | flash[:error] = l(:flash_unknown_error)
101 | end
102 | end
103 |
104 | def key_exists?
105 | Pgpkey.find_by user_id: @user_id
106 | end
107 |
108 | def key_valid?
109 | if @user_id == 0
110 | regex = /^\A\s*-----BEGIN PGP PRIVATE KEY BLOCK-----.*-----END PGP PRIVATE KEY BLOCK-----\s*?\z/m
111 | else
112 | regex = /^\A\s*-----BEGIN PGP PUBLIC KEY BLOCK-----.*-----END PGP PUBLIC KEY BLOCK-----\s*?\z/m
113 | end
114 | @key.match(regex)
115 | end
116 |
117 | def update_allowed?
118 | return true if User.current.admin
119 | return true if User.current.id == @user_id
120 | end
121 | end
122 |
--------------------------------------------------------------------------------
/app/helpers/pgpkeys_helper.rb:
--------------------------------------------------------------------------------
1 | module PgpkeysHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/models/pgpkey.rb:
--------------------------------------------------------------------------------
1 | class Pgpkey < ActiveRecord::Base
2 | unloadable
3 |
4 | def public_key
5 | GPGME::Key.get(self.fpr).export(:armor => true).to_s
6 | end
7 |
8 | def metadata
9 | GPGME::Key.get(self.fpr).to_s
10 | end
11 |
12 | def subkeys
13 | GPGME::Key.get(self.fpr).subkeys
14 | end
15 |
16 | def can_encrypt
17 | key = GPGME::Key.find(:public, self.fpr, :encrypt)
18 | return key
19 | end
20 |
21 | def self.import(user_id: , key: , secret: nil)
22 | gpgme_import = GPGME::Key.import(key)
23 | if import = gpgme_import.imports[0] and
24 | fpr = import.fpr and
25 | key = GPGME::Key.get(fpr) and
26 | fpr == key.fingerprint
27 |
28 | if user_id == 0
29 | begin
30 | gpgme = GPGME::Crypto.new
31 | enc = gpgme.encrypt('test', recipients: fpr, always_trust: true).to_s
32 | dec = gpgme.decrypt(enc, password: secret).to_s
33 | unless "test" == dec
34 | fail "bad encryption result (should be >test<): >#{dec}<"
35 | end
36 | rescue
37 | key.delete!(true)
38 | raise $!
39 | end
40 | end
41 |
42 | create user_id: user_id, fpr: fpr, secret: secret
43 | end
44 | end
45 | end
46 |
--------------------------------------------------------------------------------
/app/views/mailer/attachments_added.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_attachments_added),
3 | @added_to_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
--------------------------------------------------------------------------------
/app/views/mailer/attachments_added.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_attachments_added) %>
2 | <%= @added_to_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/document_added.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_document_added),
3 | @document_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/document_added.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_document_added) %>
2 | <%= @document_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/issue_add.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_issue_add),
3 | @issue_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/issue_add.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_issue_add) %>
2 | <%= @issue_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/issue_edit.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_issue_edit),
3 | @issue_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/issue_edit.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_issue_edit) %>
2 | <%= @issue_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/lost_password.filtered.html.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_lost_password) %>
2 |
3 |
4 | <%= l(:filtered_mail_footer) %>
5 | <%= link_to l(:header_openpgp),
6 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
7 |
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
10 |
11 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
12 |
13 | <% end %>
14 |
--------------------------------------------------------------------------------
/app/views/mailer/lost_password.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_lost_password) %>
2 |
3 | ----------------------------------------
4 |
5 | <%= l(:filtered_mail_footer) %>
6 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
7 |
8 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
9 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
10 | <% end -%>
11 |
--------------------------------------------------------------------------------
/app/views/mailer/message_posted.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_message_posted),
3 | @message_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/message_posted.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_message_posted) %>
2 | <%= @message_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/news_added.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_news_added),
3 | @news_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/news_added.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_news_added) %>
2 | <%= @news_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/news_comment_added.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_news_comment_added),
3 | @news_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/news_comment_added.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_news_comment_added) %>
2 | <%= @news_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/security_notification.filtered.html.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_security_notification) %>
2 |
3 |
4 | <% if @url %>
5 | <%= link_to @url, @url %>
6 | <% else %>
7 | <%= @title %>
8 | <% end %>
9 |
10 |
11 |
12 | <%= l(:filtered_mail_footer) %>
13 | <%= link_to l(:header_openpgp),
14 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
15 |
16 |
17 | <% if Setting.plugin_openpgp['filtered_mail_footer'].present? %>
18 |
19 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
20 |
21 | <% end %>
22 |
--------------------------------------------------------------------------------
/app/views/mailer/security_notification.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_security_notification) %>
2 |
3 | <%= @url || @title %>
4 |
5 | ----------------------------------------
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
9 |
10 | <% if Setting.plugin_openpgp['filtered_mail_footer'].present? -%>
11 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
12 | <% end -%>
13 |
14 |
--------------------------------------------------------------------------------
/app/views/mailer/settings_updated.filtered.html.erb:
--------------------------------------------------------------------------------
1 | <%= link_to l(:filtered_mail_security_notification), @url %>
2 |
3 |
4 | <%= l(:filtered_mail_footer) %>
5 | <%= link_to l(:header_openpgp),
6 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
7 |
8 |
9 | <% if Setting.plugin_openpgp['filtered_mail_footer'].present? %>
10 |
11 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
12 |
13 | <% end %>
14 |
15 |
--------------------------------------------------------------------------------
/app/views/mailer/settings_updated.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_security_notification) %>
2 |
3 | <%= @url %>
4 |
5 | ----------------------------------------
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
9 |
10 | <% if Setting.plugin_openpgp['filtered_mail_footer'].present? -%>
11 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
12 | <% end -%>
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/views/mailer/wiki_content_added.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_wiki_content_added),
3 | @wiki_content_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/wiki_content_added.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_wiki_content_added) %>
2 | <%= @wiki_content_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/mailer/wiki_content_updated.filtered.html.erb:
--------------------------------------------------------------------------------
1 |
2 | <%= link_to l(:filtered_mail_wiki_content_updated),
3 | @wiki_content_url %>
4 |
5 |
6 |
7 | <%= l(:filtered_mail_footer) %>
8 | <%= link_to l(:header_openpgp),
9 | Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
10 |
11 |
12 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? %>
13 |
14 | <%= Setting.plugin_openpgp['filtered_mail_footer'].html_safe %>
15 |
16 | <% end %>
17 |
--------------------------------------------------------------------------------
/app/views/mailer/wiki_content_updated.filtered.text.erb:
--------------------------------------------------------------------------------
1 | <%= l(:filtered_mail_wiki_content_updated) %>
2 | <%= @wiki_content_url %>
3 |
4 | ----------------------------------------
5 |
6 | <%= l(:filtered_mail_footer) %>
7 | <%= Setting['protocol'] + '://' + Setting['host_name'] + '/pgp' %>
8 |
9 | <% if not Setting.plugin_openpgp['filtered_mail_footer'].blank? -%>
10 | <%= Setting.plugin_openpgp['filtered_mail_footer'] %>
11 | <% end -%>
--------------------------------------------------------------------------------
/app/views/pgpkeys/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= l(:header_openpgp) %>
2 |
3 | <% if not User.current.logged? %>
4 | <%= l(:note_login) %>
5 |
6 | <% else %>
7 |
8 |
9 |
<%= User.current.name %> (<%= User.current.mail %>)
10 | <%= field_set_tag '', class: 'box tabular' do %>
11 | <% if @user_pgpkey %>
12 |
13 | <% # user key given %>
14 | <%= form_tag method: "post", action: "delete" do %>
15 |
16 | <%= label_tag 'fpr', l(:label_fingerprint) %>
17 | <%= text_field_tag 'fpr', @user_pgpkey.fpr, size: "40", readonly: true %>
18 |
19 |
20 | <%= label_tag 'metadata', l(:label_metadata) %>
21 | <%= text_area_tag 'meta', @user_pgpkey.metadata, rows: "4", readonly: true %>
22 |
23 |
24 | <%= label_tag 'key', l(:label_public_key) %>
25 | <%= text_area_tag 'key', @user_pgpkey.public_key, rows: "30", readonly: true %>
26 |
27 |
28 | <%= l(:note_user_public_key_given) %>
29 | (<%= l(:note_sent_to) %> <%= User.current.mail %>)
30 |
31 | <%= hidden_field_tag 'user_id', User.current.id %>
32 |
<%= submit_tag l(:button_delete) %>
33 | <% end %>
34 |
35 | <% else %>
36 |
37 | <% # no user key given %>
38 | <%= form_tag method: "post", action: "create" do %>
39 |
40 | <%= label_tag 'key', l(:label_public_key) %>
41 | <%= text_area_tag 'key', nil, :size => "50x30" %>
42 |
43 |
44 | <%= l(:note_user_public_key_missing) %>
45 | (<%= l(:note_sent_to) %> <%= User.current.mail %>)
46 |
47 | <%= hidden_field_tag 'user_id', User.current.id %>
48 |
<%= submit_tag l(:button_create) %>
49 | <% end %>
50 |
51 | <% end %>
52 | <% end %>
53 |
54 |
55 |
56 |
Redmine Server (<%= Setting['mail_from'] %>)
57 | <%= field_set_tag '', class: 'box tabular' do %>
58 | <% if @server_pgpkey %>
59 |
60 | <% # server key given %>
61 | <%= form_tag method: "post", action: "delete" do %>
62 |
63 | <%= label_tag 'fpr', l(:label_fingerprint) %>
64 | <%= text_field_tag 'fpr', @server_pgpkey.fpr, size: "40", readonly: true %>
65 |
66 |
67 | <%= label_tag 'metadata', l(:label_metadata) %>
68 | <%= text_area_tag 'meta', @server_pgpkey.metadata, rows: "4", readonly: true %>
69 |
70 |
71 | <%= label_tag 'key', l(:label_public_key) %>
72 | <%= text_area_tag 'key', @server_pgpkey.public_key, rows: "30", readonly: true %>
73 |
74 |
75 | <%= l(:note_server_public_key_given) %>
76 | (<%= l(:note_sent_to) %> <%= Setting['mail_from'] %>)
77 |
78 | <% if User.current.admin? && !Setting.plugin_openpgp['disable_updating_server_key_in_ui'] %>
79 | <%= hidden_field_tag 'user_id', 0 %>
80 |
<%= submit_tag l(:button_delete) %>
81 | <% end %>
82 | <% end %>
83 |
84 | <% else %>
85 |
86 | <% # no server key given %>
87 | <% if not User.current.admin? %>
88 |
<%= l(:note_server_public_key_missing) %>
89 | <% else %>
90 |
<%= l(:note_admin_only) %>
91 |
92 | <% # save existing key %>
93 |
<%= l(:subheader_save_key) %>
94 |
95 | <%= l(:note_cleartext_create) %>
96 | redmine:update_redmine_pgpkey.
97 |
98 | <%= form_tag method: "post", action: "create" do %>
99 |
100 | <%= label_tag 'key', l(:label_private_key) %>
101 | <%= text_area_tag 'key', nil, :size => "50x30" %>
102 |
103 |
104 | <%= label_tag 'secret', l(:label_secret) %>
105 | <%= password_field_tag 'secret', nil, size: "40" %>
106 |
107 |
108 | <%= l(:note_server_private_key_create) %>
109 | (<%= l(:note_receiving_emails) %> Receiving emails)
110 |
111 | <%= hidden_field_tag 'user_id', 0 %>
112 |
<%= submit_tag l(:button_create) %>
113 | <% end %>
114 |
115 |
116 | <% # save existing key %>
117 |
<%= l(:subheader_generate_key) %>
118 |
119 | <%= l(:note_cleartext_generate) %>
120 | redmine:generate_redmine_pgpkey.
121 |
122 | <%= form_tag method: "post", action: "generate" do %>
123 |
124 | <%= label_tag 'key_type', l(:label_key_type) %>
125 | <%= text_field_tag 'key_type', 'RSA', size: "40", readonly: true %>
126 |
127 |
128 | <%= label_tag 'key_length', l(:label_key_length) %>
129 | <%= text_field_tag 'key_length', '2048', size: "40", readonly: true %>
130 |
131 |
132 | <%= label_tag 'subkey_type', l(:label_subkey_type) %>
133 | <%= text_field_tag 'subkey_type', 'RSA', size: "40", readonly: true %>
134 |
135 |
136 | <%= label_tag 'subkey_length', l(:label_subkey_length) %>
137 | <%= text_field_tag 'subkey_length', '2048', size: "40", readonly: true %>
138 |
139 |
140 | <%= label_tag 'name_real', l(:label_name_real) %>
141 | <%= text_field_tag 'name_real', 'Redmine Server', size: "40", readonly: true %>
142 |
143 |
144 | <%= label_tag 'name_comment', l(:label_name_comment) %>
145 | <%= text_field_tag 'name_comment', '', size: "40", readonly: true %>
146 |
147 |
148 | <%= label_tag 'name_email', l(:label_name_email) %>
149 | <%= text_field_tag 'name_email', Setting['mail_from'], size: "40", readonly: true %>
150 |
151 |
152 | <%= label_tag 'expire_date', l(:label_expire_date) %>
153 | <%= text_field_tag 'expire_date', '0', size: "40", readonly: true %>
154 |
155 |
156 | <%= label_tag 'passphrase', l(:label_secret) %>
157 | <%= password_field_tag 'passphrase', nil, size: "40" %>
158 |
159 |
160 | <%= l(:note_server_private_key_generate) %>
161 | (<%= l(:note_receiving_emails) %> Receiving emails)
162 |
163 |
<%= submit_tag l(:button_generate) %>
164 | <% end %>
165 |
166 | <% end %>
167 |
168 | <% end %>
169 | <% end %>
170 |
171 |
172 | <% end %>
173 |
--------------------------------------------------------------------------------
/app/views/settings/_openpgp.erb:
--------------------------------------------------------------------------------
1 | <%= l(:subheader_openpgp_incoming) %>
2 |
3 | <%= label_tag 'settings[signature_needed]', l(:label_signature_needed) %>
4 | <%= check_box_tag 'settings[signature_needed]', nil,
5 | @settings['signature_needed'] %>
6 |
7 |
8 | <%= l(:subheader_openpgp_outgoing) %>
9 |
10 | <%= label_tag 'settings[activation]', l(:label_openpgp_activation) %>
11 | <%= select_tag 'settings[activation]',
12 | options_for_select([
13 | [l(:option_activation_all), "all"],
14 | [l(:option_activation_project), "project"],
15 | [l(:option_activation_none), "none"]
16 | ],
17 | @settings['activation']
18 | ) %>
19 |
20 |
21 | <%= label_tag 'settings[unencrypted_mails]', l(:label_unencrypted_mails) %>
22 | <%= select_tag 'settings[unencrypted_mails]',
23 | options_for_select([
24 | [l(:option_unencrypted_mails_blocked), "blocked"],
25 | [l(:option_unencrypted_mails_filtered), "filtered"],
26 | [l(:option_unencrypted_mails_unchanged), "unchanged"]
27 | ],
28 | @settings['unencrypted_mails']
29 | ) %>
30 |
31 |
32 | <%= label_tag 'settings[encrypted_html]', l(:label_encrypted_html) %>
33 | <%= check_box_tag 'settings[encrypted_html]', nil,
34 | @settings['encrypted_html'] %>
35 |
36 |
37 | <%= label_tag 'settings[filtered_mail_footer]', l(:label_filtered_mail_footer) %>
38 | <%= text_area_tag 'settings[filtered_mail_footer]',
39 | @settings['filtered_mail_footer'],
40 | {:size => "160x10"} %>
41 |
42 | <%= l(:subheader_openpgp_general) %>
43 |
44 | <%= label_tag 'settings[disable_updating_server_key_in_ui]', l(:label_disable_updating_server_key_in_ui) %>
45 | <%= check_box_tag 'settings[disable_updating_server_key_in_ui]', nil,
46 | @settings['disable_updating_server_key_in_ui'] %>
47 |
48 |
--------------------------------------------------------------------------------
/config/locales/de.yml:
--------------------------------------------------------------------------------
1 | de:
2 | header_openpgp: PGP Schlüsselverwaltung
3 | subheader_openpgp_incoming: Eingehende Mails
4 | subheader_openpgp_outgoing: Ausgehende Mails
5 | subheader_save_key: Existierenden Schlüssel hinzufügen
6 | subheader_generate_key: Neuen Schlüssel generieren
7 | label_openpgp_activation: Plugin Aktivierung
8 | label_unencrypted_mails: Unverschlüsselte Mails
9 | label_signature_needed: Nur valide Signaturen
10 | label_encrypted_html: HTML in verschlüsselten Mails
11 | label_filtered_mail_footer: Angehängte Nachricht in gefilterten Mails
12 | label_public_key: Öffentlicher PGP Schlüssel
13 | label_private_key: Privater PGP Schlüssel
14 | label_fingerprint: Fingerabdruck
15 | label_metadata: Metadaten
16 | label_key_type: Schlüsseltyp
17 | label_key_length: Schlüssellänge
18 | label_subkey_type: Unterschlüsseltyp
19 | label_subkey_length: Unterschlüssellänge
20 | label_name_real: Name
21 | label_name_comment: Kommentar
22 | label_name_email: Email
23 | label_expire_date: Ablaufdatum
24 | label_secret: Passphrase
25 | option_activation_all: für alle Projekte
26 | option_activation_project: abhängig von Projekteinstellungen
27 | option_activation_none: deaktiviert
28 | option_unencrypted_mails_blocked: blockiert
29 | option_unencrypted_mails_filtered: gefiltert
30 | option_unencrypted_mails_unchanged: unverändert
31 | button_create: Speichern
32 | button_delete: Löschen
33 | button_generate: Generieren
34 | note_login: Bitte einloggen, um die PGP Schlüssel zu verwalten.
35 | note_admin_only: Nur Administratoren können den privaten Schlüssel und die Passphrase für den Redmine Server verändern
36 | note_user_public_key_missing: Füge Deinen öffentlichen PGP Schlüssel hier hinzu, um verschlüsselte Mails zu erhalten
37 | note_user_public_key_given: Mit diesem PGP Schlüssel werden Mails von Redmine verschlüsselt
38 | note_server_public_key_missing: Kein Schlüssel hinterlegt. Benachrichtige bitte die Administratoren, falls Du Mails an Redmine verschlüsseln möchtest.
39 | note_server_public_key_given: Benutze diesen öffentlichen PGP Schlüssel, um Mails an Redmine zu verschlüsseln
40 | note_server_private_key_create: Füge einen privaten PGP Schlüssel für den Redmine Server hinzu, um verschlüsselte Mails an Redmine zu ermöglichen
41 | note_server_private_key_generate: Generiere einen privaten PGP Schlüssel für den Redmine Server, um verschlüsselte Mails an Redmine zu ermöglichen
42 | note_receiving_emails: Die Annahme von Emails muss in Redmine konfiguriert sein, siehe
43 | note_cleartext_create: "Der private Schlüssel und die Passphrase werden im Klartext übertragen! Verwende zumindest https oder nutze besser den serverseitigen rake taks: "
44 | note_cleartext_generate: "Die Passphrase wird im Klartext übertragen! Verwende zumindest https oder nutze besser den serverseitigen rake taks: "
45 | note_sent_to: gesendet an
46 | flash_public_key_not_valid: PGP Schlüssel ist ungültig (er sollte mit '-----BEGIN PGP PUBLIC KEY BLOCK-----' starten und mit '-----END PGP PUBLIC KEY BLOCK-----' enden)
47 | flash_private_key_not_valid: PGP Schlüssel ist ungültig (er sollte mit '-----BEGIN PGP PRIVATE KEY BLOCK-----' starten und mit '-----END PGP PRIVATE KEY BLOCK-----' enden)
48 | flash_bad_passphrase: Passphrase war falsch (bitte nochmal versuchen)
49 | flash_key_exists: Schlüssel existiert schon (bitte zuerst löschen)
50 | flash_key_not_exists: Schlüssel existiert nicht
51 | flash_update_not_allowed: Operation nicht erlaubt (bitte nicht weiter versuchen)
52 | flash_import_error: Fehler beim Importieren des Schlüssels (bitte nochmal versuchen oder die Administratoren benachrichtigen)
53 | flash_unknown_error: Unbekannter Fehler (bitte nochmal versuchen oder die Administratoren benachrichtigen)
54 | flash_no_secret: Keine Passphrase angegeben (kein Problem, aber bist Du Dir sicher?)
55 | flash_create_successful: PGP Schlüssel erfolgreich gepeichert
56 | flash_delete_successful: PGP Schlüssel erfolgreich gelöscht
57 | flash_generate_successful: PGP Schlüssel erfolgreich generiert
58 | filtered_mail_footer: "Diese Mail wurde aus Gründen der Sicherheit gefiltert. Folge diesem Link und füge Deinen öffentlichen PGP Schlüssel hinzu, um ungefilterte, verschlüsselte Mails zu erhalten:"
59 | filtered_mail_attachments_added: Anhänge hinzugefügt
60 | filtered_mail_document_added: Dokument hinzugefügt
61 | filtered_mail_issue_add: Ticket hinzugefügt
62 | filtered_mail_issue_edit: Ticket bearbeitet
63 | filtered_mail_message_posted: Nachricht hinzugefügt
64 | filtered_mail_news_added: Neuigkeit hinzugefügt
65 | filtered_mail_news_comment_added: Kommentar hinzugefügt
66 | filtered_mail_wiki_content_added: Seite hinzugefügt
67 | filtered_mail_wiki_content_updated: Seite editiert
68 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | en:
2 | header_openpgp: PGP Key Management
3 | subheader_openpgp_incoming: Incoming
4 | subheader_openpgp_outgoing: Outgoing
5 | subheader_save_key: Save existing key
6 | subheader_generate_key: Generate new key
7 | subheader_openpgp_general: General settings
8 | label_openpgp_activation: Plugin activation
9 | label_unencrypted_mails: Unencrypted mails
10 | label_signature_needed: Valid signatures only
11 | label_encrypted_html: HTML in encrypted mails
12 | label_filtered_mail_footer: Custom footer of filtered mails
13 | label_disable_updating_server_key_in_ui: Disable ability to update server key in UI (use Rake only)
14 | label_public_key: Public PGP key
15 | label_private_key: Private PGP key
16 | label_fingerprint: Fingerprint
17 | label_metadata: Metadata
18 | label_key_type: Key type
19 | label_key_length: Key length
20 | label_subkey_type: Subkey type
21 | label_subkey_length: Subkey length
22 | label_name_real: Name
23 | label_name_comment: Comment
24 | label_name_email: Email
25 | label_expire_date: Expire date
26 | label_secret: Passphrase
27 | option_activation_all: for all projects
28 | option_activation_project: dependent on project settings
29 | option_activation_none: deactivated
30 | option_unencrypted_mails_blocked: blocked
31 | option_unencrypted_mails_filtered: filtered
32 | option_unencrypted_mails_unchanged: unchanged
33 | button_create: Save
34 | button_delete: Remove
35 | button_generate: Generate
36 | note_login: Please log in to manage PGP keys.
37 | note_admin_only: Only administrators will be able to change the private PGP key and passphrase for the Redmine server
38 | note_user_public_key_missing: Add your public PGP key here to receive encrypted mails by Redmine
39 | note_user_public_key_given: This PGP key is used to encrypt mails by Redmine
40 | note_server_public_key_missing: No key given. Please contact your administrator if you want to send encrypted mails to Redmine.
41 | note_server_public_key_given: Use this public PGP key to encrypt mails for Redmine
42 | note_server_private_key_create: Add a private PGP key for Redmine to enable encryption of mails sent to Redmine
43 | note_server_private_key_generate: Generate a private PGP key for Redmine to enable encryption of mails sent to Redmine
44 | note_receiving_emails: receiving emails must be configured within Redmine, see
45 | note_cleartext_create: "The private key and passphrase will be sent in cleartext! Ensure using HTTPS, or better, use the server-side rake task: "
46 | note_cleartext_generate: "The passphrase will be sent in cleartext! Ensure using HTTPS, or better, use the server-side rake task: "
47 | note_sent_to: sent to
48 | flash_public_key_not_valid: PGP key not valid (it should start with '-----BEGIN PGP PUBLIC KEY BLOCK-----' and end with '-----END PGP PUBLIC KEY BLOCK-----')
49 | flash_private_key_not_valid: PGP key not valid (it should start with '-----BEGIN PGP PRIVATE KEY BLOCK-----' and end with '-----END PGP PRIVATE KEY BLOCK-----')
50 | flash_bad_passphrase: Passphrase was wrong (please try again)
51 | flash_key_exists: Key does exist already (please delete it first)
52 | flash_key_not_exists: Key does not exist
53 | flash_update_not_allowed: Operation not allowed (please don't try)
54 | flash_import_error: Error importing the key (please try again or contact your administrator)
55 | flash_unknown_error: Unknown error (please try again or contact your administrator)
56 | flash_no_secret: Passphrase is empty (no problem, but are you sure?)
57 | flash_create_successful: PGP key successfully saved
58 | flash_delete_successful: PGP key successfully deleted
59 | flash_generate_successful: PGP key successfully generated
60 | filtered_mail_footer: "This mail was filtered for enhanced security. Follow this link and add your public PGP key to receive unfiltered encrypted mails:"
61 | filtered_mail_attachments_added: Attachments added
62 | filtered_mail_document_added: Document added
63 | filtered_mail_issue_add: Issue added
64 | filtered_mail_issue_edit: Issue edited
65 | filtered_mail_lost_password: Password requested. Add a PGP key (or contact the administrator) to receive a reset link.
66 | filtered_mail_message_posted: Message added
67 | filtered_mail_news_added: News added
68 | filtered_mail_news_comment_added: Comment added
69 | filtered_mail_security_notification: Security notification
70 | filtered_mail_wiki_content_added: Page added
71 | filtered_mail_wiki_content_updated: Page edited
72 |
--------------------------------------------------------------------------------
/config/locales/ja.yml:
--------------------------------------------------------------------------------
1 | ja:
2 | header_openpgp: PGP 鍵管理
3 | subheader_openpgp_incoming: 受信
4 | subheader_openpgp_outgoing: 送信
5 | subheader_save_key: 作成済みの秘密鍵を保存する
6 | subheader_generate_key: 新たな秘密鍵を生成する
7 | label_openpgp_activation: プラグインの有効化対象
8 | label_unencrypted_mails: 暗号化されないメール
9 | label_signature_needed: 有効な署名が必要
10 | label_encrypted_html: HTMLで暗号化メールを送る
11 | label_filtered_mail_footer: フィルタされたメールフッタのカスタマイズ
12 | label_public_key: PGP公開鍵
13 | label_private_key: PGP秘密鍵
14 | label_fingerprint: ハッシュ値
15 | label_metadata: メタデータ
16 | label_key_type: 主鍵タイプ
17 | label_key_length: 主鍵長
18 | label_subkey_type: 副鍵タイプ
19 | label_subkey_length: 副鍵長
20 | label_name_real: 名前
21 | label_name_comment: コメント
22 | label_name_email: メールアドレス
23 | label_expire_date: 有効期限
24 | label_secret: パスフレーズ
25 | option_activation_all: 全プロジェクト
26 | option_activation_project: プロジェクト毎に設定
27 | option_activation_none: なし
28 | option_unencrypted_mails_blocked: 送信しない
29 | option_unencrypted_mails_filtered: 内容をフィルタして送信する
30 | option_unencrypted_mails_unchanged: 内容を変更せずに送信する
31 | button_create: 保存
32 | button_delete: 削除
33 | button_generate: 生成
34 | note_login: PGP鍵管理をするにはログインしてください
35 | note_admin_only: Redmine ServerのPGP秘密鍵およびパスフレーズを変更できるのは管理者のみです
36 | note_user_public_key_missing: Redmineからのメールを暗号化して受信するには、あなたのPGP公開鍵を登録してください
37 | note_user_public_key_given: Redmineが送信するメールを暗号化する際、このPGP公開鍵を使用します
38 | note_server_public_key_missing: RedmineのPGP公開鍵がありません。Redmineに送信するメールを暗号化する場合は管理者にご相談ください
39 | note_server_public_key_given: Redmineに送信するメールを暗号化する際、このPGP公開鍵を使用してください
40 | note_server_private_key_create: Redmineに送信するメールを暗号化するためのRedmineのPGP秘密鍵を保存します
41 | note_server_private_key_generate: Redmineに送信するメールを暗号化するためのRedmineのPGP秘密鍵を生成します
42 | note_receiving_emails: Redmineでメールを受信するように設定されている必要があります。参照:
43 | note_cleartext_create: "HTTPでは秘密鍵とパスフレーズは平文で送信されます。HTTPSであることを確認してください。(サーバ側でRakeタスクの使用を推奨): "
44 | note_cleartext_generate: "HTTPではパスフレーズは平文で送信されます。HTTPSであることを確認してください。(サーバ側でRakeタスクの使用を推奨): "
45 | note_sent_to: 送信先アドレス:
46 | flash_public_key_not_valid: PGP公開鍵が無効です ('-----BEGIN PGP PUBLIC KEY BLOCK-----' で始まり'-----END PGP PUBLIC KEY BLOCK-----'で終わる必要があります)
47 | flash_private_key_not_valid: PGP秘密鍵が無効です ('-----BEGIN PGP PUBLIC KEY BLOCK-----' で始まり'-----END PGP PUBLIC KEY BLOCK-----'で終わる必要があります)
48 | flash_bad_passphrase: パスフレーズが誤っています (再実行してください)
49 | flash_key_exists: 鍵が作成済みです (再実行の前に削除してください)
50 | flash_key_not_exists: 鍵がありません
51 | flash_update_not_allowed: 操作が許可されません (実行しないでください)
52 | flash_import_error: 鍵インポートエラー (再実行するか管理者に連絡してください)
53 | flash_unknown_error: 不明なエラー (再実行するか管理者に連絡してください)
54 | flash_no_secret: パスフレーズが入力されていません (機能上は問題ありませんが、本当によろしいですか?)
55 | flash_create_successful: PGP鍵が正常に作成されました
56 | flash_delete_successful: PGP鍵が正常に削除されました
57 | flash_generate_successful: PGP鍵が正常に生成されました
58 | filtered_mail_footer: "このメールの本文はセキュリティ上の理由によりフィルタリングされました。以下のリンクからPGP公開鍵を登録すれば本文を暗号化して受信することができます:"
59 | filtered_mail_attachments_added: チケットにファイルが添付されました
60 | filtered_mail_document_added: 文書が追加されました
61 | filtered_mail_issue_add: チケットが追加されました
62 | filtered_mail_issue_edit: チケットが編集されました
63 | filtered_mail_lost_password: パスワード再設定が実行されました。パスワード再設定のリンクを受信するにはPGP公開鍵を追加するか、管理者にご相談ください。
64 | filtered_mail_message_posted: メッセージが投稿されました
65 | filtered_mail_news_added: ニュースが投稿されました
66 | filtered_mail_news_comment_added: 注記が追加されました
67 | filtered_mail_security_notification: セキュリティ通知
68 | filtered_mail_wiki_content_added: Wikiページが追加されました
69 | filtered_mail_wiki_content_updated: Wikiページが編集されました
70 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | # Plugin's routes
2 | # See: http://guides.rubyonrails.org/routing.html
3 |
4 | get 'pgp', :to => 'pgpkeys#index'
5 | post 'pgp/create', :to => 'pgpkeys#create'
6 | post 'pgp/delete', :to => 'pgpkeys#delete'
7 | post 'pgp/generate', :to => 'pgpkeys#generate'
--------------------------------------------------------------------------------
/db/migrate/001_create_pgpkeys.rb:
--------------------------------------------------------------------------------
1 | class CreatePgpkeys < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :pgpkeys do |t|
4 | t.integer :user_id
5 | t.string :fpr
6 | t.string :secret
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/init.rb:
--------------------------------------------------------------------------------
1 | #!/bin/env ruby
2 | # encoding: utf-8
3 |
4 | Redmine::Plugin.register :openpgp do
5 | name 'OpenPGP'
6 | author 'Alexander Blum'
7 | description 'Email encryption with the OpenPGP standard'
8 | version '1.0.1'
9 | author_url 'mailto:a.blum@free-reality.net'
10 | url 'https://github.com/C3S/redmine_openpgp'
11 | settings(:default => {
12 | 'signature_needed' => false,
13 | 'activation' => 'project',
14 | 'unencrypted_mails' => 'filtered',
15 | 'encrypted_html' => false,
16 | 'filtered_mail_footer' => ''
17 | }, :partial => 'settings/openpgp')
18 | project_module :openpgp do
19 | permission :block_email, { :openpgp => :show }
20 | end
21 | menu :account_menu, :pgpkeys, { :controller => 'pgpkeys', :action => 'index' },
22 | :caption => 'PGP', :after => :my_account,
23 | :if => Proc.new { User.current.logged? }
24 | end
25 |
26 | require File.dirname(__FILE__) + '/lib/redmine_openpgp'
27 |
28 | Rails.application.config.after_initialize do
29 | # encrypt outgoing mails
30 | Mailer.send(:prepend, RedmineOpenpgp::EncryptMails)
31 |
32 | # decrypt received mails, handle them based on signature requirements
33 | MailHandler.send(:prepend, RedmineOpenpgp::DecryptMails)
34 | end
35 |
--------------------------------------------------------------------------------
/lib/redmine_openpgp.rb:
--------------------------------------------------------------------------------
1 | module RedmineOpenpgp
2 | # true if the plugin is active on the given project
3 | #
4 | # if global is true, this will be true for setting=='project' if project is nil
5 | def self.active_on_project?(project, global: false)
6 | case Setting.plugin_openpgp["activation"]
7 | when 'all'
8 | true
9 | when 'project'
10 | project.nil? ? global : project.module_enabled?('openpgp')
11 | else
12 | false
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/lib/redmine_openpgp/decrypt_mails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineOpenpgp
4 | module DecryptMails
5 |
6 | def dispatch_to_default
7 | project = target_project
8 | if @ignore_email and RedmineOpenpgp.active_on_project?(project)
9 | logger.info "MailHandler: invalid email rejected to project #{project}" if logger
10 | else
11 | super
12 | end
13 | end
14 |
15 | def receive(email, options={})
16 | # Extract useful metadata for logging
17 | sender_email = email.from.to_a.first.to_s.strip
18 | # Sometimes this isn't available after decryption. This seems like a bug,
19 | # so extract it here so we're guaranteed to have it
20 | message_id = email.message_id
21 |
22 | # We need to store this before decryption, because after decryption
23 | # email.encrypted? == false
24 | encrypted = email.encrypted?
25 |
26 | valid_signature = false
27 | signatures = []
28 |
29 | # encrypt and check validity of signature
30 | if encrypted
31 | email = email.decrypt(
32 | :password => Pgpkey.find_by(:user_id => 0),
33 | :verify => true
34 | )
35 | if valid_signature = email.signature_valid?
36 | signatures = email.signatures
37 | end
38 | elsif email.signed?
39 | verified = email.verify
40 | if valid_signature = verified.signature_valid?
41 | signatures = verified.signatures
42 | end
43 | end
44 |
45 | # compare identity of signature with sender
46 | if valid_signature and
47 | signatures.any? and
48 | sender_email.present? and
49 | user = User.having_mail(sender_email).first and
50 | key = Pgpkey.find_by(user_id: user.id)
51 |
52 | valid_signature = signatures.any? do |sig|
53 | key.subkeys.any? do |subkey|
54 | subkey.capability.include?(:sign) and subkey.fpr == sig.fpr
55 | end
56 | end
57 | else
58 | # only accept signatures that can be associated with a user
59 | valid_signature = false
60 | end
61 |
62 |
63 | # TODO right now, emails with broken signatures are treated like unsigned
64 | # mails. Do we want that?
65 | #
66 | # It might be better to ignore or flag tampered emails even when unsigned
67 | # emails are let in.
68 | #
69 | @ignore_email = !!(Setting.plugin_openpgp['signature_needed'] and not valid_signature)
70 |
71 | if logger
72 | logger.info "MailHandler: received email from #{sender_email} " +
73 | "with Message-ID #{message_id}: " +
74 | "encrypted=#{encrypted}, " +
75 | "valid=#{valid_signature}, "+
76 | "ignored=#{@ignore_email}"
77 | end
78 |
79 | super(email, options)
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/lib/redmine_openpgp/encrypt_mails.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module RedmineOpenpgp
4 | module EncryptMails
5 |
6 | # action names to be processed by this plugin
7 | ENCRYPT_ACTIONS = %w(
8 | attachments_added
9 | document_added
10 | issue_add
11 | issue_edit
12 | lost_password
13 | message_posted
14 | news_added
15 | news_comment_added
16 | security_notification
17 | settings_updated
18 | wiki_content_added
19 | wiki_content_updated
20 | )
21 |
22 | GLOBAL_ACTIONS = %w(
23 | lost_password
24 | security_notification
25 | settings_updated
26 | )
27 |
28 | # dispatched mail method
29 | def mail(headers={}, &block)
30 |
31 | # no project defined during the lost_password action, so we need to handle
32 | # special case when act == 'project' instead of 'all'
33 | active = RedmineOpenpgp.active_on_project?(
34 | project, global: GLOBAL_ACTIONS.include?(@_action_name)
35 | )
36 |
37 | unless active and ENCRYPT_ACTIONS.include?(@_action_name)
38 | return super
39 | end
40 |
41 | # email headers for password resets contain a single recipient e-mail address instead of an array of users
42 | # so we need to rewrite them to work with the relocate_recipients function
43 | if @_action_name == 'lost_password'
44 | headers = password_reset_headers(headers)
45 | end
46 |
47 | # relocate recipients
48 | recipients = relocate_recipients(headers)
49 | header = @_message.header.to_s
50 |
51 | # render and deliver encrypted mail
52 | reset(header)
53 | m = super prepare_headers(
54 | headers, recipients[:encrypted], encrypt = true, sign = true
55 | ) do |format|
56 | format.text
57 | format.html if not Setting.plain_text_mail? and
58 | Setting.plugin_openpgp['encrypted_html']
59 | end
60 | m.deliver
61 |
62 | # render and deliver filtered mail
63 | reset(header)
64 | tpl = @_action_name + '.filtered'
65 | m = super prepare_headers(
66 | headers, recipients[:filtered], encrypt = false, sign = true
67 | ) do |format|
68 | format.text { render tpl }
69 | format.html { render tpl } unless Setting.plain_text_mail?
70 | end
71 | m.deliver
72 |
73 | # render unchanged mail (deliverd by calling method)
74 | reset(header)
75 | m = super prepare_headers(
76 | headers, recipients[:unchanged], encrypt = false, sign = false
77 | ) do |format|
78 | format.text
79 | format.html unless Setting.plain_text_mail?
80 | end
81 |
82 | m
83 |
84 | end
85 |
86 | # get project dependent on action and object
87 | def project
88 |
89 | case @_action_name
90 | when 'attachments_added'
91 | @attachments.first.project
92 | when 'document_added'
93 | @document.project
94 | when 'issue_add', 'issue_edit'
95 | @issue.project
96 | when 'message_posted'
97 | @message.project
98 | when 'news_added', 'news_comment_added'
99 | @news.project
100 | when 'wiki_content_added', 'wiki_content_updated'
101 | @wiki_content.project
102 | else
103 | nil
104 | end
105 |
106 | end
107 |
108 | # loads a user object by e-mail (necessary for password reset emails
109 | # if we want to be able to look up their PGP key)
110 | def password_reset_headers(headers)
111 |
112 | headers[:to] = [User.find_by_mail(headers[:to])]
113 | headers
114 |
115 | end
116 |
117 | # relocates recipients (to, cc) of message
118 | def relocate_recipients(headers)
119 |
120 | # hash to be returned
121 | recipients = {
122 | :encrypted => {:to => [], :cc => []},
123 | :blocked => {:to => [], :cc => []},
124 | :filtered => {:to => [], :cc => []},
125 | :unchanged => {:to => [], :cc => []},
126 | :lost => {:to => [], :cc => []}
127 | }
128 |
129 | # relocation of recipients
130 | [:to, :cc].each do |field|
131 | Array(headers[field]).each do |user|
132 |
133 | # Try to catch case where an email was passed where the address isn't a current user
134 | begin
135 | # encrypted
136 | if Pgpkey.find_by(user_id: user.id).nil?
137 | logger.info "No public key found for #{user} <#{user.mail}> (#{user.id})" if logger
138 | # If there was no Pgpkey found for the user, but the request is
139 | # to reset the password, then just let it go out unencrypted
140 | if @_action_name == 'lost_password'
141 | recipients[:unchanged][field].push user and next
142 | end
143 | else
144 | if Pgpkey.find_by(user_id: user.id).can_encrypt.none?
145 | logger.error "Found a key for #{user} (#{user.mail}) but could not use it for encrypting the message. Sending filtered" if logger
146 | recipients[:filtered][field].push user and next
147 | else
148 | recipients[:encrypted][field].push user and next
149 | end
150 | end
151 | rescue NoMethodError
152 | logger.info "Tried to encrypt non-system user #{user}"
153 | end
154 |
155 | # unencrypted
156 | case Setting.plugin_openpgp['unencrypted_mails']
157 | when 'blocked'
158 | recipients[:blocked][field].push user
159 | when 'filtered'
160 | recipients[:filtered][field].push user
161 | when 'unchanged'
162 | recipients[:unchanged][field].push user
163 | else
164 | recipients[:lost][field].push user
165 | end
166 |
167 | end unless headers[field].blank?
168 | end
169 |
170 | recipients
171 |
172 | end
173 |
174 | # resets the mail for sending mails multiple times
175 | def reset(header)
176 |
177 | @_mail_was_called = false
178 | @_message = Mail.new
179 | @_message.header header
180 |
181 | end
182 |
183 | # prepares the headers for different configurations
184 | def prepare_headers(headers, recipients, encrypt, sign)
185 |
186 | h = headers.deep_dup
187 |
188 | # headers for recipients
189 | h[:to] = recipients[:to]
190 | h[:cc] = recipients[:cc]
191 |
192 | # headers for gpg
193 | h[:gpg] = {
194 | encrypt: false,
195 | sign: false
196 | }
197 |
198 | # headers for encryption
199 | if encrypt
200 | h[:gpg][:encrypt] = true
201 | # add pgp keys for emails
202 | h[:gpg][:keys] = {}
203 | [:to, :cc].each do |field|
204 | h[field].each do |user|
205 | user_key = Pgpkey.find_by user_id: user.id
206 | unless user_key.nil?
207 | h[:gpg][:keys][user.mail] = user_key.fpr
208 | end
209 | end unless h[field].blank?
210 | end
211 | end
212 |
213 | # headers for signature
214 | if sign
215 | server_key = Pgpkey.find_by(:user_id => 0)
216 | unless server_key.nil?
217 | h[:gpg][:sign] = true
218 | h[:gpg][:sign_as] = Setting['mail_from']
219 | h[:gpg][:password] = server_key.secret
220 | end
221 | end
222 |
223 | h
224 |
225 | end
226 |
227 | end
228 | end
229 |
--------------------------------------------------------------------------------
/lib/tasks/generate_redmine_pgpkey.rake:
--------------------------------------------------------------------------------
1 | require "io/console"
2 |
3 | desc <<-END_DESC
4 | Generate the private PGP key for the redmine server.
5 | Warning: will override and delete the existing one.
6 |
7 | Available options:
8 | * secret => passphrase (interactive, if not given)
9 |
10 | Example:
11 | RAILS_ENV="production" rake redmine:generate_redmine_pgpkey
12 | RAILS_ENV="production" rake redmine:generate_redmine_pgpkey secret="passphrase"
13 | END_DESC
14 |
15 | namespace :redmine do
16 | task :generate_redmine_pgpkey => :environment do |task|
17 | keyfile = ENV['keyfile']
18 | @secret = ENV['secret']
19 |
20 | # sanity checks
21 | puts 'Warning: passphrase is empty (no problem, but are you sure?)' if @secret == ""
22 |
23 | # interactive mode: secret
24 | if @secret == nil
25 | print "Enter secret: "
26 | STDOUT.flush
27 | @secret = STDIN.noecho(&:gets).chomp!
28 | puts
29 | puts 'Warning: passphrase is empty (no problem, but are you sure?)' if @secret == ""
30 | end
31 |
32 | # remove old key from db and from gpg ring, if present
33 | old_key = Pgpkey.find_by user_id: 0
34 | if old_key
35 | old_fpr = old_key.fpr
36 | old_key.delete
37 | puts '... removed old key from db.'
38 | if not Pgpkey.find_by fpr: old_fpr
39 | gpgme_key = GPGME::Key.get(old_fpr)
40 | gpgme_key.delete!(true)
41 | puts '... removed old key from gpg key ring.'
42 | else
43 | puts 'Warning: old key not removed (still referenced by a user)'
44 | end
45 | end
46 |
47 | # prepare gpg parameter
48 | params = {
49 | :key_type => 'RSA',
50 | :key_length => '2048',
51 | :subkey_type => 'RSA',
52 | :subkey_length => '2048',
53 | :name_real => 'Redmine Server',
54 | :name_comment => '',
55 | :name_email => Setting['mail_from'],
56 | :expire_date => '0',
57 | :passphrase => @secret
58 | }
59 | data = "\n"
60 | data += "Key-Type: " +params[:key_type] +"\n"
61 | data += "Key-Length: " +params[:key_length] +"\n"
62 | data += "Subkey-Type: " +params[:subkey_type] +"\n"
63 | data += "Subkey-Length: " +params[:subkey_length] +"\n"
64 | data += "Name-Real: " +params[:name_real] +"\n"
65 | data += "Name-Comment: " +params[:name_comment] +"\n" unless params['name_comment'].blank?
66 | data += "Name-Email: " +params[:name_email] +"\n"
67 | data += "Expire-Date: " +params[:expire_date] +"\n"
68 | data += "Passphrase: " +params[:passphrase] +"\n" unless params['passphrase'].blank?
69 | data += ""
70 | puts '... PGP key parameters built.'
71 |
72 | # create key and save into gpg key ring
73 | GPGME::Ctx.new.genkey(data)
74 | puts '... PGP key generated and saved into gpg key ring.'
75 |
76 | # save generated key into db
77 | key = GPGME::Key.find(nil, params[:name_email]).first
78 | if Pgpkey.create(:user_id => 0, :fpr => key.fingerprint, :secret => params['passphrase'])
79 | puts '... saved generated key to db.'
80 | else
81 | abort "Error: Unkown error."
82 | end
83 |
84 | puts 'PGP key successfully generated. Exiting.'
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/lib/tasks/update_redmine_pgpkey.rake:
--------------------------------------------------------------------------------
1 | require "io/console"
2 |
3 | desc <<-END_DESC
4 | Update the private PGP key and passphrase for the redmine server.
5 | Warning: will override and delete the existing one.
6 |
7 | Available options:
8 | * key => absolute path to key file (interactive, if not given)
9 | * secret => passphrase (interactive, if not given)
10 |
11 | Example:
12 | RAILS_ENV="production" rake redmine:update_redmine_pgpkey
13 | RAILS_ENV="production" rake redmine:update_redmine_pgpkey keyfile="/path/to/key.asc" secret="passphrase"
14 | END_DESC
15 |
16 | namespace :redmine do
17 | task :update_redmine_pgpkey => :environment do |task|
18 | keyfile = ENV['keyfile']
19 | @secret = ENV['secret']
20 |
21 | # sanity checks
22 | puts 'Warning: passphrase is empty (no problem, but are you sure?)' if @secret == ""
23 | abort 'Error: cannot access "'+keyfile+'". Wrong path?' if not keyfile.blank? and not File.file? keyfile
24 |
25 | # interactive mode: keyfile
26 | if keyfile.blank?
27 | while true
28 | print "Enter absolute path to key file: "
29 | STDOUT.flush
30 | keyfile = STDIN.gets.chomp!
31 | abort 'Abort.' if keyfile.empty?
32 | break if File.file? keyfile
33 | puts 'Error: cannot access "'+keyfile+'". Wrong path?'
34 | end
35 | end
36 | puts '... "'+keyfile+'" does exist.'
37 |
38 | # interactive mode: secret
39 | if @secret == nil
40 | print "Enter secret: "
41 | STDOUT.flush
42 | @secret = STDIN.noecho(&:gets).chomp!
43 | puts
44 | puts 'Warning: passphrase is empty (no problem, but are you sure?)' if @secret == ""
45 | end
46 |
47 | # open and validate keyfile
48 | @key = File.open(keyfile, "rb").read
49 | regex = /^\A\s*-----BEGIN PGP PRIVATE KEY BLOCK-----(?:(?!-----BEGIN).)*?-----END PGP PRIVATE KEY BLOCK-----\s*\z/m
50 | abort 'Error: PGP key not valid (it should start with "-----BEGIN PGP PRIVATE KEY BLOCK-----" and end with "-----END PGP PRIVATE KEY BLOCK-----")' if not @key.match(regex)
51 | puts '... PGP key seems to be valid.'
52 |
53 | # remove old key from db and from gpg ring, if present
54 | old_key = Pgpkey.find_by user_id: 0
55 | if old_key
56 | old_fpr = old_key.fpr
57 | old_key.delete
58 | puts '... removed old key from db.'
59 | if not Pgpkey.find_by fpr: old_fpr
60 | gpgme_key = GPGME::Key.get(old_fpr)
61 | gpgme_key.delete!(true)
62 | puts '... removed old key from gpg key ring.'
63 | else
64 | puts 'Wanring: old key not removed (still referenced by a user)'
65 | end
66 | end
67 |
68 | # save new key into gpg key ring
69 | gpgme_import = GPGME::Key.import(@key)
70 | abort 'Error: import of the key into gpg key ring failed.' if gpgme_import.imports.empty?
71 | puts '... PGP key imported into gpg key ring.'
72 | gpgme_key = GPGME::Key.get(gpgme_import.imports[0].fpr)
73 | @fpr = gpgme_key.fingerprint
74 |
75 | # test secret
76 | gpgme = GPGME::Crypto.new
77 | enc = gpgme.encrypt('test', {:recipients => @fpr, :always_trust => true}).to_s
78 | begin
79 | dec = gpgme.decrypt(enc, :password => @secret).to_s
80 | rescue GPGME::Error::BadPassphrase
81 | gpgme_key.delete!(true)
82 | abort "Error: Passphrase was wrong."
83 | end
84 | puts '... passphrase is correct.'
85 |
86 | # save new key to db
87 | if Pgpkey.create(:user_id => 0, :fpr => @fpr, :secret => @secret)
88 | puts '... saved new key to db.'
89 | else
90 | abort "Error: Unkown error."
91 | end
92 |
93 | puts 'PGP key successfully saved. Exiting.'
94 | end
95 | end
96 |
--------------------------------------------------------------------------------
/test/functional/pgpkeys_controller_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class PgpkeysControllerTest < ActionController::TestCase
4 | # Replace this with your real tests.
5 | def test_truth
6 | assert true
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/test/integration/pgpkeys_administration_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class PgpkeysAdministrationTest < Redmine::IntegrationTest
4 | include PgpTestHelper
5 |
6 | fixtures :users, :email_addresses, :user_preferences
7 |
8 | setup do
9 | @priv_key = read_key "pgp.server.private.asc"
10 | @pub_key = read_key "pgp.user.public.asc"
11 | @passphrase_key = read_key "pgp.passphrase.private.asc"
12 | @priv_key_fpr = "4DBBD6E21C39DBD75DFC5FFFBC84D5A85E4D733E"
13 | end
14 |
15 | # TODO: use standard require_login before action
16 | test "should redirect anonymous to login page" do
17 | skip "future enhancement"
18 | get "/pgp"
19 | assert_redirected_to "/login"
20 |
21 | post "/pgp"
22 | assert_redirected_to "/login"
23 | end
24 |
25 | test "should handle missing key arg" do
26 | skip "future enhancement"
27 | log_user "admin", "admin"
28 | post "/pgp/create", params: { }
29 | assert_response :success
30 | end
31 |
32 | test "should check server key passphrase" do
33 | assert Pgpkey.where(user_id: 0).none?
34 | log_user "admin", "admin"
35 | get "/pgp"
36 | assert_response :success
37 |
38 | assert_no_difference ->{ Pgpkey.count } do
39 | post "/pgp/create", params: { key: @passphrase_key, user_id: "0", secret: "wrong" }
40 | end
41 | assert_redirected_to "/pgp"
42 | end
43 |
44 | test "admin should be able to set server key" do
45 | assert Pgpkey.where(user_id: 0).none?
46 | log_user "admin", "admin"
47 | get "/pgp"
48 | assert_response :success
49 |
50 | assert_difference ->{ Pgpkey.where(user_id: 0).count } do
51 | post "/pgp/create", params: { key: @priv_key, user_id: "0" }
52 | end
53 | assert_redirected_to "/pgp"
54 |
55 | assert k = Pgpkey.where(user_id: 0).first
56 | assert_equal @priv_key_fpr, k.fpr
57 | assert k.secret.blank?
58 | end
59 |
60 | test "non-admin should not be able to set server key" do
61 | assert Pgpkey.where(user_id: 0).none?
62 | log_user "jsmith", "jsmith"
63 | get "/pgp"
64 | assert_response :success
65 |
66 | assert_no_difference ->{ Pgpkey.count } do
67 | post "/pgp/create", params: { key: @priv_key, user_id: "0" }
68 | end
69 | assert_redirected_to "/pgp"
70 | end
71 |
72 | test "user should be able to set own public key" do
73 | assert Pgpkey.none?
74 |
75 | log_user "jsmith", "jsmith"
76 | get "/pgp"
77 | assert_response :success
78 |
79 | assert_difference ->{ Pgpkey.where(user_id: 2).count } do
80 | post "/pgp/create", params: { key: @pub_key, user_id: 2 }
81 | end
82 | assert_redirected_to "/pgp"
83 | end
84 |
85 | test "user should not be able to set other users public key" do
86 | assert Pgpkey.none?
87 | log_user "jsmith", "jsmith"
88 | get "/pgp"
89 | assert_response :success
90 |
91 | assert_no_difference ->{ Pgpkey.count } do
92 | post "/pgp/create", params: { key: @pub_key, user_id: 3 }
93 | end
94 | assert_redirected_to "/pgp"
95 | end
96 |
97 |
98 | end
99 |
--------------------------------------------------------------------------------
/test/pgp.passphrase.private.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PRIVATE KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | lQO+BFXaxSsBCADYBi8VaIXFWu/J8WEErH/HQ7ZIHenGW68fgeiKysrL0GlrlJTl
5 | f5XX/pLZzLHFTMxgTlQ7ECmEQd+KlODQE27VPKltWC1xT73Qq9UILOI6JdG+hT4V
6 | jdxBKI0Xque4sUqQJSj3/PzgoEIiRf7bpcqH0YUOtF8RI0s3mc4HTA10859GXA+m
7 | duG/LOBKLdyBTR3Qj+M6qVesecCa9uAXQZ54sPjSA9fTwee8Jcw7VpE0CWJUvbEU
8 | AXz1lU3dh62j7gionHxAasBhtzLIEPLy6esBJrLWZhLpthfNW2ccZujL0iGGaNju
9 | OOd6MJeFNm7jGIeBOokG2CynFUIIae5hOD67ABEBAAH+AwMCqaPDbcUp+ZpgVL2w
10 | jH/Et5Z4GgtgthYj9JD2CExTZ/PBlyLNU0MDC7fGXvX3vc/CndWns6xMD5q5jf4d
11 | 877gJIhlvA/SaoqBoaxffqnB0JCAi+uu9EhLW8Mw/JL3EHWm5zQqH/LrFK1TVY4r
12 | G0JzDwzcNshMBzF/GMnTlSIrolkzU12Q4lnAyEzpxXfDdnzFsj8VeiaoDx5gCiH5
13 | Zt07VbCZ9svUOubOD/t+Hg0H5wBMWf4MbhEpthqQlGS72+DmDcorbW1kz+nDfXCB
14 | XBpHDpaKtWGFUAx8jDrkqA4Q5PuCPdta/aPHCbDJva12v6uu1YOGyzrusfdVr5PF
15 | XjRZ10PahUFBTXvtxNmC2G5tLqliLeri9i6GafEASxUoSow9YgrCfiK4Tou6/Zrk
16 | 6x69/I94UfHWwyhCKiboG0gAbHZ/RBqDNm63/C4kBdvYTs/b+9jIvTNp8U9qPSoz
17 | 1oNvNKTwMFIXoGbPQy2YKO1qSXfAOnzywn8RV6wUa8HYejtvmMo176oPXVTp6uAv
18 | 5xxaSbWNs5bfebSttu+VzWanNi+bMuLswCdIzzHC1hRBS/NmlCONYkDAGLEsR2/7
19 | EdXyOzGu0GOU0+DUERRcoQp9JSVOXS8boXmh9sb/XXowBfQqSb3c7NyB1XEHyHYn
20 | mRA5viMPa2t4z1Et5wplNF8wh05ngerNfVDOQzw3Ne35hP3COCvPyPN1z7np4pph
21 | F2iHVE/kJzXkgd9kcXSsWMdGRQbuw5qxC2JYReBBjbft9kwjtTuzCKBMEiRowJJU
22 | X31HsuOhXGPR+cib+jNTEHlq5RTCXfo8QgG/xQ34aHC2j4hFm9pTF+4DuFdTM75R
23 | FGSCOXn0mBJb4V8Lf/Si1qE9Pn9yOeLQ0j6rHOM8XZBvzwMuN5SC5N5qTSpl7oVI
24 | 3LQoUGFzc3BocmFzZSBUZXN0IDxwYXNzcGhyYXNlQGV4YW1wbGUubmV0PokBOAQT
25 | AQIAIgUCVdrFKwIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQkl2eH2EL
26 | v2KQHgf/erSvirsMb7Vn0tzgMwoQfNMf+frhVmMzQsr9INxADfCtCVdSzSrbnTiC
27 | j9uDvaYtKwq4J+alP4Qd8xJheUNHXnziKO/2grnXR13+QPuy1/ysBLHvLp6wrGCi
28 | C0lAlk+gRC6gLb81S4C9/zOwf+cOeDBhpTInRX6RdQ3ADOK8RVA6jIhHjyPBn1Fy
29 | /0l1VdhbahlgreHEQufyy/4zqWiVX35iFJ/QkG2C+LmAAQWLfjLhPKD8lVy/GcIZ
30 | aAEtHhpl3vmlHa+Dj7CtOF9fifsuLU2Uzn9eKHJmzXgNPm+RVnIlb6oYJngW16Hs
31 | Yk2SU7v+QqRLJva3zK7NyogylV+6Zp0DvgRV2sUrAQgAw6zw3F98+dXsrG+oQwTa
32 | Rpgmj9+OAaLlofavUOZ3941CTvZaVV1ifVgYvc1lDo1ZR3g/2omG1u0GIt42vBBj
33 | sFollLtuFPqLNSR7CPoQ1iGCqigi6Dkmr01ZjZIzgqMeaJr0EowwZdpN2P5dWZIV
34 | S0VdTogxXTE4kqhljYADY8VU5QEZHj7u+AmMpmmWnKSwxZww63MOH8x3KB+tK04D
35 | PBlaYiePKDYUTZ8veJioEaHRiK6WAVKAYGVIIYy0iSdgqrYal6LIgQXQzK0qCS8i
36 | DrHWfsrzKMTHKey+jiP7K2m/XtsmsdKjwzbAHKnZ+jJ8cJ0S0eEnTVILxQKQZ05y
37 | LQARAQAB/gMDAqmjw23FKfmaYF7bpIj48NRmoarBe1hug+qag2+FO2xXgmbIQKnl
38 | +DKy1bUSUYRE7NpnoJkrJGIixMlQzM97Va+rnla9HWPh3ybxfAvjAE5Iagid4/Bu
39 | gj4kqX33/HADdNdbrimn5T7dn2iwI0LCd3f3Ym78vSe39tNdEHX21MFUwoK25B86
40 | yq08CT+R0RpUjqCVEgN5rmbMfP1jVe1iCE60TaHROMw3eWT7YvBEX3c9inhxHvWq
41 | 6Ojmxq275DtCqOjFxTA7UN4ikBBdq5K/LbYJ+axUhgi3/q8gexa7HfygQ5z0f/wZ
42 | n9S/xkpsCFv3C+9d0hAigC524RDSWK19b7VSIvwhR/ywJsvD1rZC6XosyKwBxEWs
43 | rL3DshEBdvXzvkLmSqJ7Teau3QaeZzyStHBDyASho8B8PtlObLmDw5epH4VChRPh
44 | 4Cvo9kl3H3jfw6HIFmUaS77v+ZuegIVIiEsQIsUiyuCTn+ylm3yAqvnNkucDw7HD
45 | Acao7ad5uIKvzbOli1ExuD6WgPhznTz2Y9HTNJDCU9D98AoscNl5P0lsXojBkl+j
46 | OMbxoO63q2mCuNfUnoXHcKLVD+3DE5J+YcMT4bUbJlU10TUMWNhtjn0ih6dLuW0j
47 | eir7ADlo+rIcz/kvPZbI0xWQKY9q07d6Z2lekFDtyEXxQrYRdUyQxFzMCj2cXUuf
48 | aVckx4u7cInxZIZHLjnqPQHcnYJkrlzSHkj7NEwuRsSP7zqKzpLPfQhBI+8qkvOS
49 | LmwpqGAXVTR+4igwU39+OMgqHthdgC78K4gius5XQlOX7na+ZqFvTroumfZNtTM3
50 | 7yMyoxuvTgP9G5iknm0MrxEXZHLAzb+VG9xPKA1gLrKQfmk1dRBzJjYyYU+LNclu
51 | ZodziT1NykBjH6r/XBSDFxVNKu0QznyJAR8EGAECAAkFAlXaxSsCGwwACgkQkl2e
52 | H2ELv2JJoQf+ON9/llw4ye6vPepmwpDEmmzS1TlzoSjDXFF0ahlf15Zu9rXhekTv
53 | xxQPzeGv4nPN6ZNut35CVTky/da6HFDHexdEEQpKHEzcVdZFOvRqs61xgbGVUarc
54 | A3ZdMv946d6cH4SEk4jG2SM9XSqMpYtS8Jx9nCQD5qpEViyPmB0T9yJiIdnVxtd/
55 | fijn5hTl1sRw1Z6bZ3OH2ssFDLSo7wnh0u+8hmYaCN9R4IqpPHKSAhuYkbWGwx+t
56 | YHKZzg0IlesR3o0VnAI1TPh6t2JLUQY2qZ+YGfdAPaLdGbtzl3RefL7B7i99TD8U
57 | BFD2P5YZg7ykb4FNYQBl44yi5IhjKgyAlQ==
58 | =cA8J
59 | -----END PGP PRIVATE KEY BLOCK-----
60 |
--------------------------------------------------------------------------------
/test/pgp.passphrase.public.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PUBLIC KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | mQENBFXaxSsBCADYBi8VaIXFWu/J8WEErH/HQ7ZIHenGW68fgeiKysrL0GlrlJTl
5 | f5XX/pLZzLHFTMxgTlQ7ECmEQd+KlODQE27VPKltWC1xT73Qq9UILOI6JdG+hT4V
6 | jdxBKI0Xque4sUqQJSj3/PzgoEIiRf7bpcqH0YUOtF8RI0s3mc4HTA10859GXA+m
7 | duG/LOBKLdyBTR3Qj+M6qVesecCa9uAXQZ54sPjSA9fTwee8Jcw7VpE0CWJUvbEU
8 | AXz1lU3dh62j7gionHxAasBhtzLIEPLy6esBJrLWZhLpthfNW2ccZujL0iGGaNju
9 | OOd6MJeFNm7jGIeBOokG2CynFUIIae5hOD67ABEBAAG0KFBhc3NwaHJhc2UgVGVz
10 | dCA8cGFzc3BocmFzZUBleGFtcGxlLm5ldD6JATgEEwECACIFAlXaxSsCGwMGCwkI
11 | BwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEJJdnh9hC79ikB4H/3q0r4q7DG+1Z9Lc
12 | 4DMKEHzTH/n64VZjM0LK/SDcQA3wrQlXUs0q2504go/bg72mLSsKuCfmpT+EHfMS
13 | YXlDR1584ijv9oK510dd/kD7stf8rASx7y6esKxgogtJQJZPoEQuoC2/NUuAvf8z
14 | sH/nDngwYaUyJ0V+kXUNwAzivEVQOoyIR48jwZ9Rcv9JdVXYW2oZYK3hxELn8sv+
15 | M6lolV9+YhSf0JBtgvi5gAEFi34y4Tyg/JVcvxnCGWgBLR4aZd75pR2vg4+wrThf
16 | X4n7Li1NlM5/XihyZs14DT5vkVZyJW+qGCZ4Fteh7GJNklO7/kKkSyb2t8yuzcqI
17 | MpVfuma5AQ0EVdrFKwEIAMOs8NxffPnV7KxvqEME2kaYJo/fjgGi5aH2r1Dmd/eN
18 | Qk72WlVdYn1YGL3NZQ6NWUd4P9qJhtbtBiLeNrwQY7BaJZS7bhT6izUkewj6ENYh
19 | gqooIug5Jq9NWY2SM4KjHmia9BKMMGXaTdj+XVmSFUtFXU6IMV0xOJKoZY2AA2PF
20 | VOUBGR4+7vgJjKZplpyksMWcMOtzDh/MdygfrStOAzwZWmInjyg2FE2fL3iYqBGh
21 | 0YiulgFSgGBlSCGMtIknYKq2GpeiyIEF0MytKgkvIg6x1n7K8yjExynsvo4j+ytp
22 | v17bJrHSo8M2wByp2foyfHCdEtHhJ01SC8UCkGdOci0AEQEAAYkBHwQYAQIACQUC
23 | VdrFKwIbDAAKCRCSXZ4fYQu/YkmhB/4433+WXDjJ7q896mbCkMSabNLVOXOhKMNc
24 | UXRqGV/Xlm72teF6RO/HFA/N4a/ic83pk263fkJVOTL91rocUMd7F0QRCkocTNxV
25 | 1kU69GqzrXGBsZVRqtwDdl0y/3jp3pwfhISTiMbZIz1dKoyli1LwnH2cJAPmqkRW
26 | LI+YHRP3ImIh2dXG139+KOfmFOXWxHDVnptnc4faywUMtKjvCeHS77yGZhoI31Hg
27 | iqk8cpICG5iRtYbDH61gcpnODQiV6xHejRWcAjVM+Hq3YktRBjapn5gZ90A9ot0Z
28 | u3OXdF58vsHuL31MPxQEUPY/lhmDvKRvgU1hAGXjjKLkiGMqDICV
29 | =OaGq
30 | -----END PGP PUBLIC KEY BLOCK-----
31 |
--------------------------------------------------------------------------------
/test/pgp.server.private.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PRIVATE KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | 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
56 | =LyJ/
57 | -----END PGP PRIVATE KEY BLOCK-----
58 |
--------------------------------------------------------------------------------
/test/pgp.server.public.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PUBLIC KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | mQENBFXY6w4BCADMOAkUgRcxl9JrLmOGIvuZFcfAbA6RweoGTSKe4zqAcsxH7Z17
5 | xk5vpW+Bv+mORKIvo6ZFvzXzauzy/3Q6HXsbO4Hp+Fi8FULhO0kzXE2DWrz5Rg25
6 | suEh39haUHookpNx6vYMwFwAE5E/pGYU1WuFzk/pikHEdas7rmchQkwGMq0javlc
7 | DeGjsivqHHm+njVuNkoM0Rt72+dBytNLta88Pducftvm+2qIAuiwd6VrZplJroTJ
8 | gVDvGX42cDe2Yipa44+wHx1wzr7WvF0BBGjX1sRRoh5S3eZOHijl7hSvGG/2rqwl
9 | Z7pBC0TUXUAyYY8vOLPFmKjT46b/7k5l8wQxABEBAAG0JFJlZG1pbmUgU2VydmVy
10 | IDxyZWRtaW5lQGV4YW1wbGUubmV0PokBOAQTAQIAIgUCVdjrDgIbAwYLCQgHAwIG
11 | FQgCCQoLBBYCAwECHgECF4AACgkQvITVqF5Ncz5UmQgAoF+92KtS+ze8E8xV7Mib
12 | t8K5YGjmknTv9UkURKJfH8+/yMFxgiRtecpo8UzpwTnDg53Hgb7PaOhOvleqBSSm
13 | Dgjjx8SLHak8Ps2Owkc4afOb5PsaJoovVdm+Rc5UThFLj1QD9aOIPbc6Mw0NPvwB
14 | oJhRpQdEUSeB3Dn0GN1Gumg/TcJX+ivpNg5zuyhuc5kEsBZ8O303nIpySV2yLx7M
15 | neZnYPURfSn8cn8JVpesC4bxD1UYgmqqkq/lkn3rRyfx3kied8PYhTyWbTZa8C2V
16 | M9ad+kHDnQ5AfjSXxfJweNYiW90F9jggNUxuTgCTQpZLFBE3rjKqG/96++q+BPuf
17 | yLkBDQRV2OsOAQgAs/O3AxB5ztLpF2kdVXBP+aHiqlXLk4Jv6A8iQ5pnJL36q7+J
18 | gQJQSynHGua/H5Bvl+4z1SGwoPxXxFXfbEf3xD6VQQEtgB7UiIHIMudNtMe3tbT3
19 | zq63qe51ClGTFDw8GVu+r6+jG1oTaDrI6i1Kk+X8IWvD8USR/LEwihJGLcu72A2a
20 | CqaWC/sS/DUUN3twBSb0vNAL8FMwrSUYAlWlUSY2OzEa/4PL+0thB1VUdcG2xN9E
21 | GSyJD/O+ZKt+NY6ohVLWcby3ptvxFfv77K54ZisRYZ1bIrwiX2SJe59QYh3XnYwn
22 | jT+SRsBqe5xRsWmrXcJaX22WVUV8wOu1UwKP5wARAQABiQEfBBgBAgAJBQJV2OsO
23 | AhsMAAoJELyE1aheTXM+J4YIAIIMwbSk5Wcy4fDQZrEkhEoFgljQsAM3mFfmHfhQ
24 | npeMJNQLYcQuQtDrfu0HpMjMEI0lts126lHd2mSEJxv5FQXZa3CKP/ygdhMNaWGx
25 | 1nvUEG4X1ZTlnvjg3bOHiQiyE/xtM81JyKCwm1r9aREHdsaQ7OtADga/hUCLUGeV
26 | 83HDpl5XRyxe/zpczuNIYSWZT8+dcyvHbkeFPCGP4inzgKTWYWsjl4JkoKJNPXdz
27 | kJSDqPW+WrUlMATPFhQ121ApP4PgmIKmRo2qGF4wZf4WRB8LBmKgPuYb1MIlAi+w
28 | X5HgSvPklKng8TS1PRMQmiYSyiqTeuqFm6jtf5GoYQx5AZk=
29 | =xuI4
30 | -----END PGP PUBLIC KEY BLOCK-----
31 |
--------------------------------------------------------------------------------
/test/pgp.user.private.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PRIVATE KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | 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
56 | =x7UE
57 | -----END PGP PRIVATE KEY BLOCK-----
58 |
--------------------------------------------------------------------------------
/test/pgp.user.public.asc:
--------------------------------------------------------------------------------
1 | -----BEGIN PGP PUBLIC KEY BLOCK-----
2 | Version: GnuPG v1
3 |
4 | mQENBFXY6ywBCADgZYPypF61Fncr96dsWgLH+u94+iTPTiOdlneYqMKz3Ti2MDE1
5 | jPj5VHwT2x1F1+g31ASrzWX1SIq63HaJv6pF4cGoEhjfaKyI6YTFCIkV6Z5X1HYl
6 | MfAQB7ZVgUX7vVVl5NmJgf0GhPYv9ORD+OZxVyigSD8SSZUq9lSHFzx/uLUJZOgW
7 | ++51ueMbV2nz5UJ83R/bGtjQyk6Vnf5fIP1wX1lXT009osJEpsM6ja0RVilzmChM
8 | kk9emiqS3ckVHYyFxFrX4iBD/LCKv0E89LWMxTx0frlv2D/4o2mK2nNBSrqOHzjx
9 | T0KjIncp8XMdAhl1tA6NtLx+m2vKcRXAYIhXABEBAAG0IVJlZG1pbmUgQWRtaW4g
10 | PGFkbWluQGV4YW1wbGUubmV0PokBOAQTAQIAIgUCVdjrLAIbAwYLCQgHAwIGFQgC
11 | CQoLBBYCAwECHgECF4AACgkQAF2MmMBVZmowHQgAgGqOb2dUttbb/YRyheNyeStA
12 | x5x0kQT2uxvK3scn+k+2bW7CyWPVcULgVcIGn22nqcVrvQ9ocOFTcBU//vCTNjkm
13 | BDSANq8eb8mOGMLlFk2mxrLI2BGN/5XKchi20Pbmi9+02LsBe2aiNoDnfRoq9u8Z
14 | xjJPBlWxJei7AOD+JzZ7D2uMoqj1yqziqU3JNuuekS7a0OjS3pa9MSGayFhWgUUO
15 | eTDksn07rc03EfGapwdyYd94DQDxAnRjztOq2LOvnHfwcTx5eoTKW4PzbohVp0Bj
16 | kaopIR92tj/xn+nICLZ2vP3HpX5SCOO//jOy0k9L5SAKDhegGnYkHWaflvC2FbkB
17 | DQRV2OssAQgAy3qf7ZvQRG5dgQ4m4bdHtEuKjqeQgHIM6tEhh3kPtiarm5+p/YvP
18 | uCHVJCzq1EAB0r0SCPo/4eQBosElI4uW504hZUq+02pGz7Vr5JXwI1U9Otqv2mro
19 | vf3uLFcxMdm2r0Qle6/tDJDpuj/6Bea52DcI97sAFiaVfXugyw0NTSbZKXhsYRlM
20 | 3wQ6GJW9bAsaF18jdMN1CeNmlnX86KL8ZDeodG+tY1gqzvq7+VWvoUf2DRGGLGZe
21 | tsgHAtPzlNLPUFLfP1KUKiGsT7Ew4F0Xl+ATKlyuPKCDyTU1gFP6Z0D/84iCEUkB
22 | xWTVlrBY4WciYXeUBbAOCLu7ljijWo9m5wARAQABiQEfBBgBAgAJBQJV2OssAhsM
23 | AAoJEABdjJjAVWZqmxYH/j/zA1IS30jtZmdUU4HzcpZBYCkJnMPGUjcwjnXqVloG
24 | QoTS+ndyjsAGYXexBeW0zHtBH4KSPWGMsXA9plXXXzfHiYLRbqfEBFwh1gAuIMce
25 | TGaSbtZBGv3Tl7qgJsqSD0HCXlyQBN69trmGeCfyf0sDvYDYn0wZu/2U6zg6twj9
26 | NkqkqxGOAM98oyhHeXD1yKcQj4+PnHMDuna1aqUN54Tga2inF36qyRKEzeO548DC
27 | 81ReWPULB7ZLnUtrwQ8h4a6+pdS8thFWMU9MPsZtuyZKI/MWTaxqV+Ha8XcSm4Z+
28 | 6wR8fxGwZyD2fOQxeWjm+FSN7HUzNFwV67KHcKdLXcs=
29 | =6WZE
30 | -----END PGP PUBLIC KEY BLOCK-----
31 |
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | # Load the Redmine helper
2 | require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
3 |
4 | ActiveSupport::TestCase.class_eval do
5 | def with_plugin_settings(settings = {})
6 | with_settings(plugin_openpgp: {
7 | 'activation' => 'all',
8 | 'signature_needed' => false,
9 | 'unencrypted_mails' => 'filtered',
10 | 'encrypted_html' => false,
11 | 'filtered_mail_footer' => '',
12 | }.merge(settings)) do
13 | yield
14 | end
15 | end
16 | end
17 |
18 | module PgpTestHelper
19 |
20 | def read_key(name)
21 | IO.read Rails.root.join("plugins", "openpgp", "test", name)
22 | end
23 |
24 | def delete_keys
25 | @keys.each{|k| k.delete!(true) rescue nil} if @keys
26 | end
27 |
28 | def generate_key(email: , password: nil, name: email)
29 | @keys ||= []
30 | unless k = GPGME::Key.find(:secret, email).first
31 | GPGME::Ctx.new do |gpg|
32 | gpg.generate_key <<-END
33 |
34 | Key-Type: DSA
35 | Key-Length: 1024
36 | Subkey-Type: ELG-E
37 | Subkey-Length: 1024
38 | Name-Real: #{email}
39 | Name-Email: #{email}
40 | Expire-Date: 0
41 | Passphrase: #{password}
42 |
43 | END
44 | end
45 | k = GPGME::Key.find(:secret, email).first
46 | end
47 | if k
48 | @keys << k
49 | k
50 | end
51 | end
52 |
53 | end
54 |
--------------------------------------------------------------------------------
/test/unit/encrypted_mailer_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class EncryptedMailerTest < ActiveSupport::TestCase
4 | include PgpTestHelper
5 | include Redmine::I18n
6 | include Rails::Dom::Testing::Assertions
7 | fixtures :projects, :enabled_modules, :issues, :users, :email_addresses, :user_preferences, :members,
8 | :member_roles, :roles, :documents, :attachments, :news,
9 | :tokens, :journals, :journal_details, :changesets,
10 | :trackers, :projects_trackers,
11 | :issue_statuses, :enumerations, :messages, :boards, :repositories,
12 | :wikis, :wiki_pages, :wiki_contents, :wiki_content_versions,
13 | :versions,
14 | :comments
15 |
16 | setup do
17 | ActionMailer::Base.deliveries.clear
18 | Setting.plain_text_mail = '0'
19 | Setting.default_language = 'en'
20 | Setting.emails_footer = "footer"
21 | User.current = nil
22 | Pgpkey.import user_id: 0, key: read_key("pgp.server.private.asc")
23 | end
24 |
25 | teardown do
26 | delete_keys
27 | end
28 |
29 | test "should preserve HTML part" do
30 | user1 = User.generate!
31 | k = generate_key email: user1.mail, password: 'abc'
32 | Pgpkey.create user_id: user1.id, fpr: k.fingerprint
33 |
34 | with_plugin_settings("activation" => "all", "encrypted_html" => true) do
35 | news = News.find(1)
36 | news.project.enabled_module('news').add_watcher(user1)
37 | Mailer.deliver_news_added(news)
38 | assert m = decrypt_email(to: user1.mail, password: 'abc')
39 | assert m.multipart?
40 | assert_equal 2, m.parts.size
41 | assert_mail_body_match "eCookbook first release", m
42 | assert_select_email do
43 | assert_select 'h1', text: "eCookbook first release"
44 | end
45 | end
46 | end
47 |
48 |
49 | test "should not encrypt news_added notification when not active for project" do
50 | user1 = User.generate!
51 | k = generate_key email: user1.mail, password: 'abc'
52 | Pgpkey.create user_id: user1.id, fpr: k.fingerprint
53 |
54 | user2 = User.generate!
55 | k = generate_key email: user2.mail, password: 'def'
56 | Pgpkey.create user_id: user2.id, fpr: k.fingerprint
57 |
58 | news = News.find(1)
59 | with_plugin_settings("activation" => "project") do
60 | news.project.enabled_module('news').add_watcher(user1)
61 | Mailer.deliver_news_added(news)
62 | assert m = last_email
63 | assert_include "eCookbook first release", m.text_part.decoded
64 | end
65 | end
66 |
67 | test "should encrypt news_added notification when active for project" do
68 | user1 = User.generate!
69 | k = generate_key email: user1.mail, password: 'abc'
70 | Pgpkey.create user_id: user1.id, fpr: k.fingerprint
71 |
72 | user2 = User.generate!
73 | k = generate_key email: user2.mail, password: 'def'
74 | Pgpkey.create user_id: user2.id, fpr: k.fingerprint
75 |
76 | news = News.find(1)
77 | news.project.enabled_modules.create! name: 'openpgp'
78 | with_plugin_settings("activation" => "project") do
79 | news.project.enabled_module('news').add_watcher(user1)
80 | Mailer.deliver_news_added(news)
81 |
82 | assert_include user1.mail, recipients
83 | assert_not_include user2.mail, recipients
84 |
85 | assert m = decrypt_email(to: user1.mail, password: 'abc')
86 | assert_include "eCookbook first release", m.decoded
87 | end
88 | end
89 |
90 | test "should encrypt news_added notification" do
91 | user1 = User.generate!
92 | k = generate_key email: user1.mail, password: 'abc'
93 | Pgpkey.create user_id: user1.id, fpr: k.fingerprint
94 |
95 | user2 = User.generate!
96 | k = generate_key email: user2.mail, password: 'def'
97 | Pgpkey.create user_id: user2.id, fpr: k.fingerprint
98 |
99 | news = News.find(1)
100 | with_plugin_settings("activation" => "all") do
101 | news.project.enabled_module('news').add_watcher(user1)
102 | Mailer.deliver_news_added(news)
103 |
104 | assert_include user1.mail, recipients
105 | assert_not_include user2.mail, recipients
106 |
107 | assert m = decrypt_email(to: user1.mail, password: 'abc')
108 | assert_include "eCookbook first release", m.decoded
109 | end
110 | end
111 |
112 | test "should encrypt security notification" do
113 | user = User.find 1
114 | set_language_if_valid user.language
115 | k = generate_key email: user.mail, password: 'abc'
116 | Pgpkey.create user_id: user.id, fpr: k.fingerprint
117 |
118 | with_plugin_settings("activation" => "all") do
119 | sender = User.find(2)
120 | sender.remote_ip = '192.168.1.1'
121 | assert Mailer.deliver_security_notification(user, sender, message: :notice_account_password_updated)
122 |
123 | assert mail = decrypt_email(to: user.mail, password: 'abc')
124 | assert_include sender.login, mail.decoded
125 | assert_include '192.168.1.1', mail.decoded
126 | assert_include I18n.t(:notice_account_password_updated), mail.decoded
127 | assert_include "footer", mail.decoded
128 | end
129 | end
130 |
131 | test "should filter mail when active and user has no key" do
132 | user = User.find 1
133 | set_language_if_valid user.language
134 |
135 | with_plugin_settings("activation" => "all") do
136 | sender = User.find(2)
137 | sender.remote_ip = '192.168.1.1'
138 | assert Mailer.deliver_security_notification(user, sender, message: :notice_account_password_updated)
139 |
140 | assert mail = last_email(to: user.mail)
141 | assert text = mail.text_part
142 | assert_not_include sender.login, text.decoded
143 | assert_not_include '192.168.1.1', text.decoded
144 | assert_not_include I18n.t(:notice_account_password_updated), text.decoded
145 | assert_include "footer", text.decoded
146 | assert_include "This mail was filtered", text.decoded
147 | end
148 | end
149 |
150 | test "should not encrypt settings update notification when not active" do
151 | user = User.find 1
152 | set_language_if_valid user.language
153 | k = generate_key email: user.mail, password: 'abc'
154 | Pgpkey.create user_id: user.id, fpr: k.fingerprint
155 |
156 | user.remote_ip = '192.168.1.1'
157 |
158 | with_plugin_settings("activation" => "none") do
159 | assert Mailer.deliver_settings_updated(user, %w(host_name))
160 |
161 | assert mail = last_email.text_part
162 | assert_include 'settings were changed', mail.decoded
163 | assert_include 'Host name and path', mail.decoded
164 | assert_include '192.168.1.1', mail.decoded
165 | assert_include user.login, mail.decoded
166 | end
167 | end
168 |
169 | test "should encrypt settings update notification with global activation" do
170 | user = User.find 1
171 | set_language_if_valid user.language
172 | k = generate_key email: user.mail, password: 'abc'
173 | Pgpkey.create user_id: user.id, fpr: k.fingerprint
174 |
175 | user.remote_ip = '192.168.1.1'
176 |
177 | with_plugin_settings("activation" => "all") do
178 | assert Mailer.deliver_settings_updated(user, %w(host_name))
179 |
180 | assert mail = decrypt_email(to: user.mail, password: 'abc')
181 | assert_include 'settings were changed', mail.decoded
182 | assert_include 'Host name and path', mail.decoded
183 | assert_include '192.168.1.1', mail.decoded
184 | assert_include user.login, mail.decoded
185 | end
186 | end
187 |
188 | test "should encrypt settings update notification with project activation" do
189 | user = User.find 1
190 | set_language_if_valid user.language
191 | k = generate_key email: user.mail, password: 'abc'
192 | Pgpkey.create user_id: user.id, fpr: k.fingerprint
193 |
194 | user.remote_ip = '192.168.1.1'
195 |
196 | with_plugin_settings("activation" => "project") do
197 | assert Mailer.deliver_settings_updated(user, %w(host_name))
198 |
199 | assert mail = decrypt_email(to: user.mail, password: 'abc')
200 | assert_include 'settings were changed', mail.decoded
201 | assert_include 'Host name and path', mail.decoded
202 | assert_include '192.168.1.1', mail.decoded
203 | assert_include user.login, mail.decoded
204 | end
205 | end
206 |
207 | private
208 |
209 | def decrypt_email(to:, password: nil)
210 | mail = last_email to: to
211 | encrypted = mail.parts.detect{|p| p.content_type =~ /encrypted\.asc/}
212 | assert encrypted.present?, "found email to #{to} but it's not encrypted"
213 | assert clear = GPGME::Crypto.new.decrypt(encrypted.body.to_s, password: password)
214 | Mail.new clear
215 | end
216 |
217 | def read_key(name)
218 | IO.read Rails.root.join("plugins", "openpgp", "test", name)
219 | end
220 |
221 | # Returns an array of email addresses to which emails were sent
222 | def recipients
223 | ActionMailer::Base.deliveries.map(&:bcc).flatten.sort
224 | end
225 |
226 | def last_email(to: nil)
227 | mail = if to
228 | ActionMailer::Base.deliveries.detect{|m|Array(m.bcc).include? to}
229 | else
230 | ActionMailer::Base.deliveries.last
231 | end
232 | assert_not_nil mail, "no mail for #{to.presence || "any recipient"} found"
233 | mail
234 | end
235 |
236 | def text_part
237 | last_email.parts.detect {|part| part.content_type.include?('text/plain')}
238 | end
239 |
240 | def html_part
241 | last_email.parts.detect {|part| part.content_type.include?('text/html')}
242 | end
243 | end
244 |
--------------------------------------------------------------------------------
/test/unit/mail_handler_decryption_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class MailHandlerDecryptionTest < ActiveSupport::TestCase
4 | include PgpTestHelper
5 | include Redmine::I18n
6 | include Rails::Dom::Testing::Assertions
7 | fixtures :projects, :enabled_modules, :issues, :users,
8 | :email_addresses, :user_preferences, :members,
9 | :member_roles, :roles, :tokens, :journals,
10 | :journal_details, :trackers, :projects_trackers,
11 | :issue_statuses, :enumerations, :versions
12 |
13 | FIXTURES_PATH = File.dirname(__FILE__) + '/../../../../test/fixtures/mail_handler'
14 | SERVER_TO = "redmine@example.net"
15 |
16 | setup do
17 | (@mails = Mail::TestMailer.deliveries).clear
18 | ActionMailer::Base.deliveries.clear
19 | Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
20 | User.current = nil
21 | Pgpkey.import user_id: 0, key: read_key("pgp.server.private.asc")
22 | @user = User.find_by_login 'jsmith'
23 | end
24 |
25 | teardown do
26 | Setting.clear_cache
27 | delete_keys
28 | end
29 |
30 | test "should add unsigned issue if not active" do
31 | k = generate_key email: @user.mail, password: 'abc'
32 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
33 |
34 | with_plugin_settings("signature_needed" => true, "activation" => "none") do
35 | assert_difference ->{Issue.count} do
36 | submit_email( 'ticket_on_given_project.eml', { :issue => {:tracker => 'Support request'} })
37 | end
38 | end
39 |
40 | with_plugin_settings("signature_needed" => true, "activation" => "project") do
41 | assert_difference ->{Issue.count} do
42 | submit_email( 'ticket_on_given_project.eml', { :issue => {:tracker => 'Support request'} })
43 | end
44 | end
45 | end
46 |
47 |
48 | test "should not add unsigned issue if active" do
49 | k = generate_key email: @user.mail, password: 'abc'
50 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
51 |
52 | with_plugin_settings("signature_needed" => true, "activation" => "all") do
53 | assert_no_difference ->{Issue.count} do
54 | submit_email( 'ticket_on_given_project.eml', { :issue => {:tracker => 'Support request'} })
55 | end
56 | end
57 |
58 | with_plugin_settings("signature_needed" => true, "activation" => "project") do
59 | Project.find('onlinestore').enabled_modules.create! name: 'openpgp'
60 | assert_no_difference ->{Issue.count} do
61 | submit_email( 'ticket_on_given_project.eml', { :issue => {:tracker => 'Support request'} })
62 | end
63 | end
64 | end
65 |
66 | test "should not add issue from invalid signed mail when sigs are required" do
67 | k = generate_key email: @user.mail, password: 'abc'
68 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
69 |
70 | with_plugin_settings("signature_needed"=>true, "activation" => "all") do
71 | assert_no_difference ->{Issue.count} do
72 | submit_signed_email(
73 | 'ticket_on_given_project.eml',
74 | options: { password: 'abc' },
75 | mh_options: { :issue => {:tracker => 'Support request'} }
76 | ) do |mail|
77 | # modify the already signed content
78 | mail.text_part.body.raw_source.sub!(/Resolved/, 'Closed')
79 | end
80 | end
81 | end
82 | end
83 |
84 | test "should not add issue from invalid signed mail even when unsigned mails are OK" do
85 | skip "possible enhancement - never accept invalid signed mails"
86 | k = generate_key email: @user.mail, password: 'abc'
87 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
88 |
89 | with_plugin_settings("signature_needed"=>false, "activation" => "all") do
90 | assert_no_difference ->{Issue.count} do
91 | submit_signed_email(
92 | 'ticket_on_given_project.eml',
93 | options: { password: 'abc' },
94 | mh_options: { :issue => {:tracker => 'Support request'} }
95 | ) do |mail|
96 | # modify the already signed content
97 | mail.text_part.body.raw_source.sub!(/Resolved/, 'Closed')
98 | end
99 | end
100 | end
101 | end
102 |
103 | test "should add issue from signed mail" do
104 | k = generate_key email: @user.mail, password: 'abc'
105 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
106 |
107 | with_plugin_settings("signature_needed" => true, "activation" => "all") do
108 | issue = nil
109 | assert_difference ->{Issue.count} do
110 | issue = submit_signed_email(
111 | 'ticket_on_given_project.eml',
112 | options: { password: 'abc' },
113 | mh_options: { :issue => {:tracker => 'Support request'} }
114 | )
115 | end
116 | assert issue.is_a?(Issue)
117 | assert !issue.new_record?
118 | issue.reload
119 | assert_equal 'Support request', issue.tracker.name
120 | assert_equal @user, issue.author
121 | end
122 | end
123 |
124 | test "should add issue from signed / encrypted mail" do
125 | k = generate_key email: @user.mail, password: 'abc'
126 | Pgpkey.create user_id: @user.id, fpr: k.fingerprint
127 |
128 | with_plugin_settings("signature_needed" => true, "activation" => "all") do
129 | issue = nil
130 | assert_difference ->{Issue.count} do
131 | issue = submit_encrypted_email(
132 | 'ticket_on_given_project.eml',
133 | options: { password: 'abc', sign: true },
134 | mh_options: { :issue => {:tracker => 'Support request'} }
135 | )
136 | end
137 | assert issue.is_a?(Issue)
138 | assert !issue.new_record?
139 | issue.reload
140 | assert_equal 'Support request', issue.tracker.name
141 | assert_equal @user, issue.author
142 | end
143 | end
144 |
145 |
146 | test "should not add issue from unsigned encrypted mail" do
147 | with_plugin_settings("signature_needed" => true, "activation" => "all") do
148 | assert_no_difference ->{Issue.count} do
149 | submit_encrypted_email(
150 | 'ticket_on_given_project.eml',
151 | mh_options: { :issue => {:tracker => 'Support request'} }
152 | )
153 | end
154 | end
155 | end
156 |
157 | test "should add issue from encrypted mail" do
158 | with_plugin_settings("signature_needed" => false, "activation" => "all") do
159 | # This email contains: 'Project: onlinestore'
160 | issue = submit_encrypted_email(
161 | 'ticket_on_given_project.eml',
162 | mh_options: { :issue => {:tracker => 'Support request'} }
163 | )
164 | assert issue.is_a?(Issue)
165 | assert !issue.new_record?
166 | issue.reload
167 | assert_equal 'Support request', issue.tracker.name
168 | assert_equal @user, issue.author
169 | end
170 | end
171 |
172 |
173 | private
174 |
175 | def submit_signed_email(filename, options: {}, mh_options: {})
176 | mail = sign_email filename, options
177 | if block_given?
178 | yield mail
179 | end
180 | MailHandler.receive(mail.to_s, mh_options)
181 | end
182 |
183 | def sign_email(filename, options)
184 | mail = Mail.new IO.read File.join FIXTURES_PATH, filename
185 | Mail::Gpg.sign mail, options.merge(sign: true)
186 | end
187 |
188 | def submit_encrypted_email(filename, options: {}, mh_options: {})
189 | mail = encrypt_email filename, options
190 | MailHandler.receive(mail.to_s, mh_options)
191 | end
192 |
193 | def encrypt_email(filename, options)
194 | mail = Mail.new IO.read File.join FIXTURES_PATH, filename
195 | mail.to = SERVER_TO
196 | Mail::Gpg.encrypt mail, options
197 | end
198 |
199 | def submit_email(filename, options={})
200 | raw = IO.read(File.join(FIXTURES_PATH, filename))
201 | yield raw if block_given?
202 | MailHandler.receive(raw, options)
203 | end
204 |
205 | end
206 |
--------------------------------------------------------------------------------
/test/unit/pgpkey_test.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path('../../test_helper', __FILE__)
2 |
3 | class PgpkeyTest < ActiveSupport::TestCase
4 | include PgpTestHelper
5 |
6 | setup do
7 | @priv_key = read_key "pgp.server.private.asc"
8 | @pub_key = read_key "pgp.user.public.asc"
9 | @passphrase_key = read_key "pgp.passphrase.private.asc"
10 | end
11 |
12 | test "should import public key" do
13 | assert key = Pgpkey.import(user_id: 1, key: @pub_key)
14 | assert_equal "BE49710891B0D87485423EB7005D8C98C055666A", key.fpr
15 | assert_equal 1, key.user_id
16 | assert key.secret.blank?
17 | assert key.persisted?
18 | end
19 |
20 | test "should import private key" do
21 | assert key = Pgpkey.import(user_id: 0, key: @priv_key)
22 | assert_equal "4DBBD6E21C39DBD75DFC5FFFBC84D5A85E4D733E", key.fpr
23 | assert_equal 0, key.user_id
24 | assert key.secret.blank?
25 | assert key.persisted?
26 | end
27 |
28 | test "import should check passphrase" do
29 | assert_raise(GPGME::Error::BadPassphrase) do
30 | assert_no_difference ->{ Pgpkey.count } do
31 | Pgpkey.import(user_id: 0, key: @passphrase_key, secret: "wrong")
32 | end
33 | end
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------