[DRE-commits] [schleuder] 42/52: New upstream version 3.0.0
Georg Faerber
georg-alioth-guest at moszumanska.debian.org
Mon Feb 6 11:21:22 UTC 2017
This is an automated email from the git hooks/post-receive script.
georg-alioth-guest pushed a commit to branch master
in repository schleuder.
commit 390fce646050e2419b043564a49a8a7d99715fc9
Author: Georg Faerber <georg at riseup.net>
Date: Thu Jan 26 00:14:39 2017 +0100
New upstream version 3.0.0
---
.travis.yml | 2 +-
CHANGELOG.md | 38 +++-
Gemfile.lock | 2 +-
README.md | 16 +-
Rakefile | 2 +-
bin/pinentry-clearpassphrase | 25 +--
bin/schleuder | 4 +
bin/schleuder-api-daemon | 56 ++---
etc/postfix/schleuder_sqlite.cf | 28 +++
etc/schleuder.yml | 12 +-
lib/schleuder/cli.rb | 20 +-
lib/schleuder/cli/cert.rb | 1 -
lib/schleuder/conf.rb | 12 +-
lib/schleuder/errors/keyword_admin_only.rb | 4 +-
lib/schleuder/gpgme/ctx.rb | 92 +++++++-
lib/schleuder/gpgme/key.rb | 107 ++++++---
lib/schleuder/list.rb | 40 ++--
lib/schleuder/list_builder.rb | 8 +
lib/schleuder/listlogger.rb | 3 +-
lib/schleuder/logger_notifications.rb | 10 +-
lib/schleuder/mail/message.rb | 15 +-
lib/schleuder/plugins/key_management.rb | 55 ++++-
lib/schleuder/plugins/subscription_management.rb | 26 ++-
lib/schleuder/plugins_runner.rb | 32 ++-
lib/schleuder/runner.rb | 4 +-
lib/schleuder/subscription.rb | 23 +-
lib/schleuder/version.rb | 2 +-
locales/de.yml | 30 ++-
locales/en.yml | 28 ++-
man/schleuder-api-daemon.8 | 27 +--
man/schleuder-api-daemon.8.ron | 14 +-
man/schleuder.8 | 12 +-
man/schleuder.8.ron | 10 +-
spec/fixtures/expired_key.txt | 29 +++
spec/fixtures/expired_key_extended.txt | 29 +++
spec/schleuder.yml | 1 +
spec/schleuder/integration/cli_spec.rb | 39 ++++
spec/schleuder/integration/keywords_spec.rb | 269 +++++++++++++++++++++++
spec/schleuder/runner_spec.rb | 37 +++-
spec/schleuder/unit/list_spec.rb | 2 +-
spec/schleuder/unit/subscription_spec.rb | 2 +-
spec/sks-mock.rb | 26 +++
spec/spec_helper.rb | 14 ++
43 files changed, 981 insertions(+), 227 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 86690b9..1fbbd69 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@ rvm:
- 2.1
- 2.2
- 2.3
- - 2.4
+ - 2.4.0-rc1
before_install:
- gem install bundler
- sudo apt-get -qq update
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d44de8..d3107e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,42 @@ This project adheres to [Semantic Versioning](http://semver.org/).
The format of this file is based on [Keep a Changelog](http://keepachangelog.com/).
+## [3.0.0] / 2017-01-26
+
+### Changed
+
+* **API-keys always required!** From now on all requests to schleuder-api-daemon require API-keys, even via localhost. This helps protecting against rogue non-root-accounts or -scripts on the local machine.
+* **TLS always used!** schleuder-api-daemon now always uses TLS.
+* Switched project-site and git-repository to <https://0xacab.org/schleuder/schleuder>.
+* Set proper usage flags when creating a new OpenPGP-key: the primary key gets "SC", the subkey "E". (Thanks, dkg!)
+* Avoid possible future errors by ignoring every unknown output of gpg (like GnuPG's doc/DETAILS recommends). (Thanks, dkg!)
+* Friendlier error message if delivery to subscription fails.
+* Set list-email as primary address after adding UIDs. Previously it was a little random, for reasons only known to GnuPG.
+* Only use temporary files where neccessary, and with more secure paths.
+* Tighten requirements for valid email-addresses a little: The domain-part may now only contain alpha-numeric characters, plus these: `._-`
+* Required version of schleuder-cli: 0.0.2.
+
+### Added
+
+* X-LISTNAME: A **new mandatory keyword** to accompany all keywords. From now on every message containing keywords must also include the listname-keyword like this: `X-LISTNAME: list at hostname`
+
+ The other keywords will only be run if the given listname matches the email-address of the list that the message is sent to. This mitigates replay-attacks among different lists.
+* Also send helpful message if a subscription's key is present but unusable.
+* Provide simpler postfix integration, now using virtual_domains and an sql-script. (Thanks, dkg!)
+* Enable refreshing keys from keyservers: A script that is meant to be run regularly from cron. It refreshes each key of each list one by one from a configurable keyserver, and sends the result to the respective list-admins.
+* Import attached, ascii-armored keys from messages with `add-key`-keyword. (Thanks, Kéfir!)
+* Check possible key-material for expected format before importing it. (Thanks, Kéfir!)
+
+### Fixed
+
+* Allow fingerprints to be prefixed with '0x' in `subscribe`-keyword.
+* Also delete directory of list-logfile on deletion if that resides outside of the list-dir.
+* Sign and possibly encrypt error notifications.
+* Fix setting admin- and delivery-flags while subscribing.
+* Fix subscribing from schleuder-cli.
+* Fix finding subscriptions from signatures made by a signing-capable sub-key.
+
+
## [3.0.0.beta17] / 2017-01-12
### Changed
@@ -12,7 +48,7 @@ The format of this file is based on [Keep a Changelog](http://keepachangelog.com
* Stopped using SCHLEUDER_ROOT in specs. Those make life difficult for packaging for debian.
* While running specs, ensure smtp-daemon.rb has been stopped before starting it anew.
-###
+### Added
* A Code of Conduct.
diff --git a/Gemfile.lock b/Gemfile.lock
index dc92336..0efdbb4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
- schleuder (3.0.0.beta17)
+ schleuder (3.0.0)
activerecord (~> 4.1)
mail-gpg (~> 0.3.0)
rake (~> 10)
diff --git a/README.md b/README.md
index 6dec597..878ff89 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ Schleuder, version 3
Schleuder is a gpg-enabled mailing list manager with resending-capabilities. Subscribers can communicate encrypted (and pseudonymously) among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list.
Version 3 of schleuder is a complete rewrite, which aims to be more robust, flexible, and internationalized. It
-also provides an API for the optional web interface called [schleuder-web](https://git.codecoop.org/schleuder/schleuder-web).
+also provides an API for the optional web interface called [schleuder-web](https://0xacab.org/schleuder/schleuder-web).
For more details see <https://schleuder.nadir.org/docs/>.
@@ -42,15 +42,15 @@ Additionally these **rubygems** are required (will be installed automatically un
Installing Schleuder
------------
-1. Download [the gem](https://git.codecoop.org/schleuder/schleuder3/raw/master/gems/schleuder-3.0.0.beta17.gem) and [the OpenPGP-signature](https://git.codecoop.org/schleuder/schleuder3/raw/master/gems/schleuder-3.0.0.beta17.gem.sig) and verify:
+1. Download [the gem](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.0.0.gem) and [the OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.0.0.gem.sig) and verify:
```
gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
- gpg --verify schleuder-3.0.0.beta17.gem.sig
+ gpg --verify schleuder-3.0.0.gem.sig
```
2. If all went well install the gem:
```
- gem install schleuder-3.0.0.beta17.gem
+ gem install schleuder-3.0.0.gem
```
3. Set up schleuder:
@@ -81,11 +81,11 @@ List administration
-------------------
Please use
-[schleuder-cli](https://git.codecoop.org/schleuder/schleuder-cli) to create and
+[schleuder-cli](https://0xacab.org/schleuder/schleuder-cli) to create and
manage lists from the command line.
Optionally consider installing
-[schleuder-web](https://git.codecoop.org/schleuder/schleuder-web), the web
+[schleuder-web](https://0xacab.org/schleuder/schleuder-web), the web
interface for schleuder. It enables list-admins to manage their lists through
the web instead of using [request-keywords](https://schleuder.nadir.org/docs/#subscription-and-key-management).
@@ -94,7 +94,7 @@ the web instead of using [request-keywords](https://schleuder.nadir.org/docs/#su
Todo
----
-See <https://git.codecoop.org/schleuder/schleuder3/issues>.
+See <https://0xacab.org/schleuder/schleuder/issues>.
Testing
-------
@@ -130,4 +130,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
Alternative Download
--------------------
-Alternatively to the gem-files you can download the latest release as [a tarball](https://git.codecoop.org/schleuder/schleuder3/raw/master/gems/schleuder-3.0.0.beta17.tar.gz) and [its OpenPGP-signature](https://git.codecoop.org/schleuder/schleuder3/raw/master/gems/schleuder-3.0.0.beta17.tar.gz.sig).
+Alternatively to the gem-files you can download the latest release as [a tarball](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.0.0.tar.gz) and [its OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.0.0.tar.gz.sig).
diff --git a/Rakefile b/Rakefile
index 0564fd5..d5f61a0 100644
--- a/Rakefile
+++ b/Rakefile
@@ -124,7 +124,7 @@ end
desc 'Check if version-tag already exists'
task :check_version do
# Check if Schleuder::VERSION has been updated since last release
- if `git tag`.include?(@tagname)
+ if `git tag`.match?(/^#{@tagname}$/)
$stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update #{project}/version.rb?"
$stderr.print "Delete tag to continue? [yN] "
if $stdin.gets.match(/^y/i)
diff --git a/bin/pinentry-clearpassphrase b/bin/pinentry-clearpassphrase
index f171aae..7eef8cf 100755
--- a/bin/pinentry-clearpassphrase
+++ b/bin/pinentry-clearpassphrase
@@ -3,11 +3,10 @@
# This file can be deleted once we cease to support gnupg 2.0.
require 'fileutils'
-require 'openssl'
require 'cgi'
+require 'openssl'
def respond(msg, flush=true)
- log '>', msg
$stdout.puts msg
if flush
$stdout.flush
@@ -35,44 +34,30 @@ end
def do_exit
if File.exist?(EMPTYPWDSENTFILE2)
- log "deleting tmpdir"
FileUtils.rm_rf(TMPDIR)
end
- log "exiting\n"
exit 0
end
-def log(*args)
- if DEBUG
- File.open('/tmp/pinentry.log', 'a') do |f|
- f.puts "#{args.join(' ')}"
- end
- end
-end
-
-
OLDPASSWD = CGI.escape(ENV['PINENTRY_USER_DATA'].to_s)
if OLDPASSWD.empty?
respond "Fatal error: passed PINENTRY_USER_DATA was empty, cannot continue"
exit 1
end
-DEBUG = false
-SALT = 'Thank you, Edward!'
-TMPNAME = Digest::SHA256.hexdigest(SALT + ENV['GNUPGHOME'].to_s)
-TMPDIR = "/tmp/schleuder-#{TMPNAME}"
-OLDPWDSENTFILE = File.join(TMPDIR, "1")
+# We need a static directory name to maintain the state across invocations of
+# this file.
+TMPDIR = File.join(ENV['GNUPGHOME'], '.tmp-pinentry-clearpassphrase')
+OLDPWDSENTFILE = File.join(TMPDIR, '1')
EMPTYPWDSENTFILE1 = File.join(TMPDIR, '2')
EMPTYPWDSENTFILE2 = File.join(TMPDIR, '3')
if ! Dir.exist?(TMPDIR)
Dir.mkdir(TMPDIR)
end
-log "\nnew connection at #{Time.now}"
respond "OK - what's up?"
while line = $stdin.gets do
- log '<', line
case line
when /^GETPIN/
send_password
diff --git a/bin/schleuder b/bin/schleuder
index 9cfaaed..d7062a8 100755
--- a/bin/schleuder
+++ b/bin/schleuder
@@ -1,5 +1,9 @@
#!/usr/bin/env ruby
+# Don't emit any warnings, they would be sent back to the email-sender as an
+# error-message.
+$VERBOSE=nil
+
trap("INT") { exit 1 }
diff --git a/bin/schleuder-api-daemon b/bin/schleuder-api-daemon
index 4044c6d..497ceb2 100755
--- a/bin/schleuder-api-daemon
+++ b/bin/schleuder-api-daemon
@@ -9,11 +9,21 @@ require 'sinatra/namespace'
require 'thin'
require_relative '../lib/schleuder.rb'
-TLS_CERT = Conf.api['tls_cert_file']
-TLS_KEY = Conf.api['tls_key_file']
+
+%w[tls_cert_file tls_key_file].each do |config_key|
+ path = Conf.api[config_key]
+ if ! File.readable?(path)
+ $stderr.puts "Error: '#{path}' is not a readable file (from #{config_key} in config)."
+ exit 1
+ end
+end
class SchleuderApiDaemon < Sinatra::Base
register Sinatra::Namespace
+ use Rack::Auth::Basic, "Schleuder API Daemon" do |username, key|
+ username == 'schleuder' && Conf.api_valid_api_keys.include?(key)
+ end
+
configure do
set :server, :thin
set :port, Schleuder::Conf.api['port'] || 4443
@@ -25,28 +35,6 @@ class SchleuderApiDaemon < Sinatra::Base
end
end
- # TODO: Move this into a method, call that in the configure block
- @use_tls = Conf.api_use_tls?
- if @use_tls
- [TLS_CERT, TLS_KEY].each do |const|
- if const.nil?
- @use_tls = false
- elsif ! File.readable?(const)
- $stderr.puts "Error: '#{const}' is not a readable file."
- exit 1
- end
- end
- end
-
- if @use_tls
- use Rack::Auth::Basic, "Schleuder API Daemon" do |username, key|
- username == 'schleuder' && Conf.api_valid_api_keys.include?(key)
- end
- else
- $stderr.puts "\nWarning: Without TLS, schleuder-api-daemon enforces binding to localhost only!\nExecute `schleuder cert generate` and follow the instructions.\n\n"
- set :bind, 'localhost'
- end
-
before do
cast_param_values
end
@@ -164,7 +152,7 @@ class SchleuderApiDaemon < Sinatra::Base
email: key.email,
ascii: key.armored,
expiry: key.expires,
- trust_issues: key.trust
+ trust_issues: key.usability_issue
}
end
end
@@ -256,11 +244,13 @@ class SchleuderApiDaemon < Sinatra::Base
post '.json' do
begin
list = list(requested_list_id)
+ adminflag = [true, 1, '1'].include?(parsed_body['admin'])
+ deliveryflag = [true, 1, '1'].include?(parsed_body['delivery_enabled'])
sub = list.subscribe(
parsed_body['email'],
parsed_body['fingerprint'],
- parsed_body['admin'],
- parsed_body['delivery_enabled']
+ adminflag,
+ deliveryflag
)
logger.debug "subcription: #{sub.inspect}"
if sub.valid?
@@ -354,13 +344,11 @@ class SchleuderApiDaemon < Sinatra::Base
def self.run!
super do |server|
- if @use_tls == true
- server.ssl = true
- server.ssl_options = {
- :cert_chain_file => TLS_CERT,
- :private_key_file => TLS_KEY
- }
- end
+ server.ssl = true
+ server.ssl_options = {
+ :cert_chain_file => Conf.api['tls_cert_file'],
+ :private_key_file => Conf.api['tls_key_file']
+ }
end
end
diff --git a/etc/postfix/schleuder_sqlite.cf b/etc/postfix/schleuder_sqlite.cf
new file mode 100644
index 0000000..b252843
--- /dev/null
+++ b/etc/postfix/schleuder_sqlite.cf
@@ -0,0 +1,28 @@
+# Use this as a table for postfix to select addresses that schleuder
+# thinks belong to it. This is useful when
+# smtpd_reject_unlisted_recipient = yes (which is the default for
+# modern Postfix)
+
+# For example, you might dedicate Postfix's "virtual" domains to
+# schleuder with the following set of configs in main.cf:
+#
+# virtual_domains = lists.example.org
+# virtual_transport = schleuder
+# virtual_alias_maps = hash:/etc/postfix/virtual_aliases
+# virtual_mailbox_maps = sqlite:/etc/postfix/schleuder_sqlite.cf
+# schleuder_destination_recipient_limit = 1
+
+# it is not recommended to use this table for more powerful
+# configuration options (e.g. transport_maps) because it could give
+# the schleuder user (which can write the given sqlite database) the
+# power to change settings for for other mail handled by this Postfix
+# instance.
+
+dbpath = /var/lib/schleuder/db.sqlite
+
+query = select 'present' where '%s' in (
+ select email from lists union
+ select replace(email, '@', '-bounces@') from lists union
+ select replace(email, '@', '-owner@') from lists union
+ select replace(email, '@', '-request@') from lists union
+ select replace(email, '@', '-sendkey@') from lists)
diff --git a/etc/schleuder.yml b/etc/schleuder.yml
index 9b152d2..ddf9be9 100644
--- a/etc/schleuder.yml
+++ b/etc/schleuder.yml
@@ -10,6 +10,15 @@ plugins_dir: /etc/schleuder/plugins
# How verbose should Schleuder log to syslog? (list-specific messages are written to the list's log-file).
log_level: warn
+# Which keyserver to refresh keys from (used by `schleuder refresh_keys`, meant
+# to be run from cron weekly).
+# If you have gnupg 2.1, we strongly suggest to use a hkps-keyserver:
+#keyserver: hkps://hkps.pool.sks-keyservers.net
+# If you have gnupg 2.1 and TOR running locally, use a onion-keyserver:
+#keyserver: hkp://jirk5u4osbsr34t5.onion
+# The default works for all supported versions of gnupg:
+keyserver: pool.sks-keyservers.net
+
# For these options see documentation for ActionMailer::smtp_settings, e.g. <http://api.rubyonrails.org/classes/ActionMailer/Base.html>.
smtp_settings:
address: localhost
@@ -28,12 +37,9 @@ database:
database: /var/lib/schleuder/db.sqlite
timeout: 5000
-# Note: The API-daemon will bind only to localhost if no TLS-cert+keys are available.
api:
host: localhost
port: 4443
- # Serve the API via https?
- use_tls: false
# Certificate and key to use. You can create new ones with `schleuder cert generate`.
tls_cert_file: /etc/schleuder/schleuder-certificate.pem
tls_key_file: /etc/schleuder/schleuder-private-key.pem
diff --git a/lib/schleuder/cli.rb b/lib/schleuder/cli.rb
index 947f31b..87622a3 100644
--- a/lib/schleuder/cli.rb
+++ b/lib/schleuder/cli.rb
@@ -63,6 +63,18 @@ module Schleuder
end
end
+ desc 'refresh_keys', "Refresh all keys of all list from the keyservers sequentially (one by one). (This is supposed to be run from cron weekly.)"
+ def refresh_keys
+ List.all.each do |list|
+ I18n.locale = list.language
+ output = list.refresh_keys
+ if output.present?
+ msg = "#{I18n.t('refresh_keys_intro', email: list.email)}\n\n#{output}"
+ list.logger.notify_admin(msg, nil, I18n.t('refresh_keys'))
+ end
+ end
+ end
+
desc 'install', "Set-up or update Schleuder environment (create folders, copy files, fill the database)."
def install
config_dir = Pathname.new(ENV['SCHLEUDER_CONFIG']).dirname
@@ -97,7 +109,6 @@ module Schleuder
end
end
-
if ActiveRecord::SchemaMigration.table_exists?
say `cd #{root_dir} && rake db:migrate`
else
@@ -107,6 +118,10 @@ module Schleuder
end
end
+ if ! File.exist?(Conf.api['tls_cert_file']) || ! File.exist?(Conf.api['tls_key_file'])
+ Schleuder::Cert.new.generate
+ end
+
say "Schleuder has been set up. You can now create a new list using `schleuder-cli`.\nWe hope you enjoy!"
rescue => exc
fatal exc.message
@@ -159,7 +174,7 @@ module Schleuder
# Clear passphrase of imported list-key.
output = list.key.clearpassphrase(conf['gpg_password'])
- if output
+ if output.present?
fatal "while clearing passphrase of list-key: #{output.inspect}"
end
@@ -268,6 +283,7 @@ Please notify the users and admins of this list of these changes.
KEYWORDS[keyword.downcase]
end.compact
end
+
end
end
end
diff --git a/lib/schleuder/cli/cert.rb b/lib/schleuder/cli/cert.rb
index ce5b8ee..9f3689a 100644
--- a/lib/schleuder/cli/cert.rb
+++ b/lib/schleuder/cli/cert.rb
@@ -9,7 +9,6 @@ module Schleuder
fingerprint = SchleuderCertManager.generate('schleuder', key, cert)
puts "Fingerprint of generated certificate: #{fingerprint}"
puts "Have this fingerprint included into the configuration-file of all clients that want to connect to your Schleuder API."
- puts "To activate TLS set `use_tls: true` in #{ENV['SCHLEUDER_CONFIG']} and restart schleuder-api-daemon."
if Process.euid == 0
puts "! Warning: this process was run as root — please make sure the above files are accessible by the user that is running `schleuder-api-daemon`."
end
diff --git a/lib/schleuder/conf.rb b/lib/schleuder/conf.rb
index 461dd11..dba5674 100644
--- a/lib/schleuder/conf.rb
+++ b/lib/schleuder/conf.rb
@@ -2,13 +2,14 @@ module Schleuder
class Conf
include Singleton
- EMAIL_REGEXP = /\A.+ at .+\z/i
+ EMAIL_REGEXP = /\A.+@[[:alnum:]_.-]+\z/i
DEFAULTS = {
'lists_dir' => '/var/lib/schleuder/lists',
'listlogs_dir' => '/var/lib/schleuder/lists',
'plugins_dir' => '/etc/schleuder/plugins',
'log_level' => 'warn',
+ 'keyserver' => 'hkp://pool.sks-keyservers.net',
'smtp_settings' => {
'address' => 'localhost',
'port' => 25,
@@ -31,7 +32,6 @@ module Schleuder
'api' => {
'host' => 'localhost',
'port' => 4443,
- 'use_tls' => false,
'tls_cert_file' => '/etc/schleuder/schleuder-certificate.pem',
'tls_key_file' => '/etc/schleuder/schleuder-private-key.pem',
'valid_api_keys' => []
@@ -74,10 +74,6 @@ module Schleuder
instance.config['api'] || {}
end
- def self.api_use_tls?
- api['use_tls'].to_s == 'true'
- end
-
def self.api_valid_api_keys
Array(api['valid_api_keys'])
end
@@ -109,6 +105,10 @@ module Schleuder
settings
end
+ def self.keyserver
+ instance.config['keyserver']
+ end
+
private
def load_config(filename)
diff --git a/lib/schleuder/errors/keyword_admin_only.rb b/lib/schleuder/errors/keyword_admin_only.rb
index 8d1f362..633d8b1 100644
--- a/lib/schleuder/errors/keyword_admin_only.rb
+++ b/lib/schleuder/errors/keyword_admin_only.rb
@@ -1,12 +1,12 @@
module Schleuder
module Errors
- class KeywordAdminOnly
+ class KeywordAdminOnly < Base
def initialize(keyword)
@keyword = keyword
end
def message
- t('errors.keyword_admin_only', keyword: keyword)
+ t('errors.keyword_admin_only', keyword: @keyword)
end
end
end
diff --git a/lib/schleuder/gpgme/ctx.rb b/lib/schleuder/gpgme/ctx.rb
index b86d5ee..7f8b69b 100644
--- a/lib/schleuder/gpgme/ctx.rb
+++ b/lib/schleuder/gpgme/ctx.rb
@@ -1,5 +1,12 @@
module GPGME
class Ctx
+ IMPORT_FLAGS = {
+ 'new key' => 1,
+ 'new_uids' => 2,
+ 'new_signatures' => 4,
+ 'new_subkeys' => 8
+ }
+
def keyimport(*args)
self.import_keys(*args)
result = self.import_result
@@ -35,29 +42,78 @@ module GPGME
GPGME::Engine.info.find {|e| e.protocol == GPGME::PROTOCOL_OpenPGP }
end
+ def self.refresh_keys(keys)
+ output = []
+ base_args = "--quiet --no-auto-check-trustdb --keyserver #{Conf.keyserver} --refresh-keys"
+ keys.each do |key|
+ args = "#{base_args} #{key.fingerprint}"
+ err, gpgout, _ = gpgcli(args)
+ gpgout = filter_gpgcli_output(gpgout)
+ output << filter_gpgcli_output(err)
+ # Add any gpgkeys-message (gpg 2.0 writes those messages to stdout).
+ # Those could e.g. report a failure to connect to the keyserver.
+ output << gpgout.select { |line| line.match(/^gpgkeys: .*$/) }
+
+ import_stats = translate_import_data(gpgout)
+ if import_stats.present?
+ output << I18n.t("key_updated", { fingerprint: key.fingerprint,
+ states: import_stats.join(', ') })
+ end
+ sleep rand(1.0..5.0)
+ end
+ GPGME::Ctx.gpgcli("--check-trustdb")
+ output.flatten.uniq.join
+ end
+
+ def self.translate_import_data(gpgoutput)
+ result = []
+ import_ok = gpgoutput.grep(/IMPORT_OK/).first
+ return result if import_ok.blank?
+
+ import_status = import_ok.split(/\s/).slice(2).to_i
+ return result if import_status.zero?
+
+ # TODO: Raise alarm if new key is found?
+ IMPORT_FLAGS.each do |text, int|
+ if (import_status & int) > 0
+ result << I18n.t("import_states.#{text}")
+ end
+ end
+ result
+ end
+
+ # Unfortunately we can't distinguish between a failure to connect the
+ # keyserver, and a failure to find the key on the server. So we try to
+ # filter misleading errors to check if there are any to be reported.
+ def self.filter_gpgcli_output(strings)
+ strings.reject do |line|
+ line.chomp == 'gpg: keyserver refresh failed: No data' ||
+ line.match(/^gpgkeys: key .* not found on keyserver/) ||
+ line.match(/^gpg: refreshing /) ||
+ line.match(/^gpg: requesting key /) ||
+ line.match(/^gpg: no valid OpenPGP data found/)
+ end
+ end
+
def self.gpgcli(args)
exitcode = -1
- errors = ''
- output = ''
+ errors = []
+ output = []
base_cmd = gpg_engine.file_name
- base_args = "--armor --trust-model always --quiet --no-tty --command-fd 0 --status-fd 1"
+ base_args = "--no-greeting --no-permission-warning --quiet --armor --trust-model always --no-tty --command-fd 0 --status-fd 1"
cmd = [base_cmd, base_args, args].flatten.join(' ')
Open3.popen3(cmd) do |stdin, stdout, stderr, thread|
if block_given?
output = yield(stdin, stdout, stderr)
+ else
+ output = stdout.readlines
end
stdin.close
errors = stderr.readlines
exitcode = thread.value.exitstatus
end
- if output.present?
- output
- elsif exitcode > 0
- errors.join("\n")
- else
- nil
- end
+ [errors, output, exitcode]
rescue Errno::ENOENT
raise 'Need gpg in $PATH or in $GPGBIN'
end
@@ -78,7 +134,21 @@ module GPGME
end
end
end
- nil
+ end
+
+ def self.spawn_daemon(name, args)
+ delete_daemon_socket(name)
+ cmd = "#{name} #{args} --daemon > /dev/null 2>&1"
+ if ! system(cmd)
+ return [false, "#{name} exited with code #{$?}"]
+ end
+ end
+
+ def self.delete_daemon_socket(name)
+ path = File.join(ENV["GNUPGHOME"], "S.#{name}")
+ if File.exist?(path)
+ File.delete(path)
+ end
end
end
end
diff --git a/lib/schleuder/gpgme/key.rb b/lib/schleuder/gpgme/key.rb
index efba5d9..a4bca4f 100644
--- a/lib/schleuder/gpgme/key.rb
+++ b/lib/schleuder/gpgme/key.rb
@@ -28,6 +28,42 @@ module GPGME
orig_fingerprint.encode(Encoding::US_ASCII)
end
+ def usable?
+ usability_issue.blank?
+ end
+
+ def usability_issue
+ if trust.present?
+ trust
+ elsif ! usable_for?(:encrypt)
+ "not capable of encryption"
+ else
+ nil
+ end
+ end
+
+ def set_primary_uid(email)
+ # We rely on the order of UIDs here. Seems to work.
+ index = self.uids.map(&:email).index(email)
+ uid_number = index + 1
+ primary_set = false
+ args = "--edit-key '#{self.fingerprint}' #{uid_number}"
+ errors, _ = GPGME::Ctx.gpgcli_expect(args) do |line|
+ case line.chomp
+ when /keyedit.prompt/
+ if ! primary_set
+ primary_set = true
+ "primary"
+ else
+ "save"
+ end
+ else
+ nil
+ end
+ end
+ errors.join
+ end
+
def adduid(uid, email)
# This block can be deleted once we cease to support gnupg 2.0.
if ! GPGME::Ctx.sufficient_gpg_version?('2.1.4')
@@ -35,12 +71,14 @@ module GPGME
end
# Specifying the key via fingerprint apparently doesn't work.
- GPGME::Ctx.gpgcli("--quick-adduid #{uid} '#{uid} <#{email}>'")
+ errors, _ = GPGME::Ctx.gpgcli("--quick-adduid #{uid} '#{uid} <#{email}>'")
+ errors.join
end
# This method can be deleted once we cease to support gnupg 2.0.
def adduid_expect(uid, email)
- GPGME::Ctx.gpgcli_expect("--allow-freeform-uid --edit-key '#{self.fingerprint}' adduid") do |line|
+ args = "--allow-freeform-uid --edit-key '#{self.fingerprint}' adduid"
+ errors, _ = GPGME::Ctx.gpgcli_expect(args) do |line|
case line.chomp
when /keygen.name/
uid
@@ -50,12 +88,11 @@ module GPGME
''
when /keyedit.prompt/
"save"
- when /USERID_HINT|GOT_IT|GOOD_PASSPHRASE/
- nil
else
- [false, "Unexpected line: #{line}"]
+ nil
end
end
+ errors.join
end
def clearpassphrase(oldpw)
@@ -66,7 +103,8 @@ module GPGME
oldpw_given = false
# Don't use '--passwd', it claims to fail (even though it factually doesn't).
- GPGME::Ctx.gpgcli_expect(" --pinentry-mode loopback --edit-key '#{self.fingerprint}' passwd") do |line|
+ args = "--pinentry-mode loopback --edit-key '#{self.fingerprint}' passwd"
+ errors, _, exitcode = GPGME::Ctx.gpgcli_expect(args) do |line|
case line
when /passphrase.enter/
if ! oldpw_given
@@ -81,26 +119,26 @@ module GPGME
'y'
when /keyedit.prompt/
"save"
- when /USERID_HINT|NEED_PASSPHRASE|GOT_IT|GOOD_PASSPHRASE|MISSING_PASSPHRASE|KEY_CONSIDERED|INQUIRE_MAXLEN|PROGRESS/
- nil
else
- [false, "Unexpected line: #{line}"]
+ nil
end
end
+
+ # Only show errors if something apparently went wrong. Otherwise we might
+ # leak useless strings from gpg and make the caller report errors even
+ # though this method succeeded.
+ if exitcode > 0
+ errors.join
+ else
+ nil
+ end
end
# This method can be deleted once we cease to support gnupg 2.0.
def clearpassphrase_v20(oldpw)
- ENV['PINENTRY_USER_DATA'] = oldpw
- pinentry = File.join(ENV['SCHLEUDER_ROOT'], 'bin', 'pinentry-clearpassphrase')
- delete_gpg_agent_socket
- gpg_agent_log = "/tmp/schleuder-gpg-agent-#{rand}.log"
- gpg_agent_cmd = "gpg-agent --use-standard-socket --pinentry-program #{pinentry} --daemon > #{gpg_agent_log} 2>&1"
- if ! system(gpg_agent_cmd)
- return [false, "gpg-agent exited with code #{$?}, output in #{gpg_agent_log}"]
- end
+ start_gpg_agent(oldpw)
# Don't use '--passwd', it claims to fail (even though it factually doesn't).
- output = GPGME::Ctx.gpgcli_expect("--edit-key '#{self.fingerprint}' passwd") do |line|
+ errors, _, exitcode = GPGME::Ctx.gpgcli_expect("--edit-key '#{self.fingerprint}' passwd") do |line|
case line
when /BAD_PASSPHRASE/
[false, 'bad passphrase']
@@ -108,29 +146,32 @@ module GPGME
'y'
when /keyedit.prompt/
"save"
- when /USERID_HINT|NEED_PASSPHRASE|GOT_IT|GOOD_PASSPHRASE|MISSING_PASSPHRASE|KEY_CONSIDERED|INQUIRE_MAXLEN|PROGRESS/
- nil
else
- [false, "Unexpected line: #{line}"]
+ nil
end
end
- # gpg-agent terminates itself if its socket goes away.
- delete_gpg_agent_socket
- delete_file(gpg_agent_log)
- output
+ stop_gpg_agent
+
+ # Only show errors if something apparently went wrong. Otherwise we might
+ # leak useless strings from gpg and make the caller report errors even
+ # though this method succeeded.
+ if exitcode > 0
+ errors.join
+ else
+ nil
+ end
end
# This method can be deleted once we cease to support gnupg 2.0.
- def delete_gpg_agent_socket
- delete_file(ENV['GNUPGHOME'], 'S.gpg-agent')
+ def stop_gpg_agent
+ # gpg-agent terminates itself if its socket goes away.
+ GPGME::Ctx.delete_daemon_socket('gpg-agent')
end
- # This method can be deleted once we cease to support gnupg 2.0.
- def delete_file(*args)
- path = File.join(Array(args))
- if File.exist?(path)
- File.delete(path)
- end
+ def start_gpg_agent(oldpw)
+ ENV['PINENTRY_USER_DATA'] = oldpw
+ pinentry = File.join(ENV['SCHLEUDER_ROOT'], 'bin', 'pinentry-clearpassphrase')
+ GPGME::Ctx.spawn_daemon('gpg-agent', "--use-standard-socket --pinentry-program #{pinentry}")
end
end
end
diff --git a/lib/schleuder/list.rb b/lib/schleuder/list.rb
index c254d02..6f2e2e3 100644
--- a/lib/schleuder/list.rb
+++ b/lib/schleuder/list.rb
@@ -2,7 +2,7 @@ module Schleuder
class List < ActiveRecord::Base
has_many :subscriptions, dependent: :destroy
- before_destroy :delete_listdir
+ before_destroy :delete_listdirs
serialize :headers_to_meta, JSON
serialize :bounces_drop_on_headers, JSON
@@ -140,8 +140,8 @@ module Schleuder
expiring << [key, expdays]
end
- if key.trust
- unusable << [key, key.trust]
+ if ! key.usable?
+ unusable << [key, key.usability_issue]
end
end
@@ -154,9 +154,9 @@ module Schleuder
})
end
- unusable.each do |key,trust|
+ unusable.each do |key,usability_issue|
text << I18n.t('key_unusable', {
- trust: Array(trust).join(', '),
+ usability_issue: usability_issue,
fingerprint: key.fingerprint,
email: key.email
})
@@ -164,6 +164,10 @@ module Schleuder
text
end
+ def refresh_keys
+ GPGME::Ctx.refresh_keys(self.keys)
+ end
+
def self.by_recipient(recipient)
listname = recipient.gsub(/-(sendkey|request|owner|bounce)@/, '@')
where(email: listname).first
@@ -228,14 +232,16 @@ module Schleuder
end
def subscribe(email, fingerprint=nil, adminflag=false, deliveryflag=true)
- adminflag ||= false
- deliveryflag ||= true
+ # Ensure we have true or false as values for these two attributes.
+ admin = adminflag.to_s == 'true'
+ delivery_enabled = deliveryflag.to_s != 'false'
+
sub = Subscription.new(
list_id: self.id,
email: email,
fingerprint: fingerprint,
- admin: adminflag,
- delivery_enabled: deliveryflag
+ admin: admin,
+ delivery_enabled: delivery_enabled
)
sub.save
sub
@@ -271,7 +277,7 @@ module Schleuder
def from_admin?(mail)
return false if ! mail.was_validly_signed?
admins.find do |admin|
- admin.fingerprint == mail.signature.fingerprint
+ admin.fingerprint == mail.signing_key.fingerprint
end.presence || false
end
@@ -285,13 +291,17 @@ module Schleuder
ENV['GNUPGHOME'] = listdir
end
- def delete_listdir
+ def delete_listdirs
if File.exists?(self.listdir)
FileUtils.rm_rf(self.listdir, secure: true)
- Schleuder.logger.info "Deleted listdir"
- else
- # Don't use list-logger here — if the list-dir isn't present we can't log to it!
- Schleuder.logger.info "Couldn't delete listdir, directory not present"
+ Schleuder.logger.info "Deleted #{self.listdir}"
+ end
+ # If listlogs_dir is different from lists_dir, the logfile still exists
+ # and needs to be deleted, too.
+ logfile_dir = File.dirname(self.logfile)
+ if File.exists?(logfile_dir)
+ FileUtils.rm_rf(logfile_dir, secure: true)
+ Schleuder.logger.info "Deleted #{logfile_dir}"
end
true
rescue => exc
diff --git a/lib/schleuder/list_builder.rb b/lib/schleuder/list_builder.rb
index f9599ce..5387bda 100644
--- a/lib/schleuder/list_builder.rb
+++ b/lib/schleuder/list_builder.rb
@@ -107,6 +107,12 @@ module Schleuder
raise err
end
end
+ # Go through list.key() to re-fetch the key from the keyring, otherwise
+ # we don't see the new UIDs.
+ errors = list.key.set_primary_uid(list.email)
+ if errors.present?
+ raise errors
+ end
rescue => exc
raise Errors::KeyAdduidFailed.new(exc.to_s)
end
@@ -116,8 +122,10 @@ module Schleuder
<GnupgKeyParms format=\"internal\">
Key-Type: RSA
Key-Length: 4096
+ Key-Usage: sign
Subkey-Type: RSA
Subkey-Length: 4096
+ Subkey-Usage: encrypt
Name-Real: #{list.email}
Name-Email: #{list.email}
Expire-Date: 0
diff --git a/lib/schleuder/listlogger.rb b/lib/schleuder/listlogger.rb
index 67939cd..36cdcdd 100644
--- a/lib/schleuder/listlogger.rb
+++ b/lib/schleuder/listlogger.rb
@@ -4,7 +4,8 @@ module Schleuder
def initialize(list)
super(list.logfile, 'daily')
@from = list.email
- @adminaddresses = list.admins.map(&:email)
+ @list = list
+ @adminaddresses = list.admins.map { |sub| [sub.email, sub.key] }
@level = ::Logger.const_get(list.log_level.upcase)
remove_old_logfiles(list)
end
diff --git a/lib/schleuder/logger_notifications.rb b/lib/schleuder/logger_notifications.rb
index c503017..c5a4d18 100644
--- a/lib/schleuder/logger_notifications.rb
+++ b/lib/schleuder/logger_notifications.rb
@@ -15,7 +15,8 @@ module Schleuder
end
def notify_admin(string, original_message=nil, subject='Error')
- Array(adminaddresses).each do |address|
+ # Minimize using other classes here, we don't know what caused the error.
+ Array(adminaddresses).each do |address, key|
mail = Mail.new
mail.from = @from
mail.to = address
@@ -31,6 +32,13 @@ module Schleuder
orig_part.body = original_message.to_s
mail.add_part orig_part
end
+ if @list.present?
+ gpg_opts = @list.gpg_sign_options
+ if key.present? && key.usable?
+ gpg_opts.merge!(encrypt: true, keys: { address => key.fingerprint })
+ end
+ mail.gpg gpg_opts
+ end
mail.deliver
end
true
diff --git a/lib/schleuder/mail/message.rb b/lib/schleuder/mail/message.rb
index 8b9aa86..eafe53b 100644
--- a/lib/schleuder/mail/message.rb
+++ b/lib/schleuder/mail/message.rb
@@ -106,8 +106,17 @@ module Mail
end
def signer
- if fingerprint = self.signature.try(:fpr)
- list.subscriptions.where(fingerprint: fingerprint).first
+ if signing_key.present?
+ list.subscriptions.where(fingerprint: signing_key.fingerprint).first
+ end
+ end
+
+ # The fingerprint of the signature might be the one of a sub-key, but the
+ # subscription-assigned fingerprints are (should be) the ones of the
+ # primary keys, so we need to look up the key.
+ def signing_key
+ if signature.present?
+ @signing_key ||= list.keys(signature.fpr).first
end
end
@@ -208,7 +217,7 @@ module Mail
# Some versions of gpgme return nil if the key is unknown, so we check
# for that manually and provide our own fallback. (Calling
# `signature.key` results in an EOFError in that case.)
- if list.key(signature.fingerprint)
+ if signing_key.present?
msg = signature.to_s
else
# TODO: I18n
diff --git a/lib/schleuder/plugins/key_management.rb b/lib/schleuder/plugins/key_management.rb
index 94f8ad8..e3184a8 100644
--- a/lib/schleuder/plugins/key_management.rb
+++ b/lib/schleuder/plugins/key_management.rb
@@ -1,17 +1,17 @@
module Schleuder
module RequestPlugins
def self.add_key(arguments, list, mail)
- key_material = if mail.parts.first.present?
- mail.parts.first.body
- else
- mail.body
- end.to_s
- result = list.import_key(key_material)
-
out = [I18n.t('plugins.key_management.import_result')]
- out << result.map do |import_result|
- str = I18n.t("plugins.key_management.key_import_status.#{import_result.action}")
- "#{import_result.fpr}: #{str}"
+
+ if mail.has_attachments?
+ results = self.import_keys_from_attachments(list, mail)
+ else
+ results = [self.import_key_from_body(list, mail)]
+ end
+
+ out << results.compact.collect(&:imports).flatten.map do |import_status|
+ str = I18n.t("plugins.key_management.key_import_status.#{import_status.action}")
+ "#{import_status.fpr}: #{str}"
end
end
@@ -47,5 +47,40 @@ module Schleuder
hkp.fetch_and_import(argument)
end
end
+
+ def self.is_armored_key?(material)
+ return false unless /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ =~ material
+ return false unless /^-----END PGP PUBLIC KEY BLOCK-----$/ =~ material
+
+ lines = material.split("\n").reject(&:empty?)
+ # remove header
+ lines.shift
+ # remove tail
+ lines.pop
+ # verify the rest
+ # TODO: verify length except for lasts lines?
+ # headers according to https://tools.ietf.org/html/rfc4880#section-6.2
+ lines.map do |line|
+ /\A((comment|version|messageid|hash|charset):.*|[0-9a-z\/=+]+)\Z/i =~ line
+ end.all?
+ end
+
+ def self.import_keys_from_attachments(list, mail)
+ mail.attachments.map do |attachment|
+ material = attachment.body.to_s
+
+ list.import_key(material) if self.is_armored_key?(material)
+ end
+ end
+
+ def self.import_key_from_body(list, mail)
+ if mail.parts.first.present?
+ key_material = mail.parts.first.body.to_s
+ else
+ key_material = mail.body.to_s
+ end
+
+ list.import_key(key_material) if self.is_armored_key?(key_material)
+ end
end
end
diff --git a/lib/schleuder/plugins/subscription_management.rb b/lib/schleuder/plugins/subscription_management.rb
index 5f9359e..c1e3aee 100644
--- a/lib/schleuder/plugins/subscription_management.rb
+++ b/lib/schleuder/plugins/subscription_management.rb
@@ -1,22 +1,29 @@
module Schleuder
module RequestPlugins
def self.subscribe(arguments, list, mail)
- sub = list.subscriptions.new(
- email: arguments.first,
- fingerprint: arguments.last
- )
+ email = arguments.shift
+ fingerprint = arguments.shift
+ if fingerprint.present?
+ fingerprint.sub!(/^0x/, '')
+ end
+ adminflag = arguments.shift
+ deliveryflag = arguments.shift
+
+ sub = list.subscribe(email, fingerprint, adminflag, deliveryflag)
- if sub
+ if sub.persisted?
I18n.t(
"plugins.subscription_management.subscribed",
email: sub.email,
- fingerprint: sub.fingerprint
+ fingerprint: sub.fingerprint,
+ admin: sub.admin,
+ delivery_enabled: sub.delivery_enabled
)
else
I18n.t(
"plugins.subscription_management.subscribing_failed",
email: sub.email,
- errors: sub.errors.full_messages
+ errors: sub.errors.full_messages.join(".\n")
)
end
end
@@ -70,7 +77,10 @@ module Schleuder
out << subs.map do |subscription|
# Fingerprints are at most 40 characters long, and lines shouldn't
# exceed 80 characters if possible.
- s = "#{subscription.email}\t0x#{subscription.fingerprint}"
+ s = subscription.email
+ if subscription.fingerprint.present?
+ s << "\t0x#{subscription.fingerprint}"
+ end
if ! subscription.delivery_enabled?
s << "\tDelivery disabled!"
end
diff --git a/lib/schleuder/plugins_runner.rb b/lib/schleuder/plugins_runner.rb
index f7ef3bd..399a16e 100644
--- a/lib/schleuder/plugins_runner.rb
+++ b/lib/schleuder/plugins_runner.rb
@@ -12,11 +12,14 @@ module Schleuder
else
@plugin_module = ListPlugins
end
+
+ check_listname_keyword
+
mail.keywords.each do |keyword, arguments|
@list.logger.debug "Running keyword '#{keyword}'"
if @list.admin_only?(keyword) && ! @list.from_admin?(@mail)
@list.logger.debug "Error: Keyword is admin-only, sent by non-admin"
- output << Schleuder::Errors::KeywordAdminOnly.new(keyword)
+ output << Schleuder::Errors::KeywordAdminOnly.new(keyword).to_s
next
end
output << run_plugin(keyword, arguments)
@@ -26,7 +29,11 @@ module Schleuder
def self.run_plugin(keyword, arguments)
command = keyword.gsub('-', '_')
- if @plugin_module.respond_to?(command)
+ if command == 'listname'
+ return nil
+ elsif ! @plugin_module.respond_to?(command)
+ return I18n.t('plugins.unknown_keyword', keyword: keyword)
+ else
out = @plugin_module.send(command, arguments, @list, @mail)
response = Array(out).flatten.join("\n\n")
if @list.keywords_admin_notify.include?(keyword)
@@ -37,9 +44,7 @@ module Schleuder
)
@list.logger.notify_admin("#{explanation}\n\n#{response}\n", nil, 'Notice')
end
- response
- else
- I18n.t('plugins.unknown_keyword', keyword: keyword)
+ return response
end
rescue => exc
# Log to system, this information is probably more useful for
@@ -54,6 +59,23 @@ module Schleuder
require file
end
end
+
+ def self.check_listname_keyword
+ return nil if @mail.keywords.blank?
+
+ listname_kw = @mail.keywords.assoc('listname')
+ if listname_kw.blank?
+ @mail.reply_to_signer I18n.t(:missing_listname_keyword_error)
+ exit
+ else
+ listname_args = listname_kw.last
+ if ! [@list.email, @list.request_address].include?(listname_args.first)
+ @mail.reply_to_signer I18n.t(:wrong_listname_keyword_error)
+ exit
+ end
+ end
+
+ end
end
end
diff --git a/lib/schleuder/runner.rb b/lib/schleuder/runner.rb
index 089e86a..e9638ae 100644
--- a/lib/schleuder/runner.rb
+++ b/lib/schleuder/runner.rb
@@ -68,7 +68,9 @@ module Schleuder
begin
subscription.send_mail(new)
rescue => exc
- logger.error exc
+ msg = I18n.t('errors.delivery_error',
+ { email: subscription.email, error: exc.to_s })
+ logger.error msg
end
end
end
diff --git a/lib/schleuder/subscription.rb b/lib/schleuder/subscription.rb
index 306f8b0..fb9e4e4 100644
--- a/lib/schleuder/subscription.rb
+++ b/lib/schleuder/subscription.rb
@@ -41,16 +41,26 @@ module Schleuder
end
mail = ensure_headers(mail)
- gpg_opts = self.list.gpg_sign_options.merge(encrypt: true, keys: {self.email => "0x#{self.fingerprint}"})
+ gpg_opts = self.list.gpg_sign_options
+
if self.key.blank?
if self.list.send_encrypted_only?
- self.list.logger.warn "Not sending to #{self.email}: no key present and sending plain text not allowed"
- notify_of_missed_message
+ notify_of_missed_message(:absent)
+ return false
+ else
+ list.logger.warn "Sending plaintext because no key is present!"
+ end
+ elsif ! self.key.usable?
+ if self.list.send_encrypted_only?
+ notify_of_missed_message(key.usability_issue)
return false
else
- gpg_opts.merge!(encrypt: false)
+ list.logger.warn "Sending plaintext because assigned key is #{key.usability_issue}!"
end
+ else
+ gpg_opts.merge!(encrypt: true, keys: {self.email => "0x#{self.fingerprint}"})
end
+
list.logger.info "Sending message to #{self.email}"
mail.gpg gpg_opts
mail.deliver
@@ -63,10 +73,11 @@ module Schleuder
mail
end
- def notify_of_missed_message
+ def notify_of_missed_message(reason)
+ self.list.logger.warn "Not sending to #{self.email}: key is unusable because it is #{reason} and sending plain text not allowed"
mail = ensure_headers(Mail.new)
mail.subject = I18n.t('notice')
- mail.body = I18n.t("missed_message_due_to_absent_key", list_email: self.list.email) + I18n.t('errors.signoff')
+ mail.body = I18n.t("missed_message_due_to_unusable_key", list_email: self.list.email) + I18n.t('errors.signoff')
mail.gpg self.list.gpg_sign_options
mail.deliver
end
diff --git a/lib/schleuder/version.rb b/lib/schleuder/version.rb
index 8286dee..7efefc1 100644
--- a/lib/schleuder/version.rb
+++ b/lib/schleuder/version.rb
@@ -1,3 +1,3 @@
module Schleuder
- VERSION = '3.0.0.beta17'
+ VERSION = '3.0.0'
end
diff --git a/locales/de.yml b/locales/de.yml
index 6788e43..6b419cf 100644
--- a/locales/de.yml
+++ b/locales/de.yml
@@ -10,8 +10,8 @@ de:
public_footer:
invalid: "enthält nicht druckbare Zeichen"
invalid_email: "ist keine valide E-Mail-Adresse"
- invalid_fingerprint: "ist kein valider Fingerprint"
- fingerprint_blank: 'Fingerprint der Liste ist nicht gesetzt, kann nicht arbeiten!'
+ invalid_fingerprint: "ist kein valider OpenPGP-Fingerabdruck"
+ fingerprint_blank: 'Fingerabdruck der Liste ist nicht gesetzt, kann nicht arbeiten!'
list_key_missing: 'Schlüssel der Liste nicht im Schlüsselring gefunden, kann nicht arbeiten!'
list_secret_key_missing: 'Geheimer Schlüssel der Liste nicht im Schlüsselring gefunden, kann nicht arbeiten!'
admins_missing: 'List hat keine Admins konfiguriert, kann nicht arbeiten!'
@@ -70,6 +70,7 @@ de:
must_be_greater_than_zero: "muss größer als null sein"
file_not_found: "Die Datei existiert nicht: '%{file}'."
not_pgp_mime: "Deine Email war nicht im pgp/mime-Format verschlüsselt."
+ delivery_error: "Beim Versenden einer Email an %{email} ist der folgende Fehler aufgetreten: %{error}"
plugins:
unknown_keyword: Unbekanntes Schlüsselwort '%{keyword}'.
plugin_failed: Das Schlüsselwort '%{keyword}' verursachte einen unbekannten Fehler. Die System-Administratoren wurden benachrichtigt.
@@ -91,10 +92,16 @@ de:
unsubscribing_failed: |
Abo für %{email} nicht gelöscht:
%{errors}
- subscribed: Abo für %{email} mit Fingerabdruck %{fingerprint} eingetragen.
+ subscribed: |
+ Abo für %{email} mit diesen Werten eingetragen:
+
+ Fingerabdruck: %{fingerprint}
+ Admin? %{admin}
+ Email-Zustellung aktiv? %{delivery_enabled}
subscribing_failed: |
Abo für %{email} nicht eingetragen:
- %{errors}
+
+ %{errors}.
list_of_subscriptions: Abos
fingerprint_set: Fingerabdruck für %{email} auf %{fingerprint} gesetzt.
setting_fingerprint_failed: |
@@ -105,6 +112,8 @@ de:
no_output_result: Deine Email ergab keinen Ausgabe-Text.
owner_forward_prefix: Die folgende Email ging für die Listen-Besitzer/innen ein.
no_keywords_error: Deine Email enthielt keine Schlüsselwörter, daher gab es nichts zu tun.
+ missing_listname_keyword_error: Deine Email enthielt nicht das notwendige X-LISTNAME-Schlüsselwort, daher wurde sie zurückgewiesen.
+ wrong_listname_keyword_error: Deine Email enthielt ein falsches X-LISTNAME-Schlüsselwort. Der Wert dieses Schlüsselworts muss der Emailadresse dieser Liste gleichen.
bounces_drop_all: Die angehängte Email hätte zurückgewiesen (bounced) werden sollen, wurde aber stillschweigend fallen gelassen, weil die Konfiguration dieser Liste definiert, dass für diese Liste nie Email zurückgewiesen werden soll.
bounces_drop_on_headers: "Die angehängte Email hätte zurückgewiesen (bounce) werden sollen, wurde aber stillschweigend fallen gelassen, weil diese Kopfzeile gefunden wurde: %{key}: %{value}"
bounces_notify_admins: "Die angehängte Email wurde mit folgender Nachricht zurückgewiesen:"
@@ -116,5 +125,14 @@ de:
check_keys: Schlüsselprüfung
check_keys_intro: "Bitte kümmere dich um die folgenden Schlüssel für Liste %{email}."
key_expires: "Läuft in %{days} Tagen ab:\n0x%{fingerprint} %{email}"
- key_unusable: "Ist %{trust}:\n0x%{fingerprint} %{email}"
- missed_message_due_to_absent_key: "Du hast eine Email von %{list_email} verpasst weil mit deinem Abo keinen (benutzbarer) OpenPGP-Schlüssel verknüpft ist. Bitte kümmere dich darum."
+ key_unusable: "Ist %{usability_issue}:\n0x%{fingerprint} %{email}"
+ missed_message_due_to_unusable_key: "Du hast eine Email von %{list_email} verpasst weil mit deinem Abo kein (benutzbarer) OpenPGP-Schlüssel verknüpft ist. Bitte kümmere dich darum."
+ refresh_keys: Schlüsselaktualisierung
+ refresh_keys_intro: "Die Aktualisierung aller Schlüssel des Schlüsselrings für Liste %{email} ergab dies:"
+ key_updated: Schlüssel %{fingerprint} wurde aktualisiert (%{states}).
+ import_states:
+ new_keys: neue Schlüssel
+ new_uids: neue User-IDs
+ new_subkeys: neue Unterschlüssel
+ new_signatures: neue Signaturen
+
diff --git a/locales/en.yml b/locales/en.yml
index 53b2d8c..b1c5758 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -10,7 +10,7 @@ en:
public_footer:
invalid: "includes non-printable characters"
invalid_email: "is not a valid email address"
- invalid_fingerprint: "is not a valid fingerprint"
+ invalid_fingerprint: "is not a valid OpenPGP-fingerprint"
list_fingerprint_missing: "List has no fingerprint configured, cannot run!"
list_key_missing: "List-key is missing in keyring, cannot run!"
list_secret_key_missing: 'Secret key of list is missing in keyring, cannot run!'
@@ -70,6 +70,7 @@ en:
must_be_greater_than_zero: "must be a number greater than zero"
file_not_found: "File not found: '%{file}'."
not_pgp_mime: "Message was not encrypted in the pgp/mime-format."
+ delivery_error: "The following error occurred while sending a message to %{email}: %{error}"
plugins:
unknown_keyword: Unknown keyword '%{keyword}'.
plugin_failed: Running keyword '%{keyword}' caused an unknown error. System-admins have been notified.
@@ -91,10 +92,16 @@ en:
unsubscribing_failed: |
Unsubscribing %{email} failed:
%{errors}
- subscribed: "%{email} has been subscribed with fingerprint %{fingerprint}."
+ subscribed: |
+ %{email} has been subscribed with these attributes:
+
+ Fingerprint: %{fingerprint}
+ Admin? %{admin}
+ Email-delivery enabled? %{delivery_enabled}
subscribing_failed: |
Subscribing %{email} failed:
- %{errors}
+
+ %{errors}.
list_of_subscriptions: Subscriptions
fingerprint_set: Fingerprint for %{email} set to %{fingerprint}.
setting_fingerprint_failed: |
@@ -105,6 +112,8 @@ en:
no_output_result: Your message resulted in no output.
owner_forward_prefix: The following message was received for the list-owners.
no_keywords_error: Your message didn't contain any keywords, thus there was nothing to do.
+ missing_listname_keyword_error: Your message didn't contain the mandatory X-LISTNAME-keyword, thus it was rejected.
+ wrong_listname_keyword_error: Your message didn't contain a wrong X-LISTNAME-keyword. The value of that keyword muss match the email address of this list.
bounces_drop_all: The attached message should have been bounced but was dropped without further notice because the list's configuration defines that no message should ever be bounced.
bounces_drop_on_headers: "The attached message should have been bounced but was dropped without further notice because it matched this header-line: %{key}: %{value}"
bounces_notify_admins: "The attached message was bounced with the following notice:"
@@ -116,5 +125,14 @@ en:
check_keys: Keys check
check_keys_intro: "Please take care of these keys for list %{email}.\n"
key_expires: "Expires in %{days} days:\n0x%{fingerprint} %{email}"
- key_unusable: "Is %{trust}:\n0x%{fingerprint} %{email}"
- missed_message_due_to_absent_key: "You missed an email from %{list_email} because your subscription isn't associated with a (usable) OpenPGP key. Please fix this."
+ key_unusable: "Is %{usability_issue}:\n0x%{fingerprint} %{email}"
+ missed_message_due_to_unusable_key: "You missed an email from %{list_email} because your subscription isn't associated with a (usable) OpenPGP key. Please fix this."
+ refresh_keys: Keys update
+ refresh_keys_intro: "Refreshing all keys from the keyring of list %{email} resulted in this:"
+ key_updated: Key %{fingerprint} was updated (%{states}).
+ import_states:
+ new_keys: new keys
+ new_uids: new user-IDs
+ new_subkeys: new subkeys
+ new_signatures: new signatures
+
diff --git a/man/schleuder-api-daemon.8 b/man/schleuder-api-daemon.8
index 74f12ec..91cd4c5 100644
--- a/man/schleuder-api-daemon.8
+++ b/man/schleuder-api-daemon.8
@@ -1,16 +1,13 @@
.\" generated with Ronn/v0.7.3
-.\" https://github.com/rtomayko/ronn/tree/0.7.3
+.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "SCHLEUDER\-API\-DAEMON" "8" "November 2016" "" ""
-.
-.SH "NAME"
-\fBschleuder\-api\-daemon\fR \- HTTP\-API of Schleuder(8)
+.TH "SCHLEUDER\-API\-DAEMON" "8" "January 2017" "" ""
.
.SH "SYNOPSIS"
\fBschleuder\-api\-daemon\fR
.
.SH "DESCRIPTION"
-schleuder\-api\-daemon provides the HTTP\-API of Schleuder(8) to clients\.
+schleuder\-api\-daemon provides the HTTP\-API of \fBschleuder(8)\fR to clients\.
.
.SH "ENVIRONMENT"
.
@@ -22,33 +19,29 @@ The available options are:
.
.TP
\fBhost\fR
-The hostname/IP to listen at\. This is overwritten with \'localhost\' unless \fBuse_tls\fR is true\.
+The hostname/IP to listen at\.
.
.TP
\fBport\fR
The port to listen at\. Default: 4443\.
.
.TP
-\fBuse_tls\fR
-Serve the API via HTTPS? Default: false\. Requires a usable certificate and key specified as \fBtls_cert_file\fR and \fBtls_key_file\fR\.
-.
-.TP
\fBtls_cert_file\fR
-Path to the file that contains the TLS\-certificate to use for HTTPS\. You can generate a new one with \fBschleuder cert generate\fR\.
+Path to the file that contains the TLS\-certificate to use\. You can generate a new one with \fBschleuder cert generate\fR\.
.
.TP
\fBtls_key_file\fR
-Path to the file that contains the TLS\-key to use for HTTPS\.
+Path to the file that contains the TLS\-key to use\.
.
.TP
\fBvalid_api_keys\fR
List of api_keys to allow access to the API\.
.
.SS "Clients"
-Available clients using the API are \fBschleuder\-cli\fR(8) and \fBschleuder\-web\fR\. URLs to their websites are listed in \fISEE ALSO\fR\.
+Available clients using the API are \fBschleuder\-cli\fR(8) and \fBschleuder\-web\fR\. URLs to their websites are listed below (\fISEE ALSO\fR)\.
.
.SH "BUGS"
-Known bugs are listed on the Schleuder bugtracker at \fIhttps://codecoop\.org/schleuder/schleuder3\fR
+Known bugs are listed on the Schleuder bugtracker at \fIhttps://0xacab\.org/schleuder/schleuder\fR
.
.SH "SEE ALSO"
\fBschleuder\fR(8), \fBschleuder\-cli\fR(8)
@@ -63,9 +56,9 @@ More extensive documentation for \fBschleuder\fR
.
.TP
\fBschleuder\-cli\fR, the command line interface for list\-management
-\fIhttps://codecoop\.org/schleuder/schleuder\-cli/\fR
+\fIhttps://0xacab\.org/schleuder/schleuder\-cli/\fR
.
.TP
\fBschleuder\-web\fR, the web interface for list\-management
-\fIhttps://codecoop\.org/schleuder/schleuder\-web/\fR
+\fIhttps://0xacab\.org/schleuder/schleuder\-web/\fR
diff --git a/man/schleuder-api-daemon.8.ron b/man/schleuder-api-daemon.8.ron
index 0e3a088..f674c8f 100644
--- a/man/schleuder-api-daemon.8.ron
+++ b/man/schleuder-api-daemon.8.ron
@@ -19,15 +19,13 @@ schleuder-api-daemon provides the HTTP-API of `schleuder(8)` to clients.
The available options are:
* `host`:
- The hostname/IP to listen at. This is overwritten with 'localhost' unless `use_tls` is true.
+ The hostname/IP to listen at.
* `port`:
The port to listen at. Default: 4443.
- * `use_tls`:
- Serve the API via HTTPS? Default: false. Requires a usable certificate and key specified as `tls_cert_file` and `tls_key_file`.
* `tls_cert_file`:
- Path to the file that contains the TLS-certificate to use for HTTPS. You can generate a new one with `schleuder cert generate`.
+ Path to the file that contains the TLS-certificate to use. You can generate a new one with `schleuder cert generate`.
* `tls_key_file`:
- Path to the file that contains the TLS-key to use for HTTPS.
+ Path to the file that contains the TLS-key to use.
* `valid_api_keys`:
List of api_keys to allow access to the API.
@@ -40,7 +38,7 @@ Available clients using the API are `schleuder-cli`(8) and `schleuder-web`. URLs
## BUGS
Known bugs are listed on the Schleuder bugtracker at
-<https://codecoop.org/schleuder/schleuder3>
+<https://0xacab.org/schleuder/schleuder>
## SEE ALSO
@@ -54,8 +52,8 @@ Known bugs are listed on the Schleuder bugtracker at
<https://schleuder.nadir.org/docs/>
* `schleuder-cli`, the command line interface for list-management:
- <https://codecoop.org/schleuder/schleuder-cli/>
+ <https://0xacab.org/schleuder/schleuder-cli/>
* `schleuder-web`, the web interface for list-management:
- <https://codecoop.org/schleuder/schleuder-web/>
+ <https://0xacab.org/schleuder/schleuder-web/>
diff --git a/man/schleuder.8 b/man/schleuder.8
index ed99bbf..a4bd1c1 100644
--- a/man/schleuder.8
+++ b/man/schleuder.8
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
-.TH "SCHLEUDER" "8" "December 2016" "" ""
+.TH "SCHLEUDER" "8" "January 2017" "" ""
.
.SH "NAME"
\fBschleuder\fR \- an email hub for groups
@@ -64,7 +64,7 @@ To connect the MTA with Schleuder it must pipe the incoming message into Schleud
For more information on how to integrate Schleuder with your existing mail setup, please read the Schleuder documentation online (\fISEE ALSO\fR)\.
.
.SS "Data storage"
-The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under \fIlists_dir\fR/\fIhostname\fR/\fIlistname\fR/ (\fIlists_dir\fR is read from schleuder\.yml, by default it is </var/schleuder/lists>)\. They can be used manually using gpg2\. Please be careful to maintain proper file permissions if you touch the files\.
+The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under \fIlists_dir\fR/\fIhostname\fR/\fIlistname\fR/ (\fIlists_dir\fR is read from schleuder\.yml, by default it is </var/lib/schleuder/lists>)\. They can be used manually using gpg2\. Please be careful to maintain proper file permissions if you touch the files\.
.
.P
In the list\-directory there’s also a list specific log\-file (might be missing if the log\-level is high and no error occurred yet)\.
@@ -135,7 +135,7 @@ Internal failure in incoming email processing\.
\fB/etc/schleuder/list\-defaults\.yml\fR: default path of default list settings
.
.IP "\(bu" 4
-\fB/var/schleuder/\fR default path of lists_dir
+\fB/var/lib/schleuder/lists\fR default path of lists_dir
.
.IP "\(bu" 4
\fB<lists_dir>\fR/\fB<hostname>\fR/`\fIlistname\fR: list internal data
@@ -149,7 +149,7 @@ Internal failure in incoming email processing\.
All configuration files are formatted as YAML\. See \fIhttp://www\.yaml\.org/\fR for more details\.
.
.SH "BUGS"
-Known bugs are listed on the Schleuder bugtracker at \fIhttps://codecoop\.org/schleuder/schleuder\fR
+Known bugs are listed on the Schleuder bugtracker at \fIhttps://0xacab\.org/schleuder/schleuder\fR
.
.SH "SEE ALSO"
\fBschleuder\-cli\fR(8), \fBgnupg\fR(7)\.
@@ -164,9 +164,9 @@ More extensive documentation for \fBschleuder\fR
.
.TP
\fBschleuder\-cli\fR, the command line interface for list\-management
-\fIhttps://codecoop\.org/schleuder/schleuder\-cli/\fR
+\fIhttps://0xacab\.org/schleuder/schleuder\-cli/\fR
.
.TP
\fBschleuder\-web\fR, the web interface for list\-management
-\fIhttps://codecoop\.org/schleuder/schleuder\-web/\fR
+\fIhttps://0xacab\.org/schleuder/schleuder\-web/\fR
diff --git a/man/schleuder.8.ron b/man/schleuder.8.ron
index 328ce7d..82b6fb8 100644
--- a/man/schleuder.8.ron
+++ b/man/schleuder.8.ron
@@ -47,7 +47,7 @@ setup, please read the Schleuder documentation online ([SEE ALSO][]).
### Data storage
-The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under <lists_dir>/<hostname>/<listname>/ (<lists_dir> is read from schleuder.yml, by default it is </var/schleuder/lists>). They can be used manually using gpg2. Please be careful to maintain proper file permissions if you touch the files.
+The keyrings for each list are standard GnuPG keyrings and sit in the filesystem under <lists_dir>/<hostname>/<listname>/ (<lists_dir> is read from schleuder.yml, by default it is </var/lib/schleuder/lists>). They can be used manually using gpg2. Please be careful to maintain proper file permissions if you touch the files.
In the list-directory there’s also a list specific log-file (might be missing if the log-level is high and no error occurred yet).
@@ -104,7 +104,7 @@ Write to <listname-owner at hostname> to contact the list-owner(s) even if you don'
* `/etc/schleuder/list-defaults.yml`:
default path of default list settings
- * `/var/schleuder/`
+ * `/var/lib/schleuder/lists`
default path of lists_dir
* `<lists_dir>`/`<hostname>`/`<listname>:
@@ -119,7 +119,7 @@ All configuration files are formatted as YAML. See
## BUGS
Known bugs are listed on the Schleuder bugtracker at
-<https://codecoop.org/schleuder/schleuder>
+<https://0xacab.org/schleuder/schleuder>
## SEE ALSO
@@ -133,7 +133,7 @@ Known bugs are listed on the Schleuder bugtracker at
<https://schleuder.nadir.org/docs/>
* `schleuder-cli`, the command line interface for list-management:
- <https://codecoop.org/schleuder/schleuder-cli/>
+ <https://0xacab.org/schleuder/schleuder-cli/>
* `schleuder-web`, the web interface for list-management:
- <https://codecoop.org/schleuder/schleuder-web/>
+ <https://0xacab.org/schleuder/schleuder-web/>
diff --git a/spec/fixtures/expired_key.txt b/spec/fixtures/expired_key.txt
new file mode 100644
index 0000000..4e64973
--- /dev/null
+++ b/spec/fixtures/expired_key.txt
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBExlilkBCACb2AQyclf7latAIE1kCTfKQ9jmcKyf959ymyhzoeNmBDpKjILC
+7MOXtICo/V/xAzhWBK/vT9+56brGUBTugnW3yK+zllQprI3kIYaRS1SrbmKVwVse
+9qLVUL1BssohFaEeQqT4MNh62ziJymqCguGEGXpYlEqzEDTmmhTANiPKRBZDrdfq
+3FU3OJUMTGzuG34mKmXMRr0azprF228LUujMMKyWhG1hxh3El04C4jPuMSbaVcwN
+E2rgIg8jaNAQuSyXkaprPZ8/nRG8UFGRtCMEIEh6Ou6KybF1NI9LQKCwcsGcLHKU
+2u/8vOCExxdwl9Jjlqmof4FQV7bT++6SC0n3ABEBAAG0EWV4cGlyZWQgPGJsYUBm
+b28+iQE+BBMBAgAoBQJMZYpZAhsDBQkAAVGABgsJCAcDAgYVCAIJCgsEFgIDAQIe
+AQIXgAAKCRD3Gj+EEtg4iV6yB/4uDLoN1+TswtGjpUlu2CyjHe7pb05dAU4sWfTV
+I+fxBuyEo+cf/23nOeoGyltBDR0heSg3TIfXQrbWD4WoVsOXPaT0fq6UEzeadkmn
+A5NN3PGkv46o3ZSF1ltkY9ybMgnmRLHYCojSu5bSBMRVyurr0ozwNRPtFUTka8Lj
+wxiwDJ394D5y6PjL56FPkUdKydzFGV2ptSKsqyAJvMBeGlQ4I6TpiBx0Lz2A1Qn+
+4uXgTVPqgalC5YKTTTjOfQcieOOeqtI0LHqDpS/DIPLnwTUCN8OL2TQIeDudm3YI
+P8FCKvImh840vTbpgFSgQeaJzJFv9UrloNyyvbVtaeoxnxoBuQENBExlilkBCADD
+Gbf0TEs5HDpbUC78tJetGWipmQRTq8gS9dDoKjG5mvlpFARPTAJvewgI7DICXMtV
+Y8P8eFbsZDGMETduunadutDvyP9J/wuknYHJkE5jJeZjEKyofrWM1BHxHb2bkNU0
+hr8EVNvEhVjAb62cJPj5emi/3UhynLoJrrO+AZSr9Bq2QW38ntSUxTXM7VbGUR1M
+WZSoJ+gEg/IeR2HJziCyb1mhlvo9pZqJT959bNVQ/xU4NHtYca5cV3X88Ald4Dwz
+2HM+PIBbsiz3C3fIMNrRxRvMCU4PgsjSZFoRxcdDRT5OjBxNgQ4UmWQH0sTFw9Tx
+8B2OtwuguGwaXIV3FpRDABEBAAGJASUEGAECAA8FAkxlilkCGwwFCQABUYAACgkQ
+9xo/hBLYOImqngf/QQt0S6tlJ9OMknmAr2pNg+DQkCqTNaSk/iQj4leGGwkpRVoH
+5VFTZ0nkmZjcDTTrCjj5rEDaRo8Q38KsB1po8P25ABoK0b28rHw8I3L2Byl1+IB7
++dNKVyFJVfHAYOQsbI/p/2KdtZZIbpxnRVHY+Vlu2p/fx3JqPmqCiaVMcUFw55Qj
+SKaI+omfnN0WGyrK1Rub925Lch0vZkxVwmTse7qufg0iwTREMy9VZfMavMhkAtM2
+AsiEG8j3mwU9JQfBkXqtWxG2VvtsBJ0rLafh5sR3NgjtR2vbSdzJRCV2xO4z6Drh
+3Pug3ReHmcLUFTDPE+vmeH+xpEZ2nhvNRFDhmQ==
+=Nao0
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/fixtures/expired_key_extended.txt b/spec/fixtures/expired_key_extended.txt
new file mode 100644
index 0000000..1587675
--- /dev/null
+++ b/spec/fixtures/expired_key_extended.txt
@@ -0,0 +1,29 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: SKS 1.1.6+
+Comment: Hostname: sks.spodhuis.org
+
+mQENBExlilkBCACb2AQyclf7latAIE1kCTfKQ9jmcKyf959ymyhzoeNmBDpKjILC7MOXtICo
+/V/xAzhWBK/vT9+56brGUBTugnW3yK+zllQprI3kIYaRS1SrbmKVwVse9qLVUL1BssohFaEe
+QqT4MNh62ziJymqCguGEGXpYlEqzEDTmmhTANiPKRBZDrdfq3FU3OJUMTGzuG34mKmXMRr0a
+zprF228LUujMMKyWhG1hxh3El04C4jPuMSbaVcwNE2rgIg8jaNAQuSyXkaprPZ8/nRG8UFGR
+tCMEIEh6Ou6KybF1NI9LQKCwcsGcLHKU2u/8vOCExxdwl9Jjlqmof4FQV7bT++6SC0n3ABEB
+AAG0EWV4cGlyZWQgPGJsYUBmb28+iQFVBBMBAgA/AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIe
+AQIXgBYhBJh2nooQkfNr2IQD7PcaP4QS2DiJBQJYgOG1BQkMHGNXAAoJEPcaP4QS2DiJxiUH
+/2xYCT+mldoaWMyJlBleSjx0wtWeGNayMuv0RjU2pIkBmslYp/ZZkt+JC3thpJneBW5pJuRB
+qeUi7yTigdtOrH47g+FfIDCY89ymTDbYW4vpNnnsV7s+ke8tbEmTtMpjFypoTvbnGYlq8VLz
+87eRcsLwADOAJfFBdnDD0tyNCrUY/V+Ti/ZI4bHoFA14t8Hm7MIDkfB6sVfzpnZd1ACj+klv
+1rfq+9m56lsavS/dM+BlhwfRORT9cenuBs++AXXWvh1CZW/J06kFECG+ptqU5246nQcjE5GX
+W8sC+TSq7OXSTQAJDF+aWqjA/JrbpSf/3r2/IU+mGH2Bwi7B5uBN6lG5AQ0ETGWKWQEIAMMZ
+t/RMSzkcOltQLvy0l60ZaKmZBFOryBL10OgqMbma+WkUBE9MAm97CAjsMgJcy1Vjw/x4Vuxk
+MYwRN266dp260O/I/0n/C6SdgcmQTmMl5mMQrKh+tYzUEfEdvZuQ1TSGvwRU28SFWMBvrZwk
++Pl6aL/dSHKcugmus74BlKv0GrZBbfye1JTFNcztVsZRHUxZlKgn6ASD8h5HYcnOILJvWaGW
++j2lmolP3n1s1VD/FTg0e1hxrlxXdfzwCV3gPDPYcz48gFuyLPcLd8gw2tHFG8wJTg+CyNJk
+WhHFx0NFPk6MHE2BDhSZZAfSxMXD1PHwHY63C6C4bBpchXcWlEMAEQEAAYkBJQQYAQIADwUC
+TGWKWQIbDAUJAAFRgAAKCRD3Gj+EEtg4iaqeB/9BC3RLq2Un04ySeYCvak2D4NCQKpM1pKT+
+JCPiV4YbCSlFWgflUVNnSeSZmNwNNOsKOPmsQNpGjxDfwqwHWmjw/bkAGgrRvbysfDwjcvYH
+KXX4gHv500pXIUlV8cBg5Cxsj+n/Yp21lkhunGdFUdj5WW7an9/Hcmo+aoKJpUxxQXDnlCNI
+poj6iZ+c3RYbKsrVG5v3bktyHS9mTFXCZOx7uq5+DSLBNEQzL1Vl8xq8yGQC0zYCyIQbyPeb
+BT0lB8GReq1bEbZW+2wEnSstp+HmxHc2CO1Ha9tJ3MlEJXbE7jPoOuHc+6DdF4eZwtQVMM8T
+6+Z4f7GkRnaeG81EUOGZ
+=4p8E
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/schleuder.yml b/spec/schleuder.yml
index 6fc619b..abb0e30 100644
--- a/spec/schleuder.yml
+++ b/spec/schleuder.yml
@@ -6,3 +6,4 @@ lists_dir: /tmp/schleuder-test/
listlogs_dir: /tmp/schleuder-test/
smtp_settings:
port: 2523
+keyserver: hkp://127.0.0.1:11371
diff --git a/spec/schleuder/integration/cli_spec.rb b/spec/schleuder/integration/cli_spec.rb
index 664d86a..8477481 100644
--- a/spec/schleuder/integration/cli_spec.rb
+++ b/spec/schleuder/integration/cli_spec.rb
@@ -87,4 +87,43 @@ describe 'cli' do
expect(subscription_emails).to eq ['schleuder2 at example.org']
end
end
+
+ context '#refresh_keys' do
+ it 'updates one key from the keyserver' do
+ list = create(:list)
+ list.subscribe("admin at example.org", nil, true)
+ list.import_key(File.read("spec/fixtures/expired_key.txt"))
+
+ with_sks_mock do
+ Cli.new.refresh_keys
+ end
+ mail = Mail::TestMailer.deliveries.first
+
+ expect(Mail::TestMailer.deliveries.length).to eq 1
+ expect(mail.to_s).to include("Refreshing all keys from the keyring of list #{list.email} resulted in this")
+ expect(mail.to_s).to include("98769E8A1091F36BD88403ECF71A3F8412D83889 was updated (new signatures)")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it 'reports errors from refreshing keys' do
+ list = create(:list)
+ list.subscribe("admin at example.org", nil, true)
+ list.import_key(File.read("spec/fixtures/expired_key.txt"))
+
+ Cli.new.refresh_keys
+ mail = Mail::TestMailer.deliveries.first
+
+ expect(Mail::TestMailer.deliveries.length).to eq 1
+ expect(mail.to_s).to include("Refreshing all keys from the keyring of list #{list.email} resulted in this")
+ if GPGME::Ctx.sufficient_gpg_version?('2.1')
+ expect(mail.to_s).to include("keyserver refresh failed: No keyserver available")
+ else
+ # The wording differs slightly among versions.
+ expect(mail.to_s).to match(/gpgkeys: .* error .* connect/)
+ end
+
+ teardown_list_and_mailer(list)
+ end
+ end
end
diff --git a/spec/schleuder/integration/keywords_spec.rb b/spec/schleuder/integration/keywords_spec.rb
new file mode 100644
index 0000000..703b34b
--- /dev/null
+++ b/spec/schleuder/integration/keywords_spec.rb
@@ -0,0 +1,269 @@
+require "spec_helper"
+
+describe "user sends keyword" do
+ it "x-subscribe without attributes" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-listname: #{list.email}\nX-SUBSCRIBE: test at example.org"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.request_address, list)
+ subscription = list.subscriptions.where(email: 'test at example.org').first
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("test at example.org has been subscribed")
+ expect(message.to_s).to match(/Fingerprint:\s*$/)
+ expect(message.to_s).to include("Admin? false")
+ expect(message.to_s).to include("Email-delivery enabled? true")
+
+ expect(subscription).to be_present
+ expect(subscription.fingerprint).to be_blank
+ expect(subscription.admin).to eql(false)
+ expect(subscription.delivery_enabled).to eql(true)
+
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-subscribe with attributes" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-listname: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint} true false"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.request_address, list)
+ subscription = list.subscriptions.where(email: 'test at example.org').first
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("test at example.org has been subscribed")
+ expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint.downcase}/)
+ expect(message.to_s).to include("Admin? true")
+ expect(message.to_s).to include("Email-delivery enabled? false")
+
+ expect(subscription).to be_present
+ expect(subscription.fingerprint).to eql(list.fingerprint.downcase)
+ expect(subscription.admin).to eql(true)
+ expect(subscription.delivery_enabled).to eql(false)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-add-key with inline key-material" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ keymaterial = File.read('spec/fixtures/example_key.txt')
+ mail.body = "x-listname: #{list.email}\nX-ADD-KEY:\n#{keymaterial}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.request_address, list)
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE: imported")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-add-key with attached key-material" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-listname: #{list.email}\nX-ADD-KEY:"
+ mail.add_file('spec/fixtures/example_key.txt')
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.request_address, list)
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE: imported")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend" do
+ list = create(:list, public_footer: "-- \nblablabla")
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ mail.body = "x-listname: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ resent_message = raw.verify
+ resent_message_body = resent_message.parts.map { |p| p.body.to_s }.join
+ raw = Mail::TestMailer.deliveries.last
+ message = raw.setup(list.email, list)
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("Resent: Unencrypted to someone at example.org")
+ expect(resent_message.to).to include("someone at example.org")
+ expect(resent_message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(resent_message_body).to eql(content_body + list.public_footer.to_s)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend without x-listname" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ mail.body = "X-resend: someone at example.org\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.email, list)
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(message.to_s).to include("Your message didn't contain the mandatory X-LISTNAME-keyword, thus it was rejected.")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend with wrong x-listname" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ mail.body = "x-listname: somethingelse at example.org\nX-resend: someone at example.org\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = raw.setup(list.email, list)
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(message.to_s).to include("Your message didn't contain a wrong X-LISTNAME-keyword. The value of that keyword muss match the email address of this list.")
+
+ teardown_list_and_mailer(list)
+ end
+end
+
+
+
diff --git a/spec/schleuder/runner_spec.rb b/spec/schleuder/runner_spec.rb
index 45db7d9..e229ccc 100644
--- a/spec/schleuder/runner_spec.rb
+++ b/spec/schleuder/runner_spec.rb
@@ -128,9 +128,40 @@ describe Schleuder::Runner do
end
end
- def teardown_list_and_mailer(list)
- FileUtils.rm_rf(list.listdir)
- Mail::TestMailer.deliveries.clear
+ it "delivers a signed error message if a subscription's key is expired on a encrypted-only list" do
+ list = create(:list, send_encrypted_only: true)
+ list.subscribe("admin at example.org", nil, true, false)
+ list.subscribe("expired at example.org", '98769E8A1091F36BD88403ECF71A3F8412D83889')
+ key = File.read("spec/fixtures/expired_key.txt")
+ list.import_key(key)
+ mail = File.read("spec/fixtures/mails/plain/thunderbird.eml")
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+ verified = message.verify
+ signature_fingerprints = verified.signatures.map(&:fpr)
+
+ expect(Mail::TestMailer.deliveries.size).to eq 1
+ expect(message.to).to include('expired at example.org')
+ expect(message.to_s).to include("You missed an email from ")
+ expect(signature_fingerprints).to eq([list.fingerprint])
+
+ teardown_list_and_mailer(list)
end
+
+ it "delivers a signed error message if a subscription's key is not available on a encrypted-only list" do
+ list = create(:list, send_encrypted_only: true)
+ list.subscribe("admin at example.org", 'AAAAAAAABBBBBBBBBCCCCCCCCCDDDDDDDDEEEEEE', true)
+ mail = File.read("spec/fixtures/mails/plain/thunderbird.eml")
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+
+ expect(message.to).to eq ['admin at example.org']
+ expect(message.to_s).to include("You missed an email from #{list.email} ")
+
+ teardown_list_and_mailer(list)
+ end
+
end
end
diff --git a/spec/schleuder/unit/list_spec.rb b/spec/schleuder/unit/list_spec.rb
index 7fe1c74..040db80 100644
--- a/spec/schleuder/unit/list_spec.rb
+++ b/spec/schleuder/unit/list_spec.rb
@@ -92,7 +92,7 @@ describe Schleuder::List do
list = build(:list, fingerprint: "&$$$$67923AAA")
expect(list).not_to be_valid
- expect(list.errors.messages[:fingerprint]).to include("is not a valid fingerprint")
+ expect(list.errors.messages[:fingerprint]).to include("is not a valid OpenPGP-fingerprint")
end
BOOLEAN_LIST_ATTRIBUTES.each do |list_attribute|
diff --git a/spec/schleuder/unit/subscription_spec.rb b/spec/schleuder/unit/subscription_spec.rb
index 72ee3c8..0a5d608 100644
--- a/spec/schleuder/unit/subscription_spec.rb
+++ b/spec/schleuder/unit/subscription_spec.rb
@@ -72,7 +72,7 @@ describe Schleuder::Subscription do
subscription = build(:subscription, fingerprint: "&$$$$123AAA")
expect(subscription).not_to be_valid
- expect(subscription.errors.messages[:fingerprint]).to include("is not a valid fingerprint")
+ expect(subscription.errors.messages[:fingerprint]).to include("is not a valid OpenPGP-fingerprint")
end
BOOLEAN_SUBSCRIPTION_ATTRIBUTES.each do |subscription_attribute|
diff --git a/spec/sks-mock.rb b/spec/sks-mock.rb
new file mode 100755
index 0000000..6d7bd17
--- /dev/null
+++ b/spec/sks-mock.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require 'sinatra/base'
+
+class SksMock < Sinatra::Base
+ set :environment, :production
+ set :port, 11371
+ set :bind, '127.0.0.1'
+ set :logging, true
+
+ get '/status' do
+ 'ok'
+ end
+
+ get '/pks/lookup' do
+ case params['search']
+ when '0x98769E8A1091F36BD88403ECF71A3F8412D83889'
+ File.read('spec/fixtures/expired_key_extended.txt')
+ else
+ 404
+ end
+ end
+
+ # Run this class as application
+ run!
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2024bcd..ec293f8 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,6 +4,7 @@ ENV["SCHLEUDER_LIST_DEFAULTS"] = "etc/list-defaults.yml"
require 'bundler/setup'
Bundler.setup
require 'schleuder'
+require 'schleuder/cli'
require 'database_cleaner'
require 'factory_girl'
@@ -54,6 +55,14 @@ RSpec.configure do |config|
File.join(Conf.lists_dir, 'smtp-daemon-output')
end
+ def with_sks_mock
+ pid = Process.spawn('spec/sks-mock.rb', [:out, :err] => ["/tmp/sks-mock.log", 'w'])
+ sleep 1
+ yield
+ Process.kill 'TERM', pid
+ Process.wait pid
+ end
+
def start_smtp_daemon
if ! File.directory?(smtp_daemon_outputdir)
FileUtils.mkdir_p(smtp_daemon_outputdir)
@@ -88,4 +97,9 @@ RSpec.configure do |config|
Mail.defaults do
delivery_method :test
end
+
+ def teardown_list_and_mailer(list)
+ FileUtils.rm_rf(list.listdir)
+ Mail::TestMailer.deliveries.clear
+ end
end
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/schleuder.git
More information about the Pkg-ruby-extras-commits
mailing list