tanketorsken.dk/2008/07/

Feeds

Foreach loop og en hashtable

Har mit udgangspunkt i PHP og er blevet svært vandt til PHP’s foreach loop.

$output ="";

$movie_ratings = array(
    'kungfu_panda'=>4,
    'anaconda'=>0.5,
    'american_gangster'=>5,
    'la_confidential'=>4.5,
    'enemy_of_the_state'=>4
);

foreach($movie_ratings as $movie => $rating)
{
    $output .= "The movie $movie scored $rating\n";
}

Men hvordan fungerer det i C#? Det er ganske simpelt, men af en eller anden grund bliver jeg ved med at skulle slå det op, så nu skriver jeg det ned et sted hvor jeg hurtigt kan få fat på det.

string output = "";
Hashtable movie_ratings = new Hashtable();
 
movie_ratings.Add("kungfu_panda", 4);
movie_ratings.Add("anaconda", 0.5);
movie_ratings.Add("american_gangster", 5);
movie_ratings.Add("la_confidential", 4.5);
movie_ratings.Add("enemy_of_the_state", 4);
 
foreach(string key in movie_ratings.Keys)
{
    output += "The movie " + key + " scored " + movie_ratings[key];
}

Bitmasking for begyndere af en begynder

Er igang med mit første asp.net projekt hvor jeg har valgt at bruge den indbyggede Membership/Roles – funktionalitet. Jeg har dog brug for at kunne lave adgangsregler på et mere detaljeret plan end på sideniveau, og har derfor lavet en lille Authorization-klasse der klarer dette. I en static Globals klasse er der defineret to Enumerations:

[Flags]
private enum Roles
{
    Administrator = 1,
    Editor = 2,
    Member = 4,
    ThirdParty = 8,
    All = Administrator | Editor | Member | ThirdParty
}
 
[Flags]
public enum Actions
{
    Frontpage = Roles.All,
    Articles_PostComment = Roles.Administrator | Roles.Member,
    Jobs_List = Roles.Administrator | Roles.Editor | Roles.Member,
    Jobs_Create = Roles.Administrator | Roles.Editor
}

I Authorization-klassen udregner jeg brugerens værdi ved at lægge værdierne for brugerens roller sammen:

private int GetUserValue()
{
    int sum = 0;
    
    foreach(string role in Roles.GetRolesForUser())
    {
        sum += (int)Enum.Parse(typeof(Globals.Roles), role);
    }
    
    return sum;
}

Da de forskellige Roles har værdier der alle er potenser af 2, kan jeg let lave en Authorization.IsAllowed() funktion der ved hjælp af bitmasking tjekker om en bruger har den ønskede rolle:


// userRoleValue er den returnerede værdi af
// Authorization.GetUserValue()
 
public bool IsAllowed(Globals.Actions allowedRoleLevel)
{
    return (((int)allowedRoleLevel & userRoleValue) != 0);
}

Og hvad er bitmasking så?
I bitmasking udfører man operationer på et tals binære repræsentation. I mit tilfælde brugte jeg de to bitwise-operators AND & og OR | der fungerer som følger:

// AND sammenligner to binære repræsentationer
// af samme længde og for hvert bitpar er
// resultatet 1 hvis den første bit er 1 OG
// den anden bit er 1
 
    010011
AND 111001
    010001
 
// OR sammenligner to binære repræsentationer
// af samme længde og for hvert bitpar er
// resultatet 1 hvis den første bit er 1 ELLER
// den anden bit er 1 (eller begge bits er 1)
 
   010011
OR 111001
   111011

Ved at kigge på mit konkrete eksempel, kan det ses hvordan bitmasking fungerer:


// De forskellige værdier for Roles er i binær repræsentation:
 
Administrator = 0001
Editor = 0010
Member = 0100
ThirdParty = 1000
 
// I Globals.Roles kombineres flere a disse roller
// ved OR operatoren |
// Resultatet af f.eks Administrator | Member bliver:
 
   0001
OR 0100
   0101

Her ses vigtigheden af at alle værdier er potenser af 2. Ved at benytte OR operatoren kan flere rollers status gemmes i et tal. Når man skal gå den anden vej og tjekke om en rolle har adgang til en action benyttes AND operatoren:


// For en action er følgende rettigheder defineret:
Article_Create = Administrator | Editor // 0011
 
// En bruger (anders) har rolerne Member og ThirdParty
// og vi vil nu undersøge om han har rettighed til at
// udføre Article_Create.
// Brugerens værdi er udregnet som:
 
anders = Member | ThirdParty // 1100
 
// Ved at benytte AND operatoren på brugerens værdi (1100)
// og værdien for den pågældende action (0011) og se om
// resultatet er forskellig fra 0 har vi praksis svaret
// på spørgsmålet:
 
((anders & Article_Create) != 0) // Boolean svar på om anders må udføre Article_Create

Jeg har Enum-indekset som streng i C#. Hvad nu?

Jeg sidder i en situation hvor jeg har en streng med indekset på den enum-værdi jeg skal bruge. Men hvordan får jeg fat i værdien? Efter at kigget lidt på nettet er jeg kommet frem til en løsning der virker:

/*
    Globals.Roles er enum'en der indeholder den værdi jeg skal bruge
    userRoleName indeholder strengen med indekset
*/
int roleValue = (int)Enum.Parse(typeof(Globals.Roles), userRoleName);

IF() funktion fra MySQL i T-SQL

I MySQL kan man bruge IF()-funktionen til at styre sit output baseret på værdier i ens celler:

SELECT IF(Active=1, 'Aktiv', 'Passiv') as UserStatus FROM Users

På den måde kan man få beskrivende output uden yderligere arbejde.
Da jeg pt. er i gang med mit første projekt i asp.net og i den forbindelse skal bruge SQL Server, skal jeg finde en tilsvarende funktionalitet i T-SQL. Det viser sig at kunne gøres på følgende lidt “ord-rige” måde:
SELECT CASE WHEN Active=1 THEN 'Aktiv' ELSE 'Passiv' END as UserStatus FROM Users

Husk at salte passwords

Husk at salte passwords, hvis de bliver envejs-krypteret inden de gemmes i en database. Ellers vil en hacker, der får adgang til databasen, have mulighed for at gætte på et kodeord der med god chance eksisterer i databasen. Ved at kryptere sit gæt med samme krypterings-algoritme som du selv anvender (kræver evt. også et gæt fra hackeren) kan hackeren lave en SQL-forespørgsel:

SELECT Username FROM Users WHERE Password=[Krypteret gæt]

Og på den måde få en oversigt over alle brugere med samme password som hans gæt. Selv om det ikke er helt så godt som adgang til en tabel med ukrypterede password er det nok til at muligheden for at bruge ovenstående teknik skal udryddes.

Heldigvis er løsningen ganske simpel. Ved at “salte” en brugers password, med en unik teksstreng, inden det krypteres og gemmes gør man det umuligt for en hacker at gætte på et kodeord og derefter se ALLE der bruger det kodeord.

Rent praktisk kan man salte med brugernavnet, så det bliver noget i retning af (i PHP):

// Retrieve post vars from Request object
$username = Request::POST('username');
$password = Request::POST('password');
// Salt the password with the submitted username
$encryptedPassword = sha1($username . $password);
// Insert in database
$query = 'INSERT INTO Users (Username, Password) VALUES (?,?)';
DB::Execute($query, array($username, $encryptedPassword));