Secure Store Service

[SP2013] – Accéder aux données du Secure Store Service en C#

Posted on Updated on

Bonjour à tous,

Aujourd’hui un article technique pour vous montrer comment accéder aux données stockées dans le Secure Store Service de SharePoint Server 2013.

A quoi sert ce service ? Il suffit de suivre le lien : https://technet.microsoft.com/fr-fr/library/Ee806866.aspx

1/ Contexte général

Le but étant de stocker des données de type login, mot de passe, domaine, Pin, etc. afin d’accéder à des systèmes externe à SharePoint.

Habituellement, on utilise le Secure Store Service (SSS) de SharePoint pour enregistrer les credentials utilisés pour la mise en œuvre de listes externes dans SharePoint par le biais du Business Connectivity Service (BCS, ex-BDC). imaginons le cas où l’on souhaite afficher dans SharePoint une base de clients/fournisseurs qui provient d’une base de données SQL Server. Cette base peut utiliser un mode d’authentification SQL (et non Annuaire AD) et il faut donc envoyer un login/mot de passe pour pouvoir se connecter et requêter la base depuis SharePoint. C’est donc un SSO (Single Sign-On) mais Inter-Domaine (Cross-Domain) que nous propose Microsoft. Les données de cette base d’identifiants sont chiffrées via une clé générée par SharePoint lui-même (à partir d’une phrase secrète qu’il ne faut pas perdre !).

Dans le cas présent, le but est d’accéder à ce Secure Store Service afin de stocker, lire, mettre à jour les données d’un utilisateur, et proposer par exemple un formulaire de connexion, de mise à jour de ces données directement depuis un portail SharePoint.

2/ Architecture du service

Le Secure Store Service est une Application de Service proposée par SharePoint Server 2013 (Standard ou Enterprise). Elle peut se déployer par la console d’administration centrale ou PowerShell (voir l’article Technet précédent).

La clé de chiffrement permet de chiffrer les données stockées dans la base de données associée à l’application de service.

Les données sont stockées dans des Target Applications (Applications cibles). Chaque Target application stocke les données pour 1 application, par exemple :

  • Une Target Application pour la connexion à une base SQL Server
  • Une Target Application pour la connexion à une ferme Dynamics CRM
  • Une Target Application pour la connexion à SAP
  • Une Target Application pour la connexion à une base Oracle

Chacune de ces Target Application est définie par l’administrateur SharePoint où il doit fournir :

  1. ID de l’application cible : identifiant unique de la Target Application
  2. Nom complet : nom d’affichage de la Target Application
  3. Messagerie de contact : adresse e-mail de contact pour l’administrateur de cette Target Application
  4. Url de la page d’application cible : voir article Technet
  5. Type d’application cible : il s’agit ici d’indiquer si chaque utilisateur aura sa propre identification ou si l’on va définir une identification pour tout un groupe de personnes (individual ou group).

Ensuite vous devrez spécifier les champs que vous souhaitez enregistrer : champs d’identification Windows, champs d’identification standards (login/mot de passe), code Pin, domaine, etc.

Et enfin les administrateurs de la Target Application. Bref, tout cela est plutôt bien défini dans l’article Technet.

3/ Lire des credentials depuis mon code en C#

Voici enfin le cœur du problème. Comment accéder aux données stockées dans le SSS depuis mon code ?

J’ai tout d’abord défini une classe métier pour stocker quelques informations : Login, password, Login SharePoint, Nom Complet et E-mail de l’utilisateur :


namespace MonDev.LogMe.Utils
{
   public class SSSCredentials
   {
      public string Login { get; set; }
      public string Password { get; set; }
      public string SPLogin { get; set; }
      public string SPFullName { get; set; }
      public string SPEmail { get; set; }
   }
}

