Just a little bit of background. We are moving away from storing our user passwords hashed in a database to authenticating directly against Active Directory. Unfortunately we have so many internal applications that updating every application is not an option at this point.
So instead of requiring all our developers to change their code, I thought I could just build a .NET CLR Assembly for SQL and just modify the existing authentication stored procedure to validate the users password against our domain controllers instead of a table.
This has been successful in our initial testing, except when the domain controller cannot be communicated with for whatever reason (shutdown, restart, network, etc). When this happens we get timeouts. I added a try/catch and while loop to just skip to the next domain controller and attempt to authenticate against it, but it still takes the first attempt well over 20 seconds to fail before it tries to authenticate against the next server. Since this is a SQL assembly/function and is being called in a stored procedure 20 seconds is way too long.
What is the best approach to modify my code and fail the authentication attempt against a server and try the next server, say, after 3 seconds? Threading somehow?
Imports System
Imports System.Data
Imports System.Collections
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Runtime.InteropServices
Imports System.DirectoryServices.AccountManagement
Partial Public Class UserDefinedFunctions
<SqlFunction(Name:="ValidCredentials")>
Public Shared Function ValidCredentials(ByVal Domain As SqlString, ByVal Username As SqlString, ByVal Password As SqlString) As SqlBoolean
'Validates the username and password are correct against the domain.
Dim ServerNum As Integer = 1
Dim Success As Boolean = False
Dim ValidCreds As SqlBoolean = False
'Loop through all 4 domain controllers to attempt authentication against each of them.
While Success = False And ServerNum <= 4
Try
Dim pc As PrincipalContext
If Domain = "employees" Then
pc = New PrincipalContext(ContextType.Domain, "DC-E-0" & ServerNum.ToString & ".employees.ourdomain.loc", "EMPLOYEES\BindAcct", "PASS")
Else
pc = New PrincipalContext(ContextType.Domain, "DC-S-0" & ServerNum.ToString & ".students.ourdomain.loc", "STUDENTS\BindAcct", "PASS")
End If
ValidCreds = pc.ValidateCredentials(Username, Password)
Success = True
Return ValidCreds
Catch ex As Exception
ValidCreds = False
Success = False
ServerNum += 1
End Try
End While
Return ValidCreds
End Function
End Class