schleuder 3.2.2 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|