Problém
V jedné z aplikací přistupujeme pomocí API jako klient do Team Foundation Serveru 2013.
ICredentials credentials = new NetworkCredential(...); TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), credentials); collection.EnsureAuthenticated();
Po migraci aplikace z vlastních serverů do Azure Website nám připojení přestalo fungovat, konkrétně začalo docházet k této výjimce:
System.IO.IOException: The specified registry key does not exist. at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str) at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions) at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options) at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorageHelper.GetRootKey(String subkeyName) at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorage.RetrieveToken(VssTokenKey tokenKey) at Microsoft.VisualStudio.Services.Common.TokenStorage.VssTokenStorage.Retrieve(VssTokenKey tokenKey) at Microsoft.TeamFoundation.Client.TfsClientCredentialStorage.RetrieveToken(Uri serverUrl, VssCredentialsType credentialType) at Microsoft.TeamFoundation.Client.CookieCredential.OnCreateTokenProvider(Uri serverUrl, HttpWebResponse response) at Microsoft.TeamFoundation.Client.IssuedTokenCredential.CreateTokenProvider(Uri serverUrl, HttpWebResponse response, IssuedToken failedToken) at Microsoft.TeamFoundation.Client.TfsClientCredentials.TryGetTokenProvider(Uri serverUrl, IssuedTokenProvider& provider) at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.PrepareWebRequest(HttpWebRequest webRequest, Guid sessionId, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken& currentToken, IssuedTokenProvider& tokenProvider) at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.CreateSoapRequest(Uri requestUri, Guid sessionId, String soapAction, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken& currentToken, IssuedTokenProvider& tokenProvider) at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.CreateWebRequest() at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest() at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage message, TimeSpan timeout) at Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation operation, Object[] parameters, TimeSpan timeout, Object[]& outputs) at Microsoft.TeamFoundation.Framework.Client.Registration.GetRegistrationEntries(String toolId) at Microsoft.TeamFoundation.Framework.Client.RegistrationProxy.GetRegistrationEntries(String toolId) at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshMemoryCache() at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshCachesIfNeeded(Boolean direct) at Microsoft.TeamFoundation.Framework.Client.RegistrationService.GetRegistrationEntries(String toolId) at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.FindServiceLocation(String serviceType, String toolId) at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.LocationForCurrentConnection(String serviceType, Guid serviceIdentifier) at Microsoft.TeamFoundation.Client.TfsConnection.EnsureProviderConnected() at Microsoft.TeamFoundation.Client.TfsConnection.EnsureAuthenticated()
TFS klient v průběhu přihlašování šahá do registrů. Po dlouhém pátrání pomocí .NET Reflectoru jsem dospěl k názoru, že v nich hledá dříve uložený autentizační token. To pro nás není podstatné, důležité je, že se bez toho můžeme obejít.
Řešení
Řešením je použití třídy TfsClientCredentials. Tu vyrobíme pomocí WindowCredentials a tu s pomocí dvou tříd: NetworkCredentials a SimpleWebTokenCredential, přičemž v NetworkCredentials máme údaje k autorizaci a SimpleWebTokenCredential nemusí nést žádné hodnoty, pouze zajišťuje workaround popsaného problému.
NetworkCredential networkCredentials = new NetworkCredential(...) TfsClientCredentials tfsCredentials = new TfsClientCredentials(new WindowsCredential(networkCredentials), new SimpleWebTokenCredential(null, null), allowInteractive: false); TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), tfsCredentials); collection.EnsureAuthenticated();
You saved my job. You definitely deserve a bow. Thanks a ton.
To se mi líbíTo se mi líbí