##// END OF EJS Templates
Ability to accept incoming emails from unknown users (#2230, #3003)....
Jean-Philippe Lang -
r2689:b3afde14fa60
parent child
Show More
@@ -0,0 +1,18
1 Return-Path: <john.doe@somenet.foo>
2 Received: from osiris ([127.0.0.1])
3 by OSIRIS
4 with hMailServer ; Sun, 22 Jun 2008 12:28:07 +0200
5 Message-ID: <000501c8d452$a95cd7e0$0a00a8c0@osiris>
6 From: "John Doe" <john.doe@somenet.foo>
7 To: <redmine@somenet.foo>
8 Subject: Ticket by unknown user
9 Date: Sun, 22 Jun 2008 12:28:07 +0200
10 MIME-Version: 1.0
11 Content-Type: text/plain;
12 format=flowed;
13 charset="iso-8859-1";
14 reply-type=original
15 Content-Transfer-Encoding: 7bit
16
17 This is a ticket submitted by an unknown user.
18
@@ -38,15 +38,34 class MailHandler < ActionMailer::Base
38 38 end
39 39
40 40 # Processes incoming emails
41 # Returns the created object (eg. an issue, a message) or false
41 42 def receive(email)
42 43 @email = email
43 @user = User.active.find_by_mail(email.from.to_a.first.to_s.strip)
44 unless @user
45 # Unknown user => the email is ignored
46 # TODO: ability to create the user's account
47 logger.info "MailHandler: email submitted by unknown user [#{email.from.first}]" if logger && logger.info
44 @user = User.find_by_mail(email.from.to_a.first.to_s.strip)
45 if @user && !@user.active?
46 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]" if logger && logger.info
48 47 return false
49 48 end
49 if @user.nil?
50 # Email was submitted by an unknown user
51 case @@handler_options[:unknown_user]
52 when 'accept'
53 @user = User.anonymous
54 when 'create'
55 @user = MailHandler.create_user_from_email(email)
56 if @user
57 logger.info "MailHandler: [#{@user.login}] account created" if logger && logger.info
58 Mailer.deliver_account_information(@user, @user.password)
59 else
60 logger.error "MailHandler: could not create account for [#{email.from.first}]" if logger && logger.error
61 return false
62 end
63 else
64 # Default behaviour, emails from unknown users are ignored
65 logger.info "MailHandler: ignoring email from unknown user [#{email.from.first}]" if logger && logger.info
66 return false
67 end
68 end
50 69 User.current = @user
51 70 dispatch
52 71 end
@@ -239,4 +258,23 class MailHandler < ActionMailer::Base
239 258 def self.full_sanitizer
240 259 @full_sanitizer ||= HTML::FullSanitizer.new
241 260 end
261
262 # Creates a user account for the +email+ sender
263 def self.create_user_from_email(email)
264 addr = email.from_addrs.to_a.first
265 if addr && !addr.spec.blank?
266 user = User.new
267 user.mail = addr.spec
268
269 names = addr.name.blank? ? addr.spec.gsub(/@.*$/, '').split('.') : addr.name.split
270 user.firstname = names.shift
271 user.lastname = names.join(' ')
272 user.lastname = '-' if user.lastname.blank?
273
274 user.login = user.mail
275 user.password = ActiveSupport::SecureRandom.hex(5)
276 user.language = Setting.default_language
277 user.save ? user : nil
278 end
279 end
242 280 end
@@ -15,6 +15,11
15 15 # -k, --key Redmine API key
16 16 #
17 17 # General options:
18 # --unknown-user=ACTION how to handle emails from an unknown user
19 # ACTION can be one of the following values:
20 # ignore: email is ignored (default)
21 # accept: accept as anonymous user
22 # create: create a user account
18 23 # -h, --help show this help
19 24 # -v, --verbose show extra information
20 25 # -V, --version show version information and exit
@@ -64,7 +69,7 end
64 69 class RedmineMailHandler
65 70 VERSION = '0.1'
66 71
67 attr_accessor :verbose, :issue_attributes, :allow_override, :url, :key
72 attr_accessor :verbose, :issue_attributes, :allow_override, :uknown_user, :url, :key
68 73
69 74 def initialize
70 75 self.issue_attributes = {}
@@ -80,7 +85,8 class RedmineMailHandler
80 85 [ '--tracker', '-t', GetoptLong::REQUIRED_ARGUMENT],
81 86 [ '--category', GetoptLong::REQUIRED_ARGUMENT],
82 87 [ '--priority', GetoptLong::REQUIRED_ARGUMENT],
83 [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT]
88 [ '--allow-override', '-o', GetoptLong::REQUIRED_ARGUMENT],
89 [ '--unknown-user', GetoptLong::REQUIRED_ARGUMENT]
84 90 )
85 91
86 92 opts.each do |opt, arg|
@@ -99,6 +105,8 class RedmineMailHandler
99 105 self.issue_attributes[opt.gsub(%r{^\-\-}, '')] = arg.dup
100 106 when '--allow-override'
101 107 self.allow_override = arg.dup
108 when '--unknown-user'
109 self.unknown_user = arg.dup
102 110 end
103 111 end
104 112
@@ -108,7 +116,9 class RedmineMailHandler
108 116 def submit(email)
109 117 uri = url.gsub(%r{/*$}, '') + '/mail_handler'
110 118
111 data = { 'key' => key, 'email' => email, 'allow_override' => allow_override }
119 data = { 'key' => key, 'email' => email,
120 'allow_override' => allow_override,
121 'unknown_user' => unknown_user }
112 122 issue_attributes.each { |attr, value| data["issue[#{attr}]"] = value }
113 123
114 124 debug "Posting to #{uri}..."
@@ -21,6 +21,13 namespace :redmine do
21 21 desc <<-END_DESC
22 22 Read an email from standard input.
23 23
24 General options:
25 unknown_user=ACTION how to handle emails from an unknown user
26 ACTION can be one of the following values:
27 ignore: email is ignored (default)
28 accept: accept as anonymous user
29 create: create a user account
30
24 31 Issue attributes control options:
25 32 project=PROJECT identifier of the target project
26 33 status=STATUS name of the target status
@@ -47,6 +54,7 END_DESC
47 54 options = { :issue => {} }
48 55 %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
49 56 options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
57 options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
50 58
51 59 MailHandler.receive(STDIN.read, options)
52 60 end
@@ -54,6 +62,13 END_DESC
54 62 desc <<-END_DESC
55 63 Read emails from an IMAP server.
56 64
65 General options:
66 unknown_user=ACTION how to handle emails from an unknown user
67 ACTION can be one of the following values:
68 ignore: email is ignored (default)
69 accept: accept as anonymous user
70 create: create a user account
71
57 72 Available IMAP options:
58 73 host=HOST IMAP server host (default: 127.0.0.1)
59 74 port=PORT IMAP server port (default: 143)
@@ -107,6 +122,7 END_DESC
107 122 options = { :issue => {} }
108 123 %w(project status tracker category priority).each { |a| options[:issue][a.to_sym] = ENV[a] if ENV[a] }
109 124 options[:allow_override] = ENV['allow_override'] if ENV['allow_override']
125 options[:unknown_user] = ENV['unknown_user'] if ENV['unknown_user']
110 126
111 127 Redmine::IMAP.check(imap_options, options)
112 128 end
@@ -1,5 +1,5
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
@@ -130,6 +130,41 class MailHandlerTest < Test::Unit::TestCase
130 130 assert_equal 1, issue.watchers.size
131 131 end
132 132
133 def test_add_issue_by_unknown_user
134 assert_no_difference 'User.count' do
135 assert_equal false, submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'})
136 end
137 end
138
139 def test_add_issue_by_anonymous_user
140 Role.anonymous.add_permission!(:add_issues)
141 assert_no_difference 'User.count' do
142 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'accept')
143 assert issue.is_a?(Issue)
144 assert issue.author.anonymous?
145 end
146 end
147
148 def test_add_issue_by_created_user
149 Setting.default_language = 'en'
150 assert_difference 'User.count' do
151 issue = submit_email('ticket_by_unknown_user.eml', :issue => {:project => 'ecookbook'}, :unknown_user => 'create')
152 assert issue.is_a?(Issue)
153 assert issue.author.active?
154 assert_equal 'john.doe@somenet.foo', issue.author.mail
155 assert_equal 'John', issue.author.firstname
156 assert_equal 'Doe', issue.author.lastname
157
158 # account information
159 email = ActionMailer::Base.deliveries.first
160 assert_not_nil email
161 assert email.subject.include?('account activation')
162 login = email.body.match(/\* Login: (.*)$/)[1]
163 password = email.body.match(/\* Password: (.*)$/)[1]
164 assert_equal issue.author, User.try_to_login(login, password)
165 end
166 end
167
133 168 def test_add_issue_without_from_header
134 169 Role.anonymous.add_permission!(:add_issues)
135 170 assert_equal false, submit_email('ticket_without_from_header.eml')
General Comments 0
You need to be logged in to leave comments. Login now