Application Security, Part 22

The Authentication Web Service is configured for Windows Authentication, so the credentials are automatically encrypted and validated against the Active Directory for the domain. As we know already, though, not all of the users identified in Active Directory are meant to be users of our application: only those in the YourApplicationUsers group, who, thanks to our work with MIIS, are now in a dedicated directory partition in ADAM. So, let us turn our attention to what happens in the Authentication Web Service.

 

using System;

using System.Collections;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.Web;

using System.Web.Services;

using Authorization = AZROLESLib;

using Microsoft.DeveloperAndPlatformEvangelism.Demonstrations.Service;

namespace Microsoft.DeveloperAndPlatformEvangelism.Demonstrations.Service

{

public class TaskVisionII_Authentication : System.Web.Services.WebService

{

public TaskVisionII_Authentication()

{

//CODEGEN: This call is required by the ASP.NET Web Services Designer

InitializeComponent();

}

#region Component Designer generated code

//Required by the Web Services Designer

private IContainer components = null;

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

}

/// <summary>

/// Clean up any resources being used.

/// </summary>

protected override void Dispose( bool disposing )

{

if(disposing && components != null)

{

components.Dispose();

}

base.Dispose(disposing);

}

#endregion

private const string c_sConfigurationSection_Authorization = @"Authorization";

private const string c_sKey_Authorization_Store = @"Store";

private const string c_sKey_Authorization_Application = @"Application";

private const byte c_byFlag_Authorized = 0;

[WebMethod]

public CUser rAuthenticate()

{

try

{

CUser rUser = new CUser();

rUser.sName = this.User.Identity.Name.Split(@"\".ToCharArray(),2)[1];

if((rUser.sName == null)||(rUser.sName == string.Empty))

{

throw new Exception();

}

//Authorization (as opposed to authentication code omitted . . . we'll see it later.

ActiveDs.IADsUser rUser_ADAM = CADAM.rGetUser(rUser.sName);

if(rUser_ADAM != null)

{

rUser.sLanguage_Preferred = rUser_ADAM.Get(@"preferredLanguage").ToString();

}

else

{

throw new Exception();

}

return rUser;

}

catch(Exception rException)

{

return null;

}

}

}

}

 

 

That Web Service passes the user identifier portion of the user’s credentials to the GetUser method of a class that knows how to talk to ADAM.

 

using System;

using System.Collections;

using System.Configuration;

using System.DirectoryServices;

namespace Microsoft.DeveloperAndPlatformEvangelism.Demonstrations.Service

{

public class CADAM

{

private const string c_sConfigurationSection_Credentials = @"ADAMCredentials";

private const string c_sConfigurationSection_ADAMInstance = @"ADAMInstance";

private const string c_sKey_Credentials_UserName = @"UserName";

private const string c_sKey_Credentials_Password = @"Password";

private const string c_sKey_ADAMInstance_ADsPath = @"ADsPath";

private const string c_sKey_ADAMInstance_Server = @"Server";

private const string c_sKey_ADAMInstance_Port = @"Port";

private const string c_sKey_ADAMInstance_Country = @"Country";

private const string c_sKey_ADAMInstance_ApplicationDirectoryPartition = @"ApplicationDirectoryPartition";

private const string c_sKey_ADAMInstance_UserContainer = @"UserContainer";

private const string c_sProvider = "LDAP";

private CADAM()

{

}

public static ActiveDs.IADsUser rGetUser(string sIdentifier)

{

try

{

ActiveDs.IADsUser rUser = null;

System.Collections.Hashtable rConfigurationSection = null;

rConfigurationSection = (System.Collections.Hashtable)System.Configuration.ConfigurationSettings.GetConfig(CADAM.c_sConfigurationSection_Credentials);

string sUserName = (string)rConfigurationSection[c_sKey_Credentials_UserName];

string sPassword = (string)rConfigurationSection[c_sKey_Credentials_Password];

rConfigurationSection = (System.Collections.Hashtable)System.Configuration.ConfigurationSettings.GetConfig(CADAM.c_sConfigurationSection_ADAMInstance);

string sServer = (string)rConfigurationSection[CADAM.c_sKey_ADAMInstance_Server];

string sPort = (string)rConfigurationSection[CADAM.c_sKey_ADAMInstance_Port];

string sCountry = (string)rConfigurationSection[CADAM.c_sKey_ADAMInstance_Country];

string sApplicationDirectoryPartition = (string)rConfigurationSection[CADAM.c_sKey_ADAMInstance_ApplicationDirectoryPartition];

string sUserContainer = (string)rConfigurationSection[CADAM.c_sKey_ADAMInstance_UserContainer];

try

{

rUser = (ActiveDs.IADsUser)new DirectoryEntry(string.Format("{0}://{1}:{2}/CN={6},CN={5},O={4},C={3}",CADAM.c_sProvider,sServer,sPort,sCountry,sApplicationDirectoryPartition,sUserContainer,sIdentifier),sUserName,sPassword).NativeObject;

}

catch(Exception rException)

{

System.Diagnostics.Debug.WriteLine(rException);

}

return rUser;

}

catch(Exception)

{

return null;

}

}

}

}

The configuration file used by this class looks like so:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<configSections>

<section name="Authorization"

type="System.Configuration.DictionarySectionHandler,System, Version=1.0.5000.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089"

