Ongelezen berichten in openbare mappen met PowerShell

In tegenstelling tot klassieke mailmappen is het bij Openbare mappen (de zogenaamde Public Folders) niet mogelijk het aantal ongelezen berichten weer te geven en/of de map in kwestie vetjes te plaatsen als er tout court ongelezen berichten zijn. Behoorlijk vervelend als je zo’n 100 mapjes hebt die mail enabled zijn …

Met regels op die mappen kan je de berichten uiteraard naar je eigen mailbox laten doorsturen bij wijze van “alarm” maar met dit klein PowerShell scriptje heb je direct een overzichtje zonder je eigen mailbox te laten vollopen.

 1: $ol = new-object -comobject Outlook.Application
 2: $folderarray= @()
 3: $prefix = ""
 4: 
 5: function GetRecurrentFolder($map)
 6: {
 7:   $prefix += "--"
 8:   foreach ($item in $map.Folders)
 9:   {
 10:     $olkf = New-Object PSObject -Property @{
 11:         tName = $prefix + $($item.Name)
 12:         tUnRead = $($item.UnReadItemCount)
 13: 
 14:     }
 15:     $global:folderarray += $olkf
 16:     GetRecurrentFolder $item
 17:     }
 18: }
 19: 
 20: foreach ($x in ($ol.Session.Folders | where { $_.FolderPath -match '\\my public folder'}))
 21: {
 22:     GetRecurrentFolder $x
 23: }
 24: $global:folderarray| ft

 

Het scriptje geef het aantal ONGELEZEN mailtjes in de MAP zelf weer en niet de cumul met eventuele onderliggende mappen.


Powershell scripts digitaal ondertekenen

Out-of-the-box is uitvoeren van PowerShell scriptjes niet toestaan uit veiligheidsoverweging. Tijdens “de speeltijd” is dit makkelijk aan te passen met het PowerShell commando “Set-ExecutionPolicy”. Er zijn 4 verschillende execution policy’s nl.”Restricted” (default), “AllSigned”, “RemoteSigned” en “Unrestricted”.

In de standaard policy is scripts uitvoeren dus totaal onmogelijk. Wanneer de policy gewijzigd wordt naar “Unrestricted” dan kunnen alle scripts probleemloos uitgevoerd worden. Wordt de policy ingesteld op “AllSigned” kunnen alleen digitaal ondertekende scripts uitgevoerd worden en tenslotte als er geopteerd wordt voor “RemoteSigned” dan kunnen scripts op de lokale harde schijf sowieso uitgevoerd worden terwijl scripts vanop webpagina’s, e-mail etc … enkel uitgevoerd worden als ze digitaal ondertekend zijn.

Om na te gaan in welke modus je computer nu staat maak je gebruik van het commando “Get-ExecutionPolicy”

 1: PS C:\Users\DDIT\Documents> Get-ExecutionPolicy
 2: Restricted

 

En om in een test fase hier geen “last” meer van te hebben bedien je je van het commando

 1: Set-ExecutionPolicy -ExecutionPolicy Unrestricted

Voor het uitvoeren van het commando “Set-ExecutionPolicy” zijn wel administrator rechten nodig ! Maar zoals gezegd is deze oplossing sowieso enkel geschikt binnen development en testing doeleinden en niet echt op productie machines. We gaan onze scripts digitaal ondertekenen. We kunnen dat doen met aangekocht certificaten bij GlobalSign of Thawte maar we kunnen ook onze eigen certificaten uitschrijven. Die zijn technisch gezien perfect evenwaardig met de commerciële alternatieven …

We beginnen met ons “Root” certificaat te maken, we promoveren ons als het ware tot een soort GlobalSign. Op servers waar onze scripts moeten uitgevoerd worden zal dit root certificaat moeten toegevoegd worden aan de vertrouwde basis certificaten.

