schleuder 3.2.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/Rakefile +16 -8
- data/bin/schleuder +2 -1
- data/bin/schleuder-api-daemon +3 -2
- data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +30 -0
- data/db/schema.rb +2 -2
- data/etc/list-defaults.yml +4 -2
- data/etc/schleuder.yml +11 -0
- data/lib/schleuder-api-daemon.rb +9 -354
- data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +143 -0
- data/lib/schleuder-api-daemon/routes/key.rb +40 -0
- data/lib/schleuder-api-daemon/routes/list.rb +69 -0
- data/lib/schleuder-api-daemon/routes/status.rb +5 -0
- data/lib/schleuder-api-daemon/routes/subscription.rb +99 -0
- data/lib/schleuder-api-daemon/routes/version.rb +5 -0
- data/lib/schleuder.rb +2 -3
- data/lib/schleuder/cli.rb +24 -0
- data/lib/schleuder/cli/subcommand_fix.rb +1 -1
- data/lib/schleuder/conf.rb +7 -1
- data/lib/schleuder/errors/active_model_error.rb +2 -5
- data/lib/schleuder/errors/decryption_failed.rb +2 -7
- data/lib/schleuder/errors/key_adduid_failed.rb +1 -5
- data/lib/schleuder/errors/key_generation_failed.rb +1 -8
- data/lib/schleuder/errors/keyword_admin_only.rb +1 -5
- data/lib/schleuder/errors/list_not_found.rb +1 -5
- data/lib/schleuder/errors/listdir_problem.rb +2 -7
- data/lib/schleuder/errors/loading_list_settings_failed.rb +2 -5
- data/lib/schleuder/errors/message_empty.rb +1 -5
- data/lib/schleuder/errors/message_not_from_admin.rb +2 -5
- data/lib/schleuder/errors/message_sender_not_subscribed.rb +2 -5
- data/lib/schleuder/errors/message_too_big.rb +2 -5
- data/lib/schleuder/errors/message_unauthenticated.rb +1 -4
- data/lib/schleuder/errors/message_unencrypted.rb +2 -5
- data/lib/schleuder/errors/message_unsigned.rb +2 -5
- data/lib/schleuder/errors/too_many_keys.rb +1 -8
- data/lib/schleuder/filters/{request_filter.rb → post_decryption/10_request.rb} +0 -0
- data/lib/schleuder/filters/{max_message_size.rb → post_decryption/20_max_message_size.rb} +0 -0
- data/lib/schleuder/filters/{forward_filter.rb → post_decryption/30_forward_to_owner.rb} +0 -0
- data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +10 -0
- data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{forward_incoming.rb → pre_decryption/20_forward_all_incoming_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{send_key_filter.rb → pre_decryption/30_send_key.rb} +0 -0
- data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb} +5 -3
- data/lib/schleuder/filters/{strip_alternative_filter.rb → pre_decryption/50_strip_html_from_alternative.rb} +1 -1
- data/lib/schleuder/filters_runner.rb +41 -31
- data/lib/schleuder/gpgme/ctx.rb +1 -1
- data/lib/schleuder/gpgme/import_status.rb +13 -7
- data/lib/schleuder/gpgme/key.rb +4 -0
- data/lib/schleuder/list.rb +7 -4
- data/lib/schleuder/mail/encrypted_part.rb +14 -0
- data/lib/schleuder/mail/gpg.rb +15 -0
- data/lib/schleuder/mail/message.rb +70 -30
- data/lib/schleuder/plugins/key_management.rb +32 -7
- data/lib/schleuder/plugins/subscription_management.rb +70 -3
- data/lib/schleuder/runner.rb +19 -8
- data/lib/schleuder/subscription.rb +5 -9
- data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +96 -8
- data/locales/en.yml +102 -10
- metadata +48 -27
- data/lib/schleuder/errors/file_not_found.rb +0 -14
- data/lib/schleuder/errors/invalid_listname.rb +0 -13
- data/lib/schleuder/errors/list_exists.rb +0 -13
- data/lib/schleuder/errors/unknown_list_option.rb +0 -14
- data/lib/schleuder/filters/auth_filter.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 637d9f0b81f2cb7aaee30a9761eeb0f09f49d95649a00d735ce13ecd008abdad
|
4
|
+
data.tar.gz: ca41b305951ef8ac0fb7edfb42e87521cf1d2ce5e51be6ceaa3c57c933846067
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3b7f8cd46761314484df53f64ba3d929c75eccdfd4c7a2c7ce87d76720b6fa1025d59f370126b107b08a19b9916fe7e160563739a1058dc88a537acd744af212
|
7
|
+
data.tar.gz: 149620dc7fc6549391af197396b68c8cf42b990e037d2224e337db57af00f90edb293e23120a8f2f8156e40cbfadcd35bca1009d066ba2126f98c4b7c90ae29a
|
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Schleuder is a gpg-enabled mailing list manager with resending-capabilities. Sub
|
|
6
6
|
Version 3 of schleuder is a complete rewrite, which aims to be more robust, flexible, and internationalized. It
|
7
7
|
also provides an API for the optional web interface called [schleuder-web](https://0xacab.org/schleuder/schleuder-web).
|
8
8
|
|
9
|
-
For more details see <https://schleuder.
|
9
|
+
For more details see <https://schleuder.org/docs/>.
|
10
10
|
|
11
11
|
Requirements
|
12
12
|
------------
|
@@ -16,9 +16,9 @@ Requirements
|
|
16
16
|
* sqlite3
|
17
17
|
* openssl
|
18
18
|
|
19
|
-
*If you use Debian stretch or CentOS 7, please have a look at the [installation docs](https://schleuder.
|
19
|
+
*If you use Debian stretch or CentOS 7, please have a look at the [installation docs](https://schleuder.org/docs/#installation). We do provide packages for those platforms, which simplify the installation a lot.*
|
20
20
|
|
21
|
-
*🛈 A note regarding Ubuntu: All
|
21
|
+
*🛈 A note regarding Ubuntu: All Ubuntu versions up to and including 17.10 don't meet the requirements with their packaged versions of gnupg! To run Schleuder on Ubuntu you currently have to install a more recent version of gnupg manually. Only Ubuntu 18.04 ("bionic") provides modern enough versions of Schleuder's requirements.*
|
22
22
|
|
23
23
|
On systems that base on Debian 9 ("stretch"), install the dependencies via
|
24
24
|
|
@@ -47,25 +47,25 @@ Additionally these **rubygems** are required (will be installed automatically un
|
|
47
47
|
Installing Schleuder
|
48
48
|
------------
|
49
49
|
|
50
|
-
1. Download [the gem](https://schleuder.
|
50
|
+
1. Download [the gem](https://schleuder.org/download/schleuder-3.3.0.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.3.0.gem.sig) and verify:
|
51
51
|
```
|
52
52
|
gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
|
53
|
-
gpg --verify schleuder-3.
|
53
|
+
gpg --verify schleuder-3.3.0.gem.sig
|
54
54
|
```
|
55
55
|
|
56
56
|
2. If all went well install the gem:
|
57
57
|
```
|
58
|
-
gem install schleuder-3.
|
58
|
+
gem install schleuder-3.3.0.gem
|
59
59
|
```
|
60
60
|
|
61
61
|
3. Set up schleuder:
|
62
62
|
```
|
63
63
|
schleuder install
|
64
64
|
```
|
65
|
-
This creates
|
65
|
+
This creates necessary directories, copies example configs, etc. If you see errors about missing write permissions please follow the advice given.
|
66
66
|
|
67
67
|
|
68
|
-
For further information on setup and configuration please read <https://schleuder.
|
68
|
+
For further information on setup and configuration please read <https://schleuder.org/docs/#setup>.
|
69
69
|
|
70
70
|
|
71
71
|
Command line usage
|
@@ -92,7 +92,7 @@ manage lists from the command line.
|
|
92
92
|
Optionally consider installing
|
93
93
|
[schleuder-web](https://0xacab.org/schleuder/schleuder-web), the web
|
94
94
|
interface for schleuder. It enables list-admins to manage their lists through
|
95
|
-
the web instead of using [request-keywords](https://schleuder.
|
95
|
+
the web instead of using [request-keywords](https://schleuder.org/docs/#subscription-and-key-management).
|
96
96
|
|
97
97
|
|
98
98
|
|
@@ -112,6 +112,10 @@ To execute the test suite run:
|
|
112
112
|
|
113
113
|
bundle exec rspec
|
114
114
|
|
115
|
+
Please note: Some of the specs use 'pgrep'. On systems that base on Debian 9 ("stretch") install it via
|
116
|
+
|
117
|
+
apt-get install procps
|
118
|
+
|
115
119
|
We are working on extendig the test coverage.
|
116
120
|
|
117
121
|
Contributing
|
@@ -120,6 +124,12 @@ Contributing
|
|
120
124
|
Please see [CONTRIBUTING.md](CONTRIBUTING.md).
|
121
125
|
|
122
126
|
|
127
|
+
Mission statement
|
128
|
+
-----------------
|
129
|
+
|
130
|
+
Please see [MISSION_STATEMENT.md](MISSION_STATEMENT.md).
|
131
|
+
|
132
|
+
|
123
133
|
Code of Conduct
|
124
134
|
---------------
|
125
135
|
|
@@ -135,4 +145,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
|
|
135
145
|
Alternative Download
|
136
146
|
--------------------
|
137
147
|
|
138
|
-
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.
|
148
|
+
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.3.0.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.3.0.tar.gz.sig).
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require_relative "lib/#{project}.rb"
|
|
3
3
|
|
4
4
|
@version = Schleuder::VERSION
|
5
5
|
@tagname = "#{project}-#{@version}"
|
6
|
-
@gpguid = 'schleuder
|
6
|
+
@gpguid = 'team@schleuder.org'
|
7
7
|
@filename_gem = "#{@tagname}.gem"
|
8
8
|
@filename_tarball = "#{@tagname}.tar.gz"
|
9
9
|
|
@@ -52,6 +52,7 @@ task :new_version => [
|
|
52
52
|
:sign_gem,
|
53
53
|
:build_tarball,
|
54
54
|
:sign_tarball,
|
55
|
+
:ensure_permissions,
|
55
56
|
:git_tag
|
56
57
|
] do
|
57
58
|
end
|
@@ -78,7 +79,7 @@ end
|
|
78
79
|
|
79
80
|
desc "Commit changes as new version"
|
80
81
|
task :git_commit do
|
81
|
-
`git commit -m "Version #{@version}
|
82
|
+
`git commit -m "Version #{@version}"`
|
82
83
|
end
|
83
84
|
|
84
85
|
desc 'Build, sign and commit a gem-file.'
|
@@ -88,12 +89,22 @@ end
|
|
88
89
|
|
89
90
|
desc 'OpenPGP-sign gem and tarball'
|
90
91
|
task :sign_tarball do
|
91
|
-
`gpg -u #{@gpguid} -b #{@
|
92
|
+
`gpg -u #{@gpguid} -b #{@filename_tarball}`
|
92
93
|
end
|
93
94
|
|
94
95
|
desc 'OpenPGP-sign gem'
|
95
96
|
task :sign_gem do
|
96
|
-
`gpg -u #{@gpguid} -b #{@
|
97
|
+
`gpg -u #{@gpguid} -b #{@filename_gem}`
|
98
|
+
end
|
99
|
+
|
100
|
+
desc 'Ensure download-files have correct permissions'
|
101
|
+
task :ensure_permissions do
|
102
|
+
File.chmod(0644, *Dir.glob("#{@tagname}*"))
|
103
|
+
end
|
104
|
+
|
105
|
+
desc 'Upload download-files (gem, tarball, signatures) to schleuder.org.'
|
106
|
+
task :upload_files do
|
107
|
+
puts `echo "put -p #{@tagname}* www/download/" | sftp schleuder.org@ftp.schleuder.org 2>&1`
|
97
108
|
end
|
98
109
|
|
99
110
|
desc 'Publish gem-file to rubygems.org'
|
@@ -114,10 +125,7 @@ end
|
|
114
125
|
|
115
126
|
desc 'Describe manual release-tasks'
|
116
127
|
task :website do
|
117
|
-
puts "Please
|
118
|
-
* Update changelog.
|
119
|
-
* Publish release-announcement.
|
120
|
-
"
|
128
|
+
puts "Please remember to publish the release-notes on the website and on schleuder-announce."
|
121
129
|
end
|
122
130
|
|
123
131
|
desc 'Check if version-tag already exists'
|
data/bin/schleuder
CHANGED
@@ -8,7 +8,8 @@ trap("INT") { exit 1 }
|
|
8
8
|
|
9
9
|
|
10
10
|
begin
|
11
|
-
|
11
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
12
|
+
require 'schleuder/cli'
|
12
13
|
Schleuder::Cli.start
|
13
14
|
rescue => exc
|
14
15
|
$stderr.puts "Technical Error: #{exc}\n#{exc.backtrace.first}"
|
data/bin/schleuder-api-daemon
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
class AddSigEncToHeadersToMetaDefaults < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
change_column_default :lists, :headers_to_meta, '["from", "to", "cc", "date", "sig", "enc"]'
|
4
|
+
list_klass = create_list_klass
|
5
|
+
list_klass.reset_column_information
|
6
|
+
list_klass.find_each do |list|
|
7
|
+
if (list.headers_to_meta & ['sig', 'enc']).empty?
|
8
|
+
list.update(headers_to_meta: list.headers_to_meta + ['sig', 'enc'])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
change_column_default :lists, :headers_to_meta, '["from", "to", "cc", "date"]'
|
15
|
+
list_klass = create_list_klass
|
16
|
+
list_klass.reset_column_information
|
17
|
+
list_klass.find_each do |list|
|
18
|
+
list.update(headers_to_meta: list.headers_to_meta - ['enc','sig'])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_list_klass
|
23
|
+
# Use a temporary class-definition to be independent of the
|
24
|
+
# complexities of the actual class.
|
25
|
+
Class.new(ActiveRecord::Base) do
|
26
|
+
self.table_name = 'lists'
|
27
|
+
self.serialize :headers_to_meta, JSON
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20180110203100) do
|
15
15
|
|
16
16
|
create_table "lists", force: :cascade do |t|
|
17
17
|
t.datetime "created_at"
|
@@ -24,7 +24,7 @@ ActiveRecord::Schema.define(version: 20170713215059) do
|
|
24
24
|
t.string "subject_prefix_out", limit: 255, default: ""
|
25
25
|
t.string "openpgp_header_preference", limit: 255, default: "signencrypt"
|
26
26
|
t.text "public_footer", default: ""
|
27
|
-
t.text "headers_to_meta", default: "[\"from\", \"to\", \"date\", \"
|
27
|
+
t.text "headers_to_meta", default: "[\"from\", \"to\", \"cc\", \"date\", \"sig\", \"enc\"]"
|
28
28
|
t.text "bounces_drop_on_headers", default: "{\"x-spam-flag\":\"yes\"}"
|
29
29
|
t.text "keywords_admin_only", default: "[\"subscribe\", \"unsubscribe\", \"delete-key\"]"
|
30
30
|
t.text "keywords_admin_notify", default: "[\"add-key\"]"
|
data/etc/list-defaults.yml
CHANGED
@@ -26,7 +26,7 @@ receive_authenticated_only: false
|
|
26
26
|
# NOTE: This is a very weak restriction mechanism on which you should not rely,
|
27
27
|
# as sending addresses can easily be faked! We recommend you to rather
|
28
28
|
# rely on the `receive_authenticated_only` option. Setting the
|
29
|
-
# `receive_authenticated_only` option to true, will
|
29
|
+
# `receive_authenticated_only` option to true, will authenticate senders
|
30
30
|
# based on the signature on the mail, which is the strongest
|
31
31
|
# authentication mechanism you can get.
|
32
32
|
# This option could be useful, if you would like to have a closed
|
@@ -43,6 +43,8 @@ headers_to_meta:
|
|
43
43
|
- to
|
44
44
|
- cc
|
45
45
|
- date
|
46
|
+
- sig
|
47
|
+
- enc
|
46
48
|
|
47
49
|
# Preserve the Message-IDs (In-Reply-To, References) from the incoming email.
|
48
50
|
# This setting can lead to information leakage, as replies are connectable
|
@@ -97,7 +99,7 @@ include_list_headers: true
|
|
97
99
|
# Include OpenPGP-Header into emails?
|
98
100
|
include_openpgp_header: true
|
99
101
|
|
100
|
-
#
|
102
|
+
# Preferred way to receive emails to note in OpenPGP-Header
|
101
103
|
# ('sign'|'encrypt'|'signencrypt'|'unprotected'|'none')
|
102
104
|
openpgp_header_preference: signencrypt
|
103
105
|
|
data/etc/schleuder.yml
CHANGED
@@ -7,6 +7,17 @@ listlogs_dir: /var/lib/schleuder/lists
|
|
7
7
|
# Schleuder reads plugins also from this directory.
|
8
8
|
plugins_dir: /etc/schleuder/plugins
|
9
9
|
|
10
|
+
# Schleuder reads filters also from this directory path,
|
11
|
+
# in the specific pre_decryption or post_decryption subdirectory.
|
12
|
+
# Filter files must follow the following convention for the
|
13
|
+
# filename: \d+_a_name.rb
|
14
|
+
# Where \d+ is any number, that defines the place in the
|
15
|
+
# list of filters and a_name must match the method name
|
16
|
+
# of the filter.
|
17
|
+
# The built-in filters are using round numbers for their
|
18
|
+
# positioning within the list. Increased by ten.
|
19
|
+
filters_dir: /usr/local/lib/schleuder/filters
|
20
|
+
|
10
21
|
# How verbose should Schleuder log to syslog? (list-specific messages are written to the list's log-file).
|
11
22
|
log_level: warn
|
12
23
|
|
data/lib/schleuder-api-daemon.rb
CHANGED
@@ -3,12 +3,18 @@
|
|
3
3
|
# Make sinatra use production as default-environment
|
4
4
|
ENV['RACK_ENV'] ||= 'production'
|
5
5
|
|
6
|
+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
6
7
|
require 'sinatra/base'
|
7
8
|
require 'sinatra/json'
|
8
9
|
require 'sinatra/namespace'
|
9
10
|
require 'thin'
|
10
|
-
|
11
|
-
|
11
|
+
require 'schleuder'
|
12
|
+
require 'schleuder-api-daemon/routes/status'
|
13
|
+
require 'schleuder-api-daemon/routes/version'
|
14
|
+
require 'schleuder-api-daemon/routes/list'
|
15
|
+
require 'schleuder-api-daemon/routes/subscription'
|
16
|
+
require 'schleuder-api-daemon/routes/key'
|
17
|
+
require 'schleuder-api-daemon/helpers/schleuder-api-daemon-helper'
|
12
18
|
|
13
19
|
%w[tls_cert_file tls_key_file].each do |config_key|
|
14
20
|
path = Conf.api[config_key]
|
@@ -19,7 +25,7 @@ require_relative '../lib/schleuder.rb'
|
|
19
25
|
end
|
20
26
|
|
21
27
|
class SchleuderApiDaemon < Sinatra::Base
|
22
|
-
|
28
|
+
helpers SchleuderApiDaemonHelper
|
23
29
|
|
24
30
|
configure do
|
25
31
|
set :server, :thin
|
@@ -57,357 +63,6 @@ class SchleuderApiDaemon < Sinatra::Base
|
|
57
63
|
'Not found'
|
58
64
|
end
|
59
65
|
|
60
|
-
get '/status.json' do
|
61
|
-
json status: :ok
|
62
|
-
end
|
63
|
-
|
64
|
-
get '/version.json' do
|
65
|
-
json version: Schleuder::VERSION
|
66
|
-
end
|
67
|
-
|
68
|
-
helpers do
|
69
|
-
def valid_credentials?
|
70
|
-
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
71
|
-
if @auth.provided? && @auth.basic? && @auth.credentials.present?
|
72
|
-
username, api_key = @auth.credentials
|
73
|
-
username == 'schleuder' && Conf.api_valid_api_keys.include?(api_key)
|
74
|
-
else
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def authenticate!
|
80
|
-
# Be careful to use path_info() — it can be changed by other filters!
|
81
|
-
return if request.path_info == '/status.json'
|
82
|
-
if ! valid_credentials?
|
83
|
-
headers['WWW-Authenticate'] = 'Basic realm="Schleuder API Daemon"'
|
84
|
-
halt 401, "Not authorized\n"
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def list(id_or_email=nil)
|
89
|
-
if id_or_email.blank?
|
90
|
-
if params[:list_id].present?
|
91
|
-
id_or_email = params[:list_id]
|
92
|
-
else
|
93
|
-
client_error "Parameter list_id is required"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
if is_an_integer?(id_or_email)
|
97
|
-
list = List.where(id: id_or_email).first
|
98
|
-
else
|
99
|
-
# list_id is actually an email address
|
100
|
-
list = List.where(email: id_or_email).first
|
101
|
-
end
|
102
|
-
list || halt(404)
|
103
|
-
end
|
104
|
-
|
105
|
-
def subscription(id_or_email)
|
106
|
-
if is_an_integer?(id_or_email)
|
107
|
-
sub = Subscription.where(id: id_or_email.to_i).first
|
108
|
-
else
|
109
|
-
# Email
|
110
|
-
if params[:list_id].blank?
|
111
|
-
client_error "Parameter list_id is required when using email as identifier for subscriptions."
|
112
|
-
else
|
113
|
-
sub = list.subscriptions.where(email: id_or_email).first
|
114
|
-
end
|
115
|
-
end
|
116
|
-
sub || halt(404)
|
117
|
-
end
|
118
|
-
|
119
|
-
def requested_list_id
|
120
|
-
# ActiveResource doesn't want to use query-params with create(), so here
|
121
|
-
# list_id might be included in the request-body.
|
122
|
-
params['list_id'] || parsed_body['list_id'] || client_error('Need list_id')
|
123
|
-
end
|
124
|
-
|
125
|
-
def parsed_body
|
126
|
-
@parsed_body ||= begin
|
127
|
-
b = JSON.parse(request.body.read)
|
128
|
-
logger.debug "parsed body: #{b.inspect}"
|
129
|
-
b
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def server_error(msg)
|
134
|
-
logger.warn msg
|
135
|
-
halt(500, json(error: msg))
|
136
|
-
end
|
137
|
-
|
138
|
-
# TODO: unify error messages. This method currently sends an old error format. See <https://github.com/rails/activeresource/blob/d6a5186/lib/active_resource/base.rb#L227>.
|
139
|
-
def client_error(obj_or_msg, http_code=400)
|
140
|
-
text = case obj_or_msg
|
141
|
-
when String, Symbol
|
142
|
-
obj_or_msg.to_s
|
143
|
-
when ActiveRecord::Base
|
144
|
-
obj_or_msg.errors.full_messages
|
145
|
-
else
|
146
|
-
obj_or_msg
|
147
|
-
end
|
148
|
-
logger.error "Sending error to client: #{text.inspect}"
|
149
|
-
halt(http_code, json(errors: text))
|
150
|
-
end
|
151
|
-
|
152
|
-
# poor persons type casting
|
153
|
-
def cast_param_values
|
154
|
-
params.each do |key, value|
|
155
|
-
params[key] =
|
156
|
-
case value
|
157
|
-
when 'true' then true
|
158
|
-
when 'false' then false
|
159
|
-
when '0' then 0
|
160
|
-
when is_an_integer?(value) then value.to_i
|
161
|
-
else value
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def key_to_hash(key, include_keydata=false)
|
167
|
-
hash = {
|
168
|
-
fingerprint: key.fingerprint,
|
169
|
-
email: key.email,
|
170
|
-
expiry: key.expires,
|
171
|
-
generated_at: key.generated_at,
|
172
|
-
primary_uid: key.primary_uid.uid,
|
173
|
-
oneline: key.oneline,
|
174
|
-
trust_issues: key.usability_issue
|
175
|
-
}
|
176
|
-
if include_keydata
|
177
|
-
hash[:description] = key.to_s
|
178
|
-
hash[:ascii] = key.armored
|
179
|
-
end
|
180
|
-
hash
|
181
|
-
end
|
182
|
-
|
183
|
-
def set_x_messages(messages)
|
184
|
-
if messages.present?
|
185
|
-
headers 'X-Messages' => Array(messages).join(' // ').gsub(/\n/, ' // ')
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
def find_key_material
|
190
|
-
key_material = parsed_body['key_material'].presence
|
191
|
-
# By convention key_material is either ASCII or base64-encoded.
|
192
|
-
if key_material && ! key_material.match('BEGIN PGP')
|
193
|
-
key_material = Base64.decode64(key_material)
|
194
|
-
end
|
195
|
-
key_material
|
196
|
-
end
|
197
|
-
|
198
|
-
def find_attributes_from_body(attribs)
|
199
|
-
Array(attribs).inject({}) do |memo, attrib|
|
200
|
-
if parsed_body.has_key?(attrib)
|
201
|
-
memo[attrib] = parsed_body[attrib]
|
202
|
-
end
|
203
|
-
memo
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def is_an_integer?(input)
|
208
|
-
input.to_s.match(/^[0-9]+$/).present?
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
namespace '/lists' do
|
213
|
-
get '.json' do
|
214
|
-
json List.all, include: :subscriptions
|
215
|
-
end
|
216
|
-
|
217
|
-
post '.json' do
|
218
|
-
listname = parsed_body['email']
|
219
|
-
fingerprint = parsed_body['fingerprint']
|
220
|
-
adminaddress = parsed_body['adminaddress']
|
221
|
-
adminfingerprint = parsed_body['adminfingerprint']
|
222
|
-
adminkey = parsed_body['adminkey']
|
223
|
-
list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminfingerprint, adminkey).run
|
224
|
-
if list.nil?
|
225
|
-
client_error(messages, 422)
|
226
|
-
elsif ! list.valid?
|
227
|
-
client_error(list, 422)
|
228
|
-
else
|
229
|
-
set_x_messages(messages)
|
230
|
-
body json(list)
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
get '/configurable_attributes.json' do
|
235
|
-
json(List.configurable_attributes) + "\n"
|
236
|
-
end
|
237
|
-
|
238
|
-
post '/send_list_key_to_subscriptions.json' do
|
239
|
-
json(result: list.send_list_key_to_subscriptions)
|
240
|
-
end
|
241
|
-
|
242
|
-
get '/new.json' do
|
243
|
-
json List.new
|
244
|
-
end
|
245
|
-
|
246
|
-
get '/:id.json' do |id|
|
247
|
-
json list(id)
|
248
|
-
end
|
249
|
-
|
250
|
-
put '/:id.json' do |id|
|
251
|
-
list = list(id)
|
252
|
-
if list.update(parsed_body)
|
253
|
-
204
|
254
|
-
else
|
255
|
-
client_error(list)
|
256
|
-
end
|
257
|
-
end
|
258
|
-
|
259
|
-
patch '/:id.json' do |id|
|
260
|
-
list = list(id)
|
261
|
-
if list.update(parsed_body)
|
262
|
-
204
|
263
|
-
else
|
264
|
-
client_error(list)
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
delete '/:id.json' do |id|
|
269
|
-
list = list(id)
|
270
|
-
if list.destroy
|
271
|
-
200
|
272
|
-
else
|
273
|
-
client_error(list)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
namespace '/subscriptions' do
|
279
|
-
get '.json' do
|
280
|
-
filterkeys = Subscription.configurable_attributes + [:list_id, :email]
|
281
|
-
filter = params.select do |param|
|
282
|
-
filterkeys.include?(param.to_sym)
|
283
|
-
end
|
284
|
-
|
285
|
-
logger.debug "Subscription filter: #{filter.inspect}"
|
286
|
-
if filter['list_id'] && ! is_an_integer?(filter['list_id'])
|
287
|
-
# Value is an email-address
|
288
|
-
if list = List.where(email: filter['list_id']).first
|
289
|
-
filter['list_id'] = list.id
|
290
|
-
else
|
291
|
-
status 404
|
292
|
-
return json(errors: 'No such list')
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
json Subscription.where(filter)
|
297
|
-
end
|
298
|
-
|
299
|
-
post '.json' do
|
300
|
-
begin
|
301
|
-
list = list(requested_list_id)
|
302
|
-
# We don't have to care about nil-values, subscribe() does that for us.
|
303
|
-
sub, msgs = list.subscribe(
|
304
|
-
parsed_body['email'],
|
305
|
-
parsed_body['fingerprint'],
|
306
|
-
parsed_body['admin'],
|
307
|
-
parsed_body['delivery_enabled'],
|
308
|
-
find_key_material
|
309
|
-
)
|
310
|
-
set_x_messages(msgs)
|
311
|
-
logger.debug "subcription: #{sub.inspect}"
|
312
|
-
if sub.valid?
|
313
|
-
logger.debug "Subscribed: #{sub.inspect}"
|
314
|
-
# TODO: why redirect instead of respond with result?
|
315
|
-
redirect to("/subscriptions/#{sub.id}.json"), 201
|
316
|
-
else
|
317
|
-
client_error(sub, 422)
|
318
|
-
end
|
319
|
-
rescue ActiveRecord::RecordNotUnique
|
320
|
-
logger.error "Already subscribed"
|
321
|
-
status 422
|
322
|
-
json errors: {email: ['is already subscribed']}
|
323
|
-
end
|
324
|
-
end
|
325
|
-
|
326
|
-
get '/configurable_attributes.json' do
|
327
|
-
json(Subscription.configurable_attributes) + "\n"
|
328
|
-
end
|
329
|
-
|
330
|
-
get '/new.json' do
|
331
|
-
json Subscription.new
|
332
|
-
end
|
333
|
-
|
334
|
-
get '/:id.json' do |id|
|
335
|
-
json subscription(id)
|
336
|
-
end
|
337
|
-
|
338
|
-
put '/:id.json' do |id|
|
339
|
-
sub = subscription(id)
|
340
|
-
list = sub.list
|
341
|
-
args = find_attributes_from_body(%w[email fingerprint admin delivery_enabled])
|
342
|
-
fingerprint, messages = list.import_key_and_find_fingerprint(find_key_material)
|
343
|
-
set_x_messages(messages)
|
344
|
-
# For an already existing subscription, only update fingerprint if a
|
345
|
-
# new one has been selected from the upload.
|
346
|
-
if fingerprint.present?
|
347
|
-
args["fingerprint"] = fingerprint
|
348
|
-
end
|
349
|
-
if sub.update(args)
|
350
|
-
200
|
351
|
-
else
|
352
|
-
client_error(sub, 422)
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
patch '/:id.json' do |id|
|
357
|
-
sub = subscription(id)
|
358
|
-
if sub.update(parsed_body)
|
359
|
-
200
|
360
|
-
else
|
361
|
-
client_error(sub)
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
delete '/:id.json' do |id|
|
366
|
-
if sub = subscription(id).destroy
|
367
|
-
200
|
368
|
-
else
|
369
|
-
client_error(sub)
|
370
|
-
end
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
namespace '/keys' do
|
375
|
-
get '.json' do
|
376
|
-
keys = list.keys.sort_by(&:email).map do |key|
|
377
|
-
key_to_hash(key)
|
378
|
-
end
|
379
|
-
json keys
|
380
|
-
end
|
381
|
-
|
382
|
-
post '.json' do
|
383
|
-
input = parsed_body['keymaterial']
|
384
|
-
if ! input.match('BEGIN PGP')
|
385
|
-
input = Base64.decode64(input)
|
386
|
-
end
|
387
|
-
json list(requested_list_id).import_key(input)
|
388
|
-
end
|
389
|
-
|
390
|
-
get '/check_keys.json' do
|
391
|
-
json result: list.check_keys
|
392
|
-
end
|
393
|
-
|
394
|
-
get '/:fingerprint.json' do |fingerprint|
|
395
|
-
if key = list.key(fingerprint)
|
396
|
-
json key_to_hash(key, true)
|
397
|
-
else
|
398
|
-
404
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
delete '/:fingerprint.json' do |fingerprint|
|
403
|
-
if list.delete_key(fingerprint)
|
404
|
-
200
|
405
|
-
else
|
406
|
-
404
|
407
|
-
end
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
66
|
def self.run!
|
412
67
|
super do |server|
|
413
68
|
server.ssl = true
|