Ensuite, voici le code de la méthode GetCredentials que j’utilise. Les paramètres attendus sont :

  • site : collection de sites courante
  • applicationID : identifiant (interne) de la Target Application

   /// Get credentials for current connected user from Secure Store Service.
   /// current site collection, associated to a Secure Store Service at Web Application Level
   /// Target Application ID in Secure Store Service
   /// Returns Credentials if found, null otherwise.
   public static SSSCredentials GetCredentials(SPSite site, string applicationID)
   {
      //Initialize new Credentials structure
      SSSCredentials credentials = new SSSCredentials();
      credentials.SPEmail = site.RootWeb.CurrentUser.Email;
      credentials.SPFullName = !string.IsNullOrEmpty(site.RootWeb.CurrentUser.Name) ? site.RootWeb.CurrentUser.Name : site.RootWeb.CurrentUser.LoginName;
      credentials.SPLogin = site.RootWeb.CurrentUser.LoginName;

      //new SPSite is not mandatory, but it seems to be more efficient than use SPSite from parameters.
      using (SPSite currentSite = new SPSite(site.ID))
      {
         //Initialize a new SecureProvider to access to data into SSS
         ISecureStoreProvider provider = SecureStoreProviderFactory.Create();
         if (provider == null)
            throw new InvalidOperationException("Unable to get an ISecureStoreProvider");

         ISecureStoreServiceContext providerContext = provider as ISecureStoreServiceContext;
         providerContext.Context = SPServiceContext.GetContext(currentSite);

         try
         {
            using (SecureStoreCredentialCollection creds = provider.GetCredentials(applicationID))
            {
               if (creds != null)
               {
                  foreach (SecureStoreCredential cred in creds)
                  {
                     if (cred == null)
                        continue;

                     switch (cred.CredentialType)
                     {
                        case SecureStoreCredentialType.UserName:
                           credentials.Login = GetStringFromSecureString(cred.Credential);
                           break;
                        case SecureStoreCredentialType.Password:
                           credentials.Password = GetStringFromSecureString(cred.Credential);
                           break;
                        default:
                           break;
                     }
                  }
               }
            }
         }
         catch (SecureStoreException)
         {
            //No credentials are found into SSS, so return null
            return null;
         }
         catch (Exception e)
         {
            throw new Exception("A general error occured. Please contact your administrator. Error : " + e.Message);
         }
      }
      return credentials;
   }

La méthode doit être appelée comme suit :


private const string TargetApplicationID = "MaTargetApplication";
SSSCredentials credentials = SSSUtils.GetCredentials(SPContext.Current.Site, TargetApplicationID);

Rien de très compliqué toutefois je suis tombé sur quelques exemples de codes et aucun n’a fonctionné chez moi. A noter ici, pas besoin d’impersonation ou de SPSecurity.RunWithElevatedPrivileges(), bien au contraire. Il faut se connecter au provider avec les identifiants de l’utilisateur connecté au portail pour pouvoir récupérer ses identifiants !

 

4/ Ecrire dans le Secure Store Service

Si nous avons lu, il faut bien parvenir à écrire dans le SSS. Ici la méthode est la même pour l’ajout ou la modification de credentials dans le magasin du Secure Store Service. Les paramètres attendus sont :

  • site : collection de sites courante
  • applicationID : identifiant unique (interne) de la Target Application
  • windowslogin : login Windows ou identifiant de l’utilisateur dans le portail SharePoint
  • userName : nom d’utilisateur à stocker dans le Secure Store Service, dans la Target Application
  • userPassword : mot de passe à stocker dans le Secure Store Service, dans la Target Application

 


public static void SetCredentials(SPSite site, string applicationID, string windowslogin, string userName, string userPassword)
   {
      SPClaim claim = SPClaimProviderManager.CreateUserClaim(windowslogin, SPOriginalIssuerType.Windows);
      SecureStoreServiceClaim ssClaim = new SecureStoreServiceClaim(claim);

      SPServiceContext context = SPServiceContext.GetContext(SPServiceApplicationProxyGroup.Default, SPSiteSubscriptionIdentifier.Default);

      SecureStoreServiceProxy ssp = new SecureStoreServiceProxy();
      ISecureStore iss = ssp.GetSecureStore(context);

      IList applicationFields = iss.GetUserApplicationFields(applicationID);

      IList creds = new List(applicationFields.Count);

      using (SecureStoreCredentialCollection credentials = new SecureStoreCredentialCollection(creds))
      {
         foreach (TargetApplicationField field in applicationFields)
         {
            switch (field.CredentialType)
            {
               case SecureStoreCredentialType.UserName:
                  creds.Add(new SecureStoreCredential(GetSecureStringFromString(userName), SecureStoreCredentialType.UserName));
                  break;
               case SecureStoreCredentialType.Password:
                  creds.Add(new SecureStoreCredential(GetSecureStringFromString(userPassword), SecureStoreCredentialType.Password));
                  break;
               default:
                  break;
            }
         }
         iss.SetCredentials(applicationID, credentials);
      }
   }

