Finding user name of current logged in user using VB.NET - vb.net

I'm trying to get the user name of the current user. When I log in as Johnny Smith and run my application without administrator privileges it will return me the correct user name, Johnny Smith. But the problem is that when I right click and choose "Run as Administrator", Windows will prompt me with a login screen for the administrator and after login my application returns user name admin, not the user which is logged in currently.
I have tried:
strUserLabel.Text = Environment.UserName
Also
Dim WSHNetwork = CreateObject("WScript.Network")
Dim strUser = ""
While strUser = ""
strUser = WSHNetwork.Username
End While
strUserLabel.Text = strUser
Both return me the administrator user name when prompted as administrator.

In the MSDN documentation, I discovered they changed the definition of property Environment.UserName.
Before .NET 3
Gets the user name of the person who started the current thread.
Starting from version 3
Gets the user name of the person who is currently logged on to the Windows operating system

I think the accepted answer above is a VERY resource intensive way to find a username. It has nested loops with hundreds of items. In my 8GP RAM PC this takes 2+ seconds!
How about:
Username: SystemInformation.Username, and
DomainName: Environment.UserDomainName
Tested in VS2017

I have figured it out. I used this function which will determine which process which the user is using. In my code I defined that look for username of the explorer.exe process.
Function GetUserName() As String
Dim selectQuery As Management.SelectQuery = New Management.SelectQuery("Win32_Process")
Dim searcher As Management.ManagementObjectSearcher = New Management.ManagementObjectSearcher(selectQuery)
Dim y As System.Management.ManagementObjectCollection
y = searcher.Get
For Each proc As Management.ManagementObject In y
Dim s(1) As String
proc.InvokeMethod("GetOwner", CType(s, Object()))
Dim n As String = proc("Name").ToString()
If n = "explorer.exe" Then
Return s(0)
End If
Next
End Function
Index of 0 will return username
Index of 1 will return domain name of user

SystemInformation.Username doesn't work for certain applications. In my case, code is being run as System but explorer.exe is being run as Daniel. SystemInformation.Username reports System.

if using Identity
Dim UserEmail As String = Context.User.Identity.Name.ToString

Related

Active Directory: Add New User C# / VB

