Project

General

Profile

Defect #37688 » move_to_modern_authentication(OAuth_2_0)_from_IMAP.patch

Patch for oAuth - Jan Catrysse, 2025-06-07 02:29

View differences:

lib/redmine/imap.rb (revision 87972acede20e953e34edc18e246d583af860f8f) → lib/redmine/imap.rb (revision e2df79d355a16cbe53f25d7f79edc4ab01abc246)
16 16
# You should have received a copy of the GNU General Public License
17 17
# along with this program; if not, write to the Free Software
18 18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20 19
require 'net/imap'
21 20

  
22 21
module Redmine
......
28 27
        ssl = !imap_options[:ssl].nil?
29 28
        starttls = !imap_options[:starttls].nil?
30 29
        folder = imap_options[:folder] || 'INBOX'
30
        auth_type = imap_options[:auth_type] || 'LOGIN'
31 31

  
32 32
        imap = Net::IMAP.new(host, port, ssl)
33 33
        if starttls
34 34
          imap.starttls
35 35
        end
36
        imap.login(imap_options[:username], imap_options[:password]) unless imap_options[:username].nil?
36
        if auth_type == "XOAUTH2" 
37
          require 'gmail_xoauth' unless defined?(Net::IMAP::XOauth2Authenticator) && Net::IMAP::XOauth2Authenticator.class == Class
38
        end
39
        if auth_type == "LOGIN"
40
          imap.login(imap_options[:username], imap_options[:password]) unless imap_options[:username].nil?
41
        else
42
          imap.authenticate(auth_type, imap_options[:username], imap_options[:password]) unless imap_options[:username].nil?
43
        end
37 44
        imap.select(folder)
38 45
        imap.uid_search(['NOT', 'SEEN']).each do |uid|
39 46
          msg = imap.uid_fetch(uid,'RFC822')[0].attr['RFC822']
/dev/null (revision e2df79d355a16cbe53f25d7f79edc4ab01abc246) → lib/tasks/email_oauth.rake (revision e2df79d355a16cbe53f25d7f79edc4ab01abc246)
1
# Redmine - project management software
2
# Copyright (C) 2006-2022  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

  
18
require 'net/imap'
19
require 'oauth2'
20
require 'uri'
21
require 'cgi'
22

  
23
require 'gmail_xoauth' unless defined?(Net::IMAP::XOauth2Authenticator) && Net::IMAP::XOauth2Authenticator.class == Class
24

  
25
namespace :redmine do
26
  namespace :email do
27

  
28
    # token_file = path and basename /app/redmine/config/email_token (email_token.yml and email_token_client.yml are created)
29
    # client = Azure app Client ID
30
    # secret = Azure app Secret key
31
    # tenant = Azure app Tenant ID
32

  
33
    desc "Init Office 365 authorization"
34
    task :o365_oauth2_init => :environment do
35

  
36
      token_file = ENV['token_file']
37
      client_id = ENV['client']
38
      client_secret = ENV['secret']
39
      tenant_id = ENV['tenant']
40

  
41
      scope = [
42
        "offline_access",
43
        "https://outlook.office.com/User.Read",
44
        "https://outlook.office.com/IMAP.AccessAsUser.All",
45
        "https://outlook.office.com/POP.AccessAsUser.All",
46
        "https://outlook.office.com/SMTP.Send",
47
      ]
48

  
49
      client_config = {
50
        "tenant_id" => tenant_id,
51
        "client_id" => client_id,
52
        "client_secret" => client_secret,
53
        "site" => 'https://login.microsoftonline.com',
54
        "authorize_url" => "/#{tenant_id}/oauth2/v2.0/authorize",
55
        "token_url" => "/#{tenant_id}/oauth2/v2.0/token"
56
      }
57

  
58
      client = OAuth2::Client.new(client_config['client_id'], client_config['client_secret'],
59
                                  site: client_config['site'], authorize_url: client_config['authorize_url'], token_url: client_config['token_url'])
60

  
61
      print("Go to URL: #{client.auth_code.authorize_url(scope: scope.join(" "))}\n")
62
      print("Enter full URL after authorize:")
63
      access_token = client.auth_code.get_token(CGI.parse(URI.parse(STDIN.gets.strip).query)["code"].first, client_id: client_id)
64

  
65
      File.write("#{token_file}.yml", access_token.to_hash.to_yaml)
66
      File.write("#{token_file}_client.yml", client_config.to_yaml)
67

  
68
      puts "AUTH OK!"
69
    end
70

  
71
    desc "Read emails from an IMAP server authorized via OAuth2"
72
    task :receive_imap_oauth2 => :environment do
73

  
74
      token_file = ENV['token_file']
75

  
76
      unless token_file || File.exists?("#{token_file}.yml") || File.exists?("#{token_file}_client.yml")
77
        raise "token_file not defined or not exists"
78
      end
79

  
80
      client_config = YAML.load_file("#{token_file}_client.yml")
81
      client = OAuth2::Client.new(client_config['client_id'], client_config['client_secret'],
82
                                  site: client_config['site'], authorize_url: client_config['authorize_url'], token_url: client_config['token_url'])
83

  
84
      access_token = OAuth2::AccessToken.from_hash(client, YAML.unsafe_load_file("#{token_file}.yml"))
85

  
86
      if access_token.expired?
87
        access_token = access_token.refresh!
88
        File.write("#{token_file}.yml", access_token.to_hash.to_yaml)
89
      end
90

  
91
      imap_options = {:host => ENV['host'],
92
                      :port => ENV['port'],
93
                      :ssl => ENV['ssl'],
94
                      :starttls => ENV['starttls'],
95
                      :username => ENV['username'],
96
                      :password => access_token.token,
97
                      :auth_type => 'XOAUTH2',
98
                      :folder => ENV['folder'],
99
                      :move_on_success => ENV['move_on_success'],
100
                      :move_on_failure => ENV['move_on_failure']}
101

  
102
      Mailer.with_synched_deliveries do
103
        Redmine::IMAP.check(imap_options, MailHandler.extract_options_from_env(ENV))
104
      end
105
    end
106

  
107
  end
108

  
109
end
(2-2/2)