Social Icons

jueves, 12 de enero de 2012

Sobre Windows Phone 7 y la autenticación con SharePoint

Conectar el teléfono a nuestro entorno de SharePoint era mi proyecto de navidad, y me llevó un rato... Al final conseguí hacerlo así que he pensado en compartir mi alegría. Además, he leído un montón de blogs sobre el tema ¿todos los que hay? y quería aportar mi granito de arena.

Vamos al asunto. Me he creado una clase llamada SPAuthBridge partiend del código de la SDK que puede ser usada para realizar las conexiones con el servidor de SharePoint.
using System;
using System.IO;
using System.Net;
using System.Text;

namespace PhoneUtils.Code
{
    public class SPAuthBridge
    {
        #region Properties
        public CookieContainer cookieJar = new CookieContainer();

        string SiteUrl, User;
        string Password; //This should be securestring, but I don't think it's available in WP7
        #endregion

        #region Constructors
        public SPAuthBridge(string SiteUrl, string User, string Password)
        {
            this.SiteUrl = SiteUrl;
            this.User = User;
            this.Password = Password;
        }
        #endregion

        #region Public Methods
        public void Authenticate()
        {
            try
            {
                if (string.IsNullOrEmpty(SiteUrl)) throw new ArgumentOutOfRangeException("The SPAuthBridge was not properly initialized");

                System.Uri authServiceUri = new Uri(string.Format("{0}/_vti_bin/authentication.asmx", SiteUrl));

                HttpWebRequest spAuthReq = HttpWebRequest.Create(authServiceUri) as HttpWebRequest;
                spAuthReq.CookieContainer = cookieJar;
                spAuthReq.Headers["SOAPAction"] = "http://schemas.microsoft.com/sharepoint/soap/Login";
                spAuthReq.ContentType = "text/xml; charset=utf-8";
                spAuthReq.Method = "POST";

                //add the soap message to the request
                spAuthReq.BeginGetRequestStream(new AsyncCallback(spAuthReqCallBack), spAuthReq);
            }
            catch
            {
                TriggerOnAuthenticated(false);
            }
        }
        #endregion

        #region Private Methods
        private void spAuthReqCallBack(IAsyncResult asyncResult)
        {
            string envelope =
                    @"<?xml version=""1.0"" encoding=""utf-8""?>
                    <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
                      <soap:Body>
                        <Login xmlns=""http://schemas.microsoft.com/sharepoint/soap/"">
                          <username>{0}</username>
                          <password>{1}</password>
                        </Login>
                      </soap:Body>
                    </soap:Envelope>";

            UTF8Encoding encoding = new UTF8Encoding();
            HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
            Stream _body = request.EndGetRequestStream(asyncResult);
            envelope = string.Format(envelope, User, Password);
            byte[] formBytes = encoding.GetBytes(envelope);

            _body.Write(formBytes, 0, formBytes.Length);
            _body.Close();

            request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
        }

        private void ResponseCallback(IAsyncResult asyncResult)
        {
            try
            {
                HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
                HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                Stream content = response.GetResponseStream();

                if (request != null && response != null)
                {
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        using (StreamReader reader = new StreamReader(content))
                        {
                            //Put debugging code here
                            string _responseString = reader.ReadToEnd();
                            reader.Close();
                        }
                    }
                }

                //authentication complete
                TriggerOnAuthenticated(true);
            }
            catch
            {
                TriggerOnAuthenticated(false);
            }
        }
        #endregion

        #region Events
        public delegate void OnAuthenticatedHandler(bool Success);

        public event OnAuthenticatedHandler OnAuthenticated;

        protected virtual void TriggerOnAuthenticated(bool Success)
        {
            if (OnAuthenticated != null)
                OnAuthenticated(Success);
        }
        #endregion
    }
}

La manera de usar esto es bastante simple, creas el objeto SPAuthBridge y, llamas al método Authenticate y listo, algo así:

        StratexWP7SoapClient StratexWP7 = new StratexWP7SoapClient(); //This is my web service
        SPAuthBridge SharePointAuth;

        public MainPage()
        {
            InitializeComponent();

            (...)

            SharePointAuth = new SPAuthBridge(SiteUrl, Username, Password);
            SharePointAuth.OnAuthenticated += new SPAuthBridge.OnAuthenticatedHandler(SharePointAuth_OnAuthenticated);

            if (!string.IsNullOrEmpty(Password))
                SharePointAuth.Authenticate();
            else
                MessageBox.Show("The application should be configured before use.");
        }

        void SharePointAuth_OnAuthenticated(bool Success)
        {
            if (!Success)
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                    { MessageBox.Show("There was an error on the authentication procedure. Please check the configuration."); });

                return;
            }

            StratexWP7.CookieContainer = SharePointAuth.cookieJar; //This is all you have to do to connect your web service. \m/ O.O \m/

            HookEvents();

            RequestData();
        }

Es precioso cuando funciona…

clip_image002clip_image002[4]
(Por cierto, las gráficas son de AmCharts)
Publicar un comentario

miércoles, 11 de enero de 2012

Las Barras de Desplazamiento aparecen en SharePoint 2010 en los Silverlights

Si, cada vez que fijas la altura o la anchura del web part te muestra las horribles barras de desplazamiento.

Esta no es la primera vez que trato me encuentro con esto, pero creo que esta vez lo he resuelto para siempre o por lo menos hasta la nueva versión de SharePoint el año que viene…. El problema estaba en la forma en que estaba creando el objeto Silverlight. Estaba usando estilos CSS en línea. Era algo así:
<object id='silverlightHost' style='height: 600px; width: 350px; margin: 0; padding: 0;' data='data:application/x-silverlight-2,' type='application/x-silverlight-2'>

