The Hoju Saram

Wednesday, July 02, 2008

Amazon Web Service play


I had a bit of spare time last week so I did a bit of playing with amazons web services to download album art and track details of albums.

Thought I would share the helper I wrote for the amazon service to do this, in case anyone else is trying something like this with other services.

Labels: ,

Thursday, June 12, 2008

My WebDisk Client



After a long long break from coding the webdav stuff, I have got around to doing something again. After building the SQL backend WEBDAV server I realised the limitations of windows as a webdav client and webdav as a protocol. So I have decided to start building a new server and client based on a new protocol. You can see from the screen shots that I have got the PROPFIND equivalant already working along with some nice code to automatically detect and build the associated icon for the file type.
From this client/server I am going to implement all of the standard file operations(like WEBDAV), versioning, check-in/check-out and Access Control. I will also be implementing a launcher so you can launch the the client directly from a browser.
More pics to come as I work through the development, but will probably be slow as I am finding it hard lately to find any spare time.

Labels: ,

Friday, August 24, 2007

.Net SQL Backend storage with Activex Uploader

Firstly : This solution uses an ActiveX control , so it is targetted at Intranet Applications for SOEs where IE is the standard browser.

In my previous post I talked about implementing the domino inotes activex uploader to talk to an asp .net application that could store the uploaded files in a sql database, well here is the C# vs2005 solution.

I am sure there are other activex file uploaders out there that provide similar (probably better) functionality but I knew how this one worked from previous work I had done with it in Domino using a java servlet to receive the files.

Basically in this solution I re-coded the servlet as a Httpmodule that read the request inputstream and processed the data coming from the activex control. The activex control thinks it is talking to a Domino Server, so the code is based around getting the functionality out of the control while tricking it into thinking it is being used with a Domino Backend. In fact the backend is the .net application which stores the uploaded files into the sql database.

The setup is as usually for my solutions. Just create a new sql database on your sql server/msde and run the DBSetup.sql code against it. Then edit the web.config so the connection string matches your database. Once that is done compile and run. The cab file that contains the activex control is in the project.

The inotes ActiveX control provides the following features:

  • Drag and drop functionality from windows explorer/desktop
  • File upload status bar
  • Multiple file upload and deletions
  • Each file upload uses an individual HTTP POST so upload limit is per file
  • Windows explorer look and feel.
  • Client side caching

Happy coding.

Labels: ,

Monday, August 20, 2007

Activex fileupload revisit in .net


I have had a middle school korean exchange student staying at my home for the last couple of months. He uses the korean community site naver 네이버 . While he was composing an e-mail the other day I noticed that there was a activex fileupload on the "compose" page which looked quite similar to the IBM/Lotus file upload control that is available for inotes. It also has the same feature set ( drag and drop, local file caching, file upload status, etc ).

On inspection on my computer after he had logged-off I discovered that the activex control had the details as above. The feature set is almost exactly the same as the IBM control and lead me to think there was some code sharing or something of that nature between IBM and this company ?? The control itself is obviously unnoticed by most as a search on google returns nothing of interest

Anyway recently I have been trying to find a nice browser based file manager for .net and after trolling through thousands of pages about how to do this sort of thing in .net have come to the conclusion that there really is no replacement of activex in the .net world so I shouldn't bother trying to replace this functionality I should just embrace it.
So since I already have a servlet version of the server component of the IBM upload control I thought I would try and whip up one in .net and of course give it backend DB storage. I will post my working source in the next couple of days. I will base the project on the work I have already done here and here.

Labels: , ,

Tuesday, June 26, 2007

C# Webdav server with SQL Backend Source Code

kick it on DotNetKicks.com

The C# visual studio 2005 source code for this project is here

I finally got around to getting all of the source code for the webdav server I developed in C# with a SQL backend, I have talked a bit about this previously , but only now have had the time to but it all together so others can download and use/update the code.

The main reason I wrote this code was to provide a "network share" interface to some of my .net applications. I thought it would be very useful to have a framework in place where I could trigger some form of event when someone saved a word file or updated a speadsheet. Because all of the files are stored in a database under the control of the .net application creating this sort of thing is relatively trivial. I have used this to automatically process excel data into a database when someone saves a file to a particular folder for example. (You could also do this via e-mail using this, but that is a different idea). You could also use this to provide per-user content for a single file.

