View Javadoc
1   /*
2    * DavMail POP/IMAP/SMTP/CalDav/LDAP Exchange Gateway
3    * Copyright (C) 2010  Mickael Guessant
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18   */
19  
20  package davmail.exchange.auth;
21  
22  import davmail.BundleMessage;
23  import davmail.Settings;
24  import davmail.exception.DavMailAuthenticationException;
25  import davmail.exception.DavMailException;
26  import davmail.exchange.ews.BaseShape;
27  import davmail.exchange.ews.DistinguishedFolderId;
28  import davmail.exchange.ews.GetFolderMethod;
29  import davmail.exchange.ews.GetUserConfigurationMethod;
30  import davmail.http.HttpClientAdapter;
31  import org.apache.http.client.methods.CloseableHttpResponse;
32  import org.apache.log4j.Logger;
33  
34  import javax.swing.*;
35  import java.awt.*;
36  import java.io.BufferedReader;
37  import java.io.IOException;
38  import java.io.InputStreamReader;
39  import java.lang.reflect.InvocationTargetException;
40  import java.net.URI;
41  
42  public class O365ManualAuthenticator implements ExchangeAuthenticator {
43  
44      private static final Logger LOGGER = Logger.getLogger(O365ManualAuthenticator.class);
45  
46      String errorCode = null;
47      String code = null;
48  
49      URI ewsUrl = URI.create(Settings.getO365Url());
50  
51      private O365ManualAuthenticatorDialog o365ManualAuthenticatorDialog;
52  
53      private String username;
54      private String password;
55      private O365Token token;
56  
57      public O365Token getToken() {
58          return token;
59      }
60  
61      @Override
62      public URI getExchangeUri() {
63          return ewsUrl;
64      }
65  
66      public String getUsername() {
67          return username;
68      }
69  
70      public void setUsername(String username) {
71          this.username = username;
72      }
73  
74      public void setPassword(String password) {
75          this.password = password;
76      }
77  
78      /**
79       * Return a pool enabled HttpClientAdapter instance to access O365
80       * @return HttpClientAdapter instance
81       */
82      @Override
83      public HttpClientAdapter getHttpClientAdapter() {
84          return new HttpClientAdapter(getExchangeUri(), username, password, true);
85      }
86  
87      public void authenticate() throws IOException {
88          // common DavMail client id
89          final String clientId = Settings.getProperty("davmail.oauth.clientId", "facd6cff-a294-4415-b59f-c5b01937d7bd");
90          // standard native app redirectUri
91          final String redirectUri = Settings.getProperty("davmail.oauth.redirectUri", Settings.O365_LOGIN_URL+"common/oauth2/nativeclient");
92          // company tenantId or common
93          String tenantId = Settings.getProperty("davmail.oauth.tenantId", "common");
94  
95          // first try to load stored token
96          token = O365Token.load(tenantId, clientId, redirectUri, username, password);
97          if (token != null) {
98              return;
99          }
100 
101         final String initUrl = O365Authenticator.buildAuthorizeUrl(tenantId, clientId, redirectUri, username);
102 
103         if (Settings.getBooleanProperty("davmail.server") || GraphicsEnvironment.isHeadless()) {
104             // command line mode
105             code = getCodeFromConsole(initUrl);
106         } else {
107             try {
108                 SwingUtilities.invokeAndWait(() -> o365ManualAuthenticatorDialog = new O365ManualAuthenticatorDialog(initUrl));
109             } catch (InterruptedException e) {
110                 Thread.currentThread().interrupt();
111             } catch (InvocationTargetException e) {
112                 throw new IOException(e);
113             }
114             code = o365ManualAuthenticatorDialog.getCode();
115         }
116 
117         if (code == null) {
118             LOGGER.error("Authentication failed, code not available");
119             throw new DavMailException("EXCEPTION_AUTHENTICATION_FAILED_REASON", errorCode);
120         }
121 
122         token = O365Token.build(tenantId, clientId, redirectUri, code, password);
123 
124         LOGGER.debug("Authenticated username: " + token.getUsername());
125         if (username != null && !username.isEmpty() && !username.equalsIgnoreCase(token.getUsername())) {
126             throw new DavMailAuthenticationException("Authenticated username " + token.getUsername() + " does not match " + username);
127         }
128 
129     }
130 
131     private String getCodeFromConsole(String initUrl) {
132         BufferedReader inReader = new BufferedReader(new InputStreamReader(System.in));
133         StringBuilder buffer = new StringBuilder();
134         buffer.append(BundleMessage.format("UI_0365_AUTHENTICATION_PROMPT_CONSOLE", initUrl)).append("\n")
135         .append(BundleMessage.format("UI_0365_AUTHENTICATION_CODE"));
136         try {
137             System.out.print(buffer.toString());
138             code = inReader.readLine();
139             if (code != null && code.contains("code=") && code.contains("&session_state=")) {
140                 code = code.substring(code.indexOf("code=")+5, code.indexOf("&session_state="));
141             }
142         } catch (IOException e) {
143             System.err.println(e + " " + e.getMessage());
144         }
145         return code;
146     }
147 
148     public static void main(String[] argv) {
149         try {
150             Settings.setDefaultSettings();
151             Settings.setProperty("davmail.server", "false");
152             //Settings.setLoggingLevel("httpclient.wire", Level.DEBUG);
153 
154             O365ManualAuthenticator authenticator = new O365ManualAuthenticator();
155             authenticator.setUsername("");
156             authenticator.authenticate();
157 
158             // switch to EWS url
159             HttpClientAdapter httpClientAdapter = new HttpClientAdapter(authenticator.getExchangeUri(), true);
160 
161             GetFolderMethod checkMethod = new GetFolderMethod(BaseShape.ID_ONLY, DistinguishedFolderId.getInstance(null, DistinguishedFolderId.Name.root), null);
162             checkMethod.setHeader("Authorization", "Bearer " + authenticator.getToken().getAccessToken());
163             try (
164                     CloseableHttpResponse response = httpClientAdapter.execute(checkMethod)
165             ) {
166                 checkMethod.handleResponse(response);
167                 checkMethod.checkSuccess();
168             }
169             System.out.println("Retrieved folder id " + checkMethod.getResponseItem().get("FolderId"));
170 
171             // loop to check expiration
172             int i = 0;
173             while (i++ < 12 * 60 * 2) {
174                 GetUserConfigurationMethod getUserConfigurationMethod = new GetUserConfigurationMethod();
175                 getUserConfigurationMethod.setHeader("Authorization", "Bearer " + authenticator.getToken().getAccessToken());
176                 try (
177                         CloseableHttpResponse response = httpClientAdapter.execute(checkMethod)
178                 ) {
179                     checkMethod.handleResponse(response);
180 
181                     checkMethod.checkSuccess();
182                 }
183                 System.out.println(getUserConfigurationMethod.getResponseItem());
184 
185                 Thread.sleep(5000);
186             }
187 
188         } catch (InterruptedException e) {
189             LOGGER.warn("Thread interrupted", e);
190             Thread.currentThread().interrupt();
191         } catch (Exception e) {
192             LOGGER.error(e + " " + e.getMessage(), e);
193         }
194         System.exit(0);
195     }
196 }