Checking Someone is a Member Of One of Many Groups Using Partial Group Name - vb.net

I'm a bit stuck trying to work something into my code.
What I'm looking to do is to work out whether someone is a member of any one of a collection of groups. I'm not worried about which group specifically, I only want to know:
"Is user "X" a member of at least one of this collection of groups?"
The good news is, ALL these group names start in the same way:
Google-FullAccess
Google-RestrictedAccess
Google-MailOnly
Google-Enterprise etc.
Here's what I'm using to check for a specific group:
Dim ctx As DirectoryServices.AccountManagement.PrincipalContext = New DirectoryServices.AccountManagement.PrincipalContext(DirectoryServices.AccountManagement.ContextType.Domain, "net.mydomain.co.uk")
Dim user As DirectoryServices.AccountManagement.UserPrincipal = DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(ctx, tbxuserID.Text)
Dim googleFull As DirectoryServices.AccountManagement.GroupPrincipal = DirectoryServices.AccountManagement.GroupPrincipal.FindByIdentity(ctx, "Google-FullAccess")
If user.IsMemberOf(googleFull) Then
GoogleAccess = 1
GoTo Proceed
End If
I then repeat this block of code to check for the next group and so on.
Is there a way I can adapt this to check for any group starting with "Google-"? Here's what I'd like to do but obviously doesn't work:
Dim googleCheck As DirectoryServices.AccountManagement.GroupPrincipal = DirectoryServices.AccountManagement.GroupPrincipal.FindByIdentity(ctx, "Google-*")
Help much appreciated!

I found the solution! The following works for me (after working out and storing the DistinguishedName in a String variable from a previous query - I also declare GoogleCheck as a Boolean variable beforehand):
Dim rootEntry As DirectoryServices.DirectoryEntry = New DirectoryServices.DirectoryEntry("LDAP://DC=net,DC=mydomain,DC=co,DC=uk")
Dim srch As DirectoryServices.DirectorySearcher = New DirectoryServices.DirectorySearcher(rootEntry)
srch.SearchScope = DirectoryServices.SearchScope.Subtree
srch.Filter = "(&(CN=Google-*)(objectCategory=group)(member=" + DistinguishedName + "))"
Dim res = srch.FindOne()
If res IsNot Nothing Then
GoogleCheck = True
Else
GoogleCheck = False
End If

Related

How to get the value of a column using a dataview in vb.net

I have a list of records and for Employee R1005, I need to check if that Employee has been Enabled for login alert (i.e EnableLoginAlert = Yes), then a button will be displayed.
CompanyID EmployeeNo EnableLoginAlert
10046 R1005 Yes
20041 Ajax12 No
47021 Drek Yes
I have tried the below codes:
If dCompanyDetails.Tables(0).Rows.Count > 0 Then
Dim dataView As DataView = dCompanyDetails.Tables(0).DefaultView
dataView.RowFilter = "EmployeeNo = '" & strEmployeeNumber & "'"
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
If svalue = "No" Then
AlertButton.Visible = False
ElseIf svalue = "Yes" Then
{
//Do something else
}
End If
End If
If you are going to use a DataView then use it. This:
Dim svalue As String = dataView.Table.Rows(0).ItemArray(0).ToString()
is simply going back to the DataTable and using it, ignoring the DataView. The DataView contains DataRowView objects so get the one you need and use it. It is similar to a DataRow and you can use it the same way in this case:
Dim enableLoginAlert = CStr(dataView(0)("EnableLoginAlert")) = "Yes"
Now you have an actual Boolean that represents the state you want.
That's not how you should do it though. Generally speaking, you would use a DataView when you want to bind data. In fact, if you bind a DataTable then the data you see in the UI actually comes from the DefaultView. That's why you can filter and sort it. In this case, there are better options.
If you want to find a row by its primary key then the Rows collection of a DataTable has a Find method, e.g.
Dim row = dCompanyDetails.Tables(0).Rows.Find(strEmployeeNumber)
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you're searching by other than the primary key, the DataTable itself has a Select method. Because multiple rows may match, it returns an array, so you need to get the row out of that, e.g.
Dim row = dCompanyDetails.Tables(0).Select($"EmployeeNo = '{strEmployeeNumber}'").First()
Dim enableLoginAlert = CStr(row("EnableLoginAlert")) = "Yes"
If you want to look up a single row it's perhaps easiest to use LINQ:
Dim row = dCompanyDetails.Tables(0).Rows.Cast(Of DataRow).AsQueryable().FirstOrDefault(Function(r) r("EmployeeNo").ToString() = strEmployeeNumber)
If row IsNot Nothing AndAlso row("EnableLoginAlert").ToString() = "Yes" Then
...
..though I'd be the first to claim that using LINQ on base DataTables is very verbose, because of the Cast/AsQueryable. I'd use strongly typed DataTables (in a dataset); if you were to convert your code to using strongly typed tables it would look like:
Dim r = someDataSet.AProperTableName.FirstOrDefault(Function(r) r.EmployeeNo = strEmployeeNumber)
If r?.EnableLoginALert = "Yes" Then
...
...using strongly typed datatables is much less messy..
nb: You need to Imports System.Linq for these to work
That LINQ is the same thing as:
For Each r as DataRow in dCompanyDetails.Tables(0)
If r("EmployeeNo").ToString() = "R1005" AndAlso r("EnableLoginAlert").ToString() = "Yes" Then
...
You also have the option of using DataTable.Select (not a LINQ thing, though LINQ has a Select too)
Dim matchingRows = dCompanyDetails.Tables(0).Select($"[EmployeeNo] = '{strEmployeeNumber}'")
If matchingrows.Count > 0 AndAlso matchingRows(0)("EnableLoginAlert").ToString() = "Yes"