This server is purely for use with Windows Explorer and Microsoft office, so I have not implemented alot of the webdav stuff that isn't implemented by these products. ( this includes webdav-versioning , proppatch etc ), and I can't garauntee it will work with any other webdav clients. Basically this server implementation provides the minimal functionality required to allow MS office and Windows explorer to work.

I have implemented this project to provide 2 interfaces to the virtual filesystem ( provided by an sql database) . A standard web-front end and also the webdav interface. This means that if you connect to the server via a standard web browser you can view the virtual filesystem via a standard web page.

But if connect to the server via a webdav client connection then you get the webdav experience. Which allows you to create , edit, delete , copy , move , rename lock and unlock files and create, rename and delete folders.


There are a number of ways to get a connection to a webdav server as a network share. These include adding a "network place" from "my network places". The easiest way to get connected via webdav is to open the root url url for the application in IE and tick the "Open as web folder" option.



To setup the server you need to do the following steps and also I recommend visiting http://www.zorched.net/category/webdav/ for lots of helps on configuring webdav for .net

  1. Download the code and open in vs 2005 ( link at top of this post)
  2. Create a new blank database on your sql server
  3. run the create.sql code from the database project against the new database to create the required tables, stored procs and data to support the virtual filesystem
  4. Edit the web.config of the web application project (hojuwebdisk.web) and adjust the database connection string of the "webdav" connection to suit your environment.

As this is a webdav server you will also need to adjust the IIS setting for the website so that it knows how to handles the additional HTTP methods that webdav supports (LOCK, UNLOCK,PROPFIND,MKCOL etc ). Basically because we want .net to intercept all the calls (because we are redirecting all queries to our database not the filesystem) we need to adjust the application extension mapping so all extensions on all VERBs map to the asp IIS handler.

To do this you need to open the application properties of your website from the IIS console and then click the configure button. You then need to remove all of the application extensions and then create a new one for all extensions (.*) for all verbs that executes the .net isapi handler.


A good article explaining the ins-and-outs of this is available here

Once you have completed this setup you should be able to see the webdav server through a webdav connection, and therefore be able to add files and away you go..

I have to give credit to another project for helping me out with this one. The database and database interface stuff was all me, but a lot of the webdav implementation code I got from the webdav server project on sourceforge . If you are looking at developing your own webdav server or extending this code I totally recommend you check out that project. There project uses alot of events and delegates, where as I went with a single interface handler to simplify the code.

If you need any help, or want to add feedback then please do so. Microsoft office and windows have a number of wonderful webdav quirks, which you may need to get around depending on your environment and setup. A good place to start for a list of these is here

So good luck..

Labels: , ,

Friday, June 01, 2007

More on Domino -> SharePoint migration tool

I am currently writing a C# .net windows form application to migrate IBM domino (notes) data to MS Sharepoint. This is for document libraries only, as sharepoint doesn’t do forms like notes does.(required InfoPath and a whole heap of fiddling)

So basically this is a tool that allows me to do the following:

  • Select a domino document library
  • Select a sharepoint document list
  • Choose fields in the documents in the Notes database and pair them to meta-data fields in the Sharepoint List
  • Pull the rich-text body( including embedded images and attachments ) out of notes and convert it to a word document.
  • Translate the Notes Fields -> Sharepoint fields
  • Save the word document and its meta data to the Sharepoint list.

By automating this process it means that I can simply point the tool at each domino document library database , then map its fields to a sharepoint list and press a button to migrate all of the data. This will give me one word document in the sharepoint list for each notes document in the document library.

The underlying functionality on the domino side is the agent I have talked about early I use this to produce the xml/binary and html that is used by the .net app to gather and translate the domino data. On the sharepoint side I am using a mix of SOAP and FrontPage RPC calls to the sharepoint server to get the List schemas and then place the documents and meta data into the Lists.

I should have some nice screenshots sometime next week of it all in action

Labels:

Friday, April 27, 2007

Adventures in WebDAV

I have been developing a C# .net WebDAV Server with a backend SQL database for a bit. I have all of the file system functionality ( copy , move, rename, delete make folders etc ) working as well as LOCK support. This all works great. I am now tyring to get some of the MS office features working and have hit a bit of a stumbling block.

