I am attempting to write a program in VB.Net to give to certain staff in my organization. It will let them manage some aspects of AD, but only what they have been delegated access for. This part works.
The part that seems to be puzzling me is how to lock down settings in the program to only be allowed by a certain group (i.e. Domain Admins), but not just Local Administrators (i.e. by using UAC). The settings are currently stored in My.Settings.*
, and are User-scoped settings (so they could be changed at a later date).
Ideally, I'd like a UAC-style dialog to appear, but to authenticate against an Active Directory group, instead of the local system's Administrators group, then the user can change settings. The program will not always be run by a Domain Administrator, but the option to change settings needs to be there if credentials are entered. Is this possible?
It's fairly easy to get the groups for the currently logged on user. There are several different methods, but this is probably the easiest:
UserPrincipal.Current.GetAuthorizationGroups()
That will get all the security groups that the user is a member of, recursively - so if the user is a member of group A, and that group is in group B, you will see group B in this list. (I actually wrote an article where I discussed how that method works: Finding all of a user’s groups)
Then you just check that the group you want is there by looking at the Name
property of the groups in the list.
If you find that's too slow, there is a much faster but not-as-friendly way to do it. The user's authentication token contains all the SIDs of their groups (that's actually what GetAuthorizationGroups()
uses), but it only contains the SIDs. So GetAuthorizationGroups()
will go out to AD and get the properties of each one (one network request per group).
To avoid that network traffic (and time), you can look at the SIDs directly using this:
System.Security.Principal.WindowsIdentity.GetCurrent().Groups
That returns a list of SecurityIdentifier
. If you store the SID of the group you're using, instead of the name, then you can compare it to the Value
of the SecurityIdentifier
and you can do this whole thing without contacting AD at all.
Update: All that will help you determine if the user is in the group you want (Domain Admins?). If you want to give the user the option of putting in different credentials, well... there are several ways you could go about that.
The simplest I think is to create your own dialog that takes the username and password, and use the solution here to restart your application with the new credentials (translated to VB.NET):
Dim proc As System.Diagnostics.Process = New System.Diagnostics.Process()
proc.StartInfo.UseShellExecute = False
proc.StartInfo.FileName = Application.ExecutablePath
proc.StartInfo.Domain = "domainname"
proc.StartInfo.UserName = "username"
'Translate the user-entered String into a SecureString
'If you take the password as a SecureString in the first place
'you won't have to do this
Dim password As String = "user entered password"
Dim ssPwd As System.Security.SecureString = New System.Security.SecureString()
For x As Integer = 0 To password.Length - 1
ssPwd.AppendChar(password(x))
Next
proc.StartInfo.Password = ssPwd
proc.Start()
Application.Exit()
Note that the documentation on the UserName
property says:
If you use the UPN format,
user
@DNS_domain_name
, the Domain property must benull
.
So you may have to check if the user gave you DOMAIN\Username
or Username@domain.com
and assign the UserName
and Domain
properties appropriately.