Sunday, December 25, 2011

Great Year 2011, and Welcome to 2012

It is time to close to the end of Year 2011.  At the time this blog is published, I am on my way to China to visit my mother, sister and brother.

This is another great year for me. I finished my contact for 2011 at Husky Energy and I'll move to development team for another year.  Even though I have done not much programming this year, I have learned so much by provide application support.  This has been great experience to learn much more about applications, by using application, trouble shooting issues, and providing helps to gain better understanding of applications created by other people and companies.

I think to extend knowledge to a wide range is very important. You will never know when those skills and knowledge will be useful.  My past experiences with a wide range of stuff benefit so much.  This has been proved in my work in 2011.  In so many cases, my past knowledge in SQL, Oracle, OPC, Windows System, programming knowledge in .Net, UNIX, even VI has helped me  a lot. Never stop learning and never say no something you don't know. Face the challenge and invest your time and effort, as well as keeping the habit to learn something new.  All those inputs will bring great results in long run for sure.

My Apple development has no progress at all in 2011; however, I did spend time to finish all the WWDC videos.  The reason I don't have time on iOS app development is that I just don't have time.  I have changed my focus in another none-programming area and put a lots of my time and effort on my Chinese blog.  This is another sharing experience and I think it is important for me to do.  In this area, I have set up a solid and great start up.  When I settle down in this area, I'll go back my iOS and Mac app development. Actually, I will find some interesting and great potential topics to work on.

My next year contract will be also full of challenges and new opportunities.  In the past month I have been working on an ASP.Net project based on .Net framework 2.0.  The next version will be on .Net 4.0 with MVC, and Infragistics tools.  I am exited and looking forward to taking the challenge.

However, I think my .Net knowledge and skills are up to the comfort level for me to stay for a while. My next territory will be in Apple's iOS and Mac. I have laid solid foundation.  I'll continue to head in to the new area with passion.

As always, this blog will my sharing land to mature my programming experiences. It'll be very valuable to overview my trace in the feature. Let me leave my deep and clear foot prints. I'm sure it will enjoyable time to read myself.

Read More...

Sunday, December 18, 2011

Using LDAP to Authenticate Windows Users

Here are some my programming notes about using LDAP library to authenticate Windows Users.

This request came from my ASP.Net project, which is hosted on intranet IIS server. The first login page is to authenticate Windows users in the company.  I need a library to do the job.  I tried some codes created long time before, but I found that the codes is not completed.  The authentication works only in Visual Studio, but not at an IIS server after deployment. I need to fix the issue.

I found that there are many ways to do that.  One is based on our existing codes with Novel.Directory.Ldap library, another on System.DirectoryServices. I tried both in one test console application.

Here some some references and constants used in the console application:

using System;
using Novell.Directory.Ldap;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.Protocols;
using AD_LdapConnection = System.DirectoryServices.Protocols.LdapConnection;
using ND_LdapConnection = Novell.Directory.Ldap.LdapConnection;
using System.Net;
...
private const string LDAPHOST = "xxxx.yy.zzzz.com";
private const int LDAPPORT = 389;
private const string DOMAINNAME = "yy";
private const string CN_NAME_SUFIX = "@yy.zzzz.com";

Novel.Directory.Ldap


The first one is base on Novel.Directory.Ldap:

private static bool Authenticate(string username, string pwd)
{
  bool bRet = false;
  bool connected = false;

  // connect to LDAP server
  ND_LdapConnection ldapConnLogin = new ND_LdapConnection();
  try
  {
    Console.WriteLine("Start authenticating...\nConnecting to {0}, port: {1}",
      LDAPHOST, LDAPPORT);
    ldapConnLogin.Connect(LDAPHOST, LDAPPORT);
    connected = ldapConnLogin.Connected;
    if (connected)
    {
      Console.WriteLine("Connected: {0}", connected);

      string cn = string.Format(
        "{0}{1}", username, CN_NAME_SUFFIX);
      Console.WriteLine("Binding with {0}", cn);
      ldapConnLogin.Bind(cn, pwd);
      bRet = ldapConnLogin.Bound;
      Console.WriteLine("Bound: {1}", bRet);
    }
  }
  catch (Exception ex)
  {
    string msg = string.Format(" Error message or code: {0}", ex.Message);

    Console.WriteLine(msg);
    bRet = false;
  }
  finally
  {
    if (ldapConnLogin != null && connected)
    {
      ldapConnLogin.Disconnect();
    }
    ldapConnLogin = null;
  }

  return bRet;
}

This methods depends on ldap host name, port number, and cn name(in a format like email address in our company). One thing interesting is that the exception thrown from the binding call are error codes in Message, and no implementation of ToString() method.

System.DirectoryServices


The second method is Microsoft .Net library APIs in System.DirectoryServices. The following codes are much simpler and works in the same way to authenticate a Windows user:

private static bool Authendicate2(string domain, string userName, string password)
{
  bool validation = false;
  try
  {
    Console.WriteLine("Authenticating user by AD library...");
    var ldc = new AD_LdapConnection(
        new LdapDirectoryIdentifier(LDAPHOST, false, false));
    NetworkCredential nc = new NetworkCredential(userName, password, domain);
    Console.WriteLine("Created credencial object.");
    ldc.Credential = nc;
    ldc.AuthType = AuthType.Negotiate;
    Console.WriteLine("Binding credencial...");
    ldc.Bind(nc);
    // user has authenticated at this point, as the credentials were used to login to the dc. 
    Console.WriteLine("Binding credencial is done.");
    validation = true;
  }
  catch (Exception ex)
  {
    Console.WriteLine("Exception: {0}", ex.Message);
    validation = false;
  }
  return validation;
}