Certificaten maken kan met het gratis programmaatje “makecert.exe” dat oa. in de Windows SDK te vinden is. Als developper of sysadmin heb je die wellicht sowieso op je computer staan anders => http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspxHet root certificaat dat je aanmaakt zal je kunnen beveiligen met een wachtwoord.

makecert
-n "CN=DDIT Root Cert"  <= naam van het root cert
-a sha1  <= hashing algoritme (kon md5 geweest zijn ook)
-eku 1.3.6.1.5.5.7.3.3  <= doel van het certificaat
-r  <= self signed
-sv root.pvk root.cer  <= bestandsnaam voor het certificaat + private key
-ss Root  <= de cert store waarin het cert geplaatst wordt
-sr localMachine <= de cert store location (default is Current User en we willen het cert toch voor iedereen op onze computer :) ).

 

Nu gaan we op basis van ons net gemaakt root certificaat een certificaat maken om onze code mee te ondertekenen. Dit certificaat is wat je zou krijgen van GlobalSign / Thawte.

makecert
-pe  <= zorgt ervoor dat het certificaat geëxporteerd kan worden
-n "CN=PowerShell Dieter"  <= Naam van het certificaat
-ss MY  <=
-a sha1  <= Hashing algoritme
-eku 1.3.6.1.5.5.7.3.3  <= doel van het certificaat (code signing)
-iv root.pvk  <= Private key file van het root certificaat
-ic root.cer <= Root certificaat

 

Voor het aanmaken van het certificaat heb je het paswoord nodig van het root certificaat. Tijd om even te controleren in de GUI jQuery1520729151956219397_1312664369995 Start een MMC module en voeg de “Certificaten” module toe. ( mmc => Bestand, Module toevoegen of verwijderen => Certificaten => Mijn Gebruikersaccount). We vinden ons root certificaat

1

en ons zelf uitgeschreven certificaat

2

Met het PowerShell commando “get-childitem cert:\CurrentUser\My -codesigning” kan je alle certificaten opvragen die je ter beschikking hebt om code te ondertekenen. Wellicht zal dat er maar eentje zijn. Als de output hier al goed is kan je veder gaan met het ondertekenen van je script.

Set-AuthenticodeSignature
.\GetRunningProcess.ps1  <= te ondertekenen file
@(Get-ChildItem cert:\CurrentUser\My -codesigning)[0] <= handtekening

 

Na het uitvoeren van bovenstaand commando is de script file uitgebreid met je digitale handtekening. Mission Accomplished !!

3

Als je er nu voor zorgt dat de executionpolicy op AllSigned staat kan je het script uitvoeren … maar … je krijgt een melding dat het script getekend is door een onbekend iemand … Het volstaat om één keer “A” te antwoorden. Je code certificaat wordt dan ook toegevoegd aan de “Trusted Publishers”

4

Dit alles werkt nu op die ene computer waarop we de hele tijd gewerkt hebben. Als we ons script gaan uitvoeren op een andere computer waar de executionpolicy ingesteld staat op AllSigned zal ons script niet uitgevoerd worden omdat de uitgever van het certificaat niet gekend is (dat is ook logisch het root cert in kwestie bestaat nog maar net :) ). Om dat probleem op te lossen moet je je zelf gemaakt root certificaat exporteren en importeren op de doel computer. Dit zijn zaken die je NIET hoeft te doen als je een certificaat koopt. De root certificaten van oa. GlobalSign, Thawte, VeriSign zitten standaard in de verschillende Windows omgevingen …

  1. MMC Module openen
  2. Module toevoegen/Verwijderen
  3. Certificaten
  4. Vertrouwde basiscertificeringsinstanties => Certificaten
  5. Rechtermuisklik op dit root certificaat => Alle Taken => Exporteren
  6. 2 x VOLGENDE => bestandslocatie + naam kiezen
  7. VOLTOOIEN
  1. Herhaal de stappen 1 – 4 maar nu op de doelcomputer
  2. Rechtermuisklik => Alle Taken => IMPORTEREN
  3. Kies het CER bestand
  4. Zorg ervoor dat het bestand geïmporteerd wordt in de “Vertrouwde basiscertificeringsinstanties”.