If I load a word document from my WebDAV server and then select file->Properties then Word fires a webdav PROPFIND request back to my server and displays an “OK” dialog that says “Verifying Web Properties”.

The PROPFIND request that is sent to my server looks like this

[a:propfind xmlns:a=”DAV:” xmlns:b=”urn:schemas-microsoft-com:datatypes”]
[a:prop xmlns:c=”urn:schemas-microsoft-com:office:office”]
[c:save-profile-form-location /]
[/a:prop]
[/a:propfind]

The problem is that I have no idea what save-profile-form-location is supposed to be and a search on google returns a huge 6 documents. So I guess noone else know what this means either ??

I have tried various replies from my server in the format

[D:multistatus xmlns:b=”urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882″ xmlns:c=”urn:schemas-microsoft-com:office:office” xmlns:D=”DAV:”]
[D:response]
[D:href]test.doc[/D:href]
[D:propstat]
[D:status]HTTP/1.1 200 OK[/D:status]
[D:prop]
[c:save-profile-form-location]/WDClient/profile.doc[/c:save-profile-form-location]
[/D:prop]
[/D:propstat]
[/D:response]
[/D:multistatus]

Using all sorts of values and data , but this is all ignored by word. So I am a bit stuck. I may try to arrange a support call to Microsoft about this. It would be nice if I could get word to read some properies from my webdav server. So I will do some more searching on this one.

Labels:

Wednesday, April 18, 2007

In Dev: C# WebDAV Server with SQL Backend



The work I am doing on building a WebDAV Server with SQL Backend is progressing well. I found a .net web dav server framework at sourceforge , which provides much of the plumbing for the WebDAV server. Their implementation is for a server that uses the file system for storage, I want to use an sql database so that DAV properties can be used inside other .net applications.

One of the problems with the filesystem based implementation, is that the properties of each file (including the custom properties) are binary serialised to a file. This means that they are not easily searched and/or retrieved by external system, so I definitely want to move this to a SQL database.

I am now at the stage where I have created the SQL database and have written the data and business layers, to replace the filesystem layer with an SQL Layer. This means that from windows explorer I can now map a web drive to my C# app and it will display data in the SQL database as if it is a WebDAV fileshare. I can copy, move , rename, delete the files and folders, and also open and save to/from the app to windows applications. This all works great. I still have to do a bit of work to get locking, custom properties and some of the MS Office specific stuff happening but it is looking promising.

The screen shot above shows an explorer window of the WebDAV share. All of the files and folders that are visible are in fact records in an sql table.

I have also written a small app called “Web Folder Mapper” to allow me to create WebDAV shortcuts. The source for this is downloaded here. This is very helpful because it allows you to create a shortcut and then you can simply drag files to this shortcut and then will be stored on the WebDAV server. The screen shot of the mapper tool is below, with the corresponding shortcut that was build on my desktop.



I will release the server source code as soon as I get the rest of the implementation working. I will also be sending it to the admin of the sourceforge framework project, as they have asked for code from anyone who uses the framework to create an implementation.

Labels: , ,

Wednesday, February 28, 2007

I have released http://www.yourformbuilder.com

After a few weeks of spare-time dev I have now released http://yourformbuilder.com/ into the wild. It is not 100% tested yet, but I have had enough of working on it so out it goes.

It provides a single html file download that lets people design their own validating web forms via an easy to use drag & drop interface. They can then save a "run-time"(published) version of this form which they can send to people to fill in. No servers required.

I am pretty happy with how it has gone... Especially the UI and the ease of use. The fact that it is all in a single html file and runs IE and firefox means that it is a pretty nice multi-os solution as well. I also like the feature that design->runtime->print time is all in one file.

I will post about some of the technologies and techniques I used in the future. But one of the highlights is the serialising and deserialising objects that let me make the "published" file 40k smaller than the design-time file that creates it.

Labels: , , ,

Tuesday, February 13, 2007

Lastest Project

I have just started a new project where I am developing a system that will combine some of the ideas from sites like www.wufoo.com and www.jotform.com with my ideas from www.yourtimesheets.com to create a fully self-contained html form builder that uses a single html file to design, publish and fill-out user designed html forms.

