2

I've been looking for the answer as there are many similar questions, but haven´t found this particular case.

I have an SQL server, and an Access File (front-end). This Access file has linked tables, linked with the SQL server, and every time I try to open one table, a prompt appears asking for user and password.

OK, this is perfect, as it is (almost) the behaviour I want.

Also, there's a local table, that contains User/Pass with passwords encrypted. Every time the user opens the file, his/her pass is decrypted and I would like to open a connection, so linked tables won´t ask for pass.

Does anybody know how to code this connection?

Thanks in advance!

1
  • 2
    Depending on the situation, can't you use Windows security instead of SQL Server security ? That coudl make you rlife easier. Commented Mar 22, 2012 at 13:30

2 Answers 2

5

I think what you want is a "DSN-LESS connection". I've done something similar in the past. Basically, at user login you re-link the tables using a connection string that you build based on the user's credentials.

Take a look at this prior SO Question for more details.

Also, here are a few routines I've used in the past that you are free to use \ modify. Call these from your login process.

Public Function RelinkDSNTables( _
      ByVal ServerName As String, _
      ByVal DatabaseName As String, _
      ByVal UserName As String, _
      ByVal Password As String) As Boolean
On Error GoTo Err_Handler

Dim dsn As String
dsn = GetDSNLink(ServerName, DatabaseName, UserName, Password)
Dim td As TableDef
Dim db As DAO.Database
Set db = CurrentDb
Dim rst As ADODB.Recordset

'Get a list of tables that need to be relinked'
If GetLocalRecordSet(rst, "SELECT * FROM TableMapping") < 1 Then
   Err.Raise 1000, "Missing Tables!", "Missing Table Mappings!"
End If

Dim fNeedToRefresh As Boolean

'See if we actually need to relink the tables'
For Each td In db.TableDefs
   If td.Connect <> vbNullString Then
      If td.Connect <> dsn Then
         fNeedToRefresh = True
         Exit For
      End If
   End If
Next td

If fNeedToRefresh = False Then
   RelinkDSNTables = True
   GoTo Err_Handler
End If

'Drop linked table in Access'
For Each td In CurrentDb.TableDefs
   If td.Connect <> vbNullString Then
      CurrentDb.TableDefs.Delete td.Name
   End If
Next td

'Create new linked table using new DSN'
rst.MoveFirst
Do Until rst.EOF
   Set td = db.CreateTableDef(rst!LocalTableName, dbAttachSavePWD, rst!RemoteTableName, dsn)
   db.TableDefs.Append td
   rst.MoveNext
Loop

'Because I am paranoid, refresh the link'
db.TableDefs.Refresh
For Each td In db.TableDefs
   If td.Connect <> "" Then td.RefreshLink
Next td

RelinkDSNTables = True

Err_Handler:
   If Err.Number = 3011 Then
      'Happens if user does not have permission in SQL Server; Nothing to see here, move along'
      Err.Clear
      Resume Next
   ElseIf Err.Number = 3010 Then
      'already exists; should not occur, but if it does eat the exception and move on'
      Err.Clear
      Resume Next
   ElseIf Err.Number <> 0 Then
      Err.Raise Err.Number, Err.Source, Err.Description
   End If
End Function

Private Function GetDSNLink( _
      ByVal ServerName As String, _
      ByVal DatabaseName As String, _
      ByVal UserName As String, _
      ByVal Password As String) As String
On Error GoTo Err_Handler

If ServerName = "" Or DatabaseName = "" Then
   Err.Raise -1220, "Missing Server \ DB", _
      "Unable to refresh table links because you are missing the server or database name!"
End If

Dim dsnLink As String

If UserName = "" Then
   'trusted connection'
   dsnLink = "ODBC;DRIVER=SQL Server;SERVER=" & ServerName & _
            ";DATABASE=" & DatabaseName & ";Trusted_Connection=Yes"
Else
   'MixedMode connection'
     '//WARNING: This will save the username and the password with the linked table information.
   dsnLink = "ODBC;DRIVER=SQL Server;SERVER=" & ServerName & _
            ";DATABASE=" & DatabaseName & ";UID=" & UserName & ";PWD=" & Password
End If

GetDSNLink = dsnLink

Err_Handler:
   If Err.Number <> 0 Then
      Err.Raise Err.Number, Err.Source, Err.Description
   End If
End Function

Edit: Added sample GetLocalRecordSet Function

Public Function GetLocalRecordSet(ByRef rs As ADODB.Recordset, ByVal sql As String) As Long
On Error GoTo Err_Handler

If sql = vbNullString Then
   Err.Raise vbObjectError + 1001, _
      "Empty SQL String", "Empty SQL String Passed to GetLocalRecordset Function!"
End If

Set rs = New ADODB.Recordset

rs.Open sql, CurrentProject.Connection, adOpenKeyset, adLockOptimistic

If rs Is Nothing Then
   GetLocalRecordSet = -1
   GoTo Err_Handler
End If

If rs.State = adStateOpen Then
   If Not rs.EOF Then
      rs.MoveLast
      GetLocalRecordSet = rs.RecordCount                 'store number of records
      rs.MoveFirst
   Else
      GetLocalRecordSet = 0
   End If
Else
   GetLocalRecordSet = -2
End If

Err_Handler:
   If Err.Number <> 0 Then
      Err.Raise Err.Number, Err.Source, Err.Description
   End If
End Function
Sign up to request clarification or add additional context in comments.

16 Comments

You posted your answer while I was busy typing mine. Your answer looks good, maybe even better than mine. One of the only differences I see is that I do have code in mine to create an index called "__UniqueIndex" for each linked table. I can't remember why I did this - maybe it's not needed for SQL Server ODBC linked tables.
@HK1 - Great point about the unique index. I skipped this as my tables in SQL Server always have a unique index. However, if the table does not have a unique index in SQL Server you won't be able to perform an update in MS Access against the table WITHOUT first creating the index within Access. Feel free to update my answer if you like : )
I'd be happy to update your answer with some free code but it requires you to store the primary key field(s) name(s) in order to create the unique index. So you'd need another field in your tablemapping table.
So what you are doing is relinking all the tables each time, right? As the linked tables include also User and Pass you have to delete and create them again. I would like to just establish a connection at the opening, but I will try this solution. GetLocalRecordSet is a function that you have defined?
@Pete - I use a function much like Tim's and I just relink all the tables when the database opens. Those connections seem to stay in place without a problem, until the database closes. You shouldn't have to relink or refresh the tables while the database is open unless you are making lots of table changes that need to take affect immediately.
|
0

You can write a code to programmatically connect to the SQL database with username/password stored in your local table. This should happen on Access app load (e.g. using Autoexec macro).

You can read about Autoexec macro here: http://www.vb123.com/toolshed/05_map/ch04_autoexec.htm

So, you would have a function e.g. OpenConnection() which is called from Autoexec macro at application start. Any subsequent table openings should not prompt for the password.

I am not sure if this will work with the SQL back-end, though. Hopefully, it will.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.