Windows XP Prefetcher

De prefetcher is een functie in Windows XP die poogt de computer performanter te maken door enerzijds het boot proces te monitoren en anderzijds het opstarten van applicaties in het oog te houden. Het kijkt daarbij vooral welke bestanden nodig zijn bij opstarten van Windows en veel gebruikte applicaties. De prefetcher gaat afhankelijk van die bevindingen de volgende keer dat het systeem opstart (of dat er een applicatie opstart) zoveel mogelijk bestanden op voorhand reeds in het geheugen plaatsen.
Standaard staat de prefetcher aan en die mag in 99% van de gevallen ook gewoon aanblijven, alleen in situaties waar RAM geheugen een issue wordt kan je overwegen om de prefetcher bij te sturen.

In Windows XP bestaat daar geen GUI voor, je moet zelf een sleutel wijzigen in het register. Open het register met je favoriete editor (regedit?) en ga naar de volgende sleutel

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ Memory Management \ PrefetchParameters \ EnablePrefetcher
Standaard staat deze op “3” wat erop wijst dat zowel applicaties als het bootproces mogen “geprefetched” worden. Je kan deze sleutel wijzigen in “0” => niets prefetchen, “1” enkel applicaties prefetchen en tenslotte “2” om enkel het boot proces te prefetchen.

De prefetcher bewaart zijn bevindingen in de map “c:\windows\prefetch”, daarin vind je niet de programma’s die in het geheugen geladen zullen worden maar een soort logboek per applicatie/bootproces die door de prefetcher ingelezen wordt bij het opstarten of inladen van een applicatie. Sommige mensen wissen af en toe de inhoud van die map maar dat heeft weinig zin. In een record tempo zal de logboeken terug opstappelen in de map … daarnaast zal de prefetcher sowieso af en toe grote schoonmaak houden in haar eigen bestanden.

Conclusie : het leegmaken van de prefetcher map en het bijsturen van de werking van de prefetcher is enkel zinvol in zeer specifieke situaties zoals vb. debugging/troubleshooting


Exchange 2010 upgrade perikelen

Bij het upgraden van een Exchange 2010 installatie naar Exchange 2010 SP1 hadden we recent ernstige problemen. Na een probleemloze installatie van de prerequisites vatten we de eigenlijke installatie van SP1 aan. Deze crashte ongeveer op het ogenblik dat de Hub Transport Server werd geupgrade. Een totaal “gebrickte” Exhange 2010 blijft achter. (Geen enkele management console werkt nog, OWA weigert dienst, …).

Verschillende Internet fora maakten melding van onstabiele hotfixes in de prerequisites en het advies om de meest recente versie van die hotfixes aan te vragen bij Microsoft. Maar geen soelaas …

Uiteindelijk bleek het toch om een bug te gaan die zowel in de installer van Exchange 2010 als Exchange 2010 SP1 zou zitten. Wanneer in het register, meer bepaald in de sleutel

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers] meer dan 64 sleutels zitten (en dat verkrijg je vb. door EN exchange EN sharepoint op je machine te installeren) dan sneuvelt de installer.

Het volstaat om hier (tijdelijk) grote schoonmaak in te hoduen en de SP1 installer opnieuw uit te voeren. Dit keer verloop de upgrade probleemloos en verkrijgen we een perfect draaiende Exchange 2010 SP1. Achteraf kunnen de gewiste register sleutels overigens zonder problemen teruggezet worden.


FSRM API in C# gebruiken

In sommige situaties is het interessanter om quota’s te plaatsen op mappen ipv die te koppelen aan personeen. Het klassieke quota systeem ingebouwd in Windows 2003 is hiervoor niet geschikt. In Windows 2003 SP2 en Windows 2008 komt daarvoor echter een oplossing nl. de File System Resource Manager of kortweg FSRM waarin quota’s op mappen kunnen geplaatst worden.

