Comment journaliser dans un flux UDP visible du réseau local avec NLog

Pressés? Sautez directement à la solution.

Le contexte

La librairie NLog est très bien documentée sur Internet pour une utilisation régulière. Par exemple, la configuration suivante (fichier NLog.config) permet de journaliser dans un fichier portant la date du jour dans un répertoire logs en mode Trace :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »f » />
</rules>
</nlog>

Par contre, la journalisation dans un fichier n’est pas toujours pratique. Par exemple, si vous développez une composante serveur, il se peut que les équipes souhaitant consulter le journal n’aient pas un accès physique au répertoire contenant le fichier. Aussi, le compte Windows configuré pour le processus (EXE) qui exécute le code qui journalise doit avoir les droits pour écrire ce fichier.

Je me suis donc mis à chercher une solution qui permettrait de visualiser le journal à distance, à partir d’un poste client, sans passer par un fichier.

La démarche

Je savais que la librairie NLog pouvait être utilisée avec un outil de visualisation de journal tel que Sentinel ou Log2Console. Il suffit d’ajouter la cible suivante et  de l’ajouter dans les règles :

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<logger name= »* » minlevel= »Trace » writeTo= »v » />

Mais cela fonctionne uniquement lorsque l’outil de visualisation est installé et exécuté localement sur le serveur. Ce n’est pas très avantageux vis-à-vis l’utilisation d’un fichier.

Ma première idée a été d’utiliser une adresse IP multicast (224.0.0.0 à 239.255.255.255) pour le flux UDP. Malheureusement, cela n’a pas fonctionné lors de mes tests. J’ai épuisé toutes mes idées de combinaisons de mots clé à rechercher sur Google pour NLog et multicast et je n’ai rien trouvé.

L’avantage d’un projet à code source libre est que la meilleure documentation reste encore le code en soi. J’ai découvert que les cibles Chainsaw et NLogViewer étaient des spécialisations de la cible Network :

image

