diff --git a/Documentation/FullyAnnotatedConfigReference.xml b/Documentation/FullyAnnotatedConfigReference.xml index 6b3ab7e..96abd7c 100644 --- a/Documentation/FullyAnnotatedConfigReference.xml +++ b/Documentation/FullyAnnotatedConfigReference.xml @@ -297,6 +297,27 @@ --> + + + + 00000000-0000-0000-0000-000000000000 + + 00000000-0000-0000-0000-000000000000 + + .\Resources\mail2bugAppClientSecret.bin + + Mail2Bug/1.0.0.0 + + .\Resources\mail2bug.bin + + + 00000000-0000-0000-0000-000000000000 + 00000000-0000-0000-0000-000000000000 + + .\Resources\mail2bugAppClientSecret.bin + Mail2Bug/1.0.0.0 + + true diff --git a/Mail2Bug/Config.cs b/Mail2Bug/Config.cs index 515e9d4..e97f192 100644 --- a/Mail2Bug/Config.cs +++ b/Mail2Bug/Config.cs @@ -28,6 +28,14 @@ public class KeyVaultSecret public string ApplicationSecretEnvironmentVariableName { get; set; } } + public class OAuthSecret + { + public string TenantID { get; set; } + public string ClientID { get; set; } + public string ClientSecretFile { get; set; } + public string UserAgentName { get; set; } + } + public class TfsServerConfig { // The TFS collection URL to connect to. e.g: @@ -202,6 +210,7 @@ public enum MailboxServiceType public string EWSUsername { get; set; } public string EWSPasswordFile { get; set; } public KeyVaultSecret EWSKeyVaultSecret { get; set; } + public OAuthSecret EWSOAuthSecret { get; set; } #endregion diff --git a/Mail2Bug/Email/EWS/EWSConnectionManger.cs b/Mail2Bug/Email/EWS/EWSConnectionManger.cs index 2769ef0..04aa348 100644 --- a/Mail2Bug/Email/EWS/EWSConnectionManger.cs +++ b/Mail2Bug/Email/EWS/EWSConnectionManger.cs @@ -1,7 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Net.Cache; +using System.Net; +using System.Text; using log4net; using Microsoft.Exchange.WebServices.Data; +using Newtonsoft.Json.Linq; +using Mail2Bug.Helpers; namespace Mail2Bug.Email.EWS { @@ -22,6 +28,24 @@ public struct Credentials public string EmailAddress; public string UserName; public string Password; + public CredentialsOAuth OAuthCredentials; + } + + public class CredentialsOAuth + { + public CredentialsOAuth() { } + public CredentialsOAuth(Config.EmailSettings emailSettings, string clientSecret) + { + TenantID = emailSettings.EWSOAuthSecret.TenantID; + ClientID = emailSettings.EWSOAuthSecret.ClientID; + ClientSecret = clientSecret; + UserAgentName = emailSettings.EWSOAuthSecret.UserAgentName; + } + + public string TenantID { get; set; } + public string ClientID { get; set; } + public string ClientSecret { get; set; } + public string UserAgentName { get; set; } } public struct EWSConnection @@ -82,11 +106,21 @@ static private Tuple GetKeyFromCredentials(Credential static private EWSConnection ConnectToEWS(Credentials credentials, bool useConversationGuidOnly) { Logger.DebugFormat("Initializing FolderMailboxManager for email adderss {0}", credentials.EmailAddress); - var exchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP1) + ExchangeService exchangeService; + if (credentials.OAuthCredentials != null) { - Credentials = new WebCredentials(credentials.UserName, credentials.Password), - Timeout = 60000 - }; + Logger.DebugFormat("OAuth authentication for email address {0}", credentials.EmailAddress); + exchangeService = EWSOAuthHelper.OAuthConnectPost(credentials.OAuthCredentials, credentials.EmailAddress); + } + else + { + Logger.DebugFormat("Basic authentication for email address {0}", credentials.EmailAddress); + exchangeService = new ExchangeService(ExchangeVersion.Exchange2010_SP1) + { + Credentials = new WebCredentials(credentials.UserName, credentials.Password), + Timeout = 60000 + }; + } exchangeService.AutodiscoverUrl( credentials.EmailAddress, @@ -108,7 +142,6 @@ static private EWSConnection ConnectToEWS(Credentials credentials, bool useConve }; } - private readonly Dictionary, EWSConnection> _cachedConnections; private readonly bool _enableConnectionCaching; diff --git a/Mail2Bug/Email/MailboxManagerFactory.cs b/Mail2Bug/Email/MailboxManagerFactory.cs index fcc86cf..9e6a98d 100644 --- a/Mail2Bug/Email/MailboxManagerFactory.cs +++ b/Mail2Bug/Email/MailboxManagerFactory.cs @@ -21,11 +21,15 @@ public IMailboxManager CreateMailboxManager(Config.EmailSettings emailSettings) { var credentialsHelper = new Helpers.CredentialsHelper(); string password = credentialsHelper.GetPassword(emailSettings.EWSPasswordFile, emailSettings.EncryptionScope, emailSettings.EWSKeyVaultSecret); + string clientSecret = emailSettings.EWSOAuthSecret != null + ? credentialsHelper.GetPassword(emailSettings.EWSOAuthSecret.ClientSecretFile, emailSettings.EncryptionScope, emailSettings.EWSKeyVaultSecret) + : null; var credentials = new EWSConnectionManger.Credentials { EmailAddress = emailSettings.EWSMailboxAddress, UserName = emailSettings.EWSUsername, - Password = password + Password = password, + OAuthCredentials = emailSettings.EWSOAuthSecret != null ? new EWSConnectionManger.CredentialsOAuth(emailSettings, clientSecret) : null, }; var exchangeService = _connectionManger.GetConnection(credentials, emailSettings.UseConversationGuidOnly); diff --git a/Mail2Bug/Helpers/EWSOAuthHelper.cs b/Mail2Bug/Helpers/EWSOAuthHelper.cs new file mode 100644 index 0000000..deb475f --- /dev/null +++ b/Mail2Bug/Helpers/EWSOAuthHelper.cs @@ -0,0 +1,73 @@ +using Microsoft.Exchange.WebServices.Data; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Cache; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using Mail2Bug.Email.EWS; + +namespace Mail2Bug.Helpers +{ + public class EWSOAuthHelper + { + public static ExchangeService OAuthConnectPost(EWSConnectionManger.CredentialsOAuth oAuthCredentials, string emailAddress) + { + string LoginURL = String.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", oAuthCredentials.TenantID); + + var LogValues = new Dictionary + { + { "grant_type", "client_credentials" }, + { "client_id", oAuthCredentials.ClientID }, + { "client_secret", oAuthCredentials.ClientSecret }, + { "scope", "https://outlook.office365.com/.default" } + }; + string postData = ""; + foreach (var v in LogValues) + { + postData += (String.IsNullOrWhiteSpace(postData) ? "" : "&") + v.Key + "=" + v.Value; + } + var data = Encoding.ASCII.GetBytes(postData); + + ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; + ServicePointManager.Expect100Continue = true; + ServicePointManager.DefaultConnectionLimit = 9999; + ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls + | SecurityProtocolType.Tls11 + | SecurityProtocolType.Tls12 + | SecurityProtocolType.Ssl3; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(LoginURL); + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + request.Accept = "*/*"; + request.UserAgent = oAuthCredentials.UserAgentName; + request.CachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore); + request.ContentLength = data.Length; + using (var stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + + using (var response = (HttpWebResponse)request.GetResponse()) + using (Stream stream = response.GetResponseStream()) + using (var reader = new StreamReader(stream)) + { + var json = reader.ReadToEnd(); + var aToken = JObject.Parse(json)["access_token"].ToString(); + + var ewsClient = new ExchangeService(); + ewsClient.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx"); + ewsClient.Credentials = new OAuthCredentials(aToken); + //Impersonate and include x-anchormailbox headers are required! + ewsClient.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, emailAddress); + ewsClient.HttpHeaders.Add("X-AnchorMailbox", emailAddress); + ewsClient.Timeout = 60000; + return ewsClient; + } + } + } +} diff --git a/Mail2Bug/Mail2Bug.csproj b/Mail2Bug/Mail2Bug.csproj index 22bbdc2..522d3f3 100644 --- a/Mail2Bug/Mail2Bug.csproj +++ b/Mail2Bug/Mail2Bug.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,12 +9,13 @@ Properties Mail2Bug Mail2Bug - v4.5 + v4.8 512 ..\ true + AnyCPU @@ -58,9 +59,11 @@ ..\packages\Microsoft.Azure.KeyVault.1.0.0\lib\net45\Microsoft.Azure.KeyVault.dll True - - False - ..\packages\Microsoft.Exchange.WebServices.1.2\lib\Microsoft.Exchange.WebServices.dll + + ..\packages\Microsoft.Exchange.WebServices.2.2\lib\40\Microsoft.Exchange.WebServices.dll + + + ..\packages\Microsoft.Exchange.WebServices.2.2\lib\40\Microsoft.Exchange.WebServices.Auth.dll ..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.16.204221202\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll @@ -318,6 +321,7 @@ + @@ -378,4 +382,4 @@ --> - + \ No newline at end of file diff --git a/Mail2Bug/packages.config b/Mail2Bug/packages.config index 1dfc183..849ce6f 100644 --- a/Mail2Bug/packages.config +++ b/Mail2Bug/packages.config @@ -11,7 +11,7 @@ - + diff --git a/Mail2BugUnitTests/Mail2BugUnitTests.csproj b/Mail2BugUnitTests/Mail2BugUnitTests.csproj index 9af2262..36668be 100644 --- a/Mail2BugUnitTests/Mail2BugUnitTests.csproj +++ b/Mail2BugUnitTests/Mail2BugUnitTests.csproj @@ -1,5 +1,5 @@  - + Debug AnyCPU @@ -8,7 +8,7 @@ Properties Mail2BugUnitTests Mail2BugUnitTests - v4.5 + v4.8 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -18,6 +18,7 @@ UnitTest ..\ true + true @@ -170,4 +171,4 @@ --> - + \ No newline at end of file diff --git a/Tools/DpapiTool/DpapiTool.csproj b/Tools/DpapiTool/DpapiTool.csproj index 757f1d3..34e91fb 100644 --- a/Tools/DpapiTool/DpapiTool.csproj +++ b/Tools/DpapiTool/DpapiTool.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties DpapiTool DpapiTool - v4.5 + v4.8 512 ..\..\ true