| 
  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 
   |