La méthode doit être appelée comme suit :


SSSUtils.SetCredentials(SPContext.Current.Site, TargetApplicationID, SPContext.Current.Site.RootWeb.CurrentUser.LoginName, "LOGIN_A_STOCKER", "PASSWORD_A_STOCKER");

A savoir, il est important d’exécuter le code avec le compte de l’utilisateur connecté à SharePoint afin de pouvoir lire ses credentials ou écrire/mettre à jour ses credentials. Lorsque j’essayais d’écrire le code pour faire ces manipulations, je suis tombé sur pas mal de bouts de code où ils essayaient d’exécuter cela avec le compte système via de l’impersonation ou bien SPSecurity.RunWithElevatedPrivileges(). Cela n’a pas marché pour moi !

5/ Fonctions annexes

Dans les extraits de codes précédents, j’ai utilisé quelques méthodes supplémentaire pour encoder/décoder les chaines de caractères envoyées ou reçues depuis le Secure Store Service. Pour être totalement complet, les voici.

Cette première méthode permet de décoder une chaine encodée (SecureString) dans une chaine standard (String)


internal static string GetStringFromSecureString(SecureString secStr)
{
   if (secStr == null)
      return null;

   IntPtr pPlainText = IntPtr.Zero;
   try
   {
      pPlainText = Marshal.SecureStringToBSTR(secStr);
      return Marshal.PtrToStringBSTR(pPlainText);
   }
   finally
   {
      if (pPlainText != IntPtr.Zero)
         Marshal.FreeBSTR(pPlainText);
   }
}

Et bien sûr la méthode effectuant la manipulation inverse :


internal static SecureString GetSecureStringFromString(string str)
{
   if (str == null)
      return null;
   var str2 = new SecureString();
   char[] chArray = str.ToCharArray();
   for (int i = 0; i < chArray.Length; i++)
   {
      str2.AppendChar(chArray[i]);
      chArray[i] = '0';
   }
   return str2;
}

6/ Usings…

J’ai un peu oublié d’en parler au début, mais certains peuvent avoir des difficultés pour inclure les bonnes références et using nécessaires au projet (je ne parlerai pas des assemblies standards…).

Références :

  1. Microsoft.BusinessData (à aller cherche dans) : C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.BusinessData.dll
  2. Microsoft.Office.SecureStoreService (à aller rechercher dans le GAC) : C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Office.SecureStoreService\v4.0_15.0.0.0__71e9bce111e9429c\Microsoft.Office.SecureStoreService.dll
  3. Microsoft.Office.SecureStoreService.Server.Security (à aller cherche dans) : C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\Microsoft.Office.SecureStoreService.Server.Security.dll

Il ne nous reste plus qu’à trouver un usage à tout ceci ! 🙂

Florent.

[SharePoint 2010 – 2013] – Créer une Target Application sans le Secure Store Service en PowerShell

Posted on Updated on

 

Bonjour à tous,

Cela faisait un petit moment que je n’avais pas publié. Un petit post aujourd’hui pour vous montrer comment ajouter une TargetApplication dans le Secure Store Service de SharePoint. Allons-y !

 

1/ Secure… quoi ?

Le Secure Store Service, appelé Banque d’Informations Sécurisées en français), est une application de service de SharePoint 2010 et 2013 qui vous permet de stocker des couples d’identifiants login/password dans une base encryptée. Ces couples d’identifiants sont destinés à l’usage d’autres applications de services : Business Connectivity Service, PowerPivot, Excel Services, etc. afin qu’ils soient en capacité à se connecter à des sources de données par exemple, avec des identifiants différents de ceux de l’utilisateur connecté, du pool d’application, du compte administrateur.