As I will be using the self-cloning techniques I used at www.yourtimesheets.com it will require no server. So imagine that I can send you a single html file containing everything you need. You can then create your own form just by adding form elements from a menu and then setting some properties. You then publish the form which will create a copy of your form that you can send to people to fill in. All of this from a single html file and no need for a server. This will fill the gap between pdf ( cannot fill in ) and word forms (end-user updateable design and no validation), should be easy to use and will only require a browser.

I will post more information when I get around to putting something together.

Labels: , ,

Thursday, February 01, 2007

.Net Domino and Exchange MailBox Reader over HTTP.



I work for an organisation that provides IT services to many different and separate organisations. Some of these organisations use MS Exchange and some use IBM Domino for mail infrastructure. I have recently had to write an “inbox aware” .net application that can read and process inbox data, but I needed to do this independently of the underlying mail infrastructure, as it needed to run for all organisations.
I thought about simply using POP3 for this, but this has some limitations.


  • It isn’t running by default on any of the Domino or Exchange Servers.

  • It doesn’t support rich text.

  • It doesn’t support custom fields.

  • It usually isn’t allowed through firewalls.

Custom field support was quite important because many of Domino mailboxes have custom fields that we may need to process via our .net application.

I have created a small sample application demonstrating how I went about this. I implemented a small IMailConnector interface in .Net and then created two MailConnector Classes that matched this interface. The ExchangeMailConnector class uses WEBDAV (over http obviously) to read and process mail in an exchange mailbox. The DominoMailConnector connects to an agent in the mailbox over http and processes xml/html and binary data supplied by that agent. The agent is my own creation ( yes, I program in Lotusscript as well ) and needs to be placed in the design of the Domino mailbox database for the DominoMailConnector to work.

If you are going to test this sample, please do it on test mailboxes as the default behaviour of the connector classes is: read e-mails, place the data in SQL and then DELETE the original message. So don’t blame me if you runs this app and then your mailbox is empty, you have been warned. The reason for this behaviour is because the easiest way to make sure you only process a message once is to delete it after you have processed it. If you require a different behaviour then you will have to write your own code to determine if you have or haven’t processed an e-mail already.

The zipped vs C# 2005 sample application is here. It provides the following features:


  • Connections to both an Exchange or Lotus Notes/Domino Mail box

  • Retrieves "from" , "subject","html message body" and attachments and embedded objects. (Fully Extendable)

  • Stores the retrieved mailbox data in SQL for easy access from .Net

  • Does not use POP, so is extendable to support custom fields in Both Domino & Exchange.

  • Uses HTTP for connections so can be used through web-only firewalls.

To setup the sample app you need to do the following:

Create a blank SQL database on your SQL server and then run the DBSetup.SQL file in the DBSetup folder of the solution against it. This will set up the tables and the stored procs to handle the storage of the e-mail data given by the connectors.

Once you have done this then you will need to adjust the web.config of the solution so the connectionstring of the “mailboxconnection” matches your SQL setup.


You next need to adjust the web.config to match your mailbox setup. If you are using a Exchange Server then you will need to set the MailBoxType Key to “Exchange” and set the other keys to match your mailbox server,username and password.

If you are using a Domino Server then you will need to set the MailBoxType to “Domino” and set the other keys to match the domino server,mail database location,domino userid and password.

If you are using Domino you will also need to copy the (dotNetDataViewer.xml) agent ( Found in the HojuRetriever.nsf database in the Domino Folder of the solution ) into the mail database you will be accessing. The agent needs to run with at least Designer access.

Once you have done this then you should be able to run the solution, press the “Get Mail” button to retrieve messages from the mailbox.

If you need a better understanding of some of the “plumbing” of this application then please leave a comment and I will endeavour to answer your questions.

Good Luck !!

Pic of Message View

Labels: ,

Tuesday, January 23, 2007

Update of Drag and Drop, Paging,Sorting,Filtering Gridview.



kick it on DotNetKicks.com

The Drag and Drop, Paging,Sorting ,Filtering Gridview is one of the most popular pages on my blog (1000+ page impressions since it was put up ).

So given the interest I have decided to update it. I have now added the ability to filter date columns as well as text/number columns. A couple of additonal screen shots are on this post (others can be seen on the original post) and the source of the new vs2005 solution is available here.