Hoewel er geen .NET Wrapper is voor de FSRM API kan deze toch heel makkelijk gebruikt worden in C#.  Het volstaat de de juiste DLL (c:\windows\system32\srm.dll) te referencen omd aan de slag te kunnen.

Enkele code voorbeelden.  Quota plaatsen op een map waar nog geen quota op staat kan als volgt.

 1: public static bool AddDiskQuota(string Folder, double quota)
 2: {
 3:     FsrmQuotaManager quotao = new FsrmQuotaManager();
 4:     IFsrmQuota quotadef = quotao.CreateQuota(Folder);
 5:     quotadef.QuotaLimit = quota * (1024 * 1024);
 6:     quotadef.Commit();
 7:     return true;
 8: }

Nagaan hoeveel quota op een bepaalde map werd toegekend kan met volgende code

 1: public static double GetDiskQuota(string Folder)
 2: {
 3:         FsrmQuotaManager quota = new FsrmQuotaManager();
 4:         if (quota.GetQuota(Folder) != null)
 5:             return (Convert.ToDouble(quota.GetQuota(Folder).QuotaLimit));
 6:         else
 7:             return -1;
 8: }

Ook handig is de mogelijkheid om na te gaan hoeveel schijfruimte er al gebruikt wordt binnen de map

 1: public static double GetUsedQuota(string folder)
 2: {
 3:         FsrmQuotaManager quota = new FsrmQuotaManager();
 4:         if (quota.GetQuota(folder) != null)
 5:             return (Convert.ToDouble(quota.GetQuota(folder).QuotaUsed));
 6:         else
 7:             return -1;
 8: }

Er kan max. één quota entrie op een map geplaatst worden, daarom is het goed vb. de GetUsedQuota functie te gebruiken om na te gaan of er al een Quota entry op de map staat.   Als dat het geval is kan je de entry aanpassen ipv een nieuwe aan te maken.

 1: public static bool ChangeDiskQuota(string Folder, double quota)
 2: {
 3:         if (GetDiskQuota(Folder) != Convert.ToDouble(-1))
 4:         {
 5:             FsrmQuotaManager quotao = new FsrmQuotaManager();
 6:             IFsrmQuota test = quotao.GetQuota(Folder);
 7:             test.QuotaLimit = quota * (1024 * 1024);
 8:             test.Commit();
 9:             return true;
 10:         }
 11:         else
 12:             return false;
 13: }

Happy coding !


ASP.NET MVC op IIS 6

In tegenstelling tot IIS7 is IIS6 standaard niet voorzien op ASP.NET MVC.  Een van de belangrijskte kenmerken van ASP.NET MVC is de mooie URL opbouw zonder extensies. Net daar wringt het schoentje, wanneer in IIS6 een bestand geen extensie heeft weet IIS6 niet hoe het script moet geparsed worden.

Dit kan op twee manieren worden opgelost, het eenvoudigst is een wildcard mapping op je server instellen. Simpel gezegd vertel je hier aan IIS dat ALLES door aspnet_isapi moet geparsed worden. Maar veel Shared Hosting providers zullen het niet zien zitten om zo’n wildcard mapping voor je aan te maken en dan … is er de tweede oplossing.

Door in elke URL terug een extensie te brengen lossen we het probleem op. Meest voor de hand liggend is om de .aspx extensie te gebruiken maar als je je provider zo gek krijgt om pakweg .zorro aan de aspnet_isapi te mappen dan kan dat ook prima werken.

Om die extensies terug in je URL’s te krijgen openen we het bestand “global.asax.cs”, de plaats waar we de routing definiëren.  We vervangen routing regels van de vorm

 1: routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = ""});

door

 1: routes.MapRoute("Default", "{controller}.aspx/{action}/{id}", new { controller = "Home", action = "Index", id = ""});

