summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2009-06-14 14:48:34 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2009-06-14 14:48:34 +0000
commitb3afde14fa6046a022eda73edee85ab48171909f (patch)
tree89dc8da3ab3f51ab9ed88fcc04e216822410aebe
parentc48193f8c113650c14d98b2f96279d57118df973 (diff)
downloadredmine-b3afde14fa6046a022eda73edee85ab48171909f.tar.gz
redmine-b3afde14fa6046a022eda73edee85ab48171909f.zip
Ability to accept incoming emails from unknown users (#2230, #3003).
An option lets you specify how to handle emails from unknown users: * ignore: the email is ignored (previous and default behaviour) * accept: the sender is considered as an anonymous user * create: a user account is created (username/password are sent back to the user) Permissions have to be consistent with the chosen option. Eg. if you choose 'create', the 'Non member' role must have the 'Add issues' permission so that an issue can be created by an unknown user via email. If you choose 'accept', the 'Anonymous' role must have this permission. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2789 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/models/mail_handler.rb48
-rw-r--r--extra/mail_handler/rdm-mailhandler.rb16
-rw-r--r--lib/tasks/email.rake18
-rw-r--r--test/fixtures/mail_handler/ticket_by_unknown_user.eml18
-rw-r--r--test/unit/mail_handler_test.rb39
5 files changed, 128 insertions, 11 deletions
diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb
index 027665329..6d02ae3ed 100644
--- a/app/models/mail_handler.rb
+++ b/app/models/mail_handler.rb
@@ -38,15 +38,34 @@ class MailHandler < ActionMailer::Base
end
# Processes incoming emails
+ # Returns the created object (eg. an issue, a message) or false
def receive(email)
@email = email
- @user = User.active.find_by_mail(email.from.to_a.first.to_s.strip)
- unless @user
- # Unknown user => the email is ignored
- # TODO: ability to create the user's account
- logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info
+ @user = User.find_by_mail(email.from.to_a.first.to_s.strip)
+ if @user && !@user.active?
+ logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
return false
end
+ if @user.nil?
+ # Email was submitted by an unknown user
+ case @@handler_options[:unknown_user]
+ when 'accept'
+ @user = User.anonymous
+ when 'create'
+ @user = MailHandler.create_user_from_email(email)
+ if @user
+ logger.info "MailHandler: [#{@user.login}] account created" if logger && logger.info
+ Mailer.deliver_account_information(@user, @user.password)
+ else
+ logger.error "MailHandler: could not create account for [#{email.from.first}]" if logger && logger.error
+ return false
+ end
+ else
+ # Default behaviour, emails from unknown users are ignored
+ logger.info "MailHandler: ignoring email from unknown user [#{email.from.first}]" if logger && logger.info
+ return false
+ end
+ end
User.current = @user
dispatch
end
@@ -239,4 +258,23 @@ class MailHandler < ActionMailer::Base
def self.full_sanitizer
@full_sanitizer ||= HTML::FullSanitizer.new
end
+
+ # Creates a user account for the +email+ sender
+ def self.create_user_from_email(email)
+ addr = email.from_addrs.to_a.first
+ if addr && !addr.spec.blank?
+ user = User.new
+ user.mail = addr.spec
+
+ names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split
+ user.firstname = names.shift
+ user.lastname = names.join(' ')
+ user.lastname = '-' if user.lastname.blank?
+
+ user.login = user.mail
+ user.password = ActiveSupport::SecureRandom.hex(5)
+ user.language = Setting.default_language
+ user.save ? user : nil
+ end
+ end
end
diff --git a/extra/mail_handler/rdm-mailhandler.rb b/extra/mail_handler/rdm-mailhandler.rb
index 93484ca34..2ee5d73d3 100644
--- a/extra/mail_handler/rdm-mailhandler.rb
+++ b/extra/mail_handler/rdm-mailhandler.rb
@@ -15,6 +15,11 @@
# -k, --key Redmine API key
#
# General options:
+# --unknown-user=ACTION how to handle emails from an unknown user
+# ACTION can be one of the following values:
+# ignore: email is ignored (default)
+# accept: accept as anonymous user
+# create: create a user account
# -h, --help show this help
# -v, --verbose show extra information
# -V, --version show version information and exit
@@ -64,7 +69,7 @@ end
class RedmineMailHandler
VERSION = '0.1'
- attr_accessor :verbose, :issue_attributes, :allow_override, :url, :key
+ attr_accessor :verbose, :issue_attributes, :allow_override, :uknown_user, :url, :key
def initialize
self.issue_attributes = {}
@@ -80,7 +85,8 @@ class RedmineMailHandler
[ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT],
[ '--category', GetoptLong::REQUIRED_ARGUMENT],
[ '--priority', GetoptLong::REQUIRED_ARGUMENT],
- [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT]
+ [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT],
+ [ '--unknown-user', GetoptLong::REQUIRED_ARGUMENT]
)
opts.each do |opt, arg|
@@ -99,6 +105,8 @@ class RedmineMailHandler
self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup
when '--allow-override'
self.allow_override = arg.dup
+ when '--unknown-user'
+ self.unknown_user = arg.dup
end
end
@@ -108,7 +116,9 @@ class RedmineMailHandler
def submit(email)
uri = url.gsub(%r{/*$}, '') + '/mail_handler'
- data = { 'key' => key, 'email' => email, 'allow_override' => allow_override }
+ data = { 'key' => key, 'email' => email,
+ 'allow_override' => allow_override,
+ 'unknown_user' => unknown_user }
issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
debug "Posting to #{uri}..."
diff --git a/lib/tasks/email.rake b/lib/tasks/email.rake
index 0f74d6bd3..487ce506b 100644
--- a/lib/tasks/email.rake
+++ b/lib/tasks/email.rake
@@ -21,6 +21,13 @@ namespace :redmine do
desc <<-END_DESC
Read an email from standard input.
+General options:
+ unknown_user=ACTION how to handle emails from an unknown user
+ ACTION can be one of the following values:
+ ignore: email is ignored (default)
+ accept: accept as anonymous user
+ create: create a user account
+
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
@@ -47,6 +54,7 @@ END_DESC
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
+ options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
MailHandler.receive(STDIN.read, options)
end
@@ -54,6 +62,13 @@ END_DESC
desc <<-END_DESC
Read emails from an IMAP server.
+General options:
+ unknown_user=ACTION how to handle emails from an unknown user
+ ACTION can be one of the following values:
+ ignore: email is ignored (default)
+ accept: accept as anonymous user
+ create: create a user account
+
Available IMAP options:
host=HOST IMAP server host (default: 127.0.0.1)
port=PORT IMAP server port (default: 143)
@@ -61,7 +76,7 @@ Available IMAP options:
username=USERNAME IMAP account
password=PASSWORD IMAP password
folder=FOLDER IMAP folder to read (default: INBOX)
-
+
Issue attributes control options:
project=PROJECT identifier of the target project
status=STATUS name of the target status
@@ -107,6 +122,7 @@ END_DESC
options = { :issue => {} }
%w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
+ options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
Redmine::IMAP.check(imap_options, options)
end
diff --git a/test/fixtures/mail_handler/ticket_by_unknown_user.eml b/test/fixtures/mail_handler/ticket_by_unknown_user.eml
new file mode 100644
index 000000000..a7abb05e0
--- /dev/null
+++ b/test/fixtures/mail_handler/ticket_by_unknown_user.eml
@@ -0,0 +1,18 @@
+Return-Path: <john.doe@somenet.foo>
+Received: from osiris ([127.0.0.1])
+ by OSIRIS
+ with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
+Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
+From: "John Doe" <john.doe@somenet.foo>
+To: <redmine@somenet.foo>
+Subject: Ticket by unknown user
+Date: Sun, 22 Jun 2008 12:28:07 +0200
+MIME-Version: 1.0
+Content-Type: text/plain;
+ format=flowed;
+ charset="iso-8859-1";
+ reply-type=original
+Content-Transfer-Encoding: 7bit
+
+This is a ticket submitted by an unknown user.
+
diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb
index 295b8ae68..2d780d93d 100644
--- a/test/unit/mail_handler_test.rb
+++ b/test/unit/mail_handler_test.rb
@@ -1,5 +1,5 @@
-# redMine - project management software
-# Copyright (C) 2006-2007 Jean-Philippe Lang
+# Redmine - project management software
+# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -130,6 +130,41 @@ class MailHandlerTest < Test::Unit::TestCase
assert_equal 1, issue.watchers.size
end
+ def test_add_issue_by_unknown_user
+ assert_no_difference 'User.count' do
+ assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
+ end
+ end
+
+ def test_add_issue_by_anonymous_user
+ Role.anonymous.add_permission!(:add_issues)
+ assert_no_difference 'User.count' do
+ issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
+ assert issue.is_a?(Issue)
+ assert issue.author.anonymous?
+ end
+ end
+
+ def test_add_issue_by_created_user
+ Setting.default_language = 'en'
+ assert_difference 'User.count' do
+ issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
+ assert issue.is_a?(Issue)
+ assert issue.author.active?
+ assert_equal 'john.doe@somenet.foo', issue.author.mail
+ assert_equal 'John', issue.author.firstname
+ assert_equal 'Doe', issue.author.lastname
+
+ # account information
+ email = ActionMailer::Base.deliveries.first
+ assert_not_nil email
+ assert email.subject.include?('account activation')
+ login = email.body.match(/\* Login: (.*)$/)[1]
+ password = email.body.match(/\* Password: (.*)$/)[1]
+ assert_equal issue.author, User.try_to_login(login, password)
+ end
+ end
+
def test_add_issue_without_from_header
Role.anonymous.add_permission!(:add_issues)
assert_equal false, submit_email('ticket_without_from_header.eml')