Pregunta

Encontré la solución

La solución fue agregar una colección de sitios raíz (ya que el código se ejecutaba en un subsitio, sin un sitio raíz).

Ver Esta solución


Actualización importante (basado en los comentarios recibidos hasta el momento)

No tengo Visual Studio instalado en SharePoint 2013 Server.Todo el código se compila en Visual Studio 2012 de forma remota utilizando referencias a las DLL de SharePoint 2013 requeridas y se firma digitalmente para que pueda implementarse en la caché de ensamblados global en SharePoint 2013 Server.

En el nivel más básico, esta afirmación falla en el punto de Page_Load();

SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(
  SPContext.Current.Site.Zone
);

Porque SPContext.Current es siempre null.

Fondo

El cliente necesita que se pueda acceder al sitio de SharePoint mediante la autenticación basada en formularios para usuarios externos y la autenticación de Windows para usuarios internos (corporativos).He revisado y configurado ambos proveedores de autenticación y ahora obtengo el inicio de sesión predeterminado.

Sign In

Aunque todo funciona, la autenticación de Windows aún muestra la pantalla de selección de proveedor cuando el cliente desea que la autenticación de Windows pase automáticamente y muestre una pantalla de inicio de sesión de autenticación basada en formularios si eso falla.

He estado luchando con esto durante días después de mirar varios blogs y artículos. Siento que no he avanzado más y eso es deprimente.Por el momento no puedo ejercitarme si me falta algo realmente fundamental.

Página de inicio de sesión personalizada

La página se basa en una fusión de;

  1. %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Login\default.aspx
  2. %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\15\Template\IdentityModel\Forms\default.aspx

Este es solo un primer intento y me doy cuenta de que eventualmente no necesitaré todos estos controles, solo quería ver cómo se combinan las cosas.

Ensamblaje del módulo MyCustomSignIn

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

using Microsoft.IdentityModel.Web;
using Microsoft.SharePoint;
using Microsoft.SharePoint.IdentityModel;
using Microsoft.SharePoint.IdentityModel.Pages;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Claims;
using Microsoft.SharePoint.ApplicationRuntime;
using Microsoft.SharePoint.Diagnostics;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using System.IdentityModel.Tokens;
using System.Web.Configuration;

namespace MyCustomSignInModule
{
    public class SignInForm : FormsSignInPage
    {
        protected Label DebugText;

        private static SPIisSettings _iisSettings;

        internal static SPIisSettings iisSettings
        {
            get
            {
                if (_iisSettings == null)
                {
                    SPSecurity.RunWithElevatedPrivileges(delegate()
                    {
                        _iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
                    });
                }
                return _iisSettings;
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            HttpRequest request = HttpContext.Current.Request;

            bool isWindowsAuth = false;
            string username = request["username"];
            string password = request["password"];


            // If no username is provided, it'll probably be Windows Authentication (NTLMv2 protocol)
            if (String.IsNullOrEmpty(username))
            {
                isWindowsAuth = true;
            }

            try
            {

                //SPIisSettings iisSettings = SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];

                if (isWindowsAuth)
                {
                    // Windows Authentication it is

                    if (iisSettings.UseWindowsClaimsAuthenticationProvider)
                    {
                        SPAuthenticationProvider provider = iisSettings.WindowsClaimsAuthenticationProvider;
                        RedirectToLoginPage(provider); // This should cause automatic sign in
                    }
                }
                else
                {
                    // FBA authentication it is.

                    SPFormsAuthenticationProvider formsClaimsAuthenticationProvider = iisSettings.FormsClaimsAuthenticationProvider;

                    SecurityToken token = SPSecurityContext.SecurityTokenForFormsAuthentication(new Uri(SPContext.Current.Web.Url), formsClaimsAuthenticationProvider.MembershipProvider, formsClaimsAuthenticationProvider.RoleProvider, username, password, SPFormsAuthenticationOption.PersistentSignInRequest);

                    if (null != token)
                    {
                        EstablishSessionWithToken(token);
                        base.RedirectToSuccessUrl();
                    }
                }
            }
            catch (Exception ex)
            {
                DebugText.Text = ex.ToString();
            }

        }

        // Microsoft.SharePoint.IdentityModel.LogonSelector
        private void RedirectToLoginPage(SPAuthenticationProvider provider)
        {
            string components = HttpContext.Current.Request.Url.GetComponents(UriComponents.Query, UriFormat.SafeUnescaped);
            string url = provider.AuthenticationRedirectionUrl.ToString();
            if (provider is SPWindowsAuthenticationProvider)
            {
                components = EnsureUrlSkipsFormsAuthModuleRedirection(components, true);
            }
            SPUtility.Redirect(url, SPRedirectFlags.Default, this.Context, components);
        }

        // Microsoft.SharePoint.Utilities.SPUtility
        private string EnsureUrlSkipsFormsAuthModuleRedirection
            (string url, bool urlIsQueryStringOnly)
        {
            if (!url.Contains("ReturnUrl="))
            {
                if (urlIsQueryStringOnly)
                {
                    url = url + (string.IsNullOrEmpty(url) ? "" : "&");
                }
                else
                {
                    url = url + ((url.IndexOf('?') == -1) ? "?" : "&");
                }
                url = url + "ReturnUrl=";
            }
            return url;
        }

        // Microsoft.SharePoint.IdentityModel.Pages.IdentityModelSignInPageBase
        private void EstablishSessionWithToken(SecurityToken securityToken)
        {
            if (securityToken == null)
            {
                throw new ArgumentNullException("securityToken");
            }
            SPFederationAuthenticationModule fam = SPFederationAuthenticationModule.Current;
            if (fam == null)
            {
                throw new InvalidOperationException();
            }

            fam.SetPrincipalAndWriteSessionToken(securityToken);
        }

        private SPAuthenticationProvider
                GetAuthenticationProvider(string providerName)
        {
            SPIisSettings iisSettings =
                SPContext.Current.Site.WebApplication.IisSettings[SPUrlZone.Default];

            foreach (SPAuthenticationProvider currentProvider in
                        iisSettings.ClaimsAuthenticationProviders)
            {
                if (currentProvider.DisplayName.ToLower() ==
                            providerName.ToLower())
                {
                    return currentProvider;
                }
            }

            return null;
        }
    }
}

El proceso

Tengo una instancia de prueba de Sharepoint ejecutándose en la máquina virtual, pero no tengo instalado Visual Studio ya que todo el desarrollo se realiza de forma remota.El proceso actual que sigo;

