Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional ASP.NET Security - Jeff Ferguson

.pdf
Скачиваний:
28
Добавлен:
24.05.2014
Размер:
13.26 Mб
Скачать

Custom Authentication

 

 

 

 

 

 

 

gHS^ J^MSNfitor.ey j|] MSNMoney Top10 j

 

 

 

 

 

 

 

 

 

Call Now to Test Your App 1.877.33. VOCAL

 

 

 

 

 

 

Europe {*«)207961 398S OtherInlemational {*1)408.90i'.r328

L°g

out

VbiceXML Checker

Home

Home > Tools 6 FiJe Management > VoiceXML Checker

 

 

 

 

 

 

 

Tool. & File

 

* getphonenurr.ber.vxml >

 

 

:ML Checker

Statue

 

 

 

 

 

 

 

 

 

 

 

ElilSf

 

 

 

 

 

 

 

 

Uia.&.r.SmSSr.

 

<!DOCTYPE vxm! PUBLIC V/BeVocal Inc//VoiceXML2.Q//EN" "http:|

 

TraCpTpcl

 

vxml version-"2.D">

 

 

 

 

^var name-"chentTelCo" expr--SeS5i0n.telephone.anlV>|

 

 

 

 

 

 

 

 

 

 

 

 

form id="frmData">

 

 

 

 

 

 

< block >

<scnpt>

 

 

 

 

 

 

 

