Determining a User's Group Membership - vba

How can I determine if a user, in say Access, is a member of an Active Directory Security Group?
I'd rather not build a whole authentication system into my little Access DB.
Thanks

Allain found this online
Function IsMember(strDomain As String, strGroup _
As String, strMember As String) As Boolean
Dim grp As Object
Dim strPath As String
strPath = "WinNT://" & strDomain & "/"
Set grp = GetObject(strPath & strGroup & ",group")
IsMember = grp.IsMember(strPath & strMember)
End Function
You can get the Windows account info by way of the USERDOMAIN and USERNAME environment vars:
Function GetCurrentUser() As String
GetCurrentUser = Environ("USERNAME")
End Function
Function GetCurrentDomain() As String
GetCurrentDomain = Environ("USERDOMAIN")
End Function
Putting it all together:
If IsMember(GetCurrentDomain, "AD Group", GetCurrentUser) Then
DoStuff()
End If

I'm late to the game with this, but the code you need is below. It gets user names and domain names for you.
Note that I'm not using objGroup.Ismember - that's actually the correct method to use - I'm enumerating the list of groups that the user is in, because it's much easier to debug and there's no appreciable performance penalty.
...And I lifted the code from an earlier project, in which I needed to check membership of a 'Read Reports' group, an 'Edit Data' Group, and an 'Edit System Data' group, so that I could choose which controls to enable and which forms to open read-only. Enumerating groups once was faster than three separate checks.
Public Function UserIsInGroup(GroupName As String, _
Optional Username As String, _
Optional Domain As String) As Boolean
'On Error Resume Next
' Returns TRUE if the user is in the named NT Group.
' If user name is omitted, current logged-in user's login name is assumed.
' If domain is omitted, current logged-in user's domain is assumed.
' User name can be submitted in the form 'myDomain/MyName'
' (this will run slightly faster)
' Does not raise errors for unknown user.
'
' Sample Usage: UserIsInGroup( "Domain Users")
Dim strUsername As String
Dim objGroup As Object
Dim objUser As Object
Dim objNetwork As Object
UserIsInGroup = False
If Username = "" Then
Set objNetwork = CreateObject("WScript.Network")
strUsername = objNetwork.UserDomain & "/" & objNetwork.Username
Else
strUsername = Username
End If
strUsername = Replace(strUsername, "\", "/")
If InStr(strUsername, "/") Then
' No action: Domain has already been supplied in the user name
Else
If Domain = "" Then
Set objNetwork = CreateObject("WScript.Network")
Domain = objNetwork.UserDomain
End If
strUsername = Domain & "/" & strUsername
End If
Set objUser = GetObject("WinNT://" & strUsername & ",user")
If objUser Is Nothing Then
' Insert error-handler here if you want to report an unknown user name
Else
For Each objGroup In objUser.Groups
'Debug.Print objGroup.Name
If GroupName = objGroup.Name Then
UserIsInGroup = True
Exit For
End If
Next objGroup
End If
Set objNetwork = Nothing
Set objGroup = Nothing
Set objUser = Nothing
End Function
Hopefully this late submission is of use to other developers: when I looked this up for the first time, back in 2003, it was like nobody had ever used AD groups in Excel or MS-Access.

Found this online
Function IsMember(strDomain As String, strGroup _
As String, strMember As String) As Boolean
Dim grp As Object
Dim strPath As String
strPath = "WinNT://" & strDomain & "/"
Set grp = GetObject(strPath & strGroup & ",group")
IsMember = grp.IsMember(strPath & strMember)
End Function
Now, I only need the account name of the current user. Too bad Application.CurrentUser doesn't give me their Domain Account name.

Related

Validating user name with global user list in VBA

I'm trying to create a an excel to track production in my organization. I have a user list with user names (windows). I wanted to validate the user name with the global list of the company whenever some adds a new user name to the list.
I have got a simple function for the same
Function GetUserFullName(userName) As String
Set WSHnet = CreateObject("WScript.Network")
UserDomain = WSHnet.UserDomain
On Error GoTo Err_open_esy
Set objUser = GetObject("WinNT://" & UserDomain & "/" & userName & ",user")
Exit_open_esy:
GetUserFullName = objUser.FullName
Exit Function
Err_open_esy:
GetUserFullName = "Error"
End Function
can use as
GetUserFullName("abc")

Defining and calling a global variable in VBA

I have a login screen which compares data via a Dlookup in order to authenticate users. I would like to create a global variable upon a correct login that any form on the database can call on and then open a form based on what that value is. So currently I have everything set up as such.
LOGIN FORM :
Option Compare Database
Public gstrUsr As String
LOGIN FORM:
Public Sub Command4_Click()
'Sets the login time to now and then authenticates user credentials
Dim usr As String
Me.Time = Now
Dim lvl As String
Dim lck As Integer
Dim sql As String
Dim msgapp As Integer
Dim chkusr As Variant
chkusr = Nz(DLookup("[Username]", "Login", "[Username]='" & Me.Username.Value & "'"), "")
msgapp = 0
usr = Nz(DLookup("[Password]", "Login", "[Username]='" & Me.Username.Value & "'"), "")
lvl = Nz(DLookup("[Level]", "Login", "[Username]='" & Me.Username.Value & "'"), "")
sql = "INSERT INTO Log ( [User], [Time] )SELECT [Forms]![Login]![Username] AS Expr1, [Forms]![Login]![Time] AS Expr2;"
''" & [Forms]![ItemList1]![SRCB] & "'"
'Runs above sql which adds a time record for the selected username also removes the "You are about to update X rows", will use this in the future on the accounting functions
If chkusr = "" Then msgapp = 1
If chkusr = "" Then MsgBox ("Invalid Credentials")
DoCmd.SetWarnings False
DoCmd.RunSQL (sql)
DoCmd.SetWarnings True
'If password is = to the value that is returned in the "usr" variable declared at the top via Dlookup it will open a form based on what that users "level" is otherwise displays and invalid credentials message box
Do While msgapp = 0
If usr = Me.Password.Value Then
lck = 1
msgapp = 3
Else
MsgBox ("Invalid Credentials")
msgapp = 3
End If
Loop
Do While lck = 1
If lvl = "2" Then
DoCmd.OpenForm "MainB"
gstrUsr = DLookup("[Username]", "Login", "[Username]='" & Me.Username & "'")
lck = 0
Else
DoCmd.OpenForm "Main"
lck = 0
End If
Loop
End Sub
FORM THAT LOADS AFTER SUCCESSFUL LOGIN: (Main form with buttons to get to other forms, I included a text box so I could see if the information is being passed to the second form)
Private Sub Form_Load()
Me.Text75 = gstrUsr
End Sub
How do I get the global variable to pass to the second form?
Define your public variable in a code module instead of the module of the form.
This way it'll be available from any module (if it's public)