You will again need to follow the same instructions to set it up as the previous version. These instructions are:

You need to create a new empty database on your SQL server and run the SQL code file that is in the DatabaseSetup folder of the solution this will:


  • Create a customer table

  • Create a stored procedure to query the customer table as required

  • Fill in the customer table with some test data.

You will then need to grant access to those SQL elements so that you can access them from your .net solution.

The solution file contains a single aspx page that holds a standard gridview and code that does all of the logic. The data is accessed from the database via DataClass. I have created an Interface called IpagingFilterDataSource that matches what is required if you want to write your own class to do the datahandling. This will obviously be required if you are going to extend this in anyway.

int GetDataSize();
int GetDataSize(string FilterField, string FilterVal);
int GetDataSize(string FilterField, DateTime SearchDateStart, DateTime SearchDateEnd);

DataSet GetPage(int Pagesize, int CurrPage, string SortCol, string SortDirection);
DataSet GetPage(int Pagesize, int CurrPage, string SortCol, string SortDirection, string FilterField, string FilterVal);
DataSet GetPage(int Pagesize, int CurrPage, string SortCol, string SortDirection, string FilterField, DateTime SearchDateStart, DateTime SearchDateEnd);

In this solution I put together a class MyCustomSQLPageFilterDataSource which supports the IpagingFilterDataSource and retrieves data from the customers table of the test database via a stored proc called up_Customers_list.

In the page_load event in the default.aspx.cs file there is a line

MyCustomSQLPageFilterDataSource MyDataSource = new MyCustomSQLPageFilterDataSource("SQLServer", "CustomerTestDatabaseName", "Uid", "passwd");

You will need to change this to suit your setup based on the database that you created and the userid and passwd for it.

The solution provides a page with a gridview that supports the following features:



  • Click on headers to sort columns

  • Drag & Drop headers to re-order columns, changes persist over pages.

  • Right click on Column headers and filter on entered values ( Version 2 now supports filtering dates in a selected Month)

  • Page through data and page size is adjustable in the URL (Count Parameter)

  • Exporting to excel and a highly experimental mail merge via word using the “client siding” techniques I have blogged about before.

Labels: ,

Monday, January 15, 2007

Code for .Net File Uploader/Downloader with backend SQL storage.


Probably one of the most popular controls that are talked about on web forums and available from third parties are file upload controls. I have found however there is a lack of information and code for people who want to manage those files once they get to the server.

Since I have recently been doing a lot of work with secure workflow in .Net I have needed to use this functionality, so I have created a sample project showing what I have done. The sample project performs the following functions:

  • Multiple file upload on the client (similar to gmail’s file upload facility)
  • Storage of uploaded files in SQL Server.
  • Display of existing uploaded files with associated icons and file size information.
  • Ability to add and remove file attachments from an existing record.
  • Download and display the uploaded files.

The zipped C# vs2005 solution can be downloaded here .

You will need to create a new SQL Database and run the sql code in the DBSetup Folder of the Project to create the required database tables and stored procs. You may also need to setup the security for these SQL elements depending on how you have your test environment setup. You will then need to adjust the AttachmentDatabase connection string in the web.config to match your settings.

I have used Typed Datasets through a Data Layer. I have included all of the DataSets and Data Link layer in the Web Project. Usually I would separate these out into their own projects, but for this sample I haven’t done that.

Some screen shots of it in action are below.

A record with various types of files attached.
Adding more files to an existing record.
A blank record.

Labels: ,

Monday, January 08, 2007

.Net User Managable Access Control with AD User and Group Support.

For all articles relating to Domino to .Net conversion please see this post

Previously I posted about extending the SQLRoleProvider to allow for the use of AD groups.

This by itself is not that useful, so I have now created a project that uses this to show how to create a system for User-Managable Access Control with AD User and Group Support.

I have a zipped copy of this project available for dowload here.

The features that is project provides are:



  • SQL Roleprovider that supports AD group membership to determine application roles.

  • Access Control page to support the administration of users and groups and granting them application roles.

  • AD Search system to allow for the easy look-up of users and groups in the Active Directory and add them to the Access Control List


