schleuder 2.2.4 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +138 -0
  3. data/Rakefile +136 -0
  4. data/bin/pinentry-clearpassphrase +72 -0
  5. data/bin/schleuder +9 -89
  6. data/bin/schleuder-api-daemon +4 -0
  7. data/db/migrate/20140501103532_create_lists.rb +39 -0
  8. data/db/migrate/20140501112859_create_subscriptions.rb +21 -0
  9. data/db/migrate/201508092100_add_language_to_lists.rb +11 -0
  10. data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +8 -0
  11. data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +11 -0
  12. data/db/migrate/201508141727_change_send_encrypted_only_default.rb +8 -0
  13. data/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb +11 -0
  14. data/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb +14 -0
  15. data/db/migrate/201508261815_strip_gpg_passphrase.rb +11 -0
  16. data/db/migrate/201508261827_remove_default_mime.rb +9 -0
  17. data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +8 -0
  18. data/db/migrate/20170713215059_add_internal_footer_to_list.rb +11 -0
  19. data/db/schema.rb +62 -0
  20. data/etc/init.d/schleuder-api-daemon +87 -0
  21. data/etc/list-defaults.yml +123 -0
  22. data/etc/postfix/schleuder_sqlite.cf +28 -0
  23. data/etc/schleuder-api-daemon.service +10 -0
  24. data/etc/schleuder.cron.weekly +6 -0
  25. data/etc/schleuder.yml +61 -0
  26. data/lib/schleuder-api-daemon.rb +420 -0
  27. data/lib/schleuder.rb +81 -47
  28. data/lib/schleuder/cli.rb +334 -0
  29. data/lib/schleuder/cli/cert.rb +24 -0
  30. data/lib/schleuder/cli/schleuder_cert_manager.rb +84 -0
  31. data/lib/schleuder/cli/subcommand_fix.rb +11 -0
  32. data/lib/schleuder/conf.rb +131 -0
  33. data/lib/schleuder/errors/active_model_error.rb +15 -0
  34. data/lib/schleuder/errors/base.rb +17 -0
  35. data/lib/schleuder/errors/decryption_failed.rb +16 -0
  36. data/lib/schleuder/errors/fatal_error.rb +13 -0
  37. data/lib/schleuder/errors/file_not_found.rb +14 -0
  38. data/lib/schleuder/errors/invalid_listname.rb +13 -0
  39. data/lib/schleuder/errors/key_adduid_failed.rb +13 -0
  40. data/lib/schleuder/errors/key_generation_failed.rb +16 -0
  41. data/lib/schleuder/errors/keyword_admin_only.rb +13 -0
  42. data/lib/schleuder/errors/list_exists.rb +13 -0
  43. data/lib/schleuder/errors/list_not_found.rb +14 -0
  44. data/lib/schleuder/errors/list_property_missing.rb +14 -0
  45. data/lib/schleuder/errors/listdir_problem.rb +16 -0
  46. data/lib/schleuder/errors/loading_list_settings_failed.rb +14 -0
  47. data/lib/schleuder/errors/message_empty.rb +14 -0
  48. data/lib/schleuder/errors/message_not_from_admin.rb +13 -0
  49. data/lib/schleuder/errors/message_sender_not_subscribed.rb +13 -0
  50. data/lib/schleuder/errors/message_too_big.rb +14 -0
  51. data/lib/schleuder/errors/message_unauthenticated.rb +13 -0
  52. data/lib/schleuder/errors/message_unencrypted.rb +13 -0
  53. data/lib/schleuder/errors/message_unsigned.rb +13 -0
  54. data/lib/schleuder/errors/standard_error.rb +5 -0
  55. data/lib/schleuder/errors/too_many_keys.rb +17 -0
  56. data/lib/schleuder/errors/unknown_list_option.rb +14 -0
  57. data/lib/schleuder/filters/auth_filter.rb +39 -0
  58. data/lib/schleuder/filters/bounces_filter.rb +12 -0
  59. data/lib/schleuder/filters/forward_filter.rb +17 -0
  60. data/lib/schleuder/filters/forward_incoming.rb +13 -0
  61. data/lib/schleuder/filters/hotmail_message_filter.rb +25 -0
  62. data/lib/schleuder/filters/max_message_size.rb +14 -0
  63. data/lib/schleuder/filters/request_filter.rb +26 -0
  64. data/lib/schleuder/filters/send_key_filter.rb +20 -0
  65. data/lib/schleuder/filters/strip_alternative_filter.rb +21 -0
  66. data/lib/schleuder/filters_runner.rb +83 -0
  67. data/lib/schleuder/gpgme/ctx.rb +274 -0
  68. data/lib/schleuder/gpgme/import_status.rb +27 -0
  69. data/lib/schleuder/gpgme/key.rb +212 -0
  70. data/lib/schleuder/gpgme/sub_key.rb +13 -0
  71. data/lib/schleuder/gpgme/user_id.rb +22 -0
  72. data/lib/schleuder/list.rb +318 -127
  73. data/lib/schleuder/list_builder.rb +139 -0
  74. data/lib/schleuder/listlogger.rb +31 -0
  75. data/lib/schleuder/logger.rb +23 -0
  76. data/lib/schleuder/logger_notifications.rb +69 -0
  77. data/lib/schleuder/mail/message.rb +482 -0
  78. data/lib/schleuder/mail/parts_list.rb +9 -0
  79. data/lib/schleuder/plugin_runners/base.rb +91 -0
  80. data/lib/schleuder/plugin_runners/list_plugins_runner.rb +24 -0
  81. data/lib/schleuder/plugin_runners/request_plugins_runner.rb +27 -0
  82. data/lib/schleuder/plugins/attach_listkey.rb +17 -0
  83. data/lib/schleuder/plugins/get_version.rb +7 -0
  84. data/lib/schleuder/plugins/key_management.rb +113 -0
  85. data/lib/schleuder/plugins/list_management.rb +15 -0
  86. data/lib/schleuder/plugins/resend.rb +196 -0
  87. data/lib/schleuder/plugins/sign_this.rb +46 -0
  88. data/lib/schleuder/plugins/subscription_management.rb +140 -0
  89. data/lib/schleuder/runner.rb +130 -0
  90. data/lib/schleuder/subscription.rb +98 -0
  91. data/lib/schleuder/validators/boolean_validator.rb +7 -0
  92. data/lib/schleuder/validators/email_validator.rb +7 -0
  93. data/lib/schleuder/validators/fingerprint_validator.rb +7 -0
  94. data/lib/schleuder/validators/greater_than_zero_validator.rb +7 -0
  95. data/lib/schleuder/validators/no_line_breaks_validator.rb +7 -0
  96. data/lib/schleuder/version.rb +1 -1
  97. data/locales/de.yml +179 -0
  98. data/locales/en.yml +179 -0
  99. metadata +305 -108
  100. checksums.yaml.gz.sig +0 -3
  101. data.tar.gz.sig +0 -2
  102. data/LICENSE +0 -339
  103. data/README +0 -32
  104. data/bin/schleuder-fix-gem-dependencies +0 -37
  105. data/bin/schleuder-init-setup +0 -37
  106. data/bin/schleuder-migrate-v2.1-to-v2.2 +0 -225
  107. data/bin/schleuder-newlist +0 -413
  108. data/contrib/check-expired-keys.rb +0 -60
  109. data/contrib/mutt-schleuder-colors.rc +0 -10
  110. data/contrib/mutt-schleuder-resend.vim +0 -24
  111. data/contrib/smtpserver.rb +0 -76
  112. data/ext/default-list.conf +0 -149
  113. data/ext/default-members.conf +0 -7
  114. data/ext/list.conf.example +0 -14
  115. data/ext/schleuder.conf +0 -64
  116. data/lib/schleuder/archiver.rb +0 -46
  117. data/lib/schleuder/crypt.rb +0 -210
  118. data/lib/schleuder/errors.rb +0 -5
  119. data/lib/schleuder/list_config.rb +0 -146
  120. data/lib/schleuder/log/listlogger.rb +0 -57
  121. data/lib/schleuder/log/outputter/emailoutputter.rb +0 -120
  122. data/lib/schleuder/log/outputter/metaemailoutputter.rb +0 -50
  123. data/lib/schleuder/log/schleuderlogger.rb +0 -34
  124. data/lib/schleuder/mail.rb +0 -873
  125. data/lib/schleuder/mailer.rb +0 -26
  126. data/lib/schleuder/member.rb +0 -69
  127. data/lib/schleuder/plugin.rb +0 -54
  128. data/lib/schleuder/processor.rb +0 -363
  129. data/lib/schleuder/schleuder_config.rb +0 -75
  130. data/lib/schleuder/storage.rb +0 -84
  131. data/lib/schleuder/utils.rb +0 -80
  132. data/man/schleuder-newlist.8 +0 -174
  133. data/man/schleuder.8 +0 -416
  134. data/plugins/README +0 -20
  135. data/plugins/manage_keys_plugin.rb +0 -113
  136. data/plugins/manage_members_plugin.rb +0 -156
  137. data/plugins/manage_self_plugin.rb +0 -26
  138. data/plugins/resend_plugin.rb +0 -35
  139. data/plugins/sign_this_plugin.rb +0 -14
  140. data/plugins/version_plugin.rb +0 -12
  141. 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@', '@')
@@ -0,0 +1,10 @@
1
+ [Unit]
2
+ Description=Schleuder API daemon
3
+ After=local-fs.target network.target
4
+
5
+ [Service]
6
+ ExecStart=/usr/local/bin/schleuder-api-daemon
7
+ User=schleuder
8
+
9
+ [Install]
10
+ WantedBy=multi-user.target
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ test -x /usr/local/bin/schleuder || exit 0
4
+
5
+ su -s /bin/sh schleuder -c "/usr/local/bin/schleuder refresh_keys"
6
+ su -s /bin/sh schleuder -c "/usr/local/bin/schleuder check_keys"
@@ -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