Dans mon cas, il s’agit de stocker des identifiants afin qu’une application personnalisée puisse se connecter à un serveur Exchange 2010 via ses WebServices. Je ne voulais pas stocker ces informations dans SharePoint ou dans un fichier de configuration par exemple. L’avantage étant que ces paramètres sont cryptés et modifiables par l’administrateur de la ferme.

 

2/ Prérequis

En prérequis, il faut bien sûr déployer cette application de service qui sera disponible pour :

  • · SharePoint Server 2010 Standard
  • · SharePoint Server 2010 Enterprise
  • · SharePoint Server 2013 Standard
  • · SharePoint Server 2013 Enterprise

Point sur n’importe laquelle des versions Foundation ni SharePoint 2007 donc.

Pour déployer et configurer cette application de service, je vous invite à lire : http://technet.microsoft.com/fr-fr/library/ee806866(v=office.15).aspx

 

3/ Un peu de PowerShell

Cette démo sera réalisée avec SharePoint Server 2010 Enterprise.

Pour commencer, nous allons lancer le Management Shell de SharePoint (PowerShell avec les assemblies/cmdlets nécessaires à l’utilisation avec SharePoint). Pour cela, vous trouverez le raccourci dans Démarrer > Tous les Programmes > Microsoft SharePoint 2010 Products > SharePoint 2010 Management Shell

image

 

Nous allons commencer par renseigner quelques variables nécessaires :

  • $siteurl = ” http://sp2010-dev:82/sites/florent&#8221; ==> Url d’un site pour récupérer le ServiceContext (L’application de service Secure Store Service doit être associée à cette WebApplication !)
  • $ta_name = “ExchangeWS” ==> Nom de ma Target Application
  • $ta_friendlyName = “ExchangeWS for DEMO” ==> Nom “Friendly” de ma Target Application
  • $ta_contactEmail = “admin@sp2010.local” ==> Adresse e-mail de contact
  • $ta_owner = “DEMO\spadmin” ==> Propriétaire de la Target Application
  • $ta_userName = “DEMO\exchangeuser” ==> Login à stocker
  • $ta_password = “P@ssw0rd” ==> Password à stocker

image

Puis on créé la Target Application :

$target = New-SPSecureStoreTargetApplication -Name $ta_name -FriendlyName $ta_friendlyName -ContactEmail $ta_contactEmail -ApplicationType Group

clip_image002

Puis on créé les champs Login et password à stocker :

$usernameField = New-SPSecureStoreApplicationField -name “Exchange Username” -Type WindowsUserName -Masked:$false

$passwordField = New-SPSecureStoreApplicationField -name “Exchange Password” -Type WindowsPassword -Masked:$true

$fields = $usernameField,$passwordField

clip_image004

Puis nous allons créer 2 claims pour l’administrateur et pour le propriétaire :

$adminClaims = New-SPClaimsPrincipal -Identity $a_owner -IdentityType 1

$ownerClaims = New-SPClaimsPrincipal -Identity $a_owner -IdentityType 1

clip_image006

Puis créer la nouvelle Target Application :

New-SPSecureStoreApplication -ServiceContext $contextUrl -TargetApplication $ta -Fields $fields -Administrator $adminClaims -CredentialsOwnerGroup $ownerClaims

$ssa = Get-SPSecureStoreApplication -ServiceContext $contextUrl -Name $ta.Name

clip_image008

Puis on charge les identifiants :

$user = ConvertTo-SecureString $ta_userName -AsPlainText -Force

$password = ConvertTo-SecureString $ta_password -AsPlainText -Force

$credentials = $user,$password

Update-SPSecureStoreGroupCredentialMapping -Identity $ssa -Values $credentials

clip_image010

On peut verifier dans l’administration centrale, la Target Application est bien créée :

clip_image012

clip_image014

clip_image016

On voit ici que les Members (utilisateurs ayant accès à la Target Application) contiennent seulement SPADMIN. Pour ajouter des utilisateurs, on peut modifier les Owner Claims…

clip_image018

Par contre, impossible de valider graphiquement si le login/password est bien ajouté, la fenêtre ne charge pas les valeurs chargées (formulaire vide par défaut). Il faut donc tester… et chez moi ça marche 😉

A bientôt !

Florent.