By using this system you can allow a specific group of users to manage access to the .net application themselves, as it supports AD group membership then the Access Control is very easy to use.

A couple of screen shots of the system in action are below.

Access Control List

Roles Tab

Searching Active Directory

To set it up you will firstly need to download the project, then install the standard .net SQLProvider system on your server . This can be found here, with additional set-up instructions in my previous post.

Once you have that set-up then you will need to run the additional SQL against the database found in the DBSetup folder of the project (this will add two new stored procedures to the standard SQLProvider database – you may need to alter the SQL permissions on these two stored procs to make them run in your environment).

After you have done that then you will need to alter the web.config of the project so that is matches your environment. You will need to adjust the connections string for the SQLRoleManagerConnection and some appsetting keys:

ADUser – this is the Domain\userid of the id used to query the Active directory when searching for users and groups

ADPasswd – this is the password of the ADUser

ADBrowsableDomains – this is a comma seperated list of all of the available domains in your environment.

DBRolesEdit – true or false to determine if the Access control Roles are editable. If you are implementing a system that uses static roles for access with a web.config setup like. Then set this to false. Otherwise set it to true to allow uses to update it.

ACLAdminRole – this is the Application Role that can edit the Access Control of the system. If the user ( or one of the users AD membership groups , if the user isn’t explicity listed ) is in this role then they can edit the ACL via the ACL.apsx page.

You will need to make sure that you have at least run the SQL statements

EXEC aspnet_Roles_CreateRole 'SQLProviderTest', '[ACLAdminRole]'
EXEC aspnet_UsersInRoles_AddUsersToRoles 'SQLProviderTest', ' YourDomain\YourId', '[ACLAdminRole]', 8

Once you have added yourself to the role that can edit the ACL (the value of the ACLAdminRole web.config key ) then you will be able to update the ACL.aspx page. You can then go to the default.aspx page and see which roles you are in.

For a better user experience I would suggest wrapping the content of the ACL.aspx page in a AJAX panel. I have purposely not done this because they are a variety of AJAX implemetations around, so this is up to you.

Happy Coding.

Labels: , ,

Thursday, December 21, 2006

Source for Drag & Drop, Paging , filtering gridview page



kick it on DotNetKicks.com

Note: There is an updated version of this here

I finally got around to putting a useable C# website solution file together for the Drag & Drop, Paging , filtering gridview page. You can download the zipped vs2005 solution Here .

There are a couple of steps to get it working.

You need to create a new empty database on your SQL server and run the SQL code file that is in the DatabaseSetup folder of the solution this will:

  • Create a customer table
  • Create a stored procedure to query the customer table as required
  • Fill in the customer table with some test data.

You will then need to grant access to those SQL elements so that you can access them from your .net solution.

The solution file contains a single aspx page that holds a standard gridview and code that does all of the logic. The data is accessed from the database via DataClass. I have created an Interface called IpagingFilterDataSource that matches what is required if you want to write your own class to do the datahandling. This will obviously be required if you are going to extend this in anyway.

public interface IPagingFilterDataSource
{
int GetDataSize();
int GetDataSize(string FilterField, string FilterVal);
DataSet GetPage(int Pagesize, int CurrPage, string SortCol, string SortDirection);
DataSet GetPage(int Pagesize, int CurrPage, string SortCol, string SortDirection, string FilterField, string FilterVal);

}

In this solution I put together a class MyCustomSQLPageFilterDataSource which supports the IpagingFilterDataSource and retrieves data from the customers table of the test database via a stored proc called up_Customers_list.

In the page_load event in the default.aspx.cs file there is a line

MyCustomSQLPageFilterDataSource MyDataSource = new MyCustomSQLPageFilterDataSource("SQLServer", "CustomerTestDatabaseName", "Uid", "passwd");

You will need to change this to suit your setup based on the database that you created and the userid and passwd for it.

The solution provides a page with a gridview that supports the following features:

  • Click on headers to sort columns
  • Drag & Drop headers to re-order columns, changes persist over pages.
  • Right click on Column headers and filter on entered values
  • Page through data and page size is adjustable in the URL (Count Parameter)
  • Exporting to excel and a highly experimental mail merge via word using the “client siding” techniques I have blogged about before.

Here are some additional screen shots of it in action:

