Question

Je suis la modification d'un système existant qui utilise SharePoint 2013 avec Authentification Basée sur les Formulaires (FBA) pour permettre aux utilisateurs de s'auto-service de création de comptes et de la gestion des comptes.Une des choses qu'ils peuvent gérer son rôle de membre;effectivement, ils ont un rôle à jouer code d'accès qu'ils fournissent pour le système, et il ajoute au rôle, leur permettant d'accéder à des sites dont le rôle a été attribué.Une partie de la solution est une coutume de l'écran de connexion pour aider à soutenir tout cela, et de le rendre plus convivial.Tout cela fonctionne relativement bien.

La tâche que je suis en train de travailler sur elle, l'ajout du support pour l'authentification externe fournisseurs plutôt que de l'authentification basée sur les formulaires - c'est une exigence de l'entreprise.Il y a beaucoup d'information en ligne (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 sur la façon d'ajouter Azure ACS comme un fournisseur d'identité et de travail.La question est maintenant comment ajouter l'auto-rôle des services de l'autorisation pièce.

J'ai pris de simples demandes d'augmentation de travail de l'amende suivant le guide à http://www.microsoft.com/en-us/download/details.aspx?id=27569.Le problème, je vais frapper est la "commune" de l'émission de la boîte de STS faire de la mise en cache sur le jeton, de sorte que lorsqu'un utilisateur le rôle de l'adhésion est changé, ils obtiennent l'ancien cache jeton encore.Mes demandes d'augmentation de code n'est pas appelé (bien sûr pas - pourquoi en serait-il lorsque le STS est à l'aide d'un cache billet), de sorte que le changement de statut de membre n'est pas d'être vu.

C'est à peu près le même genre de question abordée lors de vérifiez les autorisations montrant les informations incorrectes et http://www.shillier.com/archive/2010/10/25/authorization-failures-with-claims-based-authentication-in-sharepoint-2010.aspx, avec la réponse commune de la modification de la durée de vie du jeton donné.J'ai un peu de problèmes avec et questions à ce sujet.

  1. Dans les créances scénario, ce qui est la durée de vie appliquée?Ce n'est pas un Windows jeton, et ce n'est pas un FBA jeton, donc ni WindowsTokenLifetime ni FormsTokenLifetime semble approprié, bien que ceux-ci sont les deux tout le monde dit de changer.

  2. Je ne veux pas le jeton à expiration à l'utilisateur final de trop peu de temps, je veux juste la STS à l'expiration de la version en cache ou, idéalement, ne jamais cache à tous (je sais qu'il y a un problème de performance là - je n'aime pas, à notre échelle n'a pas d'importance).Il semble que ces deux choses sont couplés, cependant.Est-il possible de dissocier eux?

  3. Si je ne peux pas dissocier, il est un moyen pour l'écran de connexion pour indiquer au système que je veux la revendication d'être ré-augmentée?Puis-je à moi-même en modifiant le courant de l'identité de l'utilisateur?Qui est derrière l'arrière du système de cours donc je ne veux pas le faire si je peux l'aider.

  4. Tout semble se résument à la mise en cache dans le OOB STS puisque toutes les autres pièces mobiles semblent être de droite.Est la "bonne" façon de l'éviter de créer un custom STS qui n'ont pas le comportement de mise en cache et l'enregistrer comme un fournisseur d'identité?(Si j'ai un custom STS il me semble que je ne vous embêtez pas avec les revendications d'augmentation à ce point, je venais de problème de toutes les réclamations en premier lieu.) Cela semble être une quantité de travail considérable quoique je sais qu'il existe des échantillons, y compris http://archive.msdn.microsoft.com/SelfSTS), d'autant qu'il semble que j'ai pour le raccordement de la partie de confiance de connexion à Azure ACS puis en tant que partie du processus, où l'OOB STS n'est que "comme par magie" avec quelques lignes de PowerShell et quelques efforts.

  5. Ou, dois-je abandonner les revendications et il suffit de changer le FBA côté?Qui se sent comme une manière horrible pour aller depuis maintenant, j'ai besoin de raccorder tous les OpenID trucs dans mon code d'authentification pour l'authentification par formulaire, et je me déplace en arrière loin de la demande, lorsque je sais que je devrais être en mouvement, à des réclamations.

Un retard de 60 minutes est tout simplement pas acceptable quand dans le FBA modèle je peux faire le changement immédiatement en demandant à l'utilisateur de se connecter à nouveau, qui devient le nouveau rôle de l'adhésion.

Était-ce utile?

La solution

OK, j'ai eu ce travail (yay!!!), mais l'homme, c'était difficile.

Fondamentalement, vous devez forcer le jeton de la ré-émission de la STS, ce qui oblige les revendications d'augmentation de l'exécution de code à nouveau.Dans mon cas, les besoins de l'entreprise ont été un peu plus compliqué que ça, parce que j'avais besoin d'en fait seulement l'une des revendications possible, mais c'est à l'extérieur de la portée de cette question.

Essentiellement, je suis parti du code mentionné à https://stackoverflow.com/questions/8070456/re-calculate-claims-for-sharepoint-2010-user-while-the-user-is-still-logged-in/8185646#8185646 .Une chose que j'avais à faire était de supprimer le e.ReissueCookie depuis que complètement éclaté de l'authentification pour moi (l'utilisateur a été déconnecté par l'action).Dans mon cas, je n'ai pas envie de le faire dans une page spéciale, j'ai donc utilisé à l'utilisateur la valeur de l'Application de la collection que j'ai immédiatement clair après l'intervention.Le code pertinent dans mon cas récepteur qui ajoute et supprime le code de la global.asax suit:

        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
        }

L' ClaimSupport assemblée procède à divers code partagé:

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]";
            }
        }
    }
}

Une chose qui est vraiment important ici est que si vous êtes à la manipulation de réclamation, comme je le fais ici, et vous vous attendez à ces revendications, d'être reconnu par SharePoint, vous devez faire correspondre l'émetteur de l'information que SharePoint serait de l'utiliser avec un SPClaim, c'est pourquoi le "battement de cœur" réclamation.Dans la plupart des cas, vous n'aurez pas à s'inquiéter de ce que - les revendications vent va faire la bonne chose - mais ici, j'ai besoin d'avoir un seul de la possible jeu de revendications, de sorte que j'avais de manipuler directement dans ce code au lieu de dans les revendications d'augmentation de code.

Je sais aussi que vous pouvez importer les espaces de noms global.asax mais je suis préoccupé par un autre code d'utilisateur ou de manipulations de ce fichier, j'ai donc fait les modifications les plus indépendants possible.

Licencié sous: CC-BY-SA avec attribution
Non affilié à sharepoint.stackexchange
scroll top