17 December 2013 AD, Azure, REST, WAAD Robert Muehsig

Die Authentifizierung am Windows Azure Active Directory haben wir sowohl bereits mit dem Tooling als auch via Code gesehen. Heute geht es darum, wie man Daten lesen und sogar auch schreiben kann.

Windows Azure Active Directory?

Wer keine Ahnung von der Begrifflichkeit hat, dem empfehle ich ebenfalls die beiden vorangegangen Blogposts oder schaut einfach auf dieser Azure Info Seite.

Welche Ressourcen befinden sich da?

Im Azure AD befinden sich folgende Entitäten:

- Users
- Groups
- Contacts
- Roles

Zugriff auf das Directory oder auf den “Directory Graph”

Trotz des “Active Directory” im Namen und den bekannten Entitäten hat dieser Azure Dienst wenig Gemeinsamkeiten zu einem herkömmlichen Active Directory. Der vermutlich grösste Unterschied: Es gibt kein LDAP Zugriff – es ist eine REST API. Die Daten liegen im so genannten Windows Azure Active Directory Graph. Anwendungen die jetzt gegen ein Active Directory funktionieren müssen umgeschrieben werden für Windows Azure AD.

REST … oder auch OData

Als Endpunkt gibt es eine REST bzw. eine OData API. Ob OData der eleganteste Weg ist steht auf einem anderen Papier – allerdings arbeitet das Team daran weitere OData Features mit zu unterstützen.

Grundsätzlich gibt es zwei Arten von Queries:

“Common Queries": Eine recht einfache API mit der man alle Daten aus dem AD rausholen kann.

“Differential Queries”: Diese API wird interessant wenn man grosse Datenmengen zwischen der eigenen Anwendung und dem Azure AD synchronisieren möchte. Über diese API kann man nur die Änderungen an einer Ressource zwischen zwei Requests herausbekommen.

Aktuell: Kein nettes NuGet Package

Jetzt weg von der Theorie, hin zur Praxis. Aktuell gibt es leider kein NuGet Package oder ähnliches was die Arbeit mit der Graph API komplett vereinfacht. Zwar gibt es ein altes NuGet Package, allerdings stammt dies auch nur aus Sample Code und zudem ist die API entsprechend schon recht alt und untersützt nicht alle Funktion die es heute gibt (Gruppen-Management z.B.)

image

Alternative: Hand Made Requests

Da es eine REST API ist, benötigt man natürlich nur einen HttpClient und man kann dagegen entwickeln. Die MSDN gibt auch genügend Beispiele wie der Request aussehen kann:

GET https://graph.windows.net/contoso.onmicrosoft.com/users/[email protected]?api-version=2013-04-05 HTTP/1.1
Authorization: Bearer eyJ0eX ... FWSXfwtQ
Content-Type: application/json
Host: graph.windows.net

Da sich die API allerdings recht schnell weiter entwickelt und ich mir die “OData”-like Queries nicht von Hand zusammenschrauben möchte, gibt es noch einen Weg. Dieser scheint wohl mehr oder minder auch der “empfohlene” Weg zu sein.

Zum Code: Wir nutzen Sample Code – uhh oh…

Das Azure Graph Team publiziert auf dieser MSDN Seite verschiedene Beispiele, darunter auch eine “Graph API Helper Library”. Im Einsatz kommt diese Library auch im .NET Sample.

imageDas Sample ist eine MVC Anwendung, welche ein CRUD auf User und Groups abbildet. Der GraphHelper enthält die generierte “DataService” aus dem OData-Endpunkt und einige Utilities drum herum – so kann man recht einfach sich gegen die Graph API authentifizieren und Requests abschicken.

Das Sample kommt mit voreingestellten Settings – allerdings ist die App nur “lesend” auf das AD berechtigt.

 

 

 

 

 

 

 

 

 

 

Mal ein paar Screenshots von der Applikation:

image

image

image

Generierter Code… uh…

Der “generierte” Code stammt aus dem OData Endpunkt und ist alles andere als “schön”. Dazu gibt es noch eine “Partial” Klasse, da die generierte Klasse die eigentlichen Entitäten nicht erkennt.

Der eigentliche Code ist nicht sehr komplex, aber vom Syntax nicht ganz so sexy.

So holt man z.B. alle Gruppen ab:

   // 
        // GET: /Group/
        // Get: /Group?$skiptoken=xxx
        // Get: /Group?$filter=DisplayName eq 'xxxx'
        public ActionResult Index(string displayName, string skipToken)
        {
            QueryOperationResponse response;
            var groups = DirectoryService.groups;
            // If a filter query for displayName  is submitted, we throw away previous results we were paging.
            if (displayName != null)
            {                
                ViewBag.CurrentFilter = displayName;
                // Linq query for filter for DisplayName property.
                groups = (DataServiceQuery)(groups.Where(group => group.displayName.Equals(displayName)));
                response = groups.Execute() as QueryOperationResponse;
            }
            else
            {
                // Handle the case for first request vs paged request.
                if (skipToken == null)
                {
                    response = groups.Execute() as QueryOperationResponse;
                }
                else
                {
                    response = DirectoryService.Execute(new Uri(skipToken)) as QueryOperationResponse;
                }
            }
            List groupList = response.ToList();
            // Handle the SkipToken if present in the response.
            if (response.GetContinuation() != null)
            {
                ViewBag.ContinuationToken = response.GetContinuation().NextLinkUri;
            }
            return View(groupList);
        }
</pre>

Empfehlung: Sample anschauen und nicht direkt auf die generierten Klassen verweisen

Das Sample enthält die “Common Queries” plus CRUD-Operations und bietet einen einfachen Einstieg. Ich würde aber davon abraten direkt die Entitäten daraus zu nutzen, da die generierten Klassen auch “Unschönheiten” wie z.B. kleine Property-Namen mitbringen.

Weitere Informationen findet man in der MSDN auf der Graph API Seite.


Written by Robert Muehsig

Software Developer - from Dresden, Germany, now living & working in Switzerland. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de

If you like the content and want to support me you could buy me a beer or a coffee via Litecoin or Bitcoin - thanks for reading!