Drag and drop in action
Right-click on column header and filter
After Filter Applied
After adjustment of pagesize to 5

Labels: , ,

Wednesday, December 20, 2006

Extending SQLRoleProvider to Support Active Directory Group Membership.

This is a ASP 2.0 + solution, for ASP .Net Apps using Integrated Windows Authentication. Please note this post has been updated.

The System.Web.Security.SqlRoleProvider allows you to store and manage Roles for your .net web applications in an SQL Database. This is very useful for building ASP .Net application where you want to a group of “admin” end-users to be able manage who can access a system and what they can do. ( I will talk about building a AD integrated System access management system in a later blog, right now I will concentrate on the nuts and bolts).

Using the standard SQLProvider system roles are provided on a per user basis, so if you have thousands of users for the system then you would need to actually add each user individually. The whole point of this is to provide “end-user” system management, so I want to make it as easy as possible.

Then I thought that it would be much easier if I could extend the default SQLProfileprovider to support Active Directory groups. That way if a set of users where already defined in Active Directory group then all the “admin” users would need to do is grant that group the role in the application and that would give everyone in that group access, and keep the system far more manageable.

If you haven’t already used an SQL Roleprovider then you need to go the http://msdn2.microsoft.com/en-us/library/ms998314.aspx and follow the instructions related to the SQLRoleprovider. This will install a default installation of the Role provider database that integrates with the standard system.Web.Security.SqlRoleProvider. Make sure that if you run the stored procedures the application name matches the applicationName in the Providers Key in your web.config and make sure that the name you use is your Domain\User name.

So for example if I have AppName of SQLProviderTest a Role called Admin and my user is MYDOM\User1 so I issue the commands.

EXEC aspnet_Roles_CreateRole 'SQLProviderTest', 'Admin'
EXEC aspnet_UsersInRoles_AddUsersToRoles 'SQLProviderTest', ' MYDOM\User1', 'Admin', 8

And the web.config section would look something like this

<roleManager enabled="true" defaultProvider="SQLRoleProvider" cacheRolesInCookie="true"> <providers> <add name="SQLRoleProvider" type="System.Web.Security.SQLRoleProvider" connectionStringName="SqlRoleManagerConnection" applicationName="SQLProviderTest" /> </providers> </roleManager>


If you then compile and run default.aspx as specified in the instructions you it should display that you are in the role.

So we need to extend the standard SqlRoleProvider to support groups. Below is the code for the ADSQLRoleProvider class. To implement this do the following steps:

In visual studio create a App_Code folder under the root directory of your web project.


Add new class file in this folder called ADSQLRoleProvider.cs
Paste the following code into the class code file and save it.

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Security;
using System.Web;
using System.Security.Principal;

/// <summary>
/// Summary description for ADSQLRoleProvider
/// </summary>
public class ADSQLRoleProvider : System.Web.Security.SqlRoleProvider{

public ADSQLRoleProvider(){}

#region ADLookup & Support Methods

private string[] getusersADgroups(IIdentity User)
{

//HttpContext.Current.User;
WindowsIdentity Wident = (WindowsIdentity)User;
List<string> groups = new List<string>();

IdentityReferenceCollection UGroups = Wident.Groups;

foreach (IdentityReference Grp in UGroups)
{

NTAccount acc = (NTAccount)Grp.Translate(typeof(NTAccount));
groups.Add(acc.Value);
}

return groups.ToArray();

}
private string[] GetRolesForGroup(string groupname)
{
return base.GetRolesForUser(groupname);
}
#endregion

#region Extended SQLRoleProvider Methods
public override bool IsUserInRole(string username, string roleName)
{
if (base.IsUserInRole(username,roleName))
{
return true;
}
else
{
string[] Ugrps = getusersADgroups(HttpContext.Current.User.Identity);
string[] GroupsRoles;

foreach (string g in Ugrps)
{
GroupsRoles = GetRolesForGroup(g);
foreach (string grole in GroupsRoles)
{
if (grole == roleName)
{
return true;
}
}
}
}
return false;
}

public override string[] GetRolesForUser(string username)
{
System.Collections.Generic.List<string> roleList = new List<string>();

string[] ActualUserRoles = base.GetRolesForUser(username);

//Only do the next check if the username matches the current web user and the users roles are empty

if ((ActualUserRoles.Length == 0) && (username.ToLower() == HttpContext.Current.User.Identity.Name.ToLower()))
{

string[] Ugrps = getusersADgroups(HttpContext.Current.User.Identity);
string[] GroupsRoles;

foreach (string g in Ugrps)
{
GroupsRoles = GetRolesForGroup(g);
foreach (string grole in GroupsRoles)
{
if (!(roleList.Exists(delegate(string s) { return s == grole; })))
{
roleList.Add(grole);
}

}
}
return roleList.ToArray();
}
else
{
return ActualUserRoles;
}
}
#endregion
}