Después de un millón de experimentos basados en la técnica del ensayo / error no pienses jamás que soy capáz de crear un Web Part Silverlight de los OOTB de SharePoint 2010 y copiar el código he cambiado la primera línea del objeto a:
<object id='silverlightHost' height='600px' width='350px' data='data:application/x-silverlight-2,' type='application/x-silverlight-2' style='display:block' class='ms-dlgDisable'>
Y eso resuelve el problema.
Publicar un comentario

viernes, 6 de enero de 2012

Creando Eventos en Silverlight

He leído que es imposible en un par de sitios. No se si lo sería en las versiones anteriores pero, por lo menos desde Silverlight 3 se puede… y digo más… es fácil.

Este evento lo uso para comprobar cuándo se ha movido un objeto.

        #region Events
        public delegate void PositionChangeHandler(Object Sender);

        public event PositionChangeHandler PositionChangedEvent;

        protected virtual void OnPositionChanged()
        {
            if (PositionChangedEvent != null)
                PositionChangedEvent(this);
        }
        #endregion

Y entonces en la función en donde actualizo la posición del objeto llamo a:
OnPositionChanged();

Después de esto solo tengo que suscribirme al evento desde las otras clases:
ParentObjective.PositionChangedEvent += new Objective.PositionChangeHandler(RelatedObjectives_PositionChangedEvent);

Fácil… por una vez en la vida
Publicar un comentario

miércoles, 4 de enero de 2012

Configurando Silverlight y Servicios Web con SSL y Certificados Autofirmados

Yo tenía un servidor de SharePoint funcionando perfectamente o por lo menos tan bien como funciona un server de SharePoint en HTTP, pero de repente sentí la urgencia de hacerlo HTTPS. Extendí la aplicación etc., y era capaz de acceder a todas partes, pero ¡Oh sorpresa!, mis silverlights no funcionaban.

Cambié el <security mode="Transport" /> en el ServiceReferences.ClientConfig pero siempre me daba el infame error:
An error occurred while trying to make a request to URI 'https://MyURL/_vti_bin/Service.asmx'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent. This error may also be caused by using internal types in the web service proxy without using the InternalsVisibleToAttribute attribute.
Hay un par de cosas que arreglar aquí.

Lo primero es añadir un buen certificado autofirmado a tu sitio.
C:\Program Files\Microsoft SDKs\Windows\v7.1>MakeCert.exe -r -pe -n "CN=MyURL" -sky exchange -ss my
El MyURL es muy importante. No puedes usar un certificado autofirmado cualquiera, debes usar uno que le venga bien a la URL de tu sitio.
El certificado se almacena en tus certificados personales, asi que para exportarlo deberás ejecutar MMC.exe, y allí:
File –> Add/Remove Snap-In –> Certificates –> My user account –> Finish –> OK
Entonces podrás de ver tus certigicados. El que acabas de crear está enÑ:
Certificates – Current User –> Personal –> Certificates –> MyURL
Allí deberás:
Click derecho –> All Tasks –> Export
Y entonces asegurate de que exportas la clave privada con el certificado. Esto te dará como resultado un fichero .pfx.

Ahora podemos ir al server que queremos hacer SSL, si es que no estábamos allí ya, y copiar el fichero .pfx en algún sitio en donde luego recordemos donde esta.

Después abrimos el IIS Manager, seleccionamos el nodo raíz y hacemos click en Certificados del Servidor image.

En la columna de la derecha deberás hacer click en Importar y seleccionar el fichero .pfx que acabas de crear. Después de importarlo deberás poder verlo en la lista de Certificados del Servidor.

Entonces haremos click en la colección de sitios 443 (la mía se llama SharePoint 443) y allí, otra vez en la columna derecha, hacemos click en bindings. Seleccionamos HTTPS (añade uno si no lo tienes ya creado) y luego hacemos click en editar. En la lista Certificado SSL selecciona el que acabas de subir.

Bien, ya hemos hecho la parte difícil, ahora todo es más fácil.

Ahora añadiremos el crossdomain.xml y el clientaccesspolicy.xml tanto a la carpeta 80 como a la 443. (c:\inetpub\…\443)
El contenido de estos ficheros es:

crossdomain.xml
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <allow-access-from domain="*"/>
  <allow-http-request-headers-from domain="*" headers="SOAPAction"/>
</cross-domain-policy>

clientaccesspolicy.xml
<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="SOAPAction">
        <domain uri="http://*"/>
        <domain uri="https://*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

Y ahora la parte todavía más sencilla. Configurar el navegador.
Debemos navegar a https://MyURL y allí veremos algo así:
image

Aquí deberás hacer click en continuar (Recomendado) y después verás la barra de dirección volverse roja. Bien.
Haz click en el botón Error de Certificado y luego en el link Ver Certificados:
image

Después de eso:
Instalar Certificado… –> Next –> image & Next –> Finish –> Yes –> Ok.
Cierra el navegador y ábrelo otra vez tu https://myurl. Ahora deberías ver la barra de la URL en blanco y el icono del candado al lado:
image

Vamos bien. Ahora borraremos las cookies del navegador y todo lo demás, qué demonios:
image

Como toque final reiniciaremos el IIS y saltaremos tres veces a la pata coja.

Ahora, finalmente, debes de ser capaz de depurar tus web services SSL y tus Silverlights en Visual Studio.
Publicar un comentario