Object variable or With Block variable not set - Access 2010 VBA

Greetings to the well of knowledge...
I've been reading the numerous posts on this particular error and have not found anything that resolves my particular issue.
I have some VBA code within an Access 2010 front-end. Sometimes, but not always, I get a "Object variable or With block variable not set." error. My code is as follows:
Public Sub ValidateAddress(PassedAddress As Object, PassedCity As Object, PassedState As Object, _
PassedZIP As Object, PassedCongressionalDistrict As Object, PassedValidated As Object, HomeForm As Form)
On Error GoTo ShowMeError
Dim strUrl As String ' Our URL which will include the authentication info
Dim strReq As String ' The body of the POST request
Dim xmlHttp As New MSXML2.XMLHTTP60
Dim xmlDoc As MSXML2.DOMDocument60
Dim dbs As Database
Dim candidates As MSXML2.IXMLDOMNode, candidate As MSXML2.IXMLDOMNode
Dim components As MSXML2.IXMLDOMNode, metadata As MSXML2.IXMLDOMNode, analysis As MSXML2.IXMLDOMNode
Dim AddressToCheck As Variant, CityToCheck As Variant, StateToCheck As Variant, ZIPToCheck As Variant
Dim Validated As Boolean, District As Variant, MatchCode As Variant, Footnotes As Variant
Dim candidate_count As Long, SQLCommand As String, Start, Finish
' This URL will execute the search request and return the resulting matches to the search in XML.
strUrl = "https://api.smartystreets.com/street-address/?auth-id=<my_auth_id>" & _
"&auth-token=<my_auth_token>"
AddressToCheck = PassedAddress.Value
CityToCheck = PassedCity.Value
StateToCheck = PassedState.Value
If Len(PassedZIP) = 6 Then ZIPToCheck = Left(PassedZIP.Value, 5) Else ZIPToCheck = PassedZIP.Value
' Body of the POST request
strReq = "<?xml version=""1.0"" encoding=""utf-8""?>" & "<request>" & "<address>" & _
" <street>" & AddressToCheck & "</street>" & " <city>" & CityToCheck & "</city>" & _
" <state>" & StateToCheck & "</state>" & " <zipcode>" & ZIPToCheck & "</zipcode>" & _
" <candidates>5</candidates>" & "</address>" & "</request>"
With xmlHttp
.Open "POST", strUrl, False ' Prepare POST request
.setRequestHeader "Content-Type", "text/xml" ' Sending XML ...
.setRequestHeader "Accept", "text/xml" ' ... expect XML in return.
.send strReq ' Send request body
End With
' The request has been saved into xmlHttp.responseText and is
' now ready to be parsed. Remember that fields in our XML response may
' change or be added to later, so make sure your method of parsing accepts that.
' Google and Stack Overflow are replete with helpful examples.
Set xmlDoc = New MSXML2.DOMDocument60
If Not xmlDoc.loadXML(xmlHttp.ResponseText) Then
Err.Raise xmlDoc.parseError.errorCode, , xmlDoc.parseError.reason
Exit Sub
End If
' According to the schema (http://smartystreets.com/kb/liveaddress-api/parsing-the-response#xml),
' <candidates> is a top-level node with each <candidate> below it. Let's obtain each one.
Set candidates = xmlDoc.documentElement
' First, get a count of all the search results.
candidate_count = 0
For Each candidate In candidates.childNodes
candidate_count = candidate_count + 1
Next
Set candidates = xmlDoc.documentElement
Select Case candidate_count
Case 0 ' Bad address cannot be corrected. Try again.
Form_frmPeople.SetFocus
MsgBox "The address supplied does not match a valid address in the USPS database. Please correct this.", _
vbOKOnly, "Warning"
PassedAddress.BackColor = RGB(255, 0, 0)
PassedCity.BackColor = RGB(255, 0, 0)
PassedState.BackColor = RGB(255, 0, 0)
PassedZIP.BackColor = RGB(255, 0, 0)
Exit Sub
Case 1 ' Only one candidate address...use it and return.
For Each candidate In candidates.childNodes
Set analysis = candidate.selectSingleNode("analysis")
PassedAddress.Value = candidate.selectSingleNode("delivery_line_1").nodeTypedValue
Set components = candidate.selectSingleNode("components")
PassedCity.Value = components.selectSingleNode("city_name").nodeTypedValue
PassedState.Value = components.selectSingleNode("state_abbreviation").nodeTypedValue
PassedZIP.Value = components.selectSingleNode("zipcode").nodeTypedValue & "-" & _
components.selectSingleNode("plus4_code").nodeTypedValue
Set metadata = candidate.selectSingleNode("metadata")
PassedCongressionalDistrict.Value = CInt(metadata.selectSingleNode("congressional_district").nodeTypedValue)
PassedValidated.Value = True
Next
Exit Sub
Case Else ' Multiple candidate addresses...post them and allow the user to select.
DoCmd.SetWarnings False
Set dbs = CurrentDb
If IsTableQuery("temptbl") Then dbs.Execute "DROP TABLE temptbl"
dbs.Execute "CREATE TABLE temptbl (Selected BIT, CandidateAddress CHAR(50), CandidateCity CHAR(25), _
CandidateState CHAR(2), CandidateZIP CHAR(10), CandidateCongressionalDistrict INTEGER, _
MatchCode CHAR(1), Footnotes CHAR(30));"
DoCmd.SetWarnings True
Start = Timer
Do While Timer < Start + 1
DoEvents
Loop
For Each candidate In candidates.childNodes
Set components = candidate.selectSingleNode("components")
AddressToCheck = candidate.selectSingleNode("delivery_line_1").nodeTypedValue
CityToCheck = components.selectSingleNode("city_name").nodeTypedValue
StateToCheck = components.selectSingleNode("state_abbreviation").nodeTypedValue
ZIPToCheck = components.selectSingleNode("zipcode").nodeTypedValue & "-" & _
components.selectSingleNode("plus4_code").nodeTypedValue
Set metadata = candidate.selectSingleNode("metadata")
District = metadata.selectSingleNode("congressional_district").nodeTypedValue
Set analysis = candidate.selectSingleNode("analysis")
MatchCode = analysis.selectSingleNode("dpv_match_code").nodeTypedValue
Footnotes = analysis.selectSingleNode("dpv_footnotes").nodeTypedValue
DoCmd.SetWarnings False
dbs.Execute "INSERT INTO temptbl ( CandidateAddress, CandidateCity, CandidateState, CandidateZIP, _
CandidateCongressionalDistrict, MatchCode, Footnotes ) " & vbCrLf & "SELECT """ & AddressToCheck & _
""" AS Expr1, """ & CityToCheck & """ AS Expr2, """ & StateToCheck & """ AS Expr3, """ & _
ZIPToCheck & """ AS Expr4, " & District & " AS Expr5, """ & MatchCode & """ AS Expr6, """ & _
Footnotes & """ AS Expr7;"
DoCmd.SetWarnings True
Next
DoCmd.OpenForm "frmPeopleAddressMaintenance"
Do Until CurrentProject.AllForms("frmPeopleAddressMaintenance").IsLoaded = False
DoEvents
Loop
HomeForm.SetFocus
If IsTableQuery("temptbl") Then dbs.Execute "DROP TABLE temptbl"
End Select
dbs.Close
Exit Sub
ShowMeError:
MsgBox Err.Description, vbOKOnly, "ERROR!"
End Sub
The error occurs in two specific places:
Under the "Case 1": The error happens immediately after...
PassedCongressionalDistrict.Value = CInt(metadata.selectSingleNode("congressional_district").nodeTypedValue)
...is executed. I have debugged this and verified that the statement executed properly and that the value of the "PassedCongressionalDistrict" object is correct.
Then, under "Case Else": The For loop processes the first item list correctly, but fails with the identified error when beginning processing the second item, even though there is good and legitimate data in the second item.
I hope I've explained this well enough. I just can't seem to figure out (1) how to more fully debug this and (2) why the error occurs as it seems that I have all of my object variables defined properly.
Regards,
Ken
It's almost definitely because (on occasion) there is no child node member named "metadata" in the XML body - so when you try to bind your "metadata" object to the .selectSingleNode() method it returns Nothing. You can always check to make sure that it's actually bound...
'// ...start code snippet...
Set metadata = candidate.selectSingleNode("metadata")
If Not metadata is Nothing Then
PassedCongressionalDistrict.Value = CInt(metadata.selectSingleNode("congressional_district").nodeTypedValue)
End If
PassedValidated.Value = True
'// ...end code snippet...