/>

<section name="ADAMCredentials"

type="System.Configuration.DictionarySectionHandler,System, Version=1.0.5000.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089"

/>

<section name="ADAMInstance"

type="System.Configuration.DictionarySectionHandler,System, Version=1.0.5000.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089"

/>

<section name="Defaults"

type="System.Configuration.DictionarySectionHandler,System, Version=1.0.5000.0, Culture=neutral,

PublicKeyToken=b77a5c561934e089"

/>

</configSections>

<Authorization>

<add key="Store" value="msxml://D:\apps\TaskVisionIIb\AuthorizationStore\TaskVisionII.xml" />

<add key="Application" value="TaskVisionII" />

</Authorization>

<ADAMCredentials>

<add key="UserName" value="hoiuhiouhuyiu" />

<add key="Password" value="ghfhgrtrytfyvyt" />

</ADAMCredentials>

<Defaults>

<add key="PreferredLanguage" value="en-CA" />

</Defaults>

<ADAMInstance>

<add key="Server" value="DELLW2K" />

<add key="Port" value="50000" />

<add key="Country" value="US" />

<add key="ApplicationDirectoryPartition" value="TaskVisionII" />

<add key="ApplicationDirectoryPartitionDescription" value="TaskVisionII Directory Partition" />

<add key="UserContainer" value="Users"/>

</ADAMInstance>

<system.web>

<!-- DYNAMIC DEBUG COMPILATION

Set compilation debug="true" to enable ASPX debugging. Otherwise, setting this value to

false will improve runtime performance of this application.

Set compilation debug="true" to insert debugging symbols (.pdb information)

into the compiled page. Because this creates a larger file that executes

more slowly, you should set this value to true o_u108 ?y when debugging aÄd to

false at all other times. For more information, refer to the documentation about

debugging ASP.NET files.

-->

<compilation

defaultLanguage="c#"

debug="true"

/>

<!-- CUSTOM ERROR MESSAGES

Set customErrors mode="On" or "RemoteOnly" to enable custom error messages, "Off" to disable.

Add <error> tags for each of the errors you want to handle.

"On" Always display custom (friendly) messages.

"Off" Always display detailed ASP.NET error information.

"RemoteOnly" Display custom (friendly) messages only to users not running

on the local Web server. This setting is recommended for security purposes, so

that you do not display application detail information to remote clients.

-->

<customErrors

mode="RemoteOnly"

/>

<!-- AUTHENTICATION

This section sets the authentication policies of the application. Possible modes are "Windows",

"Forms", "Passport" and "None"

"None" No authentication is performed.

"Windows" IIS performs authentication (Basic, Digest, or Integrated Windows) according to

its settings for the application. Anonymous access must be disabled in IIS.

"Forms" You provide a custom form (Web page) for users to enter their credentials, and then

you authenticate them in your application. A user credential token is stored in a cookie.

"Passport" Authentication is performed via a centralized authentication service provided

by Microsoft that offers a single logon and core profile services for member sites.

-->

<authentication mode="Windows" />

<!-- AUTHORIZATION

This section sets the authorization policies of the application. You can allow or deny access

to application resources by user or role. Wildcards: "*" mean everyone, "?" means anonymous

(unauthenticated) users.

-->

<authorization>

<allow users="*" /> <!-- Allow all users -->

<!-- <allow users="[comma separated list of users]"

roles="[comma separated list of roles]"/>

<deny users="[comma separated list of users]"

roles="[comma separated list of roles]"/>

-->

</authorization>

<identity impersonate="true"/>

<!-- APPLICATION-LEVEL TRACE LOGGING

Application-level tracing enables trace log output for every page within an application.

Set trace enabled="true" to enable application trace logging. If pageOutput="true", the

trace information will be displayed at the bottom of each page. Otherwise, you can view the

application trace log by browsing the "trace.axd" page from your web application

root.

-->

<trace

enabled="false"

requestLimit="10"

pageOutput="false"

traceMode="SortByTime"

localOnly="true"

/>

<!-- SESSION STATE SETTINGS

By default ASP.NET uses cookies to identify which requests belong to a particular session.

If cookies are not available, a session can be tracked by adding a session identifier to the URL.

To disable cookies, set sessionState cookieless="true".

-->

<sessionState

mode="InProc"

stateConnectionString="tcpip=127.0.0.1:42424"

sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes"

cookieless="false"

timeout="20"

/>

<!-- GLOBALIZATION

This section sets the globalization settings of the application.

-->

<globalization

requestEncoding="utf-8"

responseEncoding="utf-8"

/>

</system.web>

</configuration>

That method uses the System.DirectoryServices.DirectoryEntry class of the .NET Framework to bind to ADAM and retrieve the user object for the user that is logging in, and passes back a reference to the Active Directory Services Interface for that user object. If we refer back to the code for the Web Service we see that see that if the GetUser method failed to find a user object for the user logging in to TaskVision, the service throws an exception, thereby preventing anyone who is not one of the users of our application that we had imported into ADAM from accessing the application. If, on the other hand, an object corresponding to the user is retrieved from ADAM, then the preferred language for that user is added to the data concerning that user that is passed back to the Smart Client. Of course, in this context, the user’s preferred language is meant to represent the considerable quantity of data about users that real world applications must typically maintain.