schleuder 2.2.4 → 3.2.2
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 +5 -5
- data/README.md +138 -0
- data/Rakefile +136 -0
- data/bin/pinentry-clearpassphrase +72 -0
- data/bin/schleuder +9 -89
- data/bin/schleuder-api-daemon +4 -0
- data/db/migrate/20140501103532_create_lists.rb +39 -0
- data/db/migrate/20140501112859_create_subscriptions.rb +21 -0
- data/db/migrate/201508092100_add_language_to_lists.rb +11 -0
- data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +8 -0
- data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +11 -0
- data/db/migrate/201508141727_change_send_encrypted_only_default.rb +8 -0
- data/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb +11 -0
- data/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb +14 -0
- data/db/migrate/201508261815_strip_gpg_passphrase.rb +11 -0
- data/db/migrate/201508261827_remove_default_mime.rb +9 -0
- data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +8 -0
- data/db/migrate/20170713215059_add_internal_footer_to_list.rb +11 -0
- data/db/schema.rb +62 -0
- data/etc/init.d/schleuder-api-daemon +87 -0
- data/etc/list-defaults.yml +123 -0
- data/etc/postfix/schleuder_sqlite.cf +28 -0
- data/etc/schleuder-api-daemon.service +10 -0
- data/etc/schleuder.cron.weekly +6 -0
- data/etc/schleuder.yml +61 -0
- data/lib/schleuder-api-daemon.rb +420 -0
- data/lib/schleuder.rb +81 -47
- data/lib/schleuder/cli.rb +334 -0
- data/lib/schleuder/cli/cert.rb +24 -0
- data/lib/schleuder/cli/schleuder_cert_manager.rb +84 -0
- data/lib/schleuder/cli/subcommand_fix.rb +11 -0
- data/lib/schleuder/conf.rb +131 -0
- data/lib/schleuder/errors/active_model_error.rb +15 -0
- data/lib/schleuder/errors/base.rb +17 -0
- data/lib/schleuder/errors/decryption_failed.rb +16 -0
- data/lib/schleuder/errors/fatal_error.rb +13 -0
- data/lib/schleuder/errors/file_not_found.rb +14 -0
- data/lib/schleuder/errors/invalid_listname.rb +13 -0
- data/lib/schleuder/errors/key_adduid_failed.rb +13 -0
- data/lib/schleuder/errors/key_generation_failed.rb +16 -0
- data/lib/schleuder/errors/keyword_admin_only.rb +13 -0
- data/lib/schleuder/errors/list_exists.rb +13 -0
- data/lib/schleuder/errors/list_not_found.rb +14 -0
- data/lib/schleuder/errors/list_property_missing.rb +14 -0
- data/lib/schleuder/errors/listdir_problem.rb +16 -0
- data/lib/schleuder/errors/loading_list_settings_failed.rb +14 -0
- data/lib/schleuder/errors/message_empty.rb +14 -0
- data/lib/schleuder/errors/message_not_from_admin.rb +13 -0
- data/lib/schleuder/errors/message_sender_not_subscribed.rb +13 -0
- data/lib/schleuder/errors/message_too_big.rb +14 -0
- data/lib/schleuder/errors/message_unauthenticated.rb +13 -0
- data/lib/schleuder/errors/message_unencrypted.rb +13 -0
- data/lib/schleuder/errors/message_unsigned.rb +13 -0
- data/lib/schleuder/errors/standard_error.rb +5 -0
- data/lib/schleuder/errors/too_many_keys.rb +17 -0
- data/lib/schleuder/errors/unknown_list_option.rb +14 -0
- data/lib/schleuder/filters/auth_filter.rb +39 -0
- data/lib/schleuder/filters/bounces_filter.rb +12 -0
- data/lib/schleuder/filters/forward_filter.rb +17 -0
- data/lib/schleuder/filters/forward_incoming.rb +13 -0
- data/lib/schleuder/filters/hotmail_message_filter.rb +25 -0
- data/lib/schleuder/filters/max_message_size.rb +14 -0
- data/lib/schleuder/filters/request_filter.rb +26 -0
- data/lib/schleuder/filters/send_key_filter.rb +20 -0
- data/lib/schleuder/filters/strip_alternative_filter.rb +21 -0
- data/lib/schleuder/filters_runner.rb +83 -0
- data/lib/schleuder/gpgme/ctx.rb +274 -0
- data/lib/schleuder/gpgme/import_status.rb +27 -0
- data/lib/schleuder/gpgme/key.rb +212 -0
- data/lib/schleuder/gpgme/sub_key.rb +13 -0
- data/lib/schleuder/gpgme/user_id.rb +22 -0
- data/lib/schleuder/list.rb +318 -127
- data/lib/schleuder/list_builder.rb +139 -0
- data/lib/schleuder/listlogger.rb +31 -0
- data/lib/schleuder/logger.rb +23 -0
- data/lib/schleuder/logger_notifications.rb +69 -0
- data/lib/schleuder/mail/message.rb +482 -0
- data/lib/schleuder/mail/parts_list.rb +9 -0
- data/lib/schleuder/plugin_runners/base.rb +91 -0
- data/lib/schleuder/plugin_runners/list_plugins_runner.rb +24 -0
- data/lib/schleuder/plugin_runners/request_plugins_runner.rb +27 -0
- data/lib/schleuder/plugins/attach_listkey.rb +17 -0
- data/lib/schleuder/plugins/get_version.rb +7 -0
- data/lib/schleuder/plugins/key_management.rb +113 -0
- data/lib/schleuder/plugins/list_management.rb +15 -0
- data/lib/schleuder/plugins/resend.rb +196 -0
- data/lib/schleuder/plugins/sign_this.rb +46 -0
- data/lib/schleuder/plugins/subscription_management.rb +140 -0
- data/lib/schleuder/runner.rb +130 -0
- data/lib/schleuder/subscription.rb +98 -0
- data/lib/schleuder/validators/boolean_validator.rb +7 -0
- data/lib/schleuder/validators/email_validator.rb +7 -0
- data/lib/schleuder/validators/fingerprint_validator.rb +7 -0
- data/lib/schleuder/validators/greater_than_zero_validator.rb +7 -0
- data/lib/schleuder/validators/no_line_breaks_validator.rb +7 -0
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +179 -0
- data/locales/en.yml +179 -0
- metadata +305 -108
- checksums.yaml.gz.sig +0 -3
- data.tar.gz.sig +0 -2
- data/LICENSE +0 -339
- data/README +0 -32
- data/bin/schleuder-fix-gem-dependencies +0 -37
- data/bin/schleuder-init-setup +0 -37
- data/bin/schleuder-migrate-v2.1-to-v2.2 +0 -225
- data/bin/schleuder-newlist +0 -413
- data/contrib/check-expired-keys.rb +0 -60
- data/contrib/mutt-schleuder-colors.rc +0 -10
- data/contrib/mutt-schleuder-resend.vim +0 -24
- data/contrib/smtpserver.rb +0 -76
- data/ext/default-list.conf +0 -149
- data/ext/default-members.conf +0 -7
- data/ext/list.conf.example +0 -14
- data/ext/schleuder.conf +0 -64
- data/lib/schleuder/archiver.rb +0 -46
- data/lib/schleuder/crypt.rb +0 -210
- data/lib/schleuder/errors.rb +0 -5
- data/lib/schleuder/list_config.rb +0 -146
- data/lib/schleuder/log/listlogger.rb +0 -57
- data/lib/schleuder/log/outputter/emailoutputter.rb +0 -120
- data/lib/schleuder/log/outputter/metaemailoutputter.rb +0 -50
- data/lib/schleuder/log/schleuderlogger.rb +0 -34
- data/lib/schleuder/mail.rb +0 -873
- data/lib/schleuder/mailer.rb +0 -26
- data/lib/schleuder/member.rb +0 -69
- data/lib/schleuder/plugin.rb +0 -54
- data/lib/schleuder/processor.rb +0 -363
- data/lib/schleuder/schleuder_config.rb +0 -75
- data/lib/schleuder/storage.rb +0 -84
- data/lib/schleuder/utils.rb +0 -80
- data/man/schleuder-newlist.8 +0 -174
- data/man/schleuder.8 +0 -416
- data/plugins/README +0 -20
- data/plugins/manage_keys_plugin.rb +0 -113
- data/plugins/manage_members_plugin.rb +0 -156
- data/plugins/manage_self_plugin.rb +0 -26
- data/plugins/resend_plugin.rb +0 -35
- data/plugins/sign_this_plugin.rb +0 -14
- data/plugins/version_plugin.rb +0 -12
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Use this as a table for postfix to select addresses that schleuder
|
|
2
|
+
# thinks belong to it. This is useful when
|
|
3
|
+
# smtpd_reject_unlisted_recipient = yes (which is the default for
|
|
4
|
+
# modern Postfix)
|
|
5
|
+
|
|
6
|
+
# For example, you might dedicate Postfix's "virtual" domains to
|
|
7
|
+
# schleuder with the following set of configs in main.cf:
|
|
8
|
+
#
|
|
9
|
+
# virtual_domains = lists.example.org
|
|
10
|
+
# virtual_transport = schleuder
|
|
11
|
+
# virtual_alias_maps = hash:/etc/postfix/virtual_aliases
|
|
12
|
+
# virtual_mailbox_maps = sqlite:/etc/postfix/schleuder_sqlite.cf
|
|
13
|
+
# schleuder_destination_recipient_limit = 1
|
|
14
|
+
|
|
15
|
+
# it is not recommended to use this table for more powerful
|
|
16
|
+
# configuration options (e.g. transport_maps) because it could give
|
|
17
|
+
# the schleuder user (which can write the given sqlite database) the
|
|
18
|
+
# power to change settings for for other mail handled by this Postfix
|
|
19
|
+
# instance.
|
|
20
|
+
|
|
21
|
+
dbpath = /var/lib/schleuder/db.sqlite
|
|
22
|
+
|
|
23
|
+
query = select 'present' from lists
|
|
24
|
+
where email = '%s'
|
|
25
|
+
or email = replace('%s', '-bounce@', '@')
|
|
26
|
+
or email = replace('%s', '-owner@', '@')
|
|
27
|
+
or email = replace('%s', '-request@', '@')
|
|
28
|
+
or email = replace('%s', '-sendkey@', '@')
|
data/etc/schleuder.yml
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Where are the list-directories stored (contain log-files and GnuPG-keyrings).
|
|
2
|
+
lists_dir: /var/lib/schleuder/lists
|
|
3
|
+
|
|
4
|
+
# Where to write list-logs. The actual log-file will be <lists_logs_base_dir>/<hostname>/<listname>/list.log.
|
|
5
|
+
listlogs_dir: /var/lib/schleuder/lists
|
|
6
|
+
|
|
7
|
+
# Schleuder reads plugins also from this directory.
|
|
8
|
+
plugins_dir: /etc/schleuder/plugins
|
|
9
|
+
|
|
10
|
+
# How verbose should Schleuder log to syslog? (list-specific messages are written to the list's log-file).
|
|
11
|
+
log_level: warn
|
|
12
|
+
|
|
13
|
+
# Which keyserver to refresh keys from (used by `schleuder refresh_keys`, meant
|
|
14
|
+
# to be run from cron weekly).
|
|
15
|
+
# If you have gnupg 2.1, we strongly suggest to use a hkps-keyserver:
|
|
16
|
+
#keyserver: hkps://hkps.pool.sks-keyservers.net
|
|
17
|
+
# If you have gnupg 2.1 and TOR running locally, use a onion-keyserver:
|
|
18
|
+
#keyserver: hkp://jirk5u4osbsr34t5.onion
|
|
19
|
+
# If you have an OS-wide defined keyserver, specify a blank value to have that
|
|
20
|
+
# one used:
|
|
21
|
+
#keyserver:
|
|
22
|
+
# The default works for all supported versions of gnupg:
|
|
23
|
+
keyserver: pool.sks-keyservers.net
|
|
24
|
+
|
|
25
|
+
# Who is maintaining the overall schleuder installation and should be
|
|
26
|
+
# notified about severe problems with lists.
|
|
27
|
+
# This address should be a postmaster-like account, especially it should
|
|
28
|
+
# not be another schleuder list.
|
|
29
|
+
# Is also used as an envelope sender of admin notifications.
|
|
30
|
+
superadmin: root@localhost
|
|
31
|
+
|
|
32
|
+
# For these options see documentation for ActionMailer::smtp_settings, e.g. <http://api.rubyonrails.org/classes/ActionMailer/Base.html>.
|
|
33
|
+
smtp_settings:
|
|
34
|
+
address: localhost
|
|
35
|
+
port: 25
|
|
36
|
+
#domain:
|
|
37
|
+
#enable_starttls_auto:
|
|
38
|
+
#openssl_verify_mode:
|
|
39
|
+
#authentication:
|
|
40
|
+
#user_name:
|
|
41
|
+
#password:
|
|
42
|
+
|
|
43
|
+
# The database to use. Unless you want to run the tests you only need the `production`-section.
|
|
44
|
+
database:
|
|
45
|
+
production:
|
|
46
|
+
adapter: 'sqlite3'
|
|
47
|
+
database: /var/lib/schleuder/db.sqlite
|
|
48
|
+
timeout: 5000
|
|
49
|
+
|
|
50
|
+
api:
|
|
51
|
+
host: localhost
|
|
52
|
+
port: 4443
|
|
53
|
+
# Certificate and key to use. You can create new ones with `schleuder cert generate`.
|
|
54
|
+
tls_cert_file: /etc/schleuder/schleuder-certificate.pem
|
|
55
|
+
tls_key_file: /etc/schleuder/schleuder-private-key.pem
|
|
56
|
+
# List of api_keys to allow access to the API.
|
|
57
|
+
# Example:
|
|
58
|
+
# valid_api_keys:
|
|
59
|
+
# - abcdef...
|
|
60
|
+
# - zyxwvu...
|
|
61
|
+
valid_api_keys:
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Make sinatra use production as default-environment
|
|
4
|
+
ENV['RACK_ENV'] ||= 'production'
|
|
5
|
+
|
|
6
|
+
require 'sinatra/base'
|
|
7
|
+
require 'sinatra/json'
|
|
8
|
+
require 'sinatra/namespace'
|
|
9
|
+
require 'thin'
|
|
10
|
+
require_relative '../lib/schleuder.rb'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
%w[tls_cert_file tls_key_file].each do |config_key|
|
|
14
|
+
path = Conf.api[config_key]
|
|
15
|
+
if ! File.readable?(path)
|
|
16
|
+
$stderr.puts "Error: '#{path}' is not a readable file (from #{config_key} in config)."
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class SchleuderApiDaemon < Sinatra::Base
|
|
22
|
+
register Sinatra::Namespace
|
|
23
|
+
|
|
24
|
+
configure do
|
|
25
|
+
set :server, :thin
|
|
26
|
+
set :port, Schleuder::Conf.api['port'] || 4443
|
|
27
|
+
set :bind, Schleuder::Conf.api['host'] || 'localhost'
|
|
28
|
+
if settings.development?
|
|
29
|
+
set :logging, Logger::DEBUG
|
|
30
|
+
else
|
|
31
|
+
set :logging, Logger::WARN
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
before do
|
|
36
|
+
authenticate!
|
|
37
|
+
cast_param_values
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
after do
|
|
41
|
+
# Return connection to pool after each request.
|
|
42
|
+
ActiveRecord::Base.connection.close
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
error do
|
|
46
|
+
exc = env['sinatra.error']
|
|
47
|
+
logger.error "Error: #{env['sinatra.error'].message}"
|
|
48
|
+
case exc
|
|
49
|
+
when Errno::EACCES
|
|
50
|
+
server_error(exc.message)
|
|
51
|
+
else
|
|
52
|
+
client_error(exc.to_s)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
error 404 do
|
|
57
|
+
'Not found'
|
|
58
|
+
end
|
|
59
|
+
|
|
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
|
+
def self.run!
|
|
412
|
+
super do |server|
|
|
413
|
+
server.ssl = true
|
|
414
|
+
server.ssl_options = {
|
|
415
|
+
:cert_chain_file => Conf.api['tls_cert_file'],
|
|
416
|
+
:private_key_file => Conf.api['tls_key_file']
|
|
417
|
+
}
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|