Pergunta

Eu sou a modificação de um sistema existente que usa o SharePoint 2013 com a Autenticação Baseada em Formulários (FBA) para permitir que os usuários de auto-serviço de criação de contas e de gestão de contas.Uma das coisas que eles podem gerenciar é associação da função;efetivamente, eles recebem um papel código de acesso que fornecem para o sistema, e adiciona-los para o papel, permitindo-lhes aceder a sites que a função tenha sido concedido acesso.Parte da solução é um costume tela de login para ajudar a suportar tudo isso e torná-lo mais amigável ao usuário.Tudo isso está funcionando relativamente bem.

A tarefa que eu estou trabalhando, adicionando suporte para externo provedores de autenticação em vez da autenticação baseada em formulários- este é um requisito de negócio.Há uma abundância de informações on-line (e.g. http://blogs.msdn.com/b/mvpawardprogram/archive/2011/06/17/mvps-for-sharepoint-2010-using-azure-acs-v2-to-authenticate-external-systems-users.aspx) sobre como adicionar Azure ACS como um provedor de identidade e de trabalho.O problema agora é como adicionar o auto-serviço de autorização de função peça.

Eu comecei o simples aumento de declarações trabalhar bem a seguir a guia de http://www.microsoft.com/en-us/download/details.aspx?id=27569.O problema que eu estou acertando é o "comum" problema de caixa de ms / PTS fazendo o cache de token, então, quando um usuário da associação da função é alterada, o velho em cache de token ainda.Os meus pedidos de aumento de código não é chamado (claro que não - por que haveria de ser quando o STS está usando um cache de bilheteira), portanto, a alteração no número de membros não está sendo visto.

Este é praticamente o mesmo tipo de problema discutido no verifique as permissões mostrando informações incorretas e http://www.shillier.com/archive/2010/10/25/authorization-failures-with-claims-based-authentication-in-sharepoint-2010.aspx, com a resposta comum de alterar o token de vida determinado.Eu tenho alguns problemas com o e perguntas sobre este.

  1. Em declarações externo cenário, que é o tempo de vida aplicada?Não é um token do Windows, e não é uma FBA token, então, nem WindowsTokenLifetime nem FormsTokenLifetime parece adequado, apesar de que estes são os dois, todo mundo diz para alterar.

  2. Eu não quero que o token para expirar para o usuário final em um número exageradamente curto espaço de tempo, eu só quero o STS para expirar a versão em cache ou, idealmente, nunca cache em tudo isso (eu sei que há um problema de desempenho não - eu não me importo, para a nossa escala, não importa).Parece que essas duas coisas estão interligadas, no entanto.Existe uma maneira de dissociar-los?

  3. Se eu não posso dissociar, há uma maneira para a tela de login para indicar ao sistema que eu quero o pedido para ser novamente aumentada?Posso apenas a ele próprio, modificando a atual identidade de usuário?O que vai por trás do sistema de trás do curso eu não quero fazer o que se eu pode ajudá-lo.

  4. Tudo isso parece, em última análise, vêm para a colocação em cache no OOB ms / PTS como todas as outras peças em movimento parecem ser direito.É o jeito "certo" para evitar que este crie um personalizado ms / PTS que não tem o comportamento de colocação em cache e registrá-lo como um provedor de identidade?(Se eu tiver um STS personalizado parece que eu não me preocupar com o aumento de declarações nesse ponto, eu tinha acabado de problema de todas as devidas reclamações, em primeiro lugar.) Que parece uma quantidade substancial de trabalho embora (embora eu saiba que existem amostras, incluindo http://archive.msdn.microsoft.com/SelfSTS), especialmente porque parece que eu tenho que ligar a terceira parte de conexão do Azure ACS, em seguida, como parte do processo, onde o OOB STS que "magicamente" com algumas linhas de PowerShell e algum esforço.

  5. Ou, eu preciso abandonar as reivindicações e simplesmente alterar a FBA lado?Que se sente como uma horrível forma de o fazer, já que agora eu preciso ligar todos os OpenID coisas na minha código de autenticação para FBA, e eu estou me movendo para trás, longe de declarações quando eu sei que eu deveria estar se movendo para reclamações.

Um atraso de dizer que 60 minutos simplesmente não é aceitável quando na FBA modelo que eu possa fazer a alteração imediatamente, forçando o usuário a entrar novamente, o que recebe o novo membro da função.

Foi útil?

Solução

OK, eu tenho esse trabalho (yay!), o homem, porém, foi difícil.

Basicamente, você precisa forçar o token para ser re-emitido a partir do ms / PTS, o que força o aumento de declarações de código para ser executado novamente.No meu caso, os requisitos de negócio foram um pouco mais complicado que isso, porque eu precisava, na verdade, só tem uma das reivindicações possível, mas que está fora do âmbito de aplicação imediata para esta pergunta.

Essencialmente, eu saí do código mencionado no https://stackoverflow.com/questions/8070456/re-calculate-claims-for-sharepoint-2010-user-while-the-user-is-still-logged-in/8185646#8185646 .Uma coisa que eu tinha a fazer era remover a e.ReissueCookie desde que completamente falido de autenticação para mim (o usuário foi registrado pelo que a acção).No meu caso, eu não queria fazê-lo através de uma página especial, então eu usei um usuário específico de valor na Aplicação coleção que imediatamente me limpar depois de agir sobre ele.O código relevante no meu receptor de eventos que adiciona e remove o código global.asax segue:

        private readonly string AsaxText = @"
<%@ Assembly Name=""" + System.Reflection.Assembly.GetAssembly(typeof(ClaimSupport)).FullName + @"""%>
<script runat='server'> 
    void SessionAuthentication_SessionSecurityTokenReceived(object sender, Microsoft.IdentityModel.Web.SessionSecurityTokenReceivedEventArgs e) { 
        var application = HttpContext.Current.Application;
        if (application == null) {
            return;
        }
        var key = Company.SharePoint.Authentication.Data.ClaimSupport.ApplicationEventKey(e.SessionToken.ClaimsPrincipal.Identity.Name);
        var applicationValue = application[key];
        if (applicationValue == null) { 
            return;
        }
        var applicationString = applicationValue.ToString();
        if (string.IsNullOrWhiteSpace(applicationString)) {
            return;
        }
        application[key] = null;
        var sam = sender as Microsoft.IdentityModel.Web.SessionAuthenticationModule; 
        var logonWindow = Microsoft.SharePoint.Administration.Claims.SPSecurityTokenServiceManager.Local.LogonTokenCacheExpirationWindow; 
        var newValidTo = System.DateTime.UtcNow.Add(logonWindow);
        var currentPrincipal = e.SessionToken.ClaimsPrincipal;
        var claimsIdentity = (Microsoft.IdentityModel.Claims.IClaimsIdentity)currentPrincipal.Identity;

        var heartbeatClaim = GetHeartbeatClaim(claimsIdentity);
        var issuer = heartbeatClaim.Issuer;
        var originalIssuer = heartbeatClaim.OriginalIssuer;
        RemoveExistingEventClaims(claimsIdentity);
        AddEventClaim(claimsIdentity, applicationString, issuer, originalIssuer);

        e.SessionToken = sam.CreateSessionSecurityToken( 
            currentPrincipal, 
            e.SessionToken.Context, 
            e.SessionToken.ValidFrom, 
            newValidTo, 
            e.SessionToken.IsPersistent); 
        //e.ReissueCookie = true; - commented out because it broke things for me, but kept for reference
    } 

    private Microsoft.IdentityModel.Claims.Claim GetHeartbeatClaim(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity) {
        var heartbeatClaim = (from c in claimsIdentity.Claims
                              where
                                  (c.ClaimType == Company.SharePoint.Authentication.Data.ClaimSupport.EventClaimType)
                              &&
                                  (c.Value == Company.SharePoint.Authentication.Data.ClaimSupport.HeartbeatClaimValue)
                              select c).FirstOrDefault();
        return heartbeatClaim;
    }

    private void AddEventClaim(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity, string additionalEvent, string issuer, string originalIssuer) {
        var eventClaim = new Microsoft.IdentityModel.Claims.Claim(Company.SharePoint.Authentication.Data.ClaimSupport.EventClaimType, additionalEvent, HynesITe.SharePoint.Authentication.Data.ClaimSupport.EventClaimValueType, issuer, originalIssuer);
        claimsIdentity.Claims.Add(eventClaim);
    }

    private static void RemoveExistingEventClaims(Microsoft.IdentityModel.Claims.IClaimsIdentity claimsIdentity) {
        var currentClaims = (from c in claimsIdentity.Claims
                             where
                                 (c.ClaimType == HynesITe.SharePoint.Authentication.Data.ClaimSupport.EventClaimType)
                             &&
                                 (c.Value != HynesITe.SharePoint.Authentication.Data.ClaimSupport.HeartbeatClaimValue)
                             select c).ToList();
        foreach (var claim in currentClaims) {
            claimsIdentity.Claims.Remove(claim);
        }
    }
</script> 
";

        private void AddGlobalAsax(SPFeatureReceiverProperties properties) {
            var webApp = properties.Feature.Parent as SPWebApplication;
            if (webApp == null) {
                throw new SPException("Cannot add global.asax entries.");
            }
            var zones = Enum.GetValues(typeof(SPUrlZone)).Cast<SPUrlZone>().ToArray();
            var paths =
                zones.Select(z => Path.Combine(webApp.GetIisSettingsWithFallback(z).Path.ToString(), "global.asax"))
                    .Distinct().Where(File.Exists).ToArray();
            var globalAsaxFiles = new List<string>();
            globalAsaxFiles.AddRange(paths);
            foreach (var asax in from asax in globalAsaxFiles
                                 let contents = File.ReadAllText(asax)
                                 where !contents.Contains(AsaxText)
                                 select asax) {
                File.AppendAllText(asax, AsaxText);
            }
        }

        private void RemoveGlobalAsax(SPFeatureReceiverProperties properties) {
            var webApp = properties.Feature.Parent as SPWebApplication;
            if (webApp == null) {
                throw new SPException("Cannot add global.asax entries.");
            }
            var zones = Enum.GetValues(typeof(SPUrlZone)).Cast<SPUrlZone>().ToArray();
            var paths =
                zones.Select(z => Path.Combine(webApp.GetIisSettingsWithFallback(z).Path.ToString(), "global.asax"))
                    .Distinct().Where(File.Exists).ToArray();
            var globalAsaxFiles = new List<string>();
            globalAsaxFiles.AddRange(paths);
            foreach (var asax in globalAsaxFiles) {
                var contents = File.ReadAllText(asax);
                if (contents.Contains(AsaxText)) {
                    var replaced = contents.Replace(AsaxText, string.Empty);
                    File.WriteAllText(asax, replaced);
                }
            }
        }

        public override void FeatureActivated(SPFeatureReceiverProperties properties) {
            // other stuff
            AddGlobalAsax(properties);
            // other stuff
        }

        public override void FeatureDeactivating(SPFeatureReceiverProperties properties) {
            // other stuff
            RemoveGlobalAsax(properties);
            // other stuff
        }

O ClaimSupport assembleia mantém algumas diversos código compartilhado:

namespace HynesITe.SharePoint.Authentication.Data {
    public static class ClaimSupport {
        public static string EventClaimType {
            get {
                return "http://schema.Company.com/events";
            }
        }

        public static string EventClaimValueType {
            get {
                return Microsoft.IdentityModel.Claims.ClaimValueTypes.String;
            }
        }

        public static string ApplicationEventKey(string username) {
            return username + ":CurrentEvent";
        }

        public static string ApplicationIssuerKey(string username) {
            return username + ":Issuer";
        }

        public static string ApplicationOriginalIssuerKey(string username) {
            return username + ":OriginalIssuer";
        }

        public static string HeartbeatClaimValue {
            get {
                return "[heartbeat]";
            }
        }
    }
}

Uma coisa que é realmente importante aqui é que se está a manipular reivindicações, como eu estou aqui, e você espera que essas declarações para ser reconhecido pelo SharePoint, você precisa coincidir com o emissor de informações que o SharePoint usaria com uma SPClaim, que é por isso que o "heartbeat" reclamação.Na maioria dos casos, você não se preocupa que as reivindicações augmenter vai fazer a coisa certa - mas aqui, eu preciso ter apenas um dos possíveis conjunto de declarações, então eu tinha que manipular diretamente neste código, em vez de nas reivindicações de aumento de código.

Também eu sei que você pode importar os namespaces a global.asax mas eu estou preocupado com outro código de utilizador ou as manipulações do arquivo, então eu fiz as alterações o mais independente possível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a sharepoint.stackexchange
scroll top