<![CDATA[

 

 

 

 

 

f{uftction QetReSult(cal!Id)

Dftotoyment

 

 

 

 

 

Supoetf

 

 

 

 

 

•eturn 'http://www.tat

}

 

7890987

 

ua^BBBMBBHI

 

Fch^kl [ Save j fci^Tj [ Re««tl

 

 

 

>^

 

Cafe Home | Developer ftoreement | Privacy Policy | Site Mao | Terms & Coodition:

When you're comfortable that a Voice XML code is functional - by clicking the Check button - Cafe performs a compilation on the code and presents a results screen that highlights any errors.

The following screen capture presents an example of the information the Cafe compiler provides during Voice XML development.

f

— :-

J '

,

Search

Favoi te;

%^» Med«

^

A.rjdtcss

.jD http://cafe.bevocal.com/toote/vxmlchecker/vxmlchecl'e

;; • le- s

 

---^tphonenumber. .•l.ml,iJ:?pe=F;LE !•=-!$ .^Jblogs ^development

.j^ Amerrtrade

^ BogThei MSKB

^JMSNMoney

^j MSN Money Top 10

 

 

 

 

 

 

 

 

Line 5:

 

<vart nama—'dierrtTeico" »^pr="5ef---ion.tel8phorte.4niV>

 

 

[Error]: Attribute "expr" must be declared for element type "vart".

 

 

Line 25:

 

• .

 

 

 

 

 

 

[Error]:The content of element type "vxml" must match "(catcrijhelp|noinput|nomatch|er

 

 

 

2

< fDOCTYPE vxml PUBLIC

M" "http;//cafe,b

 

 

 

"-//Be

 

 

 

 

Cafe actually highlights the run-time errors and presents information related to Voice XML schema validation, syntax, and other possible areas for concern. Using this checker, you can work until you're confident that everything's working OK. Once you've written good, clean Voice XML (which we'll explore in a little more detail further on in this chapter) that functions properly, you can activate your desired startup script on the Tools and File Management page.

Now that we've had a quick introduction to the features the BeVocal Cafe application provider can provide in our development of Voice XML scripts, we'll take a quick introduction to the language itself.

Using BeVocal Cafe as a Service Provider

By being registered with a BeVocal Cafe account, our proposed TeleBank application can use BeVocal as the main point of access. To facilitate this process, we will create a simple Voice XML script to act as the activated script in the TeleBank BeVocal storage facility. This script uses the

session, telephone . ani variable outlined earlier as the parameter to aJavaScript function. When executed later in the Voice XML markup, this script redirects the client to the TeleBank application.

<?xml version="l.0"?>

<!DOCTYPE vxml PUBLIC "-//BeVocal Inc//VoiceXML 2.0//EN" "http://cafe.bevocal.com/libraries/dtd/vxml2-0-bevocal.dtd"> <vxml version="2.0">

262

Custom Authentication

<var name="clientTelco" expr="session.telephone.ani"/>

<form id="frmData"> <block>

<script>

<![CDATA[

function getResult(callld)

{

return 'http://www.tatochip.com/WROX_TeleBank/Launch.aspx?clientTelco=' + callld;

}

]]>

</script>

<data name="srvRet" src="http://www.tatochip.com/WROX_TeleBank/Launch.aspx" namelist="clientTelco"/>

<assign name="document.srvRet" expr="srvRet.documentElement"/> <goto expr="getResult(clientTelco)"/> </block> </form>

</vxml>

Note that, for the purposes of this example, we are hosting our telebank application on a server called tatochip, and referencing the Launch. aspx page. We will investigate these parts of our application a little later.

From this point BeVocal begins to interpret markup that the TeleBank application responds with. BeVocal serves, in a way, as a text-to-speech translating application service provider in the TeleBank example. We'll now look at the parts of our application hosted on our own server: the database in which we store our users' account information, and the .NET application that implements the business logic of our application.

The SQL Server User/Account Database

The first step in designing this application will be to establish a database design. This database, created in SQL Server 2000, will contain three interrelated tables. We'll also create stored procedures that perform various functions required to obtain the relevant data elements.

Table Structures

The first of our three tables will contain the account information elements for users that will access our system. All users that register with the TeleBank service will have their account information stored in this table. Note that the account table omits a field specific to the account's telephone number. Providing a single telephone number for each user account is a little unrealistic by modern standards: think of how frustrating the system would be if it were only accessible via your home telephone number. The telephone numbers associated with an account will be housed in a separate table that we'll discuss in a moment. For now, we'll summarize the structure of the wrox_tb_user table, which contains summary information about specific user accounts. This contains the following fields:

oc-a

Field Name

Purpose

User_id

Integer-based identification number associated with a given user

 

(primary key).

User_first_name

The user's first name.

User_last_name

The user's last name.

User_email

The e-mail address of a particular user. Hypothetically, this could be

 

used for a web-based administration tool users could exploit to set up or

 

modify their account.

User_password

The password users could use to login to a web-based administration tool,

 

should one be provided.

Now that we've covered the somewhat generic table structure we'll use for our account holders, we'll present the associated account table, wrox_tb_account. This table will be used to hold information pertaining to all the accounts a given user maintains in the TeleBank service. Of course, if this were a real online banking application, this table might be replaced by more complex processes derived by obtaining account information from other back-end data stores. Yet, for the purposes of this example, we have kept things simple. The wrox_tb_account table relates to the user table according to the provided user_id field.

We'll look in a little more detail at the table relationships in a moment, so for now let's familiarize ourselves with the account table's structure.

Field Name

Purpose

account_id

Integer-based identification field for any given account (primary key).

User_id

Forms a relationship between the user table and the account table, so

 

that queries can be written to pull all accounts associated with a given

 

user (foreign key).

account label

Provides a 'pretty' name for any account a user may need to add to

 

the system.

account_balance

Decimal-based field containing the balance within a given account.

Finally, we'll provide a table that could contain multiple telephone numbers to be associated with a particular user account. In this way, any user could check their account balances via our system from one of any phone numbers they provide. Below you'll find the field structure for the

wrox_tb_user_telco_listing table.

Field Name

User id

Purpose

Forms a relationship between the user table and the account table, so that queries can be written to pull all accounts associated with a given user (foreign key).

Table continued on following page

264

Custom Authentication

Field Name

Telco

i s _ d e f a u l t _ t e l c o

Purpose

A telephone number that's associated with a particular user. Users can have multiple telephone listings in this table (primary key).

This field, a bit field, can be used to identify a particular telephone number as the default telephone number associated with a given user.

By bringing all these tables together in an Entity Relationship Diagram (ERD) the relationships between them becomes a little clearer and simpler to understand. In addition, creating these relationships within the database facilitates better relational integrity between each of the tables.

The ERD below shows the entire database our sample application will use.

(.::.

w

ox_tb_account

 

 

-

 

 

 

,

Column Name

 

[ Datatype (length (Allow Nufc g

 

 

accountjd

 

int

4

 

 

 

userjd

 

int

4

 

 

 

V accountjabel

 

varchar

50

 

 

 

• account balance

money

8

 

 

 

 

 

 

 

 

 

 

 

>•

 

w

ox_tb_user

 

 

 

r

Column Name

j Data Type [length j Allow Ndls

 

userjd

 

int

4

 

 

 

user_flrst_name

 

varchar

50

 

 

 

v* userjast_name

varchar

50

 

 

 

• user_email

 

varchar

255

 

 

 

y1 user_password

char

10

 

 

 

 

 

 

 

 

 

 

 

 

 

 

w ox_tb_user_telco_listing

 

r

Column Name

[Data Type (length | Allow Nulls j

 

 

E

 

int

4 telco

 

 

userjd

 

 

 

bigint

8 is default telco

bit

 

 

1

 

 

 

 

":

As you can see, each of the tables is related to the user table via the user_id field. From this diagram, its pretty easy to see that any user can use multiple phone numbers to access their account balances for any and all accounts they hold at WROX TeleBank.

Now that we've developed a relational database model for storing all TeleBank users and account specifics, we'll create some simple stored procedures and data-access classes to get data from the tables and into our Voice XML template.

Accessing the Data via ADO.NET and Stored Procedures

Now that the database has been designed we'll embark on the creation of a few data access methodologies. Initially, to make the process of developing data-access components as easy as possible, we'll create some stored procedures. To provide programmatic and application access to these stored procedures, we'll create a database access component that can be reused by other elements of our application.

When the BeVocal interface (which we've yet to create) redirects telephone-based clients to our application, it will pass a query string variable containing the caller identification number of the telephone being used. We'll need to use this variable as the single parameter in a query whose purpose it is to pull up a user's account information. This stored procedure, named

wrox_tb_sp_f ind_user_by_telco, receives an input parameter containing the currently accessed telephone number, and obtains the user_id associated with it:

ALTER PROCEDURE dbo.wrox_tb_sp_find_user_by_telco

{

Stelco bigint

)

AS

SET NOCOUNT ON

SELECT [user_id] FROM wrox_tb_user_telco_listing

WHERE wrox_tb_user_telco_listing.telco = @telco

RETURN

The output of this stored procedure will be an integer value that reflects the user ID of the user associated with the current telephone number. When we begin creating our own custom identity objects to serve in our application, this will come in handy for providing a simple point of access for the user ID.

In addition to this stored procedure, we'll create one that obtains the total number of accounts the active telephone number is associated with. This may seem like overkill, but think of it this way: if we tried to create a user identity object for a user that doesn't exist, we'd run into problems. So, we perform a check to see if any accounts in our database are associated with the provided telephone number. Should our check result in zero, we know that there is no reason to go any further. This stored procedure, named wrox_tb_sp_total_accounts_by_telco, is constructed as follows:

ALTER PROCEDURE dbo.wrox_tb_sp_total_accounts_by_telco

(

@telco bigint

)

AS

SET NOCOUNT ON

SELECT COUNT)*)AS total_accounts_found FROM wrox_tb_user_telco_listing INNER JOIN wrox_tb_account ON

wrox_tb_user_telco_listing.user_id = wrox_tb_account.user_id WHERE (wrox_tb_user_telco_listing.telco = @telco)

RETURN

This step should be implemented first in our process: we'll see if the active telephone number is associated with any accounts. If it is not, we also know that it isn't associated with any users, either. Of course, we may find some accounts associated with a given telephone number. In this case it would be nice to have a function that obtains the account list. This stored procedure, shown below, returns a dataset-like collection of rows containing the account names, balances, and account identification numbers.

266

Custom Authentication

ALTER PROCEDURE dbo . wrox_tb_sp_account_data

(

Stelco bigint

)

AS

SET NOCOUNT ON

SELECT wrox_tb_account . account_id, wrox_tb_account . account_label ,

FROM

wrox_tb_account . account_balance

wrox_tb_user_telco_listing INNER

JOIN wrox_tb_account

ON

wrox_tb_user_telco_listing.user_id = wrox_tb_account . user_id WHERE

wrox_tb_user_telco_listing. telco = @telco RETURN

With the stored procedures written we now have a chance to work on the actual business logic. The main database access class, DBAccess, contains a rather complicated construction method. This method's responsibility is two-fold: first, it determines whether any accounts exist that are associated with the active caller identification data, and if so, collects that data into a local property. We'll create a class containing some public properties so that classes requiring data access will have simple interfaces to those data elements. The code below outlines the beginnings of this public class, with the properties inserted for reference purposes.

using System;

using System. Collections; using System. Data;

using System. Data. SqlClient;

namespace DataAccess {

///<summary>

///Summary description for DBAccess.

///</summary>

public class DBAccess

{

private SqlConnection conn; private SqlCommand cmd;

private string str_conn = "Password=;User ID=sa; Initial Catalog=telebank;Data Source=sqll4 .wrox.com" ;

private long user_ID = 0; private int intTotalAccounts = 0;

private bool blnUserExists = false; private ArrayList arrAccounts;

//property to indicate if the user exists public bool UserExists

{

get

{

return blnUserExists;

//property to expose the total number of accounts

public int AccountTotal { get

{

return intTotalAccounts;

} }

//property to expose the current user ID public long UserlD {

get

{

return user_ID;

//property to expose the arraylist of accounts held public ArrayList Accounts

{

get

{

return arrAccounts;

You probably noticed that the Accounts property returns an instance of an ArrayList object. Each item in the collection will be a custom object of type Account. The code below demonstrates the structure of an Account object. Notice especially the constructor of the object, which takes three input parameters that are used to set the local properties' values.

//class to represent an account public class Account

public string Label; public decimalBalance;publiclong AccountID;

public Account(long acct_id, string Ibl, decimal bal)

this.Label = Ibl; this.Balance = bal; this.AccountID = acct_id;

We've exposed the valuable assets of any given TeleBank subscription via public properties of this data access component. Now, during construction, we'll retrieve all the subscription information. To make things even simpler (and more reusable!), we'll require the existence of one parameter during construction - the active telephone number data that BeVocal's interface has already obtained. The beginnings of the constructor code, overleaf, show its initial check for accounts associated with the active telephone number:

268

Custom Authentication

//constructor with no parameter

//just calls constructor with phoneNumber 0 public DBAccessO : this(O)

{

}

//constructor

public DBAccess (long phoneNumber}

{

//create the connection

conn = new SqlConnection(str_conn) ;

if (conn. State == ConnectionState. Closed) conn.

Open ( ) ;

and = new SglCommand( "wrox_tb_sp_total_accounts_by_telco" , conn); cmd . CommandType = CommandType. StoredProcedure;

cmd. Parameters .Add ( "@telco" , phoneNumber) ; intTotalAccounts = (int) cmd.ExecuteScalar ();

Following this process, the constructor should obtain all of the accounts, should any exist. To perform this task, the constructor obtains a forward-reading cursor containing the results of our wrox_tb_sp_account_data procedure. For each row in the result set, a new Account object is created and added to the Accounts collection property of the component:

if (intTotalAccounts > 0) {

SqlDataReader rdr ;

//get the user id

cmd = new SqlCommand( "wrox_tb_sp_f ind_user_by_telco" , conn); cmd . CommandType = CommandType . StoredProcedure ;

cmd. Parameters. Add ( "@telco" .phoneNumber) ; user_ID = (long) cmd.ExecuteScalar ();

//get the accounts

arrAccounts = new ArrayList (intTotalAccounts) ;

cmd = new SqlCommandt "wrox_tb_sp_account_data" , conn); cmd . CommandType = CommandType. StoredProcedure ;

cmd. Parameters .Add ( "Stelco" , phoneNumber) ;

rdr = cmd.ExecuteReader ( ) ;

while (rdr .Read () ) {

arrAccounts . Add (new Account ( (long ) rdr [0] , (string) rdr [1] , (decimal) rdr [2] ) ) ;

In this way, the data access object obtains everything we'll need as soon as it's created, resulting in fewer component calls or database connections. From here, we'll create a custom Identity object similar to (though far more useful than!) the one outlined earlier in this chapter.

Custom Identity and Principal Objects

The identity objects outlined in this example contain virtually every aspect of an account. Of course, you may choose not to extend your own identity classes in this manner. Due to the absence of a request-response or multi-URL model of Voice XML applications (we'll look at this in a moment), our client environment requires a holistic model of the user account.

269

Once again, we'll create a custom identity object that implements the System. Principal . Ildentity interface. To facilitate this implementation we must implement three properties: AuthenticationType, IsAuthenticated, and Name. In addition to these required properties, we'll add some code to the class's constructor, providing access to the data access component we just completed:

using System;

using System. Security. Principal ; using System. Collections;

using DataAccess;

namespace securityCodeConversion

{

///<summary>

///Summary description for TeleBankldentity .

///</summary>

public class TeleBankldentity : Ildentity

{

protected internal string strUsername; protected internal int strPrimaryTelco = 0; protected internal ArrayList arrAccounts; protected internal long IngActivePhone = 0 ; private DBAccess objDBAccess;

public TeleBankldentity (long telephoneNum)

{

objDBAccess = new DBAccess (telephoneNum) ; IngActivePhone = telephoneNum; arrAccounts = objDBAccess .Accounts; }

public string AuthenticationType { get

{

return ( "WROXBAUTH" ) ;

public bool IsAuthenticated { get

{

return true ;

} }

public string Name { get

{

return (objDBAccess .UserlD.ToString ( ) ) ;

To complete the custom identity object's interface, we'll provide access to the accounts associated with the active telephone number. This method, AccountList, will simply expose the global ArrayList that was set during construction.

270

Соседние файлы в предмете Программирование