Searching Active Directory for Members of Specific Group Fails When User Has No Group

the following code is supposed to run through all users in active directory and find everyone in a specific group. Once it gets the users, it will add them to a datatable which will be the source of a gridview for a final export to Excel.
However, while running and stepping through, I've noticed it gets stopped on a certain user with no group. I tried adding a conditional statement to skip that instance, but it isn't working, the code still stops running when it finds the user with no group.
Can someone show me what I could be doing differently?
Caveat: This is from a legacy site that I inherited, knowing very little about active directory and being a novice vb coder.
Dim entry As DirectoryEntry = New DirectoryEntry("LDAP://DOMAIN.EDU", "USERNAME", "PASSWORD", AuthenticationTypes.Secure)
Dim search As DirectorySearcher = New DirectorySearcher(entry) With {
.Filter = "(&(objectCategory=User)(objectClass=person))",
.PageSize = 4000
}
search.PropertiesToLoad.Add("userPrincipalName").ToString
search.PropertiesToLoad.Add("name").ToString
Dim mySearchResultColl As SearchResultCollection = search.FindAll
Dim results As DataTable = New DataTable
results.Columns.Add("User ID")
results.Columns.Add("Full Name")
Dim CurrRow = 0
For Each sr As SearchResult In mySearchResultColl
Dim dr As DataRow = results.NewRow
Dim de As DirectoryEntry = sr.GetDirectoryEntry
!!!! line below is the problem !!!!
If de.Properties("memberOf") IsNot Nothing AndAlso de.Properties("memberOf").Value.ToString = "CN=MYGROUP,OU=Security Groups,OU=Students,DC=DOMAIN,DC=edu" Then
dr("User ID") = de.Properties("userPrincipalName").Value
dr("Full Name") = de.Properties("name").Value
results.Rows.Add(dr)
de.Close
End If
Next
gvNot.DataSource = results
gvNot.DataBind()
gvNot.Visible = True
gvAgreed.Visible = False
ExportToExcel("notagreed", gvNot)
I'm not sure what you mean by "gets stopped", but you can change this up so it performs better and fixes any issues you have.
This loop will take forever since you have to inspect every user on the domain. If you only want users in a specific group, then you can speed this up by only asking for members of the group. You do that by adding a condition in the query for the memberOf attribute. This way, you only get results you care about.
(Note that if you have more than one domain in your forest, using memberOf to find members may not work the way you want. I wrote about that here. But if you only have one domain, then it'll be fine.)
In the loop, don't create a DirectoryEntry for each result, since that will force it to go back out to AD and request the attributes for the object again, even though you already got what you needed in the search results. So use the values in the SearchResult object instead.
The documentation for SearchResultCollection says:
Due to implementation restrictions, the SearchResultCollection class cannot release all of its unmanaged resources when it is garbage collected. To prevent a memory leak, you must call the Dispose method when the SearchResultCollection object is no longer needed.
So you should put that in a Using clause.
I'm also not sure why you're calling ToString on PropertiesToLoad.Add when you're not using the return value. You can just remove that.
Here it is all together:
Dim entry As DirectoryEntry = New DirectoryEntry("LDAP://DOMAIN.EDU", "USERNAME", "PASSWORD", AuthenticationTypes.Secure)
Dim search As DirectorySearcher = New DirectorySearcher(entry) With {
.Filter = "(&(objectCategory=User)(objectClass=person)(memberOf=CN=MYGROUP,OU=Security Groups,OU=Students,DC=DOMAIN,DC=edu))",
.PageSize = 4000
}
search.PropertiesToLoad.Add("userPrincipalName")
search.PropertiesToLoad.Add("name")
Dim results As DataTable = New DataTable
results.Columns.Add("User ID")
results.Columns.Add("Full Name")
Dim CurrRow = 0
Using mySearchResultColl As SearchResultCollection = search.FindAll
For Each sr As SearchResult In mySearchResultColl
Dim dr As DataRow = results.NewRow
dr("User ID") = sr.Properties("userPrincipalName")(0)
dr("Full Name") = sr.Properties("name")(0)
results.Rows.Add(dr)
Next
End Using
gvNot.DataSource = results
gvNot.DataBind()
gvNot.Visible = True
gvAgreed.Visible = False
ExportToExcel("notagreed", gvNot)
I don't see you using CurrRow anywhere, but I left it in case you're using it in other code you didn't show here.
Try using String.Equals function with StringComparer.OrdinalIgnoreCase parameter instead of using equals operator.
Also what's the error you are getting?

Determine if computer is in AD group

I am looking to determine if a computer is a member of an AD group or even getting all computers that are members of an AD group using VB.net. I have found several examples for checking to see if a user is a member of a group but none for checking for computers. I was hoping to convert this function to search for a computer in the group but I was unsuccessful. Any assistance would be appreciated. Thank you in advance.
Function IsInGroup(GroupName) As Boolean
Dim MyIdentity As System.Security.Principal.WindowsIdentity = System.Security.Principal.WindowsIdentity.GetCurrent()
Dim MyPrincipal As System.Security.Principal.WindowsPrincipal = New System.Security.Principal.WindowsPrincipal(MyIdentity)
Return MyPrincipal.IsInRole(GroupName)
End Function
Try something like this
Function IsInGroup(PCName As String, groupName As String) As Boolean
Dim vUsuario As New NTAccount(PCName & "$")
Dim sid As SecurityIdentifier = vUsuario.Translate(GetType(SecurityIdentifier))
Using vRootDSE As New DirectoryEntry("LDAP://rootDSE")
Using vSearcher As New DirectorySearcher(New DirectoryEntry("LDAP://" + CStr(vRootDSE.Properties("defaultNamingContext")(0))), "(objectSID=" & sid.ToString() & ")", New String() {"memberOf"}, SearchScope.Subtree)
Dim src As SearchResultCollection = vSearcher.FindAll()
Dim memberOf As ResultPropertyValueCollection = src(0).Properties("memberOf")
For i As Integer = 0 To memberOf.Count - 1
'Debug.Print(memberOf(i).ToString())
' I don't really like this approach, but it's quick to write ;)
If memberOf(i).ToString().Contains("=" & groupName & ",") Then
Return True
End If
Next
End Using
End Using
Return False
End Function
If you're looking for the CURRENT PC then it's a bit easier.
Function Is_CurrentPC_InADGroup(groupName As String) As Boolean
if groupName = "" then Return True
Using context = New PrincipalContext(ContextType.Domain, Environment.GetEnvironmentVariable("USERDOMAIN"))
Dim principal = ComputerPrincipal.FindByIdentity(context, Environment.MachineName)
Dim groups = principal.GetGroups()
For Each group In groups
If group.ToString = groupName Then Return True
Next
End Using
Return False
End Function

Why won't my variable set to an instance of an object?

The program is a booking system (amongst other things) for a holiday letting company. I am working on the screen where you can see properties and ammend them or add more (etc)
Okay so It works fine in my other cases, but this one it just doesn't want to accept...I expect it's something stupid. Basically In the initial loading of the entire program I filled the Data Tables with the relevant info and then accessed them when needs be, in this case I am in the Form Properties and want to access Bookings (Which were made in FrmBookings) to see when the property is next booked to have guests in.
Dim Intcounter As Integer = 0
Dim NumberBookingRecords As Integer = BookingsNumRecs
Dim PropertyName As String
Dim PropertyFromBookings As String
Do
PropertyName = DTProperties(Intcounter)("Property Name").ToString
PropertyFromBookings = (DTBookings(NumberBookingRecords)("Property").ToString)
If PropertyName = PropertyFromBookings Then
lblDateOfArrival.Text = (DTBookings(NumberBookingRecords)("Arrival").ToString)
Intcounter = Intcounter + 1
Else
If Not NumberBookingRecords = 0 Then
NumberBookingRecords = NumberBookingRecords - 1
Else
End If
End If
Loop Until Intcounter >= intNumPropertyRecs
However when I get to PropertyFromBookings = (DTBookings(NumberBookingRecords)("Property").ToString)
it tells me that it could not be set to an instance of an object...no matter what I try an access from DTBookings I get the same response.
This is in the initial load form at the opening of the program
Dim FSBookings As New FileStream(strFileNameBookings, FileMode.OpenOrCreate, FileAccess.Read)
Application.DoEvents()
If FileLen(strFileNameBookings) > 0 Then
DTBookings.ReadXmlSchema(strFileNameBookings)
DTBookings.ReadXml(strFileNameBookings)
BookingsNumRecs = DTBookings.Rows.Count
intCurrRec = 1
Else
End If
FSBookings.Close()
blnStopAuto = True
blnStopAuto = False
Based on your code sample, DTBookings() is a function call. There are two possibilties here. Either:
The result of that function is Nothing, and when you try to use a Nothing as if there were an actual object there, (in this case, when trying to look up the ("Property") indexer) you'll get that exception, or ...
The result of the ("Property") index returns Nothing, in which case you'll get that exception when you try to call the .ToString() method.

VB.NET Array/Hashtable Issue

I am trying to make an array of hashtables. I don't know if this is the best solution, in PHP I would just do a multi-dim array, but it's not so easy in .NET. I am pretty new o VB, so if there is a better solution for this please explain.
I have 2 emails fields for a contact, and a contact can have many. I just want to load in the first two on the edit page for editing, whatever they may be.
Private Sub loadPrimaryContactEmails(ByVal db As CRMDataDataContext, ByVal contactID As Guid)
Dim q = (From ce In db.ContactEmails Where ce.ContactID = contactID Select ce).Take(2)
Dim Emails As Array
Dim count = 0
For Each email In q
Emails(count) = New Hashtable
Emails(count).Add("email", email.Email)
Emails(count).Add("label", email.Label)
Emails(count).Add("id", email.ContactEmailID)
count = count + 1
Next
txtPCEmail1.Text = Emails(0).Item("email")
txtPCEmail1Label.Text = Emails(0).Item("label")
lblEmail1ID.Text = Emails(0).Item("id")
txtPCEmail2.Text = Emails(1).Item("email")
txtPCEmail2Label.Text = Emails(1).Item("label")
lblEmail2ID.Text = Emails(1).Item("id")
End Sub
I get the error the first time I try to reference my array:
txtPCEmail1.Text = Emails(0).Item("email")
The error is:
Object variable or With block variable not set.
It builds, so I thought it might work. I can't just loop through my datasource because I have to explicitly set textbox fields. Is there a better way to go about doing this? Or is there a way to make an array of hashtables work?
EDIT - here is the good code:
So I went w/ the HybridDictionary...
Private Sub loadPrimaryContactEmails(ByVal db As CRMDataDataContext, ByVal contactID As Guid)
Dim q = (From ce In db.ContactEmails Where ce.ContactID = contactID Select ce).Take(2)
Dim Emails As New HybridDictionary()
Dim count = 1
For Each email In q
Emails.Add("email" + NCStr(count), email.Email)
Emails.Add("label" + NCStr(count), email.Label)
Emails.Add("id" + NCStr(count), email.ContactEmailID)
count = count + 1
Next
txtPCEmail1.Text = Emails("email1")
txtPCEmail1Label.Text = Emails("label1")
lblEmail1ID.Text = Emails("id1")
txtPCEmail2.Text = Emails("email2")
txtPCEmail2Label.Text = Emails("label2")
lblEmail2ID.Text = Emails("id2")
End Sub
SO yeah, kind of a hack, but I don't feel like I should have to making special methods just to load some data into a dictionary or array or whatever.
Arrays in VB.NET are different than in PHP. You will need to define the size of your array before attempting to set elements of the array.
Better yet, consider using the generic List<T> collection.
Yes Phil is right you haven't specified the Initial Size of the Array.
And as suggested by him Use generic list or I would recommend
"System.Collections.Specialized.StringCollection" Class or "System.Collections.Specialized.HybridDictionary" class
Build the hashtable first and then build the array.
Dim hash As New Hashtable()
hash.Add("Header", shortModel)
hash.Add("SpecInfo", specinfo)
hash.Add("SerialNumber", serie & "-L")
hash.Add("SerialNumber2", serie)
hash.Add("seriel", serie & "-L")
hash.Add("serie", serie)
hash.Add("Product", modelBase)
hash.Add("varBC", bc)
hash.Add("box_id", boxId.Substring(4).ToString)
Dim dt As DataTable = DbUtil.GetCursor("SFISM4.PKG_AGENCY.sp_get_print_param", {New OracleParameter("in_serie", "3CE5151ZW4")})
For Each row As DataRow In dt.Rows
hash.Add(row("NAME"), row("VALUE"))
Next
Dim mArray(hash.Count() - 1, 1) As String
Dim i As Integer = 0
For Each row As DictionaryEntry In hash
mArray(i, 0) = row.Key.ToString()
mArray(i, 1) = row.Value.ToString()
i = i + 1
Next