Microsoft Excel Data Connections - Alter Connection String through VBA

I have a fairly straightforward question. I am trying to find a way to alter and change a connection string for an existing data connection in an excel workbook through VBA (macro code). The main reason I am trying to do this is to find a way to prompt the user that opens up the workbook to enter their credentials (Username/Password) or have a checkbox for Trusted Connection that would be used in the Connection String of those existing data connections.
Right now the Data connections are running off a sample user that I created and that needs to go away in the production version of the workbook. Hope that makes sense?
Is this possible? If yes, could you please give me a sample/example code block? I would really appreciate any suggestions at this point.
I also had this exact same requirement and although the duplicate question Excel macro to change external data query connections - e.g. point from one database to another was useful, I still had to modify it to meet the exact requirements above. I was working with a specific connection, while that answer targeted multiple connections. So, I've included my workings here. Thank you #Rory for his code.
Also thanks to Luke Maxwell for his function to search a string for matching keywords.
Assign this sub to a button or call it when the spreadsheet is opened.
Sub GetConnectionUserPassword()
Dim Username As String, Password As String
Dim ConnectionString As String
Dim MsgTitle As String
MsgTitle = "My Credentials"
If vbOK = MsgBox("You will be asked for your username and password.", vbOKCancel, MsgTitle) Then
Username = InputBox("Username", MsgTitle)
If Username = "" Then GoTo Cancelled
Password = InputBox("Password", MsgTitle)
If Password = "" Then GoTo Cancelled
Else
GoTo Cancelled
End If
ConnectionString = GetConnectionString(Username, Password)
' MsgBox ConnectionString, vbOKOnly
UpdateQueryConnectionString ConnectionString
MsgBox "Credentials Updated", vbOKOnly, MsgTitle
Exit Sub
Cancelled:
MsgBox "Credentials have not been changed.", vbOKOnly, MsgTitle
End Sub
The GetConnectionString function stores the connection string that you insert your username and password into. This one is for an OLEDB connection and is obviously different depending on the requirements of the Provider.
Function GetConnectionString(Username As String, Password As String)
Dim result As Variant
result = "OLEDB;Provider=Your Provider;Data Source=SERVER;Initial Catalog=DATABASE" _
& ";User ID=" & Username & ";Password=" & Password & _
";Persist Security Info=True;Extended Properties=" _
& Chr(34) & "PORT=1706;LOG=ON;CASEINSENSITIVEFIND=ON;INCLUDECALCFIELDS=ON;" & Chr(34)
' MsgBox result, vbOKOnly
GetConnectionString = result
End Function
This code does the job of actually updating a named connection with your new connection string (for an OLEDB connection).
Sub UpdateQueryConnectionString(ConnectionString As String)
Dim cn As WorkbookConnection
Dim oledbCn As OLEDBConnection
Set cn = ThisWorkbook.Connections("Your Connection Name")
Set oledbCn = cn.OLEDBConnection
oledbCn.Connection = ConnectionString
End Sub
Conversely, you can use this function to get whatever the current connection string is.
Function ConnectionString()
Dim Temp As String
Dim cn As WorkbookConnection
Dim oledbCn As OLEDBConnection
Set cn = ThisWorkbook.Connections("Your Connection Name")
Set oledbCn = cn.OLEDBConnection
Temp = oledbCn.Connection
ConnectionString = Temp
End Function
I use this sub to refresh the data when the workbook is opened but it checks that there is a username and password in the connection string before doing the refresh. I just call this sub from the Private Sub Workbook_Open().
Sub RefreshData()
Dim CurrentCredentials As String
Sheets("Sheetname").Unprotect Password:="mypassword"
CurrentCredentials = ConnectionString()
If ListSearch(CurrentCredentials, "None", "") > 0 Then
GetConnectionUserPassword
End If
Application.ScreenUpdating = False
ActiveWorkbook.Connections("My Connection Name").Refresh
Sheets("Sheetname").Protect _
Password:="mypassword", _
UserInterfaceOnly:=True, _
AllowFiltering:=True, _
AllowSorting:=True, _
AllowUsingPivotTables:=True
End Sub
Here is the ListSearch function from Luke. It returns the number of matches it has found.
Function ListSearch(text As String, wordlist As String, seperator As String, Optional caseSensitive As Boolean = False)
Dim intMatches As Integer
Dim res As Variant
Dim arrWords() As String
intMatches = 0
arrWords = Split(wordlist, seperator)
On Error Resume Next
Err.Clear
For Each word In arrWords
If caseSensitive = False Then
res = InStr(LCase(text), LCase(word))
Else
res = InStr(text, word)
End If
If res > 0 Then
intMatches = intMatches + 1
End If
Next word
ListSearch = intMatches
End Function
Finally, if you want to be able to remove the credentials, just assign this sub to a button.
Sub RemoveCredentials()
Dim ConnectionString As String
ConnectionString = GetConnectionString("None", "None")
UpdateQueryConnectionString ConnectionString
MsgBox "Credentials have been removed.", vbOKOnly, "Your Credentials"
End Sub
Hope this helps another person like me that was looking to solve this problem quickly.

