4

UCWA のドキュメントには、UCWA は現在、オンプレミスの Lync を使用しているお客様のみが利用できると記載されています。

それにもかかわらず、Office 365 アカウントに対してLync Connectivity Analyzerを使用すると、UCWA サービスに接続できるようです:モビリティ (UCWA) サービスのテストを完了しました。さらに、このツール (たとえば、Fiddler を使用) によって実行された Web 要求を検査すると、Office 365 から (何らかの方法で) 取得した認証トークンを取得し、それを使用して UCWA に対して要求を行うことができます。

  • UCWA は Office 365 によって公開されているようですが、これは正しいですか? Lync Connectivity Analyzerは、特定のWebTicket Serviceを使用して認証トークンを取得しているようです。
  • 認証トークンを取得するために WebTicket Service の使用を抽象化するライブラリはありますか? トークンを取得した後、UCWA リソースへのアクセスは非常に簡単になりますが、そのためのライブラリも便利です :)
  • WebTicket Service (WCF) に関するドキュメントはあまり見つかりませんでした。サービス参照 (Visual Studio) をhttps://lyncweb.domain.com/WebTicket/WebTicketService.svcに追加すると、オプションはそれほど多くありません。要求メッセージと応答メッセージに特定の構造がないようです。そのため、この WebTicket サービスによって公開されたIssueToken操作を呼び出すのは非常に困難です。

リンク:

4

2 に答える 2

4

Office 365 は、少なくとも UCWA 機能の適切なサブセットを公開しているようです。Lync Connectivity Analyzer は実際には .NET アプリケーションであるため、それを直接利用して面倒な作業を行うことはそれほど難しくありません。「使いやすい」ライブラリを使用するほど良くはありませんが、さまざまなプロトタイピングでうまく機能しました。以下のコードを使用すると、Web チケットの値と UCWA のベース URL を (自動検出によって) 取得し、API を照会して町に行くことができます。それは最もきれいなコードではありませんが、機能します。

注: Lync 接続アナライザーでマネージド DLL への参照を追加する必要があります。私にとって、それは次のことを意味しました:

  • Credentials.dll
  • LyncConnectivityAnalyzer.Logging.dll
  • LyncConnectivityAnalyzer.Resources.dll
  • LyncWebServices.dll
  • Utilities.dll

また、アプリと同じフォルダー (フォルダーなど) にコピーされたアンマネージ DLL (1 つだけかもしれませんが、私は両方を実行しました) も必要です\bin\

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Security;
using System.Threading.Tasks;
using LyncConnectivityAnalyzer.Logging;
using Microsoft.LyncServer.WebServices;
using Microsoft.Utilities.Credentials;

namespace SM.CoolStuff
{
    /// <summary>
    /// Helper class for performing auto-discovery and authentication to the Lync/Skype UCWA sevice on Office365
    /// </summary>
    public class SkypeHelper
    {
        private string _webTicket;
        private string _ucwaRootPath;
        private readonly ILyncLogging _logger = new Logger();

        /// <summary>
        /// Initializes the connection/authentication/etc.  Once this method completes, <see cref="_webTicket"/> and <see cref="_ucwaRootPath"/> will
        /// be populated.
        /// </summary>
        private async Task Initialize(string userName, SecureString password)
        {
            //Setup the credential
            var userCreds = new Credentials
            {
                UPN = userName,
                SecurePassword = password,
                credsType = CredInputType.CredDialog,
                URI = userName
            };

            //Perform auto-discovery to get the UCWA path
            //We'll just always allow redirects
            ValidateRedirectRequestDelegate alwaysAllowRedirect = (logger, url, destinationUrl) => true;
            var adm = new AutoDiscoverManager(_logger, "http://lyncdiscover." + userCreds.Domain, userCreds.URI, userCreds, alwaysAllowRedirect);
            await adm.StartDiscoveryJourney();

            //Save the path
            _ucwaRootPath = adm.GetAutoDiscoverAddress().ExternalUcwa;

            //Setup the 'validator' that does all the heavy lifting
            var webServicesValidation = new WebServicesValidation(_logger, _ucwaRootPath, userCreds)
            {
                HttpRequestBody = ApplicationPostBody,
                customHeaders = CustomHeaders,
                getPost = HttpMethod.Post
            };

            //Make a first request that should gracefully fail with 'authorization required'
            await webServicesValidation.CheckURL(_ucwaRootPath, false);

            //Now authorize the request
            await webServicesValidation.TryURLAuth(_ucwaRootPath);

            //Use some ugly reflection to get the ticket value.  There may be a better way but this works
            _webTicket = (string)WebTicketField.GetValue(webServicesValidation);
        }

        /// <summary>
        /// Example usage
        /// </summary>
        public async Task DoSomethingOnSkype()
        {
            //If you already have a SecureString, might as well use that.  Otherwise, convert an 'insecure' string to be 'Secure'
            var secureString = new SecureString();
            "TopSecret".ToList().ForEach(secureString.AppendChar);

            //Do the initialization
            await Initialize("user@somewhere.com", secureString);

            //TODO: Use _webTicket and _host to query something
        }

        private static readonly string ApplicationPostBody =
            string.Concat(
                "<input xmlns=\"http://schemas.microsoft.com/rtc/2012/03/ucwa\"><property name=\"culture\">en-US</property><property name=\"endpointId\">44:D8:84:3C:68:68</property><property name=\"type\">Phone</property><property name=\"userAgent\">",
                //TODO: Your app name here
                "LyncConnectivityAnalyzer", "/",
                //TODO: Your app version here
                "5.0.8308.582",
                " (Windows OS 6.0)</property></input>");

        private static readonly Dictionary<string, string> CustomHeaders = new Dictionary<string, string>
        {
            {"Accept", "application/vnd.microsoft.com.ucwa+xml"},
            {"Content-Type", "application/vnd.microsoft.com.ucwa+xml"},
            {"X-MS-Namespace", "internal"},
            {"Connection", "keep-alive"},
            {"Proxy-Connection", "keep-alive"}
        };

        private static readonly FieldInfo WebTicketField = FindWebTicketField();

        private static FieldInfo FindWebTicketField()
        {
            var fieldInfo = typeof(WebServicesValidation).GetField("_webticket", BindingFlags.Instance | BindingFlags.NonPublic);
            if (fieldInfo == null)
            {
                throw new ApplicationException("Could not find private _webticket field");
            }
            return fieldInfo;
        }
   }
}
于 2015-07-30T01:23:24.733 に答える