I browsed other questions and couldn't find the answer.
What is the appropriate way to add a new user to a active directory specific group when the "current user" running the application does not have access rights to add new users?
I am currently authenticating as the domain Administrator to do it, but obviously that is a security no-no and I don't want the Administrator user's credentials hard-codeded in my code.
I assume the answers is not in my code itself but that I have to create a user in the Active Directory that has privileges to add users.
So my question is a combination of a AD and a Coding question:
1) How do I create a AD user that ONLY has access rights to add users to specific OU and CN security group?
2) Is their any way possible to not hard-code the password for this user?
Or... am I going about this entirely wrong?
Here is my current VB.NET code:
Public Function CreateUser(ByVal UserName As String, ByVal Password As String, ByVal DisplayName As String) As Boolean
Try
'Dim catalog As Catalog = New Catalog()
Dim de As DirectoryEntry = New DirectoryEntry()
de.Path = "LDAP://OU=TS2xUsers,DC=dc,DC=example,DC=com"
de.AuthenticationType = AuthenticationTypes.Secure
de.Username = "Administrator"
de.Password = "foopassword"
'1. Create user accountd
Dim users As DirectoryEntries = de.Children
Dim newuser As DirectoryEntry = users.Add("CN=" & DisplayName, "user")
newuser.Properties("givenname").Value = DisplayName
newuser.Properties("name").Value = DisplayName
newuser.Properties("displayName").Value = DisplayName
newuser.Properties("SAMAccountName").Value = UserName
newuser.Properties("userPrincipalName").Value = UserName & "#dc.example.com"
'newuser.Properties("OU").Value = "TS2xUsers"
newuser.CommitChanges()
Dim ret As Object = newuser.Invoke("SetPassword", Password)
newuser.CommitChanges()
Dim exp As Integer = CInt(newuser.Properties("userAccountControl").Value)
exp = exp And Not &H2 'enable acccount
exp = exp Or &H10000 'dont expire password
newuser.Properties("userAccountControl").Value = exp
newuser.CommitChanges()
''' 5. Add user account to groups
If MakeTSUser(newuser) = False Then
Return False
newuser.Close()
de.Close()
End If
newuser.Close()
de.Close()
Return True
Catch ex As Exception
MsgBox("Failed to create user due to the following reason: " & ex.Message, MsgBoxStyle.Critical)
Return False
End Try
End Function
Private Function MakeTSUser(ByVal deUser As DirectoryEntry) As Boolean
Try
Dim deRBGroup As New DirectoryEntry
deRBGroup.Path = "LDAP://CN=TSUsers,CN=Builtin,DC=dc,DC=example,DC=com"
deRBGroup.AuthenticationType = AuthenticationTypes.Secure
deRBGroup.Username = "Administrator"
deRBGroup.Password = "foopassword"
Dim deDomainUsers As New DirectoryEntry
deDomainUsers.Path = "LDAP://CN=Domain Users,CN=Users,DC=dc,DC=example,DC=com"
deDomainUsers.AuthenticationType = AuthenticationTypes.Secure
deDomainUsers.Username = "Administrator"
deDomainUsers.Password = "foopassword"
Dim primaryGroupToken As Object = Nothing
deRBGroup.Invoke("Add", New Object() {deUser.Path.ToString()})
deRBGroup.CommitChanges()
'Get Primary Group Token of MYGROUP
deRBGroup.Invoke("GetInfoEx", New Object() {New Object() {"primaryGroupToken"}, 0})
primaryGroupToken = deRBGroup.Properties("primaryGroupToken").Value
'Assign Primary Group Token value of MYROUP to the User's PrimaryGroupID
deUser.Properties("primaryGroupID").Value = primaryGroupToken
deUser.CommitChanges()
'Remove the User from "Domain Users" group
deDomainUsers.Invoke("Remove", New Object() {deUser.Path.ToString()})
deDomainUsers.CommitChanges()
Return True
Catch ex As Exception
Return False
End Try
End Function
What is the appropriate way to add a new user to a active directory specific group when the "current user" running the application does not have access rights to add new users?
Run the app as a user that does have those permissions... but you already know this:
I assume the answers is ... to create a user in the Active Directory that has privileges to add users.
Is their any way possible to not hard-code the password for this user?
Good question. One way to do this by setting up a Windows Scheduled Task. You can setup a task to run on demand, with no actual schedule, and to run as any user you want, including your new service account. You will have to put in the password for that user when you setup the task, but only when you first setup the task, and the actual password won't be stored... only the authentication token, which is not transferable. Then your desktop application can trigger the task to start.
Another way to do this is to grant the unprivileged HR or helpdesk users who run your app permissions to create new users just within your specific OU. In the case of a web app, you can also do this for the user account that runs the web site, as long as you have appropriate logging and auditing in place.
The other option is to create a separate service to create the users, which runs as the appropriate user. This could be a web service, where less-privileged authenticated users can post the new account information, or it could be a Windows service doing something like watching a file share, where your unprivileged users then have the ability or write to the file share. Then your program can know how to call the service or write the correct file formats into the share.

Get First & Last Name of All AD Accounts

I'm looking for the quickest and easiest way to retrieve all Active Directory account's first & last names. The intention is to have a string list which contains all names from the Active Directory, for the purpose of auto-completion in a textbox in a small Visual Basic application used for unlocking accounts.
Usage Scenario:
On form load the application generates the list of names from the AD. I'm expecting this to take around 10-15 seconds as there's 4,500 accounts on the AD. Each name is added to a string list for use with the auto completion.
The user types the name Garry into textbox1 and the auto complete suggests all Garry's in the AD, by using a string list. I know how to do this easily, I just dont know how to populate the list with user's names efficiently.
There's a lot of samples on accessing the AD, but none which show how to loop through it. I thought asking on here would help both myself and other users in a similar usage case.
The code I have so far for accessing a single account is shown below, however I need to loop through all AD accounts and retrieve their First & Last names.
Current Code to access single account:
'Domain is declared with the LDAP path
'UserName is declared with textbox1.text value
Dim ADEntry As New DirectoryServices.DirectoryEntry("LDAP://" & Domain)
Dim ADSearch As New System.DirectoryServices.DirectorySearcher(ADEntry)
ADSearch.Filter = ("(samAccountName=" & UserName & ")")
ADSearch.SearchScope = System.DirectoryServices.SearchScope.Subtree
Dim UserFound As System.DirectoryServices.SearchResult = ADSearch.FindOne()
If Not IsNothing(UserFound) Then
Log.AppendLine("Account found, loading checks...")
Dim Attrib As String = "msDS-User-Account-Control-Computed"
Dim User As System.DirectoryServices.DirectoryEntry
User = UserFound.GetDirectoryEntry()
User.RefreshCache(New String() {Attrib})
'Display user account details
txtLogin.Text = User.Properties("userPrincipalName").ToString
txtName.Text = User.Properties("givenName").ToString & " " & User.Properties("sn").ToString
else
'User not found
end if
Any help would be much appreciated, even in C#.
You can use the same ADEntry variable as above and do something like this. This only adds a user to the list if they have both a first and last name.
Dim listNames As New AutoCompleteStringCollection
Using ADSearch As New DirectoryServices.DirectorySearcher(ADEntry, "(&(objectCategory=person)(objectClass=user))", {"givenName", "sn"}, DirectoryServices.SearchScope.Subtree)
For Each user As DirectoryServices.SearchResult In ADSearch.FindAll
Try
listNames.Add(user.GetDirectoryEntry.Properties("givenName").Value.ToString + " " + user.GetDirectoryEntry.Properties("sn").Value.ToString)
Catch ex As Exception
End Try
Next
End Using
With TextBox1
.AutoCompleteCustomSource = listNames
.AutoCompleteMode = AutoCompleteMode.SuggestAppend
.AutoCompleteSource = AutoCompleteSource.CustomSource
End With

how to identify application instance name along with its user in vb.net 3.5?

I am using the below code to identify instance of the application also if we need to check that which user is using this application what will be the code for it?
Function PrevInstance() As Boolean
If Ubound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)) > 0 Then
Return True
Else
Return False
End If
End Function
My requirement is if same user tries to open the application then it should display pop up message like "application already opened".
Please advice...
abhay
Thanks a lot guys.... solution on my problem as follows:
> Function PrevInstance() As Boolean
> If UBound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName))
> > 0 Then
> Dim CurUser As Boolean = GetProcessOwner(Diagnostics.Process.GetCurrentProcess.ProcessName)
> Return CurUser
> Else
> Return False
> End If
> End Function
Function GetProcessOwner(ByVal ProcessName As String) As Boolean
Dim boolVal As Boolean
Dim CurUserName As String
Dim CountInstance As Integer
CountInstance = 0
CurUserName = System.Environment.UserName
Dim selectQuery As SelectQuery = New SelectQuery("Select * from Win32_Process Where Name = '" + ProcessName + ".exe' ")
Dim searcher As ManagementObjectSearcher = New ManagementObjectSearcher(selectQuery)
Dim y As System.Management.ManagementObjectCollection
y = searcher.Get
For Each proc As ManagementObject In y
Dim s(1) As String
proc.InvokeMethod("GetOwner", CType(s, Object()))
Dim n As String = proc("Name").ToString()
If n = ProcessName & ".exe" Then
If s(0) = CurUserName Then
CountInstance = CountInstance + 1
If CountInstance > 1 Then
boolVal = True
End If
End If
End If
Next
Return boolVal
End Function
I have called PrevInstance() in my Form_Load() and its working perfectly.
PrevInstance Property
Returns a value indicating whether a previous instance of an application is already running.
Syntax
object.PrevInstance
The object placeholder represents an object expression that evaluates to an object in the Applies To list.
Remarks
You can use this property in a Load event procedure to specify whether a user is already running an instance of an application. Depending on the application, you might want only one instance running in the Microsoft Windows operating environment at a time.
Note Since a computer running Windows NT can support multiple desktops, if you use a component designed to work with distributed COM, it can result in the following scenario:
A client program in a user desktop requests one of the objects the component provides. Because the component is physically located on the same machine, the component is started in the user desktop.
Subsequently, a client program on another computer uses distributed COM to request one of the objects the component provides. A second instance of the component is started, in a system desktop.
There are now two instances of the component running on the same NT computer, in different desktops.
This scenario is not a problem unless the author of the component has placed a test for App.PrevInstance in the startup code for the component to prevent multiple copies of the component from running on the same computer. In this case, the remote component creation will fail.
Send feedback to MSDN.Look here for MSDN Online resources.
Session
The above tells you if it's already running. This tells you what session. An interactive user is always session1 on Vista and later. 0 for XP and earlier.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
msgbox objitem.name & " PID=" & objItem.ProcessID & " SessionID=" & objitem.sessionid
Next
PS
The rules for single instance programs is just before you exit you switch windows to the previous instance.
PPS
Due to problems introduced by 32 bit computing previnstance (Win32's one rather than VB's) becomes less meaningful. The common way to do this now is to open and lock a file on startup (Windows also has memory constructs you can use such as mailslots, pipes, etc). If a program can't lock then another is already running.

Using DirectoryEntry.Invoke("SetPassword", ...) to set initial AD account password, I get "RPC server is unavailable" error

The company I'm working in has a web service that can create new Active Directory accounts based on information that is typed in e.g. username, firstname, lastname, OU1, OU2, etc.
This web service has been working fine on Windows Server 2003. Now I'm working on moving the web service to 2008 R2 servers. However a certain functionality doesn't work anymore, which is when it tries to set a random initial password to the newly created account.
The exception that is thrown is a TargetInvocationException containing an inner exception of COMException with a message of "The RPC Server is unavailable".
Imports System.DirectoryServices
Dim ldapPath As String = "..." 'assume valid LDAP path
Dim objContainer As DirectoryEntry = New DirectoryEntry(ldapPath, vbNullString, vbNullString, AuthenticationTypes.Secure)
objUser = objContainer.Children.Add("cn=" & Username.Trim, "user")
'sets objUser.Properties e.g. givenName, displayName, userPrincipalName, samAccountName, etc. Not important...
objUser.CommitChanges()
strPassword = RandomPassword() 'function that generates random password
objUser.Invoke("SetPassword", New Object() {strPassword})
objUser.Properties("useraccountcontrol").Value = "512" ' set as normal account
objUser.CommitChanges()
'and so on...
The error happens on the line that says:
objUser.Invoke("SetPassword", New Object() {strPassword})
Strangely the account creation itself works and I can see the new user from Active Directory Users and Computers.
I have consulted a few different people who manage the security of the DCs and the web servers, they don't really know why...
In the end I figured out a different way of setting the password, which is using the System.DirectoryServices.AccountManagement library.
So the code becomes something like this:
Imports System.DirectoryServices
Imports System.DirectoryServices.AccountManagement
Dim ldapPath As String = "..." 'assume valid LDAP path
Dim objContainer As DirectoryEntry = New DirectoryEntry(ldapPath, vbNullString, vbNullString, AuthenticationTypes.Secure)
Dim objUser As DirectoryEntry = objContainer.Children.Add("cn=" & Username.Trim, "user")
'sets objUser.Properties e.g. givenName, displayName, userPrincipalName, samAccountName, etc. Not important...
objUser.CommitChanges()
Dim domainContext As PrincipalContext = New PrincipalContext(ContextType.Domain, ...)
Dim user As UserPrincipal = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, Trim(Username))
strPassword = RandomPassword() 'function that generates random password
user.SetPassword(strPassword)
user.Enabled = True 'setting these two properties
user.PasswordNotRequired = False 'result in useraccountcontrol value = 512 (normal account)
user.Save()
'and so on...
I'm mixing old code with new code in this one, but it appears to be working really well on the new servers. One thing to note is that sometimes the UserPrincipal.FindByIdentity call initially returns Nothing, I believe due to delay in the account creation or with the replication. So I have to make sure that the user object is not Nothing by trying FindByIdentity multiple times until I get the object.
Despite finding a solution (more like a way around) to the problem, still I'm confused as to why the old code does not work on the new servers, but the new one does. The error is very generic and searching the internet for clues have resulted in nothing but more confusion.
Would really appreciate if experts out there can shed some lights or even comment on the way my new code looks, any problems?
Thanks in advance.

ASP.NET - Deleting Computer Accounts Within AD

I'm building out a decommissioning application that will allow an individual to provide an computer name and the utility will go out and purge the computer record from various locations. I'm running into a problem when attempting to delete a computer account from Active Directory. I'm impersonating a service account that only has rights to "Delete All Child Objects" within a particular OU structure. The code below works if I run it with my domain admin account; however fails with an "Access Denied" when I run it with the impersonated service account. I have verified that the permissions are correct within AD as I can launch Active Directory Users and Computers using a "runas" and providing the service account credentials and I can delete computer objects perfectly fine.
Wondering if anyone has run into this before or has a different way to code this while still utilizing my current OU permissions. My gut tells me the "DeleteTree" method is doing more then just deleting the object.
Any assistance would be appreciated.
Sub Main()
Dim strAsset As String = "computer9002"
Dim strADUsername As String = "serviceaccount#domain.com"
Dim strADPassword As String = "password"
Dim strADDomainController As String = "domaincontroller.domain.com"
Dim objDirectoryEntry As New System.DirectoryServices.DirectoryEntry
Dim objDirectorySearcher As New System.DirectoryServices.DirectorySearcher(objDirectoryEntry)
Dim Result As System.DirectoryServices.SearchResult
Dim strLDAPPath As String = ""
Try
objDirectoryEntry.Path = "LDAP://" & strADDomainController
objDirectoryEntry.Username = strADUsername
objDirectoryEntry.Password = strADPassword
objDirectorySearcher.SearchScope = DirectoryServices.SearchScope.Subtree
objDirectorySearcher.Filter = "(&(ObjectClass=Computer)(CN=" & strAsset & "))"
Dim intRecords As Integer = 0
For Each Result In objDirectorySearcher.FindAll
Console.WriteLine(Result.Path)
Diagnostics.Debug.WriteLine("DN: " & Result.Path)
Dim objComputer As System.DirectoryServices.DirectoryEntry = Result.GetDirectoryEntry()
objComputer.DeleteTree()
objComputer.CommitChanges()
intRecords += 1
Next
If intRecords = 0 Then
Console.WriteLine("No Hosts Found")
End If
Catch e As System.Exception
Console.WriteLine("RESULT: " & e.Message)
End Try
End Sub
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
MSDN docs on System.DirectoryServices.AccountManagement
Basically, you can define a domain context and easily find users and/or groups in AD:
' set up domain context
Dim ctx As New PrincipalContext(ContextType.Domain, "DOMAIN", strADUsername, strADPassword)
' find a computer
Dim computerToDelete As ComputerPrincipal = ComputerPrincipal.FindByIdentity(ctx, strAsset)
If computerToDelete IsNot Nothing Then
' delete the computer, if found
computerToDelete.Delete()
End If
The new S.DS.AM makes it really easy to play around with users and groups in AD!
Delete Tree is different from delete. You're going to need the Delete Subtree permission on the child computer objects for this to work.