De .ASPX (of .ZORRO voor de liefhebbers) kan overigens op elke plaats staan, alleen niet tussen de { }.  Omdat je je gebruikers natuurlijk nooit gaat vragen een url in te tikken van de vorm http://www.domain.be/controller.aspx/action/id is het belangrijk ook een regel te maken voor een lege url (http://www.domain.be).

 1: routes.MapRoute("Root", "", new { controller = "Home", action = "Index", id = ""});

Mogelijk heb je nu nog problemen met je links bij het bezoeken van de root van je website. Links kunnen nu gaan verwijzen naar http://www.domain.be/applicationname/… terwijl het wel degelijk http://www.domain.be/… moet zijn. Dit kan opgelost worden door in default.aspx.cs het lijntje

 1: HttpContext.Current.RewritePath(Request.ApplicationPath);

te vervangen door

 1: HttpContext.Current.RewritePath(Request.ApplicationPath, false);

Vanaf  ASP.NET MVC RC2 zit dit standaard zo in de templates, ervoor moet je het zelf wijzigen als het nodig mocht zijn in je situatie.

Als ASP.NET MVC niet op de server is geïunstalleerd moeten ook de ASP.NET MVC dll’s in de bin map worden opgenomen. Met System.Web.Mvc.Dll en System.Web.Routing.Dll raak je in elk geval al ver.