Open the web.config file and make sure that the original connection string for the Roleprovider database is correct.
Change the role provider section in the web.config to

<roleManager enabled="true" defaultProvider="ADSQLRoleProvider" cacheRolesInCookie="true"> <providers> <add name="ADSQLRoleProvider" type="ADSQLRoleProvider" connectionStringName="SqlRoleManagerConnection" applicationName="SQLProviderTest" /> </providers> </roleManager>

Compile the website to make sure you don’t get errors.

You have now update the application to use a custom sqlroleprovider that will first check for roles of a user, and if it doesn’t find any it will then check for any roles belonging to any Active Directory groups that user is a member of.

To test that this is working remove yourself from the role by running the stored proc aspnet_UsersInRoles_RemoveUsersFromRoles

For my example above this is

EXEC aspnet_UsersInRoles_RemoveUsersFromRoles 'SQLProviderTest', 'MYDOM\User1', 'Admin'

Re-run the application to make sure that you are not in any roles.

Now add a domain group that you are a member of to the Admin role. For my example the user MYDOM\User1 is a member of MYDOM\IT_Apps group so I will add that one with the Stored proc command

EXEC aspnet_UsersInRoles_AddUsersToRoles 'SQLProviderTest', ' MYDOM\IT_Apps', 'Admin', 8

If you run the application again you will see that you are in the role.

In up and coming blogging I will talk about how to build the user-front end to this, that allows users to search, select and assign roles to AD users and groups. This will allow you to build easy use managed applications with role based security. Some screen shots of what I am talking about are below:




Labels: , ,

Tuesday, November 07, 2006

Enhanced SMTPClient Class

I have been working on two areas of e-mail lately with .net. These are:

  • Sending invitations from .net so that outlook with except them properly.
  • Sending in-line MIME images
Both of these things require manipulation of the Content-Type headers which the standard system.net.mail.smtpclient does not support so I have written my own class to handle these two scenarios. The invitations are sent using a vcalendar class.

The class is CorpSMTPMailer (download here) and has two public methods of the class are

public void Sendicalinvite(string frommail, string[] tomail, DateTime EventStart, DateTime EventEnd, string Summary, string Location, string Organizer, string Description, string URL, bool Async)

public void SendMimeInLineMessage(string frommail, string[] tomail, string subject,string HTMLBody,string[] InLineAttachFiles,string[] InLineAttHTMLTokens,bool Async)

Both methods have an Async parameter, this determines where the functions are run in the current thread or a new thread is created to actually do the mail delivery. I haven’t included any feedback mechanism for error trapping as I don’t require it so if you need some them you will need to add it yourself.

The only thing to note is that the SendMimeInLineMessage function requires the HTMLBody to contain the tokens that are specified in the InLineAttTMLTokens Array so it knows where to place the content ids of the files specified in the InLineAttachFiles Array.

So for instance your code might look something like this. ( Please note I have changed the greater then and less than signs to [ ] in this code snippet as the blogger editor refused to render them properly )

List[string] MesgAttach = new List[string]();
List[string] tokens = new List[string]();

string body = "[div style=\"font-family:Arial\"]First picture attachment:[br /][br /][img src=\"@@IMAGE1@@\"][br /][br /]Second picture[br /][br /][img src=\"@@IMAGE2@@\"][br /][br /][/div]";

MesgAttach.Add(@"C:\pics\pic1.jpg");
MesgAttach.Add(@"C:\pics\pic2.jpg");
tokens.Add("@@IMAGE1@@");
tokens.Add("@@IMAGE2@@");

Labels: , ,