Getting the currently logged-in windows user

I found this via google: http://www.mvps.org/access/api/api0008.htm
'******************** Code Start **************************
' This code was originally written by Dev Ashish.
' It is not to be altered or distributed,
' except as part of an application.
' You are free to use it in any application,
' provided the copyright notice is left unchanged.
'
' Code Courtesy of
' Dev Ashish
'
Private Declare Function apiGetUserName Lib "advapi32.dll" Alias _
"GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Function fOSUserName() As String
' Returns the network login name
Dim lngLen As Long, lngX As Long
Dim strUserName As String
strUserName = String$(254, 0)
lngLen = 255
lngX = apiGetUserName(strUserName, lngLen)
If ( lngX > 0 ) Then
fOSUserName = Left$(strUserName, lngLen - 1)
Else
fOSUserName = vbNullString
End If
End Function
'******************** Code End **************************
Is this the best way to do it?
You could also do this:
Set WshNetwork = CreateObject("WScript.Network")
Print WshNetwork.UserName
It also has a UserDomain property and a bunch of other things:
http://msdn.microsoft.com/en-us/library/907chf30(VS.85).aspx
You could also use Environ$ but the method specified by the question is better. Users/Applications can change the environment variables.
I generally use an environ from within VBA as in the following. I haven't had the problems that Ken mentions as possibilities.
Function UserNameWindows() As String
UserNameWindows = VBA.Environ("USERNAME") & "#" & VBA.Environ("USERDOMAIN")
End Function
Lots of alternative methods in other posts, but to answer the question: yes that is the best way to do it. Faster than creating a COM object or WMI if all you want is the username, and available in all versions of Windows from Win95 up.
Alternative way to do that - probably the API you mention is a better way to get username.
For Each strComputer In arrComputers
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)
For Each objItem in colItems
Wscript.Echo "UserName: " & objItem.UserName & " is logged in at computer " & strComputer
Next
there are lots of way to get the current logged user name in WMI.
my way is to get it through the username from process of 'explorer.exe'
because when user login into window, the access of this file according to the current user.
WMI script would be look like this:
Set objWMIService = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strIP & "\root\cimv2")
Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process")
For Each objprocess In colProcessList
colProperties = objprocess.GetOwner(strNameOfUser, strUserDomain)
If objprocess.Name = "explorer.exe" Then
UsrName = strNameOfUser
DmnName = strUserDomain
End If
Next
for more detailcheck the link on :
http://msdn.microsoft.com/en-us/library/aa394599%28v=vs.85%29.aspx