NTFS rechten instellen op remote server (in C#)

Voor één van onze klanten ontwerpen we een controle paneel die het complete server beheer omhelsd. Op die manier kunnen ook niet IT’ers gebruikers aanmaken/wijzigen/verwijderen; mappen aamnaken; ntfs rechten instellen; mappen delen enz …  Quasi alle taken kunnen gescript worden maar moeilijker wordt het als je zaken wil uitvoeren op een server waarop je code niet draait, maar moeilijk gaat ook.

Voor veel zaken heb je de “System.Management” en “System.Management.Instrumentation” objecten nodig.  Vergeet zeker niet om die dus te referencen.  Het zijn standaard .NET objecten die je zoiezo op elk toestel hebt.

 1: public void CreateFolder(String foldername)
 2: {
 3:         String path = "\\\\" + FileServer + "\\" + FileDrive + "$\\" + FileRoot + "\\" + foldername;
 4:         System.IO.Directory.CreateDirectory(path);
 5: }

FileServer, FileDrive, … zijn properties van de class waaruit ik deze functie heb gepulkt en die vb. kunnen gevuld worden door de constructor.  We gebruiken in deze en andere stukjes voorbeeld code vaak UNC paden waarbij we altijd via de administratieve share gaan (driveletter$) omdat dat zowat de enige share is waarvan je zeker bent. Andere shares kunnen verdwijnen en daarmee ook de goede werking van je code.

Een mapje delen doen we aan de hand van WMI classes (binnen C#)

 1: public void CreateShare(string Sharenaam, string Sharepad)
 2: {
 3:         ManagementScope MgScope = new ManagementScope("\\\\" + FileServer +"\\root\\cimv2");
 4:         ManagementPath MgPathShare = new ManagementPath("Win32_Share");
 5:         ManagementClass classObj = new ManagementClass(MgScope, MgPathShare, null);
 6:         ManagementBaseObject inParams = classObj.GetMethodParameters("Create");
 7:         inParams["Name"] = Sharenaam;
 8:         inParams["Path"] = Sharepad;
 9:         inParams["Type"] = 0; // Type = 0 => Het gaat om een DISK resource
 10:
 11:         ManagementBaseObject outParams = classObj.InvokeMethod("Create", inParams, null);
 12:         uint ret = (uint)(outParams.Properties["ReturnValue"].Value);
 13: }

In regel 3 geven we aan dat we WMI classes gaan aanspreken op onze FileServer, op regel 4 zien we dat het om de Win32_Share class gaat. In regel 5 wordt op basis van die twee zaken een object aangemaakt. De laatste parameter (null) wijst erop dat we geen specifieke connection paramters willen meegeven.  Om de Create functie te gebruiken moeten we een array van parameters meegeven met daarin de ShareNaam, het SharePath en het ShareType (0 = Disk).
En tenslotten NTFS rechten instellen kan op deze manier

 1: public void SetNTFS(string folder, byte[] SID)
 2: {
 3:     string path = @"\\" + FileServer + "\\" + FileDrive + "$\\" + FileRoot + "\\" + folder;
 4:
 5:     DirectoryInfo info = new DirectoryInfo(path);
 6:     DirectorySecurity security = info.GetAccessControl(AccessControlSections.All);
 7:     security.SetAccessRuleProtection(false, false);
 8:     InheritanceFlags flags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit;
 9:     SecurityIdentifier sid = new SecurityIdentifier(SID, 0);
 10:     security.AddAccessRule(new FileSystemAccessRule(sid, FileSystemRights.Modify, flags, PropagationFlags.None, AccessControlType.Allow));
 11:     info.SetAccessControl(security);
 12: }

Ook deze code is vrij recht door zee … We spreken de map waarvan we de NTFS settings willen wijzigen aan via het UNC pad.   We zouden hier kunnen opmerken dat we mogelijks een korter UNC pad kunnen gebruiken door rechtstreeks de ShareNaam van de map te gebruiken ipv via de Administratieve Share te gaan. MAAR god mag weten waarom maar bij het direct aanspreken van de share worden de “inherited NTFS instellingen” verwijderd.  We nemen dus het omwegje om dit niet voor te hebben.

In lijn 6 vragen we de huidige NTFS settings op, in lijn 10 voegen we er een extra regel aan toe om het geheel opnieuw weg te schrijven in regel 11.  Voor de nieuwe ACL in regel 10 hebben we een SID nodig dat we maken in regel 9. Op basis van het SID van ene gebruiker maken we een SecurityIdentifier. De 0 is de offset in de byte[], tenzij er nog iets anders in de byte[] zou zitten is dit dus altijd 0.  We hebben ook de inheritance flags nodig en die maken we in regel 8 we zeggen hierbij dat zowel mappen als bestanden de NTFS settings mogen overnemen.

Deze code kan makkelijk geïntegreerd worden in een ASP.NET of Winforms.NET project maar hou rekening met de nodige machtigingen.  Mogelijks moet je hier een beroep doen op Identity Impersonation. (ASP.NET Impersonation voor Active Directory bewerkingen)


“Forms Authentication” by DDIT

We maken allemaal wel eens fouten, zo ook wij hier bij DDIT. Een tijdje terug bouwden we voor een klant een backoffice waarbij de beveiliging werd afgehandeld door middel van Forms Authentication. De theorie is leuk, bijna niets programmeren en alles wat je kan dromen op gebied van authenticatie en authorisatie is voorzien. Accounts bijmaken, wachtwoorden resetten, wachtwoorden wijzigen … In theorie fijn tot de klant was bijsturingen wil die niet voorzien zijn. Een stukje bijbouwen is gekkenwerk als het al niet onmogelijk is.

Alleen idioten veranderen nooit van visie en daarom beslisten we Forms Authentication vaarwel te zeggen en de authenticatie terug zelf in handen te nemen maar wat te doen met de door de Forms Authentication gebouwde tabellen en de daarin zittende duizenden users?  Iedereen een mailtje sturen dat hun login veranderd is? Weinig professioneel, de klant zou het ons niet in dank afnemen.

Wat research leerde ons hoe de Forms Authentication tabellen in elkaar zaten. Elke gebruiker heeft naast zijn login een SALT waarde en een geëncrypteerd wachtwoord. We hoeven niet terug te kunnen keren naar het ongeencrypteerde wachtwoord (dat is trouwens onmogelijk) als we op basis van de SALT en het geëncrypteerde wachtwoord het door een gebruiker ingetikte wachtwoord kunnen verifiëren zijn we al lang blij.

En dat kan … met deze code.

 1: public string CheckPassword(string salt, string plaintext)
 2: {
 3:     byte[] un64basesalt = Convert.FromBase64String(salt);
 4:     byte[] un64plaintext = ASCIIEncoding.Unicode.GetBytes(plaintext);
 5:
 6:     int lengtesalt = un64basesalt.Length;
 7:     int lengteplain = un64plaintext.Length;
 8:
 9:     byte[] plainTextWithSaltBytes = new byte[lengtesalt+lengteplain];
 10:
 11:     for (int i = 0; i < un64basesalt.Length; i++)
 12:         plainTextWithSaltBytes[i] = un64basesalt[i];
 13:
 14:     for (int i = 0; i < un64plaintext.Length; i++)
 15:         plainTextWithSaltBytes[lengtesalt + i] = un64plaintext[i];
 16:
 17:     HashAlgorithm hash;
 18:     hash = new SHA1Managed();
 19:
 20:     byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
 21:     return Convert.ToBase64String(hashBytes);
 22: }

Een woordje uitleg. De Salt waarde die je in de DB aantreft is met BASE64 gecodeerd, je kan die makkelijk decoderen (regel 3).  Het plaintext wachtwoord ingetikt door een gebruiker zetten we net als de SALT om in een byte array (regel 4). Van beide zaken vragen we de lengte op (6+7).  Vervolgens kleven we de byte array van de salt en het plain text wachtwoord achter elkaar (eerste wachtwoord dan de salt) (11,12 + 14,15) en her resultaat daarvan gaan we ‘hashen’ met het SHA1 algoritme. (17).

Het bekomen resultaat kan vergeleken worden met de geencrypteerde waarde in de databank. Als die overeenstemmen is het wachtwoord van de gebuiker correct. Uit de hele DB structuur van de forms authentication moeten we dus enkel de logins, salts en geencrypteerde wachtwoorden overnemen in onze nieuwe applicatie.


Active Directory in .NET (deel 1)

Een tijdje geleden plaatsen we al een artikeltje betreft impersonation in ASP.NET. Impersonation komt oa. om de hoek kijken wanneer we via .NET code onze Active Directory willen manipuleren. In dit artikeltje geven we hieromtrent wat code cadeau (als nieuwjaarscadeau ;) .  Opgelet deze code is met momenten zeer ingrijpend en vraagt nooit of u het wel zeker weet. Voorzichtigheid is geboden !

Voor alles hebben we de .NET Class “System.DirectoryServices” nodig dus zeker niet vergeten …

 1: using System.DirectoryServices;
 2: using System.DirectoryServices.ActiveDirectory;

Omdat zowel gebruikers/groepen/… ergens moeten gemaakt worden schrijven we eerst een kleine functie een OU zoekt.  We kunnen een OU ook wel rechstreeks aanspreken maar zijn dan nooit zeker dat hij wel bestaat. Als hij uit ons zoekresultaat komt wel.

 1: private DirectoryEntry Find(String sLdap, String sQuery)
 2: {
 3:     DirectoryEntry Found;
 4:     DirectoryEntry OU = new DirectoryEntry(sLdap, "admin", "admin");
 5:     DirectorySearcher dsZoeken = new DirectorySearcher(OU);
 6:     dsZoeken.Filter = sQuery;
 7:     SearchResult srResultaat = dsZoeken.FindOne();
 8:     if (srResultaat != null)
 9:         Found = srResultaat.GetDirectoryEntry();
 10:     else
 11:         Found = null;
 12:     return Found;
 13: }

Het resultaat van deze routine is een DirectoryEntry object (worst case zit er wel NULL in), dus dat moet je wel even controleren als je de functie gebruikt hebt.  Een user aanmaken in de net gevonden OU kan je doen met onderstaande code.  De eerste parameter van de functie is het DirectoryEntry object wat we net vonden met de vorige routine. UserObject is een zelf gedefinieerd object. (Zie volgende stukje code).

 1: private bool CreateUser(DirectoryEntry deOU, UserObject gebruiker)
 2: {
 3:     bool gelukt = true;
 4:     try
 5:     {
 6:         DirectoryEntries workingOU = deOU.Children;
 7:         DirectoryEntry user = workingOU.Add("CN=" + gebruiker.Gebruikersnaam, "user");
 8:         user.Properties["sAMAccountName"].Add(gebruiker.Gebruikersnaam);
 9:         user.Properties["sn"].Add(gebruiker.Familienaam);
 10:         user.Properties["givenName"].Add(gebruiker.Voornaam);
 11:         user.Properties["Description"].Add("Test Account");
 12:         user.CommitChanges();
 13:     }
 14:     catch (Exception e)
 15:     {
 16:         Console.WriteLine(e.Message);
 17:         gelukt = false;
 18:     }
 19:     return gelukt;
 20:   }

 

 1: public class UserObject
 2: {
 3:     public String Gebruikersnaam { get; set; }
 4:     public String Voornaam { get; set; }
 5:     public String Familienaam { get; set; }
 6:     public String Wachtwoord { get; set; }
 7:     public String Email { get; set; }
 8: }

Met het allereerste stukje code in deze post kan je niet alleen een OU zoeken maar ook een gebruiker. Dat gaan we doen als we een gebruiker willen wijzigen.  Afhankelijk of je een OU of een User zoekt gebruik je de routine als volgt.

 1: DirectoryEntry putithere = Find("LDAP://SomeADServer/DC=Domain,DC=TLD", "OU=lookingforthisou");
 2: of
 3: DirectoryEntry putithere = Find("LDAP://SomeADServer/DC=Domain,DC=TLD", "SAMAccountName=lookingforthisou");
 4:
 5: if (putithere != null)
 6:     //object is gevonden
 7: else
 8:     //object is niet gevonden
 9:

Subquery in SQL

SQL 2005/2008 is met ruime voorsprong de meest populaire SQL server op de markt, de kostprijs zal daar voor een belangrijk stuk tussenzitten. En hoewel het een goed product is zijn er toch een aantal kleine zaken die niet helemaal lopen zoals we het wensen.

Neem nu dit stukje SQL code: “select * from tabel where (veld1, veld2) not in (select velda, veldb from tabel2).  Om het concreter te stellen. Neem de eerste tabel als zijnde de tabel met daarin alle cursussen van een school (veld1 : klas, veld2 : vak). Een tweede tabel bevat alle factuur lijnen van het programma waarmee de cursussen gefactureerd worden. Ik wil nu weten welke cursussen nog niet gefactureerd werden.  Ik wil met andere woorden weten welke records met een bepaalde combinatie “klas vak” in tabel1 niet voorkomen in tabel2.

In MS SQL lukt dat niet met bovenstaande query.  Een alternatief lijkt “select * from tabel where veld1 not in (select velda from tabel2) and veld2 not in (select veldb from tabel2)” maar uiteraard niet hetzelfde resultaat.  Want neem nu dat we al gefactureerd hebben “klas 1BEC, vak Boekhouden” en “klas 1BEC, vak Marketing” en “klas 2BEC en vak Geschiedenis” dan zal in ons resultaat de klas 2BEC met het vak Marketing niet meer voorkomen omdat zowel marketing in de DB zit en 2BEC, de combinatie van beide echter nog niet.

Overstappen naar Oracle kan een oplossing zijn als je je opdrachtgever kan overtuigen. De DDIT oplossing is een stuk eenvoudiger met name deze query “select * from tabel where (veld1+’@@@@’+veld2) not in (select velda+’@@@@’+veld2 from tabel2)”. Waarbij we de twee velden aan elkaar gaan hangen met daartussen een scheidingsteken. Het scheidingsteken mag om het even wat zijn als het maar niet frequent voorkomt in de desbetreffende velden.  Het mag dan een pruts oplossing lijken, ze werkt prima en is ook nog eens super performant.