Je suis donc allé étudier le code pour voir s’il n’y avait pas une manière de diffuser le flux UDP sur le réseau, plutôt que de toujours fonctionner localement. Lorsque la classe NetworkTarget est configurée avec une adresse UDP (udp://ip:port), sa fabrique de “NetworkSender” crée un UdpNetworkSender. C’est dans cette classe que se cachait le secret pour diffuser le journal sur le réseau local: la méthode CreateSocket contient une condition intéressante:

Uri uri;

if (Uri.TryCreate(this.Address, UriKind.Absolute, out uri) && uri.Host.Equals(IPAddress.Broadcast.ToString(), StringComparison.InvariantCultureIgnoreCase))
{
proxy.UnderlyingSocket.EnableBroadcast = true;
}

La documentation de la propriété EnableBroadcast indique clairement qu’il peut s’agir d’une technique pour diffuser le journal:

Broadcasting is limited to a specific subnet, and must use User Datagram Protocol (UDP.) For Internet Protocol version 4, you can broadcast to your local subnet by sending a packet to 255.255.255.255; or you can use the directed broadcast address, which is the network portion of an Internet Protocol (IP) address with all bits set in the host portion. For example, if your IP address is 192.168.1.40 (a Class C address, with a netmask of 255.255.255.0 — the network portion is the first three octets, and the host portion is the last octet), your directed broadcast address is 192.168.1.255.

Et la condition de la méthode CreateSocket teste si l’adresse de la cible est égale à IPAddress.Broadcast (255.255.255.255) avant de la mettre à true. Bingo!

La solution

La recette est donc la suivante :

<nlog xmlns= »http://www.nlog-project.org/schemas/NLog.xsd »
      xmlns:xsi= »http://www.w3.org/2001/XMLSchema-instance »
      xsi:schemaLocation= »http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd »
autoReload= »true »
throwExceptions= »false »
internalLogLevel= »Off » internalLogFile= »c:\temp\nlog-internal.log »>

  <targets>
<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />
</targets>
<rules>
<logger name= »* » minlevel= »Trace » writeTo= »c » />
</rules>
</nlog>

Personnellement je trouve que l’outil Log2Console est moins long à mettre en place sur le poste client. Il suffit de configurer un Receiver sur le même port que celui spécifié pour le « target » du programme qui utilise NLog pour journaliser :

clip_image002

image

Par contre, Sentinel offre plus d’options et permet de sauvegarder la configuration dans un fichier pour une utilisation ultérieure. Voici les étapes :

clip_image002[4]

image

image

image

image

image

image

image

Projet de démonstration

J’ai publié un projet de démonstration sur mon GitHub que vous pouvez utiliser pour tester les cibles NLog décrites dans cette entrée de blogue.

image

En cliquant sur le bouton, le journal contiendra une entrée pour chaque niveau de log supporté par NLog. ll y a trois cibles: File, NLogViewer et Chainsaw. La première cible crée un fichier portant la date du jour dans le répertoire logs. La seconde peut être utilisée avec un outil de visualisation de journal, s’exécutant sur le même poste. La dernière cible démontre l’utilisation de la fonctionnalité de Broadcast en UDP et il est alors possible d’exécuter le programme de visualisation de journal sur un autre poste.

<target xsi:type= »File » name= »f » fileName= »${basedir}/logs/${shortdate}.log »
layout= »${longdate} ${uppercase:${level}} ${message} ${when:when=(level = LogLevel.Error):inner=${newline}${exception:format=ToString}} » deleteOldFileOnStartup= »false » />

<target xsi:type= »NLogViewer » name= »v » address= »udp://127.0.0.1:9999″/>

<target xsi:type= »Chainsaw » name= »c »  address= »udp://255.255.255.255:9998″ />

La conclusion

J’aurais préféré trouver une solution utilisant le protocole UDP en mode multicast. Les routeurs sont intelligents et peuvent envoyer les packets uniquement aux ordinateurs “inscrits” pour un groupe multicast (une adresse IP). De plus, l’outil Log2Console a un champ pour inscrire l’adresse de ce groupe dans sa configuration:

image

Malheureusement je n’y suis pas arrivé. Je préfère ne pas avoir à modifier le code source de NLog alors je devrai me contenter de l’approche utilisant le Broadcast.

Quelques commandes à connaître pour diagnostiquer les problèmes avec Kerberos

Il n’est pas toujours possible d’accéder à la console d’administration d’Active Directory pour consulter les SPNs configurés pour un compte ou pour connaître la liste des services permis pour la délégation.

Voici donc une liste d’outils “Command Line” qui peuvent vous aider à effectuer des diagnostics lorsque votre ami des TI est absent.

KList (C:\Windows\System32)

  1. klist: liste les “tickets” actifs pour le compte courant
  2. klist purge: vide la liste des “tickets” pour le compte courant

SetSpn (C:\Windows\System32)

  1. setspn –l nomcompte: liste les SPNs configurés pour un compte donné
  2. setspn –s nomservice nomcompte: ajoute un SPN au compte spécifié après avoir vérifié qu’il n’y a aucun doublon
  3. setspn –d nomservice nomcompte: supprime un SPN au compte spécifié
  4. setspn –X: Vérifie qu’il n’y a pas de doublons, c’est à dire un SPN enregistré sur plus d’un compte. La présence de doublons peut empêcher Kerberos de fonctionner.

Script PowerShell (Maison)

Il manquait une manière d’obtenir la liste des services autorisés pour la délégation pour un compte donné. Ce script PowerShell vous permet de le faire (en obtenant la liste des SPNs configurés du même coup):

.\ListKerberos.ps1 nomcompte

Merci à mon collègue Georges Turpin pour la trouvaille et l’adaptation.

Voici le contenu du script:

param (
[string]$UserID = $(throw « -UserID required. »)
)

$strUserName = $UserID
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$strFilter = « (&(objectCategory=User)(samAccountName= » + $strUserName + « )) »
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = « Subtree »
$colResults = $objSearcher.FindAll()

foreach ($objResult in $colResults)
{
$objUser = $objResult.GetDirectoryEntry()
$objUser.RefreshCache(@(« canonicalName »))

Write-host  »  servicePrincipalNames » -ForegroundColor Green

$i=1

foreach($SPN in $objUser.servicePrincipalName)
{
Write-host  »    SPN( » $i « )   =    » $SPN
$i+=1
}

Write-host «  »

    « – – – – – »

$objHash = @{}
$objHash.Add(« ADS_UF_SCRIPT », »&h0001″)
$objHash.Add(« ADS_UF_ACCOUNTDISABLE », »&h0002″)
$objHash.Add(« ADS_UF_HOMEDIR_REQUIRED », »&h0008″)
$objHash.Add(« ADS_UF_LOCKOUT », »&h0010″)
$objHash.Add(« ADS_UF_PASSWD_NOTREQD », »&h0020″)
$objHash.Add(« ADS_UF_PASSWD_CANT_CHANGE », »&h0040″)
$objHash.Add(« ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED », »&h0080″)
$objHash.Add(« ADS_UF_TEMP_DUPLICATE_ACCOUNT », »&h0100″)
$objHash.Add(« ADS_UF_NORMAL_ACCOUNT », »&h0200″)
$objHash.Add(« ADS_UF_INTERDOMAIN_TRUST_ACCOUNT », »&h0800″)
$objHash.Add(« ADS_UF_WORKSTATION_TRUST_ACCOUNT », »&h1000″)
$objHash.Add(« ADS_UF_SERVER_TRUST_ACCOUNT », »&h2000″)
$objHash.Add(« ADS_UF_DONT_EXPIRE_PASSWD », »&h10000″)
$objHash.Add(« ADS_UF_MNS_LOGON_ACCOUNT », »&h20000″)
$objHash.Add(« ADS_UF_SMARTCARD_REQUIRED », »&h40000″)
$objHash.Add(« ADS_UF_TRUSTED_FOR_DELEGATION », »&h80000″)
$objHash.Add(« ADS_UF_NOT_DELEGATED », »&h100000″)
$objHash.Add(« ADS_UF_USE_DES_KEY_ONLY », »&h200000″)
$objHash.Add(« ADS_UF_DONT_REQUIRE_PREAUTH », »&h400000″)
$objHash.Add(« ADS_UF_PASSWORD_EXPIRED », »&h800000″)
$objHash.Add(« ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION », »&h1000000″)

[string]$intUAC = $objUser.userAccountControl.Value

Foreach ($objKey in $objHash.Keys)
{
[string]$intKey = $objHash.Item($objKey)

If (($intUAC -bAnd $intKey) -ne 0)
{
switch ($objKey)
{
« ADS_UF_SCRIPT »{« The logon script is executed. »}
« ADS_UF_ACCOUNTDISABLE »{« The user account is disabled. »}
« ADS_UF_HOMEDIR_REQUIRED »{« The home directory is required. »}
« ADS_UF_LOCKOUT »{« The account is currently locked out. »}
« ADS_UF_PASSWD_NOTREQD »{« No password is required. »}
« ADS_UF_PASSWD_CANT_CHANGE »{« The user cannot change the password. « }
« ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED »{« The user can send an encrypted password. »}
« ADS_UF_TEMP_DUPLICATE_ACCOUNT »{« This is an account for users whose primary account is in another domain. »}
« ADS_UF_NORMAL_ACCOUNT »{« This is a default account type that represents a typical user. »}
« ADS_UF_INTERDOMAIN_TRUST_ACCOUNT »{« This is a permit to trust account for a system domain that trusts other domains. »}
« ADS_UF_WORKSTATION_TRUST_ACCOUNT »{« This is a computer account for a computer that is a member of this domain. »}
« ADS_UF_SERVER_TRUST_ACCOUNT »{« This is a computer account for a system backup domain controller that is a member of this domain. »}
« ADS_UF_DONT_EXPIRE_PASSWD »{« The password for this account will never expire. »}
« ADS_UF_MNS_LOGON_ACCOUNT »{« This is an MNS logon account. »}
« ADS_UF_SMARTCARD_REQUIRED »{« The user must log on using a smart card. »}
« ADS_UF_TRUSTED_FOR_DELEGATION » { « DELEGATION – Trust this user for delegation to any service (Kerberos only). » }
« ADS_UF_NOT_DELEGATED »
{
« DELEGATION – Trust this user for delegation to specified services only (use any protocol): »
$objUser.properties[« msDS-AllowedToDelegateTo »]
}
« ADS_UF_USE_DES_KEY_ONLY »{« Restrict this principal to use only Data Encryption Standard (DES) encryption types for keys. »}
« ADS_UF_DONT_REQUIRE_PREAUTH »{« This account does not require Kerberos pre-authentication for logon. »}
« ADS_UF_PASSWORD_EXPIRED »{« The user password has expired. »}
« ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION »
{
« DELEGATION – The account is enabled for delegation: »
$objUser.properties[« msDS-AllowedToDelegateTo »]
}
default {« Could not find the UAC »}
}
}
}
}

Est-ce que cette information vous a été utile? Utilisez-vous d’autres outils? N’hésitez pas à commenter …

Comment faire disparaitre certaines erreurs suite à une installation SharePoint 2013

DCOM 10016

J’ai déjà traité des erreurs DCOM 10016 dans une entrée de blogue Comment faire disparaitre les erreurs DCOM 10016 suite à une installation SharePoint publiée il y a quelques années et suscitant encore beaucoup de traffic. Les erreurs ressemblent à:

The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID {61738644-F196-11D0-9953-00C04FD919C1}
to the user <Nom de l’utilisateur du AppPool> SID <SID du AppPool>.
This security permission can be modified using the Component Services administrative tool.

Avec SharePoint 2013, il y a de nouveaux GUIDs qui s’affichent dans le Event Viewer. Aussi, il est recommandé par Microsoft d’utiliser un compte de service différent pour le site d’administration (ex. cs-webadmin) que pour les autres applications Web (ex. cs-web). Si Microsoft SQL Server Integration Services est installé, vous pouvez aussi recevoir des erreurs similaires pour le compte de service SQL (ex. cs-sql).

Voici donc la liste complète des GUIDs pour lesquels on doit suivre mon guide précédent, ainsi que les utilisateurs pour lesquels on doit attribuer des droits:

GUID

Component Service

Utilisateur(s)

{61738644-F196-11D0-9953-00C04FD919C1} IIS WAMREG admin Service cs-webadmin, cs-web
{000C101C-0000-0000-C000-000000000046} {000C101C-0000-0000-C000-000000000046} cs-webadmin, cs-web
{83B33982-693D-4824-B42E-7196AE61BB05} Microsoft SQL Server Integration Services 11.0 cs-sql

Erreurs Reporting Services pour les extensions de données

Par exemple, vous avez peut-être ces erreurs dans les journaux SharePoint (ULS) et SSRS:

Exception caught instantiating TERADATA report server extension:
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

Voici un script PowerShell qui désactive les extensions qui ne sont pas utilisées la plupart du temps:

$ssrs = Get-SPRSServiceApplication

$ext = Get-SPRSExtension -Identity $ssrs.Id

$e = $ext | Where-Object {$_.Name -eq « TERADATA » -and $_.ExtensionType -eq « Data »}

Remove-SPRSExtension -Name $e.Name -ExtensionType $e.ExtensionType -Identity $ssrs.Id

$e = $ext | Where-Object {$_.Name -eq « TERADATA » -and $_.ExtensionType -eq « SemanticQuery » }

Remove-SPRSExtension -Name $e.Name -ExtensionType $e.ExtensionType -Identity $ssrs.Id

$e = $ext | Where-Object {$_.Name -eq « TERADATA » -and $_.ExtensionType -eq « ModelGeneration » }

Remove-SPRSExtension -Name $e.Name -ExtensionType $e.ExtensionType -Identity $ssrs.Id

$e = $ext | Where-Object {$_.Name -eq « SQLPDW » -and $_.ExtensionType -eq « Data » }

Remove-SPRSExtension -Name $e.Name -ExtensionType $e.ExtensionType -Identity $ssrs.Id

$e = $ext | Where-Object {$_.Name –eq « SQLPDW » -and $_.ExtensionType -eq « SemanticQuery » }

Remove-SPRSExtension -Name $e.Name -ExtensionType $e.ExtensionType -Identity $ssrs.Id

Erreurs SQL Server 2012 et SSAS 2012

Si vous avez les erreurs suivantes dans votre observateur d’événements, veuillez suivre ce guide pour les faire disparaître.

SQL Server

sqlservr (3472) tentative d’ouvrir le fichier « C:\Windows\system32\LogFiles\Sum\Api.log » pour uniquement un accès en lecture a échoué avec l’erreur système 5 (0 x 00000005): « Accès refusé ». L’opération d’ouverture de fichier échouera avec l’erreur -1032 (0xfffffbf8).

sqlservr (3472) erreur 1032 (0xfffffbf8) s’est produite lors de l’ouverture du fichier journal C:\Windows\system32\LogFiles\Sum\Api.log.

sqlservr (3472) An attempt to open the file « C:\Windows\system32\LogFiles\Sum\Api.log » for read only access failed with system error 5 (0x00000005): « Access is denied. « . The open file operation will fail with error -1032 (0xfffffbf8).

sqlservr (3472) Error -1032 (0xfffffbf8) occurred while opening logfile C:\Windows\system32\LogFiles\Sum\Api.log.

SSAS

msmdsrv (4680) tentative d’ouvrir le fichier « C:\Windows\system32\LogFiles\Sum\Api.chk » pour la lecture / écriture a échoué avec l’erreur système 5 (0 x 00000005): « Accès refusé ». L’opération d’ouverture de fichier échouera avec l’erreur -1032 (0xfffffbf8).

msmdsrv (4680) erreur 1032 (0xfffffbf8) s’est produite lors de l’ouverture du fichier journal C:\Windows\system32\LogFiles\Sum\Api.log.

msmdsrv (4680) An attempt to open the file « C:\Windows\system32\LogFiles\Sum\Api.chk » for read / write access failed with system error 5 (0x00000005): « Access is denied. « . The open file operation will fail with error -1032 (0xfffffbf8).

msmdsrv (4680) Error -1032 (0xfffffbf8) occurred while opening logfile C:\Windows\system32\LogFiles\Sum\Api.log.

Erreur SharePoint pour SPUsageImportJobDefinition

Si vous avez l’erreur suivante:

The Execute method of job definition Microsoft.SharePoint.Administration.SPUsageImportJobDefinition (ID ef497ec2-0cbf-4458-91ea-db75422fd9da) threw an exception. More information is included below.

Utilisez la soution décrite ici.

Comment lancer un rapport SSRS automatiquement lorsqu’un utilisateur accède à un site SharePoint

Si, comme nous, vous utilisez SharePoint comme dépôt pour vos rapports Reporting Services, vous souhaitez probablement que l’utilisateur accède directement au rapport, et non au site SharePoint.

Vous pouvez tout simplement lui donner l’URL complet vers le rapport, mais cela génère souvent des hyperliens longs.

L’autre approche est d’utiliser un petit script Power Shell:

$web = Get-SPWeb “https://server/sites/client

$folder = $web.RootFolder

$folder.WelcomePage = « FR/Rapports/Rapport1.rdl »

$folder.update()

$web.Dispose()

Ce qui fait que si le client va à l’url https://server/sites/client, il sera automatiquement redirigé à https://server/sites/client/FR/Rapports/Rapport1.rdl.

Merci à mon collègue au support technique pour cette trouvaille.

Virtualisation, plate-forme BI de Microsoft et économie d’énergie

Introduction

Les utilisateurs n’aiment pas attendre. Les entreprises comme Amazon et Google prennent cela très au sérieux, tel qu’indiqué dans cet article:

Surprising as all this may be, the implications of this impatience are even more shocking. Amazon‘s calculated that a page load slowdown of just one second could cost it $1.6 billion in sales each year. Google has calculated that by slowing its search results by just four tenths of a second they could lose 8 million searches per day–meaning they’d serve up many millions fewer online adverts.

On est d’accord sur le fait qu’un rapport BI est plus complexe à générer qu’une page listant les produits électroniques en solde. Mais les utilisateurs vont quand même exiger des performances acceptables, autrement ils n’utiliseront pas l’application.

C’est pourquoi vous devez faire passer les performances avant les économies d’énergie sur vos serveurs desservant la solution BI.

Plusieurs CPU modernes supportent des fonctions d’économie d’énergie afin de diminuer la consommation d’énergie quand les serveurs ne sont pas trop occupés. Cependant, cette mécanique ne réagit pas assez vite et fait en sorte que la peformance de l’application BI sera médiocre … sauf lors de pointes d’utilisation (charge).

The theory is in “Balanced” mode the CPU will run at a lower clock speed when it is under low workload, and bring more resources online when there is more to do. As great as it sounds, this won’t always be your best option. If your workload is incredibly bursty (quick moments of intense work), your SQL query speeds will take a hit, as by the time Windows has decided it needs more resources the work may already be done.

Symptômes

Si vous ne contrôlez que les paramètres du système d’exploitation “Guest” (machine virtuelle), voici les symptômes qui peuvent indiquer que l’hôte utilise des fonctions de gestion d’énergie:

  1. Si vous utilisez un outil de Benchmark, comme NovaBench, les résultats varient beaucoup d’un test à l’autre.
  2. Si vous demandez un rapport, les temps sont mauvais. Si vous demandez X rapports où X est le nombre de vCPU, les temps individuels sont meilleurs …

Recommandation

Il peut y avoir trois niveaux de configuration:

  1. BIOS
  2. Hypervisor
  3. OS

La recommandation est de soit désactiver la gestion de l’énergie dans le BIOS, soit l’activer en laissant le contrôle à l’OS (OS Controlled). Dans ce dernier cas, il faut s’assurer que l’hypervisor et / ou l’OS guest utilise High Performance.

Par défaut, VMWare ESX/ESXi utilise High Performance par défaut.

Par défaut, Windows Server 2008 R2 est à Balanced Performance, ce qui n’est pas désirable dans un environnement BI de Microsoft.

Références

Does CPU power management affect server performance?
http://serverfault.com/questions/94212/does-cpu-power-management-affect-server-performance

Consider CPU Power Optimization Versus Performance When Virtualizing
http://workinghardinit.wordpress.com/2011/06/20/consider-cpu-power-optimization-versus-performance-when-virtualizing/

Follow Up on Power Options for Performance When Virtualizing
http://workinghardinit.wordpress.com/2011/07/01/follow-up-on-power-options-for-performance-when-virtualizing/

Best Practices in Power Management
http://en.community.dell.com/techcenter/power-cooling/w/wiki/best-practices-in-power-management.aspx

(VMWare) Using CPU Power Management Policies
http://pubs.vmware.com/vsphere-4-esx-vcenter/index.jsp#managing_cpu_resources/c_managing_cpu_power_efficiency.html

Degraded overall performance on Windows Server 2008 R2
http://support.microsoft.com/kb/2207548/en-us

SQL Server on Power-Saving CPUs? Not So Fast.
http://www.brentozar.com/archive/2010/10/sql-server-on-powersaving-cpus-not-so-fast/

Why Microsoft Windows Server 2012 produces slow and inconsistent SQL query speeds
http://www.masterofmalt.com/software-development/blog/why-microsoft-windows-server-2012-produces-slow-and-inconsistent-sql-query-speeds/

Authentification par formulaire, SharePoint, SSRS et sécurité des données dans SSAS

Introduction à l’authentification par formulaire

L’authentification par formulaire dans SharePoint est basée sur la mécanique ASP.Net Membership Provider. Celle-ci date du .NET Framework 2.0, donc elle bien comprise des développeurs. Il y a aussi plusieurs providers disponibles, dont ceux fournis par Microsoft (ex. SqlAspNetMemberShipProvider).

Les raisons d’utiliser une authentification par formulaire sont variées:

  1. Une authentification propriétaire doit être utilisée. Dans ce cas, il faut développer un Custom Membership Provider.
  2. L’application est déployée sur Internet et l’utilisation de comptes Windows (ex. via un serveur ISA) est exclue.
  3. Plusieurs personnes utilisent un poste Windows sous le même compte et l’application doit les distinguer.

Claims et authentification par formulaire

Dans la version 2010 de SharePoint, Microsoft a ajouté un mode d’authentification Claims pour les applications Web. Le mode classique est toujours disponible mais ne fonctionne qu’avec l’authentification Windows. Cela veut dire que dès que l’on utilise l’authentification par formulaire, il faut configurer l’application Web en Claims.

image

Et il faut cocher l’authentification par formulaire en spécifiant le Membership Provider et le Role Provider:

image

Si vous avez activé plusieurs modes d’authentifications simultanément, les utilisateurs devront choisir la méthode avec laquelle ils souhaitent s’authentifier:

image

Claims et SSRS 2008 R2

Reporting Services 2008 R2 n’est pas Claims-Aware mais peut fonctionner lorsque SharePoint 2010 est configuré pour utiliser les Claims. Vous pouvez consulter ceci et ceci dont voici un extrait:

Because this functionality is built into SharePoint 2010 products, Reporting Services can work with this authentication model. Reporting Services is not claims-aware; instead it communicates with SharePoint through a trusted account. The proxy service within the SQL Server 2008 R2 add-in uses the SharePoint object model to convert the claims token into a corresponding SharePoint user context in the form of a SharePoint user token that the Report Server can understand and use to validate against the SharePoint database.

Par exemple, si l’utilisateur s’authentifie via l’authentification Windows, la variable User!Name contiendra i:0#.w|domain\user.

Claims et SSRS 2012

Pour sa part, Reporting Services 2012 est bel et bien Claims-Aware. Il est intégré comme un SharePoint shared service. Quelques extraits de l’article What’s New (Reporting Services):

The new architecture is implemented as a SharePoint 2010 shared service. The shared service architecture allows Reporting Services to leverage many of the IT features of SharePoint products.

(…)

The new Reporting Services service applications support Claims based authentication.

Claims et SQL Server

Pour sa part, SQL Server (engin relationnel et SSAS) n’est pas Claims-Aware. La seule manière de communiquer avec celui-ci est d’utiliser un compte Windows. Dès lors que SSRS est intégré à un SharePoint configuré en Claims, il faut définir un utilisateur pour exécuter les requêtes:

image

Claims et sécurité des données dans SSAS

SSAS permet de définir des rôles ayant des droits différents sur une base de données. C’est très pratique car cette sécurité est effective peu importe l’outil client utilisé pour se connecter aux données (ex. Excel).

image

Mais si tous les rapports s’exécutent avec le même compte Windows, comment utiliser la sécurité de données?

La réponse passe par l’utilisation de la clé EffectiveUserName dans la chaîne de connexion, qui doit devenir dynamique (et non partagée).

Data Source=Serveur;Initial Catalog=Catalogue;EffectiveUserName=UtilisateurÀUtiliserPourLaSécuritéDeDonnées

image

image

Dans cet exemple, j’ai défini un Dataset pour aller extraire les paramètres de la chaîne de connexion:

image

image

La procédure stockée accepte le nom de l’utilisateur (User!Name) comme paramètre et doit retourner le nom du serveur SSAS, le nom du catalogue SSAS ainsi que le nom de l’utilisateur Windows à impersonifier avec EffectiveUserName.

image

Ce DataSet, sert à alimenter trois paramètres internes:

image

Voici un exemple de ces paramètres:

image

image

Lorsque le rapport SSRS s’exécutera, les 3 premiers paramètres seront résolus en se connectant à SQL Server pour appeler la procédure stockée. Puis la chaîne de connexion dynamique vers SSAS sera résolue et contiendra un EffectiveUserName.

Prérequis pour utiliser EffectiveUserName avec SSAS

Avec ce qu’on a fait dans l’étape précédente, nous aurons le bon compte Windows dans le paramètre EffectiveUserName pour la connexion SSAS. Cependant pour que cela fonctionne il faut que le compte qui exécute la requête SSAS soit administrateur du serveur SSAS:

image

Puis il faut assigner chaque utilisateur pouvant être placé dans EffectiveUserName à un rôle SSAS.

Mais comme on le disait plus haut, lorsque l’authentification par formulaire est utilsée, il faut stoker l’identié du compte de service qui exécutera réellement les requêtes. Dans le cas des connexions vers SSAS, il faudra utiliser un compte de service qui sera dans la liste des administrateurs.

Si vous n’avez qu’une poignée de rapports, ce n’est peut-être pas trop difficile d’aller manuellement entrer ces informations au moyen de l’interface graphique de SharePoint:

image

Mais si les rapports sont mis à jour régulièrement ou que vous en avez plusieurs, cela peut devenir fastidieux.

Automatiser l’affectation des informations  d’identification stockées

Les services Web de Reporting Services vous donnent la possibilité de de spécifier l’utilisateur Windows à utiliser pour vous connecter à SSAS. Vous pourriez facilement développer un programme qui:

  1. Liste tous les rapports d’une librairie de documents SharePoint.
  2. Pour chacun, énumère les sources de données personnalisées (non partagées).
  3. Pour celles-ci, changer leur mode pour stocker l’identité spécifique.
  4. Affecte la liste des connexions modifiées au rapport déployé.

Vous pouvez utiliser le service Web accessible via un hyperlien ressemblant à ceci:

http://WebApplicationUrl/_vti_bin/ReportServer/ReportService2006.asmx

La documentation est ici:

http://technet.microsoft.com/en-us/library/reportservice2006.reportingservice2006(v=sql.105).aspx

La méthode ListChildren, acceptant en paramètre l’Url vers la librairie de documents contenant les rapports, retournera un tableau de CatalogItem.

Il est facile d’itérer sur les CatalogItem de type rapport:

foreach (ReportService2006.CatalogItem pCurItem in apItems.Where(pCI => pCI.Type == ReportService2006.ItemTypeEnum.Report)

Puis la méthode GetItemDataSources permet d’obtenir le tableau de DataSource du rapport. Il peut d’agir d’un DataSourceDefinition ou DataSourceReference.

Il faut donc itérer sur les sources de données personnalisées:

foreach (ReportService2006.DataSource pCurDataSource in apCurDataSources.Where(pDS => pDS.Item is ReportService2006.DataSourceDefinition)

Puis il suffit de modifier la DataSourceDefinition:

ReportService2006.DataSourceDefinition pCurDefinition = (ReportService2006.DataSourceDefinition)pCurDataSource.Item;pCurDefinition.CredentialRetrieval = ReportService2006.CredentialRetrievalEnum.Store;
pCurDefinition.UserName = sUserName;
pCurDefinition.Password = sPassword;
pCurDefinition.WindowsCredentials = true;
pCurDefinition.ImpersonateUser = false;
pCurDefinition.ImpersonateUserSpecified = true;

Puis il faut affecter la liste des connexions modifiées au rapport via la méthode SetItemDataSources.

Références

Working Together: SQL Server 2008 R2 Reporting Services Integration in SharePoint 2010
http://technet.microsoft.com/en-us/magazine/ff686706.aspx

Claims Authentication and Reporting Services
http://technet.microsoft.com/en-us/library/ff487970(v=sql.105).aspx

What’s New (Reporting Services 2012)
http://msdn.microsoft.com/en-us/library/ms170438.aspx#bkmk_sharepoint

Using EffectiveUserName To Impersonate in SSAS
https://www.artisconsulting.com/blogs/greggalloway/Lists/Posts/Post.aspx?ID=18&fwLinkID=301385

Caractères accentués, ASP.Net MVC Razor et boutons de dialogue JQuery UI

Développeurs MVC francophones: avez-vous déjà eu des problèmes avec des caractères accentués dans les boutons d’un dialogue JQuery UI?

Afin de développer l’application Web en anglais et français, les libellés des boutons sont stockés dans des fichiers de ressources (*.resx).

Tout fonctionnait avec des boutons comme Sauvegarder, Annuler, OK etc. Mais tout à coup, la technique habituelle échoue avec un bouton Générer …

image

Et s’il n’y avait pas de guillement pour la clé (Gén&/233;rer: function() { … ), c’est pire car cela fera planter votre script.

La solution est d’utiliser la méthode @Html.Raw et d’entourer le résultat avec des guillements:

$(« #dlgGenerator »).dialog({
  autoOpen: false,
  width: 800,
  height: 600,
  resizable: false,
  modal: true,
  open: function(event, ui) {
    // Initialization
  },
  buttons: {
    « @Html.Raw(Global.LblGenerate) »: function() {
      $(« #cmdGenerateAssignments »).click();
    },
    « @Html.Raw(Global.LblCancel) »: function() {
      $(this).dialog(« close »);
    }
  }
});

image

Il est peut-être sage de vous protéger tout de suite d’un changement de libellé éventuel! L’utilisation de Html.Raw vous protège des caractères accentués tandis que l’utilisation des guillemets empêchera les erreurs de script dûes à la présence d’un espace ou caractère illégal.

À la prochaine fois,