References

Read More...

Thursday, December 08, 2011

In the past weeks I have been working on an ASP.Net web site application. I tried to use my ILog and LogToFile initially. However it failed miserably. ASP.Net web pages are based server and client architecture pattern. The IIS Server does not know when the web page on client side will call back. This means that I cannot keep the log file opened forever. I tried to close the log file for each message log.  I found that I could not keep the same log file since my log file name is in a patten like mylog_yyyymmdd_hhmm.log. The sever side will open another log file since the file name is kept on the server side. I could remove minutes out, but I don't like it. Therefore my original library for logging messages is not good for web site applications.

Further investigating the sever side class codes, I found that on the IIS server, writing to a text file is not allowed. I tried to write to a folder out of inetpub folder, such as C:\Log, still it is not writable.  I may be able to figure out by change IIS application pool permission, but I don't that's may cause security holes.

I had to take different strategy to log messages. I changed my log helper class to write messages to a SQL server database. This actually works fine. I realize that there are some drawbacks about this. As I mentioned in the above analysis, I cannot keep database connection open all the time. So I have to close connection for each log.  This results a very slow logging process since the database connection has to be opened and closed constantly. But it works fine.  In order to improve performance, I used delegate for log message so that if log setting is off, no db connection operation at all.  The second good practice is to use a pair methods of BeginDBLog() and EndDBLog() to allow database connection open for several log processes.

Here are some class data members in DBLog class:

# region Data members
private static DBLog _log;
private IDbService _db;
private SQLQuery _sqlQuery;
private DBLogConfig _config;
# endregion

Since the instance of DBLog is singleton, I use a method to control the instance creation. The pair methods are used for keep the instance of IDbService alive:

public static DBLog GetInstance(DBLogConfig logConfig)
{
  if (_log == null)
  {
    _log = new DBLog(logConfig);
  }
  return _log;
}

#region CTOR
private DBLog(DBLogConfig config)
{
  _config = config;
  _sqlQuery = new SQLQuery(_config.StoreProcedureName);
}
#endregion

public void BeginDBLog()
{
  if (_db == null)
  {
    if (_config != null)
    {
      _db = new DBService(_config.DBConnection,
        _config.SQLOrOracle ?
        DBService.DBType.MicrosoftSQLServer :
        DBService.DBType.OracleServer);
    }
  }
}

public void EndDBLog()
{
  if ( _db != null )
  {
    _db.Close();
    _db = null;
  }
}

Here are methods for logging messages:

public void LogMessage(string flags, LogType logType, string msg)
{
  LogMessage(flags, logType, delegate() { return msg; });
}

public void LogMessage(string flags, LogType logType,
    LogHelper.GetMessageDelegate getMessageDelegate)
{
  if (IsLogSet(flags, logType) && getMessageDelegate != null)
  {
    bool close = false;
    if (_db == null)
    {
      BeginDBLog();
      close = true;
    }
    string msg = getMessageDelegate();
    if (!string.IsNullOrEmpty(msg))
    {
      if (_sqlQuery.Parameters.Count > 0)
      {
        _sqlQuery.Parameters.Clear();
      }
      bool sqlOrOracle = _config.SQLOrOracle;
      DateTime dt = DateTime.Now;
      if (msg.Length > MAX_LENGTH)
      {
        msg = msg.Substring(0, MAX_LENGTH - 1);
      }

      _sqlQuery.Parameters.Add(SQLQuery.GetParameter(sqlOrOracle,
        _config.ParameterDate, dt));
      _sqlQuery.Parameters.Add(SQLQuery.GetParameter(sqlOrOracle,
       _config.ParameterLogType, logType.ToString()));
      _sqlQuery.Parameters.Add(SQLQuery.GetParameter(sqlOrOracle,
        _config.ParameterApplication, _config.ApplicationName));
      _sqlQuery.Parameters.Add(SQLQuery.GetParameter(sqlOrOracle,
        _config.ParameterMessages, msg));
      _sqlQuery.Parameters.Add(SQLQuery.GetParameter(sqlOrOracle,
        _config.ParameterSiteID, _config.SiteID));

      int result = _db.ExecuteSqlNoneQueryCommand(_sqlQuery, CommandType.Text);
    }
    if (close)
    {
      EndDBLog();
    }
  }
}

Notes: the above codes depend on my DotNetCommonLibrary and a class of DBLogConfig for database configuration.  The configuration class is a simple class with property getters and setters for database type, stored procedure name, and parameter names, as well as related configuration values.

Read More...

Saturday, December 03, 2011

Read Password from Console

Yesterday I was write codes to test my class for user Windows authentication. The simple console app will take user name and password as input. I would like password from console input like *** from a text box. By googling search, I found the following codes. They works as expected.

The example codes are very simple. Actually I tried to search from Console class in Visual Studio, and I found ReadKey method. I did not figure out how to get input from this method, since the keys from the methods are all in caps. Then I searched the web and found this solution. Sharing is great!

public static string ReadPassword() {
  Stack<string> passbits = new Stack<string>();
  //keep reading
  for (ConsoleKeyInfo cki = Console.ReadKey(true);
      cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true)) {
    if (cki.Key == ConsoleKey.Backspace) {
      //rollback the cursor and write a space so it looks backspaced to the user
      Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
      Console.Write(" ");
      Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
      passbits.Pop();
    }
    else {
      Console.Write("*");
      passbits.Push(cki.KeyChar.ToString());
    }
  }
  string[] pass = passbits.ToArray();
  Array.Reverse(pass);
  return string.Join(string.Empty, pass);
}

Reference

Read More...