  1. Construya la biblioteca de clases C# (esto será MyCustomSignInModule.Dll)
  2. Firmar MyCustomSignInModule.Dll para que pueda colocarse en el VM GAC.
  3. Registro MyCustomSignInModule.Dll en el GAC en la instancia de VM.
  4. Ahorrar SignInForm.aspx en el %CommonProgramFiles%\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\ que es el equivalente a <SharePointSiteRoot>/_layouts/ directorio virtual en IIS.
  5. En el sitio de Administración central de SharePoint, configure la página de inicio de sesión personalizada en mi página ~/_layouts/SignInForm.aspx.
  6. Restablecer IIS usando iisreset.
  7. Pruebe el acceso al sitio de SharePoint (aquí es donde falla).

El problema

Sign In Error

Si no está tan claro, el error es;

System.NullReferenceException: Object reference not set to an instance of an object. at
MyCustomSignInModule.SignInForm.b__0() at 
Microsoft.SharePoint.SPSecurity.<>c__DisplayClass5.b__3() at
Microsoft.SharePoint.Utilities.SecurityContext.RunAsProcess(CodeToRunElevated secureCode) at 
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(WaitCallback secureCode, Object param) at 
Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(CodeToRunElevated secureCode) at MyCustomSignInModule.SignInForm.get_iisSettings() at 
MyCustomSignInModule.SignInForm.Page_Load(Object sender, EventArgs e) 

SPContext.Current es nulo ¿por qué?

El NullReferenceException está siendo causado por SPContext.Current siendo nulo, pero no puedo entender por qué es nulo y ninguna cantidad de artículos que he visto hasta ahora me han ayudado a resolver esto.Espero que al publicar todo mi proceso aquí alguien pueda indicarme lo que me falta o lo que estoy haciendo mal.

¿Fue útil?

Solución

Finalmente llegué al fondo de lo que estaba causando el problema. En primer lugar, me gustaría agradecer a ambos. @MdMazzotti y @Hugh Madera por su persistente ayuda.

Al final encontré la solución, que resultó deberse en parte a la forma en que se construyó el sitio.En mi caso particular, el sitio se creó como un subsitio en SharePoint; por alguna razón, esto significaba que no había una colección de sitios en la raíz del sitio web de SharePoint, lo cual fue la causa del problema. Null SPContext en mi código.

Después de agregar una colección de sitios raíz, mi código comenzó a funcionar como se esperaba.

Otros consejos

Solo estoy adivinando, pero aquí hay algunas cosas que creo que podrían ayudar.

Agregue este código JavaScript a la página de inicio de sesión personalizada (antes de su declaración if):

SP.SOD.executeFunc('SP.js', 'SP.ClientContext');

También puedes agregar este JavaScript a tu página para probar si funciona o no, usando la consola:

var clientContext = new  SP.ClientContext.get_current(); 
console.log(clientContext);

Si devuelve algo, ¡entonces tenemos progreso!Si no, tendré que continuar mi búsqueda, ya que encuentro interesante tu pregunta.

EDITAR

En lugar de utilizar este código del lado del servidor:

if (_iisSettings == null)
{
   SPSecurity.RunWithElevatedPrivileges(delegate()
   {
      _iisSettings = SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(SPContext.Current.Site.Zone);
   });
}
return _iisSettings;

Intente usar esto en su lugar:

if (_iisSettings == null)
{
   SPSecurity.RunWithElevatedPrivileges(delegate()
   {
      _iisSettings = Microsoft.SharePoint.SPContext.Current.Site.WebApplication.GetIisSettingsWithFallback(Microsoft.SharePoint.SPContext.Current.Site.Zone);
   });
}
return _iisSettings;

Busqué y noté que en algunos de los ejemplos de código que encontré, _iisSettings era diferente al tuyo.¿Quizás es por eso que su SPContext.Current es nulo?

Por favor revisa esta respuesta:https://stackoverflow.com/questions/5081251/how-can-this-throw-a-nullreferenceexception

SPContext.Current no funciona con privilegios elevados, por lo que debe reescribir como en la respuesta anterior.

¿Puedes consultar este artículo de MSDN? http://msdn.microsoft.com/en-us/library/office/ms468609(v=office.14).aspx

Creo que debido a que usa un proyecto de biblioteca de clases de C# en Visual Studio, no está en un contexto web y no puede usar SPContext.Current como se explica en el artículo de MSDN anterior.

Licenciado bajo: CC-BY-SA con atribución
scroll top